Files
third_party_mesa3d/src/mesa/tnl/t_save_loopback.c

304 lines
8.1 KiB
C

/*
* Mesa 3-D graphics library
* Version: 5.1
*
* Copyright (C) 1999-2003 Brian Paul All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Author:
* Keith Whitwell <keith@tungstengraphics.com>
*/
#include "glheader.h"
#include "context.h"
#include "enums.h"
#include "glapi.h"
#include "imports.h"
#include "macros.h"
#include "mtypes.h"
#include "t_context.h"
#include "t_save_api.h"
/* If someone compiles a display list like:
* glBegin(Triangles)
* glVertex()
* ... lots of vertices ...
* glEnd()
*
* or:
* glDrawArrays(...)
*
* and then tries to execute it like this:
*
* glBegin(Lines)
* glCallList()
* glEnd()
*
* it will wind up in here, as the vertex copying used when wrapping
* buffers in list compilation (Triangles) won't be right for how the
* list is being executed (as Lines).
*
* This could be avoided by not compiling as vertex_lists until after
* the first glEnd() has been seen. However, that would miss an
* important category of display lists, for the sake of a degenerate
* usage.
*
* Further, replaying degenerately-called lists in this fashion is
* probably still faster than the replay using opcodes.
*/
typedef void (*attr_func)( GLcontext *ctx, GLint target, const GLfloat * );
/* Wrapper functions in case glVertexAttrib*fvNV doesn't exist */
static void VertexAttrib1fvNV(GLcontext *ctx, GLint target, const GLfloat *v)
{
ctx->Exec->VertexAttrib1fvNV(target, v);
}
static void VertexAttrib2fvNV(GLcontext *ctx, GLint target, const GLfloat *v)
{
ctx->Exec->VertexAttrib2fvNV(target, v);
}
static void VertexAttrib3fvNV(GLcontext *ctx, GLint target, const GLfloat *v)
{
ctx->Exec->VertexAttrib3fvNV(target, v);
}
static void VertexAttrib4fvNV(GLcontext *ctx, GLint target, const GLfloat *v)
{
ctx->Exec->VertexAttrib4fvNV(target, v);
}
static attr_func vert_attrfunc[4] = {
VertexAttrib1fvNV,
VertexAttrib2fvNV,
VertexAttrib3fvNV,
VertexAttrib4fvNV
};
static void mat_attr1fv( GLcontext *ctx, GLint target, const GLfloat *v )
{
switch (target) {
case _TNL_ATTRIB_MAT_FRONT_SHININESS:
ctx->Exec->Materialfv( GL_FRONT, GL_SHININESS, v );
break;
case _TNL_ATTRIB_MAT_BACK_SHININESS:
ctx->Exec->Materialfv( GL_BACK, GL_SHININESS, v );
break;
}
}
static void mat_attr3fv( GLcontext *ctx, GLint target, const GLfloat *v )
{
switch (target) {
case _TNL_ATTRIB_MAT_FRONT_INDEXES:
ctx->Exec->Materialfv( GL_FRONT, GL_COLOR_INDEXES, v );
break;
case _TNL_ATTRIB_MAT_BACK_INDEXES:
ctx->Exec->Materialfv( GL_BACK, GL_COLOR_INDEXES, v );
break;
}
}
static void mat_attr4fv( GLcontext *ctx, GLint target, const GLfloat *v )
{
switch (target) {
case _TNL_ATTRIB_MAT_FRONT_EMISSION:
ctx->Exec->Materialfv( GL_FRONT, GL_EMISSION, v );
break;
case _TNL_ATTRIB_MAT_BACK_EMISSION:
ctx->Exec->Materialfv( GL_BACK, GL_EMISSION, v );
break;
case _TNL_ATTRIB_MAT_FRONT_AMBIENT:
ctx->Exec->Materialfv( GL_FRONT, GL_AMBIENT, v );
break;
case _TNL_ATTRIB_MAT_BACK_AMBIENT:
ctx->Exec->Materialfv( GL_BACK, GL_AMBIENT, v );
break;
case _TNL_ATTRIB_MAT_FRONT_DIFFUSE:
ctx->Exec->Materialfv( GL_FRONT, GL_DIFFUSE, v );
break;
case _TNL_ATTRIB_MAT_BACK_DIFFUSE:
ctx->Exec->Materialfv( GL_BACK, GL_DIFFUSE, v );
break;
case _TNL_ATTRIB_MAT_FRONT_SPECULAR:
ctx->Exec->Materialfv( GL_FRONT, GL_SPECULAR, v );
break;
case _TNL_ATTRIB_MAT_BACK_SPECULAR:
ctx->Exec->Materialfv( GL_BACK, GL_SPECULAR, v );
break;
}
}
static attr_func mat_attrfunc[4] = {
mat_attr1fv,
0,
mat_attr3fv,
mat_attr4fv
};
static void index_attr1fv(GLcontext *ctx, GLint target, const GLfloat *v)
{
ctx->Exec->Indexf(v[0]);
}
static void edgeflag_attr1fv(GLcontext *ctx, GLint target, const GLfloat *v)
{
ctx->Exec->EdgeFlag((v[0] == 1.0));
}
struct loopback_attr {
GLint target;
GLint sz;
attr_func func;
};
/* Don't emit ends and begins on wrapped primitives. Don't replay
* wrapped vertices. If we get here, it's probably because the the
* precalculated wrapping is wrong.
*/
static void loopback_prim( GLcontext *ctx,
struct tnl_vertex_list *list, GLuint i,
struct loopback_attr *la, GLuint nr )
{
struct tnl_prim *prim = &list->prim[i];
GLint begin = prim->start;
GLint end = begin + prim->count;
GLfloat *data;
GLint j;
GLuint k;
if (prim->mode & PRIM_BEGIN) {
glBegin( prim->mode & PRIM_MODE_MASK );
} else {
assert(i == 0);
assert(begin == 0);
begin += list->wrap_count;
}
data = list->buffer + begin * list->vertex_size;
for (j = begin ; j < end ; j++) {
GLfloat *tmp = data + la[0].sz;
for (k = 1 ; k < nr ; k++) {
la[k].func( ctx, la[k].target, tmp );
tmp += la[k].sz;
}
/* Fire the vertex
*/
la[0].func( ctx, VERT_ATTRIB_POS, data );
data = tmp;
}
if (prim->mode & PRIM_END) {
glEnd();
}
else {
assert (i == list->prim_count-1);
}
}
/* Primitives generated by DrawArrays/DrawElements/Rectf may be
* caught here. If there is no primitive in progress, execute them
* normally, otherwise need to track and discard the generated
* primitives.
*/
static void loopback_weak_prim( GLcontext *ctx,
struct tnl_vertex_list *list, GLuint i,
struct loopback_attr *la, GLuint nr )
{
if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END)
loopback_prim( ctx, list, i, la, nr );
else {
struct tnl_prim *prim = &list->prim[i];
/* Use the prim_weak flag to ensure that if this primitive
* wraps, we don't mistake future vertex_lists for part of the
* surrounding primitive.
*
* While this flag is set, we are simply disposing of data
* generated by an operation now known to be a noop.
*/
if (prim->mode & PRIM_BEGIN)
ctx->Driver.CurrentExecPrimitive |= PRIM_WEAK;
if (prim->mode & PRIM_END)
ctx->Driver.CurrentExecPrimitive &= ~PRIM_WEAK;
}
}
void _tnl_loopback_vertex_list( GLcontext *ctx, struct tnl_vertex_list *list )
{
struct loopback_attr la[_TNL_ATTRIB_MAX];
GLuint i, nr = 0;
for (i = 0 ; i <= _TNL_ATTRIB_TEX7 ; i++) {
if (list->attrsz[i]) {
la[nr].target = i;
la[nr].sz = list->attrsz[i];
la[nr].func = vert_attrfunc[list->attrsz[i]-1];
nr++;
}
}
for (i = _TNL_ATTRIB_MAT_FRONT_AMBIENT ;
i <= _TNL_ATTRIB_MAT_BACK_INDEXES ;
i++) {
if (list->attrsz[i]) {
la[nr].target = i;
la[nr].sz = list->attrsz[i];
la[nr].func = mat_attrfunc[list->attrsz[i]-1];
nr++;
}
}
if (list->attrsz[_TNL_ATTRIB_EDGEFLAG]) {
la[nr].target = _TNL_ATTRIB_EDGEFLAG;
la[nr].sz = list->attrsz[_TNL_ATTRIB_EDGEFLAG];
la[nr].func = edgeflag_attr1fv;
nr++;
}
if (list->attrsz[_TNL_ATTRIB_INDEX]) {
la[nr].target = _TNL_ATTRIB_INDEX;
la[nr].sz = list->attrsz[_TNL_ATTRIB_INDEX];
la[nr].func = index_attr1fv;
nr++;
}
for (i = 0 ; i < list->prim_count ; i++) {
if (list->prim[i].mode & PRIM_WEAK)
loopback_weak_prim( ctx, list, i, la, nr );
else
loopback_prim( ctx, list, i, la, nr );
}
}