diff options
Diffstat (limited to 'mesalib/src/mesa/vbo')
22 files changed, 7286 insertions, 0 deletions
diff --git a/mesalib/src/mesa/vbo/descrip.mms b/mesalib/src/mesa/vbo/descrip.mms new file mode 100644 index 000000000..c2a16a04f --- /dev/null +++ b/mesalib/src/mesa/vbo/descrip.mms @@ -0,0 +1,62 @@ +# Makefile for core library for VMS +# contributed by Jouk Jansen joukj@hrem.nano.tudelft.nl +# Last revision : 3 October 2007 + +.first + define gl [---.include.gl] + define math [-.math] + define vbo [-.vbo] + define tnl [-.tnl] + define shader [-.shader] + define swrast [-.swrast] + define swrast_setup [-.swrast_setup] + define main [-.main] + define glapi [-.glapi] + +.include [---]mms-config. + +##### MACROS ##### + +VPATH = RCS + +INCDIR = [---.include],[-.main],[-.glapi],[-.shader],[-.shader.slang] +LIBDIR = [---.lib] +CFLAGS = /include=($(INCDIR),[])/define=(PTHREADS=1)/name=(as_is,short)/float=ieee/ieee=denorm + +SOURCES =vbo_context.c,vbo_exec.c,vbo_exec_api.c,vbo_exec_array.c,\ + vbo_exec_draw.c,vbo_exec_eval.c,vbo_rebase.c,vbo_save.c,\ + vbo_save_api.c,vbo_save_draw.c,vbo_save_loopback.c,\ + vbo_split.c,vbo_split_copy.c,vbo_split_inplace.c + +OBJECTS =vbo_context.obj,vbo_exec.obj,vbo_exec_api.obj,vbo_exec_array.obj,\ + vbo_exec_draw.obj,vbo_exec_eval.obj,vbo_rebase.obj,vbo_save.obj,\ + vbo_save_api.obj,vbo_save_draw.obj,vbo_save_loopback.obj,\ + vbo_split.obj,vbo_split_copy.obj,vbo_split_inplace.obj + +##### RULES ##### + +VERSION=Mesa V3.4 + +##### TARGETS ##### +# Make the library +$(LIBDIR)$(GL_LIB) : $(OBJECTS) + @ library $(LIBDIR)$(GL_LIB) $(OBJECTS) + +clean : + purge + delete *.obj;* + +vbo_context.obj : vbo_context.c +vbo_exec.obj : vbo_exec.c +vbo_exec_api.obj : vbo_exec_api.c +vbo_exec_array.obj : vbo_exec_array.c +vbo_exec_draw.obj : vbo_exec_draw.c +vbo_exec_eval.obj : vbo_exec_eval.c +vbo_rebase.obj : vbo_rebase.c +vbo_save.obj : vbo_save.c +vbo_save_api.obj : vbo_save_api.c +vbo_save_draw.obj : vbo_save_draw.c +vbo_save_loopback.obj : vbo_save_loopback.c +vbo_split.obj : vbo_split.c +vbo_split_copy.obj : vbo_split_copy.c +vbo_split_inplace.obj : vbo_split_inplace.c diff --git a/mesalib/src/mesa/vbo/vbo.h b/mesalib/src/mesa/vbo/vbo.h new file mode 100644 index 000000000..5986e9357 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo.h @@ -0,0 +1,142 @@ +/* + * mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + */ + +/** + * \file vbo_context.h + * \brief VBO builder module datatypes and definitions. + * \author Keith Whitwell + */ + + +#ifndef _VBO_H +#define _VBO_H + +#include "main/mtypes.h" + +struct _mesa_prim { + GLuint mode:8; + GLuint indexed:1; + GLuint begin:1; + GLuint end:1; + GLuint weak:1; + GLuint pad:20; + + GLuint start; + GLuint count; +}; + +/* Would like to call this a "vbo_index_buffer", but this would be + * confusing as the indices are not neccessarily yet in a non-null + * buffer object. + */ +struct _mesa_index_buffer { + GLuint count; + GLenum type; + struct gl_buffer_object *obj; + const void *ptr; +}; + + + +GLboolean _vbo_CreateContext( GLcontext *ctx ); +void _vbo_DestroyContext( GLcontext *ctx ); +void _vbo_InvalidateState( GLcontext *ctx, GLuint new_state ); + + +typedef void (*vbo_draw_func)( GLcontext *ctx, + const struct gl_client_array **arrays, + const struct _mesa_prim *prims, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLboolean index_bounds_valid, + GLuint min_index, + GLuint max_index ); + + + + +/* Utility function to cope with various constraints on tnl modules or + * hardware. This can be used to split an incoming set of arrays and + * primitives against the following constraints: + * - Maximum number of indices in index buffer. + * - Maximum number of vertices referenced by index buffer. + * - Maximum hardware vertex buffer size. + */ +struct split_limits { + GLuint max_verts; + GLuint max_indices; + GLuint max_vb_size; /* bytes */ +}; + + +void vbo_split_prims( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index, + vbo_draw_func draw, + const struct split_limits *limits ); + + +/* Helpers for dealing translating away non-zero min_index. + */ +GLboolean vbo_all_varyings_in_vbos( const struct gl_client_array *arrays[] ); + +void vbo_rebase_prims( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index, + vbo_draw_func draw ); +void +vbo_get_minmax_index(GLcontext *ctx, const struct _mesa_prim *prim, + const struct _mesa_index_buffer *ib, + GLuint *min_index, GLuint *max_index); + +void vbo_use_buffer_objects(GLcontext *ctx); + + +void vbo_set_draw_func(GLcontext *ctx, vbo_draw_func func); + + +void GLAPIENTRY +_vbo_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a); + +void GLAPIENTRY +_vbo_Normal3f(GLfloat x, GLfloat y, GLfloat z); + +void GLAPIENTRY +_vbo_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); + +void GLAPIENTRY +_vbo_Materialfv(GLenum face, GLenum pname, const GLfloat *params); + +void GLAPIENTRY +_vbo_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + +#endif diff --git a/mesalib/src/mesa/vbo/vbo_attrib.h b/mesalib/src/mesa/vbo/vbo_attrib.h new file mode 100644 index 000000000..0ae928f2a --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_attrib.h @@ -0,0 +1,107 @@ +/* + Copyright (C) Intel Corp. 2006. All Rights Reserved. + Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + develop this 3D driver. + + 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 (including the + next paragraph) 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 THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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. + + **********************************************************************/ + /* + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +#ifndef VBO_ATTRIB_H +#define VBO_ATTRIB_H + + +/* + * Note: The first attributes match the VERT_ATTRIB_* definitions + * in mtypes.h. However, the tnl module has additional attributes + * for materials, color indexes, edge flags, etc. + */ +/* Although it's nice to use these as bit indexes in a DWORD flag, we + * could manage without if necessary. Another limit currently is the + * number of bits allocated for these numbers in places like vertex + * program instruction formats and register layouts. + */ +enum { + VBO_ATTRIB_POS = 0, + VBO_ATTRIB_WEIGHT = 1, + VBO_ATTRIB_NORMAL = 2, + VBO_ATTRIB_COLOR0 = 3, + VBO_ATTRIB_COLOR1 = 4, + VBO_ATTRIB_FOG = 5, + VBO_ATTRIB_INDEX = 6, + VBO_ATTRIB_EDGEFLAG = 7, + VBO_ATTRIB_TEX0 = 8, + VBO_ATTRIB_TEX1 = 9, + VBO_ATTRIB_TEX2 = 10, + VBO_ATTRIB_TEX3 = 11, + VBO_ATTRIB_TEX4 = 12, + VBO_ATTRIB_TEX5 = 13, + VBO_ATTRIB_TEX6 = 14, + VBO_ATTRIB_TEX7 = 15, + + VBO_ATTRIB_GENERIC0 = 16, /* Not used? */ + VBO_ATTRIB_GENERIC1 = 17, + VBO_ATTRIB_GENERIC2 = 18, + VBO_ATTRIB_GENERIC3 = 19, + VBO_ATTRIB_GENERIC4 = 20, + VBO_ATTRIB_GENERIC5 = 21, + VBO_ATTRIB_GENERIC6 = 22, + VBO_ATTRIB_GENERIC7 = 23, + VBO_ATTRIB_GENERIC8 = 24, + VBO_ATTRIB_GENERIC9 = 25, + VBO_ATTRIB_GENERIC10 = 26, + VBO_ATTRIB_GENERIC11 = 27, + VBO_ATTRIB_GENERIC12 = 28, + VBO_ATTRIB_GENERIC13 = 29, + VBO_ATTRIB_GENERIC14 = 30, + VBO_ATTRIB_GENERIC15 = 31, + + /* XXX: in the vertex program InputsRead flag, we alias + * materials and generics and use knowledge about the program + * (whether it is a fixed-function emulation) to + * differentiate. Here we must keep them apart instead. + */ + VBO_ATTRIB_MAT_FRONT_AMBIENT = 32, + VBO_ATTRIB_MAT_BACK_AMBIENT = 33, + VBO_ATTRIB_MAT_FRONT_DIFFUSE = 34, + VBO_ATTRIB_MAT_BACK_DIFFUSE = 35, + VBO_ATTRIB_MAT_FRONT_SPECULAR = 36, + VBO_ATTRIB_MAT_BACK_SPECULAR = 37, + VBO_ATTRIB_MAT_FRONT_EMISSION = 38, + VBO_ATTRIB_MAT_BACK_EMISSION = 39, + VBO_ATTRIB_MAT_FRONT_SHININESS = 40, + VBO_ATTRIB_MAT_BACK_SHININESS = 41, + VBO_ATTRIB_MAT_FRONT_INDEXES = 42, + VBO_ATTRIB_MAT_BACK_INDEXES = 43, + + VBO_ATTRIB_MAX = 44 +}; + +#define VBO_ATTRIB_FIRST_MATERIAL VBO_ATTRIB_MAT_FRONT_AMBIENT +#define VBO_ATTRIB_LAST_MATERIAL VBO_ATTRIB_MAT_BACK_INDEXES + +#define VBO_MAX_COPIED_VERTS 3 + +#endif diff --git a/mesalib/src/mesa/vbo/vbo_attrib_tmp.h b/mesalib/src/mesa/vbo/vbo_attrib_tmp.h new file mode 100644 index 000000000..7a889b8e2 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_attrib_tmp.h @@ -0,0 +1,486 @@ +/************************************************************************** + +Copyright 2002 Tungsten Graphics Inc., Cedar Park, Texas. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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. + +**************************************************************************/ + +#define ATTR1FV( A, V ) ATTR( A, 1, (V)[0], 0, 0, 1 ) +#define ATTR2FV( A, V ) ATTR( A, 2, (V)[0], (V)[1], 0, 1 ) +#define ATTR3FV( A, V ) ATTR( A, 3, (V)[0], (V)[1], (V)[2], 1 ) +#define ATTR4FV( A, V ) ATTR( A, 4, (V)[0], (V)[1], (V)[2], (V)[3] ) + +#define ATTR1F( A, X ) ATTR( A, 1, X, 0, 0, 1 ) +#define ATTR2F( A, X, Y ) ATTR( A, 2, X, Y, 0, 1 ) +#define ATTR3F( A, X, Y, Z ) ATTR( A, 3, X, Y, Z, 1 ) +#define ATTR4F( A, X, Y, Z, W ) ATTR( A, 4, X, Y, Z, W ) + +#define MAT_ATTR( A, N, V ) ATTR( A, N, (V)[0], (V)[1], (V)[2], (V)[3] ) + +static void GLAPIENTRY TAG(Vertex2f)( GLfloat x, GLfloat y ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR2F( VBO_ATTRIB_POS, x, y ); +} + +static void GLAPIENTRY TAG(Vertex2fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR2FV( VBO_ATTRIB_POS, v ); +} + +static void GLAPIENTRY TAG(Vertex3f)( GLfloat x, GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3F( VBO_ATTRIB_POS, x, y, z ); +} + +static void GLAPIENTRY TAG(Vertex3fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3FV( VBO_ATTRIB_POS, v ); +} + +static void GLAPIENTRY TAG(Vertex4f)( GLfloat x, GLfloat y, GLfloat z, GLfloat w ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR4F( VBO_ATTRIB_POS, x, y, z, w ); +} + +static void GLAPIENTRY TAG(Vertex4fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR4FV( VBO_ATTRIB_POS, v ); +} + +static void GLAPIENTRY TAG(TexCoord1f)( GLfloat x ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1F( VBO_ATTRIB_TEX0, x ); +} + +static void GLAPIENTRY TAG(TexCoord1fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1FV( VBO_ATTRIB_TEX0, v ); +} + +static void GLAPIENTRY TAG(TexCoord2f)( GLfloat x, GLfloat y ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR2F( VBO_ATTRIB_TEX0, x, y ); +} + +static void GLAPIENTRY TAG(TexCoord2fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR2FV( VBO_ATTRIB_TEX0, v ); +} + +static void GLAPIENTRY TAG(TexCoord3f)( GLfloat x, GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3F( VBO_ATTRIB_TEX0, x, y, z ); +} + +static void GLAPIENTRY TAG(TexCoord3fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3FV( VBO_ATTRIB_TEX0, v ); +} + +static void GLAPIENTRY TAG(TexCoord4f)( GLfloat x, GLfloat y, GLfloat z, GLfloat w ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR4F( VBO_ATTRIB_TEX0, x, y, z, w ); +} + +static void GLAPIENTRY TAG(TexCoord4fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR4FV( VBO_ATTRIB_TEX0, v ); +} + +static void GLAPIENTRY TAG(Normal3f)( GLfloat x, GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3F( VBO_ATTRIB_NORMAL, x, y, z ); +} + +static void GLAPIENTRY TAG(Normal3fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3FV( VBO_ATTRIB_NORMAL, v ); +} + +static void GLAPIENTRY TAG(FogCoordfEXT)( GLfloat x ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1F( VBO_ATTRIB_FOG, x ); +} + +static void GLAPIENTRY TAG(FogCoordfvEXT)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1FV( VBO_ATTRIB_FOG, v ); +} + +static void GLAPIENTRY TAG(Color3f)( GLfloat x, GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3F( VBO_ATTRIB_COLOR0, x, y, z ); +} + +static void GLAPIENTRY TAG(Color3fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3FV( VBO_ATTRIB_COLOR0, v ); +} + +static void GLAPIENTRY TAG(Color4f)( GLfloat x, GLfloat y, GLfloat z, GLfloat w ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR4F( VBO_ATTRIB_COLOR0, x, y, z, w ); +} + +static void GLAPIENTRY TAG(Color4fv)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR4FV( VBO_ATTRIB_COLOR0, v ); +} + +static void GLAPIENTRY TAG(SecondaryColor3fEXT)( GLfloat x, GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3F( VBO_ATTRIB_COLOR1, x, y, z ); +} + +static void GLAPIENTRY TAG(SecondaryColor3fvEXT)( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR3FV( VBO_ATTRIB_COLOR1, v ); +} + + +static void GLAPIENTRY TAG(EdgeFlag)( GLboolean b ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1F( VBO_ATTRIB_EDGEFLAG, (GLfloat)b ); +} + +static void GLAPIENTRY TAG(Indexf)( GLfloat f ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1F( VBO_ATTRIB_INDEX, f ); +} + +static void GLAPIENTRY TAG(Indexfv)( const GLfloat *f ) +{ + GET_CURRENT_CONTEXT( ctx ); + ATTR1FV( VBO_ATTRIB_INDEX, f ); +} + + +static void GLAPIENTRY TAG(MultiTexCoord1f)( GLenum target, GLfloat x ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR1F( attr, x ); +} + +static void GLAPIENTRY TAG(MultiTexCoord1fv)( GLenum target, const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR1FV( attr, v ); +} + +static void GLAPIENTRY TAG(MultiTexCoord2f)( GLenum target, GLfloat x, GLfloat y ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR2F( attr, x, y ); +} + +static void GLAPIENTRY TAG(MultiTexCoord2fv)( GLenum target, const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR2FV( attr, v ); +} + +static void GLAPIENTRY TAG(MultiTexCoord3f)( GLenum target, GLfloat x, GLfloat y, + GLfloat z) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR3F( attr, x, y, z ); +} + +static void GLAPIENTRY TAG(MultiTexCoord3fv)( GLenum target, const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR3FV( attr, v ); +} + +static void GLAPIENTRY TAG(MultiTexCoord4f)( GLenum target, GLfloat x, GLfloat y, + GLfloat z, GLfloat w ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR4F( attr, x, y, z, w ); +} + +static void GLAPIENTRY TAG(MultiTexCoord4fv)( GLenum target, const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLuint attr = (target & 0x7) + VBO_ATTRIB_TEX0; + ATTR4FV( attr, v ); +} + + +static void GLAPIENTRY TAG(VertexAttrib1fARB)( GLuint index, GLfloat x ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR1F(0, x); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR1F(VBO_ATTRIB_GENERIC0 + index, x); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib1fvARB)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR1FV(0, v); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR1FV(VBO_ATTRIB_GENERIC0 + index, v); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib2fARB)( GLuint index, GLfloat x, + GLfloat y ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR2F(0, x, y); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR2F(VBO_ATTRIB_GENERIC0 + index, x, y); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib2fvARB)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR2FV(0, v); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR2FV(VBO_ATTRIB_GENERIC0 + index, v); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib3fARB)( GLuint index, GLfloat x, + GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR3F(0, x, y, z); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR3F(VBO_ATTRIB_GENERIC0 + index, x, y, z); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib3fvARB)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR3FV(0, v); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR3FV(VBO_ATTRIB_GENERIC0 + index, v); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib4fARB)( GLuint index, GLfloat x, + GLfloat y, GLfloat z, + GLfloat w ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR4F(0, x, y, z, w); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR4F(VBO_ATTRIB_GENERIC0 + index, x, y, z, w); + else + ERROR(); +} + +static void GLAPIENTRY TAG(VertexAttrib4fvARB)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index == 0) + ATTR4FV(0, v); + else if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR4FV(VBO_ATTRIB_GENERIC0 + index, v); + else + ERROR(); +} + + +/* In addition to supporting NV_vertex_program, these entrypoints are + * used by the display list and other code specifically because of + * their property of aliasing with other attributes. (See + * vbo_save_loopback.c) + */ +static void GLAPIENTRY TAG(VertexAttrib1fNV)( GLuint index, GLfloat x ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR1F(index, x); +} + +static void GLAPIENTRY TAG(VertexAttrib1fvNV)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR1FV(index, v); +} + +static void GLAPIENTRY TAG(VertexAttrib2fNV)( GLuint index, GLfloat x, + GLfloat y ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR2F(index, x, y); +} + +static void GLAPIENTRY TAG(VertexAttrib2fvNV)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR2FV(index, v); +} + +static void GLAPIENTRY TAG(VertexAttrib3fNV)( GLuint index, GLfloat x, + GLfloat y, GLfloat z ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR3F(index, x, y, z); +} + +static void GLAPIENTRY TAG(VertexAttrib3fvNV)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR3FV(index, v); +} + +static void GLAPIENTRY TAG(VertexAttrib4fNV)( GLuint index, GLfloat x, + GLfloat y, GLfloat z, + GLfloat w ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR4F(index, x, y, z, w); +} + +static void GLAPIENTRY TAG(VertexAttrib4fvNV)( GLuint index, + const GLfloat *v ) +{ + GET_CURRENT_CONTEXT( ctx ); + if (index < VBO_ATTRIB_MAX) + ATTR4FV(index, v); +} + + +#define MAT( ATTR, N, face, params ) \ +do { \ + if (face != GL_BACK) \ + MAT_ATTR( ATTR, N, params ); /* front */ \ + if (face != GL_FRONT) \ + MAT_ATTR( ATTR + 1, N, params ); /* back */ \ +} while (0) + + +/* Colormaterial conflicts are dealt with later. + */ +static void GLAPIENTRY TAG(Materialfv)( GLenum face, GLenum pname, + const GLfloat *params ) +{ + GET_CURRENT_CONTEXT( ctx ); + switch (pname) { + case GL_EMISSION: + MAT( VBO_ATTRIB_MAT_FRONT_EMISSION, 4, face, params ); + break; + case GL_AMBIENT: + MAT( VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params ); + break; + case GL_DIFFUSE: + MAT( VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params ); + break; + case GL_SPECULAR: + MAT( VBO_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params ); + break; + case GL_SHININESS: + MAT( VBO_ATTRIB_MAT_FRONT_SHININESS, 1, face, params ); + break; + case GL_COLOR_INDEXES: + MAT( VBO_ATTRIB_MAT_FRONT_INDEXES, 3, face, params ); + break; + case GL_AMBIENT_AND_DIFFUSE: + MAT( VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params ); + MAT( VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params ); + break; + default: + ERROR(); + return; + } +} + + +#undef ATTR1FV +#undef ATTR2FV +#undef ATTR3FV +#undef ATTR4FV + +#undef ATTR1F +#undef ATTR2F +#undef ATTR3F +#undef ATTR4F + +#undef MAT +#undef MAT_ATTR diff --git a/mesalib/src/mesa/vbo/vbo_context.c b/mesalib/src/mesa/vbo/vbo_context.c new file mode 100644 index 000000000..75c32e0b9 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_context.c @@ -0,0 +1,273 @@ +/* + * Mesa 3-D graphics library + * Version: 6.3 + * + * Copyright (C) 1999-2005 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +#include "main/imports.h" +#include "main/mtypes.h" +#include "main/api_arrayelt.h" +#include "main/bufferobj.h" +#include "math/m_eval.h" +#include "vbo.h" +#include "vbo_context.h" + +#if 0 +/* Reach out and grab this to use as the default: + */ +extern void _tnl_draw_prims( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prims, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index ); +#endif + + + +#define NR_LEGACY_ATTRIBS 16 +#define NR_GENERIC_ATTRIBS 16 +#define NR_MAT_ATTRIBS 12 + +static GLuint check_size( const GLfloat *attr ) +{ + if (attr[3] != 1.0) return 4; + if (attr[2] != 0.0) return 3; + if (attr[1] != 0.0) return 2; + return 1; +} + +static void init_legacy_currval(GLcontext *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct gl_client_array *arrays = vbo->legacy_currval; + GLuint i; + + memset(arrays, 0, sizeof(*arrays) * NR_LEGACY_ATTRIBS); + + /* Set up a constant (StrideB == 0) array for each current + * attribute: + */ + for (i = 0; i < NR_LEGACY_ATTRIBS; i++) { + struct gl_client_array *cl = &arrays[i]; + + /* Size will have to be determined at runtime: + */ + cl->Size = check_size(ctx->Current.Attrib[i]); + cl->Stride = 0; + cl->StrideB = 0; + cl->Enabled = 1; + cl->Type = GL_FLOAT; + cl->Format = GL_RGBA; + cl->Ptr = (const void *)ctx->Current.Attrib[i]; + _mesa_reference_buffer_object(ctx, &cl->BufferObj, + ctx->Shared->NullBufferObj); + } +} + + +static void init_generic_currval(GLcontext *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct gl_client_array *arrays = vbo->generic_currval; + GLuint i; + + memset(arrays, 0, sizeof(*arrays) * NR_GENERIC_ATTRIBS); + + for (i = 0; i < NR_GENERIC_ATTRIBS; i++) { + struct gl_client_array *cl = &arrays[i]; + + /* This will have to be determined at runtime: + */ + cl->Size = 1; + cl->Type = GL_FLOAT; + cl->Format = GL_RGBA; + cl->Ptr = (const void *)ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + i]; + cl->Stride = 0; + cl->StrideB = 0; + cl->Enabled = 1; + _mesa_reference_buffer_object(ctx, &cl->BufferObj, + ctx->Shared->NullBufferObj); + } +} + + +static void init_mat_currval(GLcontext *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct gl_client_array *arrays = vbo->mat_currval; + GLuint i; + + ASSERT(NR_MAT_ATTRIBS == MAT_ATTRIB_MAX); + + memset(arrays, 0, sizeof(*arrays) * NR_MAT_ATTRIBS); + + /* Set up a constant (StrideB == 0) array for each current + * attribute: + */ + for (i = 0; i < NR_MAT_ATTRIBS; i++) { + struct gl_client_array *cl = &arrays[i]; + + /* Size is fixed for the material attributes, for others will + * be determined at runtime: + */ + switch (i - VERT_ATTRIB_GENERIC0) { + case MAT_ATTRIB_FRONT_SHININESS: + case MAT_ATTRIB_BACK_SHININESS: + cl->Size = 1; + break; + case MAT_ATTRIB_FRONT_INDEXES: + case MAT_ATTRIB_BACK_INDEXES: + cl->Size = 3; + break; + default: + cl->Size = 4; + break; + } + + cl->Ptr = (const void *)ctx->Light.Material.Attrib[i]; + cl->Type = GL_FLOAT; + cl->Format = GL_RGBA; + cl->Stride = 0; + cl->StrideB = 0; + cl->Enabled = 1; + _mesa_reference_buffer_object(ctx, &cl->BufferObj, + ctx->Shared->NullBufferObj); + } +} + +#if 0 + +static void vbo_exec_current_init( struct vbo_exec_context *exec ) +{ + GLcontext *ctx = exec->ctx; + GLint i; + + /* setup the pointers for the typical 16 vertex attributes */ + for (i = 0; i < VBO_ATTRIB_FIRST_MATERIAL; i++) + exec->vtx.current[i] = ctx->Current.Attrib[i]; + + /* setup pointers for the 12 material attributes */ + for (i = 0; i < MAT_ATTRIB_MAX; i++) + exec->vtx.current[VBO_ATTRIB_FIRST_MATERIAL + i] = + ctx->Light.Material.Attrib[i]; +} +#endif + +GLboolean _vbo_CreateContext( GLcontext *ctx ) +{ + struct vbo_context *vbo = CALLOC_STRUCT(vbo_context); + + ctx->swtnl_im = (void *)vbo; + + /* Initialize the arrayelt helper + */ + if (!ctx->aelt_context && + !_ae_create_context( ctx )) { + return GL_FALSE; + } + + /* TODO: remove these pointers. + */ + vbo->legacy_currval = &vbo->currval[VBO_ATTRIB_POS]; + vbo->generic_currval = &vbo->currval[VBO_ATTRIB_GENERIC0]; + vbo->mat_currval = &vbo->currval[VBO_ATTRIB_MAT_FRONT_AMBIENT]; + + init_legacy_currval( ctx ); + init_generic_currval( ctx ); + init_mat_currval( ctx ); + + /* Build mappings from VERT_ATTRIB -> VBO_ATTRIB depending on type + * of vertex program active. + */ + { + GLuint i; + + /* When no vertex program, pull in the material attributes in + * the 16..32 generic range. + */ + for (i = 0; i < 16; i++) + vbo->map_vp_none[i] = i; + for (i = 0; i < 12; i++) + vbo->map_vp_none[16+i] = VBO_ATTRIB_MAT_FRONT_AMBIENT + i; + for (i = 0; i < 4; i++) + vbo->map_vp_none[28+i] = i; + + for (i = 0; i < Elements(vbo->map_vp_arb); i++) + vbo->map_vp_arb[i] = i; + } + + + /* By default: + */ +#if 0 /* dead - see vbo_set_draw_func() */ + vbo->draw_prims = _tnl_draw_prims; +#endif + + /* Hook our functions into exec and compile dispatch tables. These + * will pretty much be permanently installed, which means that the + * vtxfmt mechanism can be removed now. + */ + vbo_exec_init( ctx ); +#if FEATURE_dlist + vbo_save_init( ctx ); +#endif + + _math_init_eval(); + + return GL_TRUE; +} + +void _vbo_InvalidateState( GLcontext *ctx, GLuint new_state ) +{ + _ae_invalidate_state(ctx, new_state); + vbo_exec_invalidate_state(ctx, new_state); +} + + +void _vbo_DestroyContext( GLcontext *ctx ) +{ + if (ctx->aelt_context) { + _ae_destroy_context( ctx ); + ctx->aelt_context = NULL; + } + + if (vbo_context(ctx)) { + vbo_exec_destroy(ctx); +#if FEATURE_dlist + vbo_save_destroy(ctx); +#endif + FREE(vbo_context(ctx)); + ctx->swtnl_im = NULL; + } +} + + +void vbo_set_draw_func(GLcontext *ctx, vbo_draw_func func) +{ + struct vbo_context *vbo = vbo_context(ctx); + vbo->draw_prims = func; +} + diff --git a/mesalib/src/mesa/vbo/vbo_context.h b/mesalib/src/mesa/vbo/vbo_context.h new file mode 100644 index 000000000..8b726dc8a --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_context.h @@ -0,0 +1,114 @@ +/* + * mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + */ + +/** + * \file vbo_context.h + * \brief VBO builder module datatypes and definitions. + * \author Keith Whitwell + */ + + +/** + * \mainpage The VBO builder module + * + * This module hooks into the GL dispatch table and catches all vertex + * building and drawing commands, such as glVertex3f, glBegin and + * glDrawArrays. The module stores all incoming vertex data as arrays + * in GL vertex buffer objects (VBOs), and translates all drawing + * commands into calls to a driver supplied DrawPrimitives() callback. + * + * The module captures both immediate mode and display list drawing, + * and manages the allocation, reference counting and deallocation of + * vertex buffer objects itself. + * + * The DrawPrimitives() callback can be either implemented by the + * driver itself or hooked to the tnl module's _tnl_draw_primitives() + * function for hardware without tnl capablilties or during fallbacks. + */ + + +#ifndef _VBO_CONTEXT_H +#define _VBO_CONTEXT_H + +#include "vbo.h" +#include "vbo_attrib.h" +#include "vbo_exec.h" +#if FEATURE_dlist +#include "vbo_save.h" +#endif + + +struct vbo_context { + struct gl_client_array currval[VBO_ATTRIB_MAX]; + + /* These point into the above. TODO: remove. + */ + struct gl_client_array *legacy_currval; + struct gl_client_array *generic_currval; + struct gl_client_array *mat_currval; + + GLuint map_vp_none[VERT_ATTRIB_MAX]; + GLuint map_vp_arb[VERT_ATTRIB_MAX]; + + GLfloat *current[VBO_ATTRIB_MAX]; /* points into ctx->Current, ctx->Light.Material */ + GLfloat CurrentFloatEdgeFlag; + + + struct vbo_exec_context exec; +#if FEATURE_dlist + struct vbo_save_context save; +#endif + + /* Callback into the driver. This must always succeed, the driver + * is responsible for initiating any fallback actions required: + */ + vbo_draw_func draw_prims; +}; + + +static INLINE struct vbo_context *vbo_context(GLcontext *ctx) +{ + return (struct vbo_context *)(ctx->swtnl_im); +} + + +/** + * Return VP_x token to indicate whether we're running fixed-function + * vertex transformation, an NV vertex program or ARB vertex program/shader. + */ +static INLINE enum vp_mode +get_program_mode( GLcontext *ctx ) +{ + if (!ctx->VertexProgram._Current) + return VP_NONE; + else if (ctx->VertexProgram._Current == ctx->VertexProgram._TnlProgram) + return VP_NONE; + else if (ctx->VertexProgram._Current->IsNVProgram) + return VP_NV; + else + return VP_ARB; +} + + +#endif diff --git a/mesalib/src/mesa/vbo/vbo_exec.c b/mesalib/src/mesa/vbo/vbo_exec.c new file mode 100644 index 000000000..e168a89ea --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_exec.c @@ -0,0 +1,97 @@ +/* + * Mesa 3-D graphics library + * Version: 6.3 + * + * Copyright (C) 1999-2005 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + + +#include "main/api_arrayelt.h" +#include "main/glheader.h" +#include "main/imports.h" +#include "main/context.h" +#include "main/macros.h" +#include "main/mtypes.h" +#include "main/vtxfmt.h" + +#include "vbo_context.h" + +void vbo_exec_init( GLcontext *ctx ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + exec->ctx = ctx; + + /* Initialize the arrayelt helper + */ + if (!ctx->aelt_context && + !_ae_create_context( ctx )) + return; + + vbo_exec_vtx_init( exec ); + vbo_exec_array_init( exec ); + + /* Hook our functions into exec and compile dispatch tables. + */ + _mesa_install_exec_vtxfmt( ctx, &exec->vtxfmt ); + + ctx->Driver.NeedFlush = 0; + ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; + ctx->Driver.BeginVertices = vbo_exec_BeginVertices; + ctx->Driver.FlushVertices = vbo_exec_FlushVertices; + + vbo_exec_invalidate_state( ctx, ~0 ); +} + + +void vbo_exec_destroy( GLcontext *ctx ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + if (ctx->aelt_context) { + _ae_destroy_context( ctx ); + ctx->aelt_context = NULL; + } + + vbo_exec_vtx_destroy( exec ); + vbo_exec_array_destroy( exec ); +} + +/* Really want to install these callbacks to a central facility to be + * invoked according to the state flags. That will have to wait for a + * mesa rework: + */ +void vbo_exec_invalidate_state( GLcontext *ctx, GLuint new_state ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + if (new_state & (_NEW_PROGRAM|_NEW_EVAL)) + exec->eval.recalculate_maps = 1; + + _ae_invalidate_state(ctx, new_state); +} + + + + + diff --git a/mesalib/src/mesa/vbo/vbo_exec.h b/mesalib/src/mesa/vbo/vbo_exec.h new file mode 100644 index 000000000..e0f44892c --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_exec.h @@ -0,0 +1,179 @@ +/************************************************************************** + +Copyright 2002 Tungsten Graphics Inc., Cedar Park, Texas. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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. + +**************************************************************************/ + +/* + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + * + */ + +#ifndef __VBO_EXEC_H__ +#define __VBO_EXEC_H__ + +#include "main/mtypes.h" +#include "vbo.h" +#include "vbo_attrib.h" + + +#define VBO_MAX_PRIM 64 + +/* Wierd implementation stuff: + */ +#define VBO_VERT_BUFFER_SIZE (1024*64) /* bytes */ +#define VBO_MAX_ATTR_CODEGEN 16 +#define ERROR_ATTRIB 16 + + +/** Current vertex program mode */ +enum vp_mode { + VP_NONE, /**< fixed function */ + VP_NV, /**< NV vertex program */ + VP_ARB /**< ARB vertex program or GLSL vertex shader */ +}; + + +struct vbo_exec_eval1_map { + struct gl_1d_map *map; + GLuint sz; +}; + +struct vbo_exec_eval2_map { + struct gl_2d_map *map; + GLuint sz; +}; + + + +struct vbo_exec_copied_vtx { + GLfloat buffer[VBO_ATTRIB_MAX * 4 * VBO_MAX_COPIED_VERTS]; + GLuint nr; +}; + + +typedef void (*vbo_attrfv_func)( const GLfloat * ); + + +struct vbo_exec_context +{ + GLcontext *ctx; + GLvertexformat vtxfmt; + + struct { + struct gl_buffer_object *bufferobj; + + GLuint vertex_size; /* in dwords */ + + struct _mesa_prim prim[VBO_MAX_PRIM]; + GLuint prim_count; + + GLfloat *buffer_map; + GLfloat *buffer_ptr; /* cursor, points into buffer */ + GLuint buffer_used; /* in bytes */ + GLfloat vertex[VBO_ATTRIB_MAX*4]; /* current vertex */ + + GLuint vert_count; + GLuint max_vert; + struct vbo_exec_copied_vtx copied; + + GLubyte attrsz[VBO_ATTRIB_MAX]; + GLubyte active_sz[VBO_ATTRIB_MAX]; + + GLfloat *attrptr[VBO_ATTRIB_MAX]; + struct gl_client_array arrays[VBO_ATTRIB_MAX]; + + /* According to program mode, the values above plus current + * values are squashed down to the 32 attributes passed to the + * vertex program below: + */ + enum vp_mode program_mode; + GLuint enabled_flags; + const struct gl_client_array *inputs[VERT_ATTRIB_MAX]; + } vtx; + + + struct { + GLboolean recalculate_maps; + struct vbo_exec_eval1_map map1[VERT_ATTRIB_MAX]; + struct vbo_exec_eval2_map map2[VERT_ATTRIB_MAX]; + } eval; + + struct { + enum vp_mode program_mode; + GLuint enabled_flags; + GLuint array_obj; + + /* These just mirror the current arrayobj (todo: make arrayobj + * look like this and remove the mirror): + */ + const struct gl_client_array *legacy_array[16]; + const struct gl_client_array *generic_array[16]; + + /* Arrays and current values manipulated according to program + * mode, etc. These are the attributes as seen by vertex + * programs: + */ + const struct gl_client_array *inputs[VERT_ATTRIB_MAX]; + } array; +}; + + + +/* External API: + */ +void vbo_exec_init( GLcontext *ctx ); +void vbo_exec_destroy( GLcontext *ctx ); +void vbo_exec_invalidate_state( GLcontext *ctx, GLuint new_state ); +void vbo_exec_FlushVertices_internal( GLcontext *ctx, GLboolean unmap ); + +void vbo_exec_BeginVertices( GLcontext *ctx ); +void vbo_exec_FlushVertices( GLcontext *ctx, GLuint flags ); + + +/* Internal functions: + */ +void vbo_exec_array_init( struct vbo_exec_context *exec ); +void vbo_exec_array_destroy( struct vbo_exec_context *exec ); + + +void vbo_exec_vtx_init( struct vbo_exec_context *exec ); +void vbo_exec_vtx_destroy( struct vbo_exec_context *exec ); +void vbo_exec_vtx_flush( struct vbo_exec_context *exec, GLboolean unmap ); +void vbo_exec_vtx_map( struct vbo_exec_context *exec ); +void vbo_exec_vtx_wrap( struct vbo_exec_context *exec ); + +void vbo_exec_eval_update( struct vbo_exec_context *exec ); + +void vbo_exec_do_EvalCoord2f( struct vbo_exec_context *exec, + GLfloat u, GLfloat v ); + +void vbo_exec_do_EvalCoord1f( struct vbo_exec_context *exec, + GLfloat u); + +extern GLboolean +vbo_validate_shaders(GLcontext *ctx); + +#endif diff --git a/mesalib/src/mesa/vbo/vbo_exec_api.c b/mesalib/src/mesa/vbo/vbo_exec_api.c new file mode 100644 index 000000000..387d4ee3d --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_exec_api.c @@ -0,0 +1,838 @@ +/************************************************************************** + +Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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. + +**************************************************************************/ + +/* + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/context.h" +#include "main/macros.h" +#include "main/vtxfmt.h" +#if FEATURE_dlist +#include "main/dlist.h" +#endif +#include "main/state.h" +#include "main/light.h" +#include "main/api_arrayelt.h" +#include "main/api_noop.h" +#include "glapi/dispatch.h" + +#include "vbo_context.h" + +#ifdef ERROR +#undef ERROR +#endif + + +/** ID/name for immediate-mode VBO */ +#define IMM_BUFFER_NAME 0xaabbccdd + + +static void reset_attrfv( struct vbo_exec_context *exec ); + + +/* Close off the last primitive, execute the buffer, restart the + * primitive. + */ +static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec ) +{ + if (exec->vtx.prim_count == 0) { + exec->vtx.copied.nr = 0; + exec->vtx.vert_count = 0; + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + } + else { + GLuint last_begin = exec->vtx.prim[exec->vtx.prim_count-1].begin; + GLuint last_count; + + if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + GLint i = exec->vtx.prim_count - 1; + assert(i >= 0); + exec->vtx.prim[i].count = (exec->vtx.vert_count - + exec->vtx.prim[i].start); + } + + last_count = exec->vtx.prim[exec->vtx.prim_count-1].count; + + /* Execute the buffer and save copied vertices. + */ + if (exec->vtx.vert_count) + vbo_exec_vtx_flush( exec, GL_FALSE ); + else { + exec->vtx.prim_count = 0; + exec->vtx.copied.nr = 0; + } + + /* Emit a glBegin to start the new list. + */ + assert(exec->vtx.prim_count == 0); + + if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + exec->vtx.prim[0].mode = exec->ctx->Driver.CurrentExecPrimitive; + exec->vtx.prim[0].start = 0; + exec->vtx.prim[0].count = 0; + exec->vtx.prim_count++; + + if (exec->vtx.copied.nr == last_count) + exec->vtx.prim[0].begin = last_begin; + } + } +} + + +/* Deal with buffer wrapping where provoked by the vertex buffer + * filling up, as opposed to upgrade_vertex(). + */ +void vbo_exec_vtx_wrap( struct vbo_exec_context *exec ) +{ + GLfloat *data = exec->vtx.copied.buffer; + GLuint i; + + /* Run pipeline on current vertices, copy wrapped vertices + * to exec->vtx.copied. + */ + vbo_exec_wrap_buffers( exec ); + + /* Copy stored stored vertices to start of new list. + */ + assert(exec->vtx.max_vert - exec->vtx.vert_count > exec->vtx.copied.nr); + + for (i = 0 ; i < exec->vtx.copied.nr ; i++) { + _mesa_memcpy( exec->vtx.buffer_ptr, data, + exec->vtx.vertex_size * sizeof(GLfloat)); + exec->vtx.buffer_ptr += exec->vtx.vertex_size; + data += exec->vtx.vertex_size; + exec->vtx.vert_count++; + } + + exec->vtx.copied.nr = 0; +} + + +/* + * Copy the active vertex's values to the ctx->Current fields. + */ +static void vbo_exec_copy_to_current( struct vbo_exec_context *exec ) +{ + GLcontext *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLuint i; + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + if (exec->vtx.attrsz[i]) { + /* Note: the exec->vtx.current[i] pointers point into the + * ctx->Current.Attrib and ctx->Light.Material.Attrib arrays. + */ + GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; + GLfloat tmp[4]; + + COPY_CLEAN_4V(tmp, + exec->vtx.attrsz[i], + exec->vtx.attrptr[i]); + + if (memcmp(current, tmp, sizeof(tmp)) != 0) + { + memcpy(current, tmp, sizeof(tmp)); + + /* Given that we explicitly state size here, there is no need + * for the COPY_CLEAN above, could just copy 16 bytes and be + * done. The only problem is when Mesa accesses ctx->Current + * directly. + */ + vbo->currval[i].Size = exec->vtx.attrsz[i]; + + /* This triggers rather too much recalculation of Mesa state + * that doesn't get used (eg light positions). + */ + if (i >= VBO_ATTRIB_MAT_FRONT_AMBIENT && + i <= VBO_ATTRIB_MAT_BACK_INDEXES) + ctx->NewState |= _NEW_LIGHT; + + ctx->NewState |= _NEW_CURRENT_ATTRIB; + } + } + } + + /* Colormaterial -- this kindof sucks. + */ + if (ctx->Light.ColorMaterialEnabled && + exec->vtx.attrsz[VBO_ATTRIB_COLOR0]) { + _mesa_update_color_material(ctx, + ctx->Current.Attrib[VBO_ATTRIB_COLOR0]); + } +} + + +static void vbo_exec_copy_from_current( struct vbo_exec_context *exec ) +{ + GLcontext *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLint i; + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + const GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; + switch (exec->vtx.attrsz[i]) { + case 4: exec->vtx.attrptr[i][3] = current[3]; + case 3: exec->vtx.attrptr[i][2] = current[2]; + case 2: exec->vtx.attrptr[i][1] = current[1]; + case 1: exec->vtx.attrptr[i][0] = current[0]; + break; + } + } +} + + +/* Flush existing data, set new attrib size, replay copied vertices. + */ +static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec, + GLuint attr, + GLuint newsz ) +{ + GLcontext *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLint lastcount = exec->vtx.vert_count; + GLfloat *tmp; + GLuint oldsz; + GLuint i; + + /* Run pipeline on current vertices, copy wrapped vertices + * to exec->vtx.copied. + */ + vbo_exec_wrap_buffers( exec ); + + + /* Do a COPY_TO_CURRENT to ensure back-copying works for the case + * when the attribute already exists in the vertex and is having + * its size increased. + */ + vbo_exec_copy_to_current( exec ); + + + /* Heuristic: Attempt to isolate attributes received outside + * begin/end so that they don't bloat the vertices. + */ + if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END && + exec->vtx.attrsz[attr] == 0 && + lastcount > 8 && + exec->vtx.vertex_size) { + reset_attrfv( exec ); + } + + /* Fix up sizes: + */ + oldsz = exec->vtx.attrsz[attr]; + exec->vtx.attrsz[attr] = newsz; + + exec->vtx.vertex_size += newsz - oldsz; + exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / + (exec->vtx.vertex_size * sizeof(GLfloat))); + exec->vtx.vert_count = 0; + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + + + /* Recalculate all the attrptr[] values + */ + for (i = 0, tmp = exec->vtx.vertex ; i < VBO_ATTRIB_MAX ; i++) { + if (exec->vtx.attrsz[i]) { + exec->vtx.attrptr[i] = tmp; + tmp += exec->vtx.attrsz[i]; + } + else + exec->vtx.attrptr[i] = NULL; /* will not be dereferenced */ + } + + /* Copy from current to repopulate the vertex with correct values. + */ + vbo_exec_copy_from_current( exec ); + + /* Replay stored vertices to translate them + * to new format here. + * + * -- No need to replay - just copy piecewise + */ + if (exec->vtx.copied.nr) + { + GLfloat *data = exec->vtx.copied.buffer; + GLfloat *dest = exec->vtx.buffer_ptr; + GLuint j; + + assert(exec->vtx.buffer_ptr == exec->vtx.buffer_map); + + for (i = 0 ; i < exec->vtx.copied.nr ; i++) { + for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) { + if (exec->vtx.attrsz[j]) { + if (j == attr) { + if (oldsz) { + COPY_CLEAN_4V( dest, oldsz, data ); + data += oldsz; + dest += newsz; + } else { + const GLfloat *current = (const GLfloat *)vbo->currval[j].Ptr; + COPY_SZ_4V( dest, newsz, current ); + dest += newsz; + } + } + else { + GLuint sz = exec->vtx.attrsz[j]; + COPY_SZ_4V( dest, sz, data ); + dest += sz; + data += sz; + } + } + } + } + + exec->vtx.buffer_ptr = dest; + exec->vtx.vert_count += exec->vtx.copied.nr; + exec->vtx.copied.nr = 0; + } +} + + +static void vbo_exec_fixup_vertex( GLcontext *ctx, + GLuint attr, GLuint sz ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + int i; + + if (sz > exec->vtx.attrsz[attr]) { + /* New size is larger. Need to flush existing vertices and get + * an enlarged vertex format. + */ + vbo_exec_wrap_upgrade_vertex( exec, attr, sz ); + } + else if (sz < exec->vtx.active_sz[attr]) { + static const GLfloat id[4] = { 0, 0, 0, 1 }; + + /* New size is smaller - just need to fill in some + * zeros. Don't need to flush or wrap. + */ + for (i = sz ; i <= exec->vtx.attrsz[attr] ; i++) + exec->vtx.attrptr[attr][i-1] = id[i-1]; + } + + exec->vtx.active_sz[attr] = sz; + + /* Does setting NeedFlush belong here? Necessitates resetting + * vtxfmt on each flush (otherwise flags won't get reset + * afterwards). + */ + if (attr == 0) + exec->ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES; +} + + + + +/* + */ +#define ATTR( A, N, V0, V1, V2, V3 ) \ +do { \ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; \ + \ + if (exec->vtx.active_sz[A] != N) \ + vbo_exec_fixup_vertex(ctx, A, N); \ + \ + { \ + GLfloat *dest = exec->vtx.attrptr[A]; \ + if (N>0) dest[0] = V0; \ + if (N>1) dest[1] = V1; \ + if (N>2) dest[2] = V2; \ + if (N>3) dest[3] = V3; \ + } \ + \ + if ((A) == 0) { \ + GLuint i; \ + \ + for (i = 0; i < exec->vtx.vertex_size; i++) \ + exec->vtx.buffer_ptr[i] = exec->vtx.vertex[i]; \ + \ + exec->vtx.buffer_ptr += exec->vtx.vertex_size; \ + exec->ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES; \ + \ + if (++exec->vtx.vert_count >= exec->vtx.max_vert) \ + vbo_exec_vtx_wrap( exec ); \ + } \ +} while (0) + + +#define ERROR() _mesa_error( ctx, GL_INVALID_ENUM, __FUNCTION__ ) +#define TAG(x) vbo_##x + +#include "vbo_attrib_tmp.h" + + + + + +/* Eval + */ +static void GLAPIENTRY vbo_exec_EvalCoord1f( GLfloat u ) +{ + GET_CURRENT_CONTEXT( ctx ); + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + { + GLint i; + if (exec->eval.recalculate_maps) + vbo_exec_eval_update( exec ); + + for (i = 0; i <= VBO_ATTRIB_TEX7; i++) { + if (exec->eval.map1[i].map) + if (exec->vtx.active_sz[i] != exec->eval.map1[i].sz) + vbo_exec_fixup_vertex( ctx, i, exec->eval.map1[i].sz ); + } + } + + + _mesa_memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, + exec->vtx.vertex_size * sizeof(GLfloat)); + + vbo_exec_do_EvalCoord1f( exec, u ); + + _mesa_memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, + exec->vtx.vertex_size * sizeof(GLfloat)); +} + +static void GLAPIENTRY vbo_exec_EvalCoord2f( GLfloat u, GLfloat v ) +{ + GET_CURRENT_CONTEXT( ctx ); + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + { + GLint i; + if (exec->eval.recalculate_maps) + vbo_exec_eval_update( exec ); + + for (i = 0; i <= VBO_ATTRIB_TEX7; i++) { + if (exec->eval.map2[i].map) + if (exec->vtx.active_sz[i] != exec->eval.map2[i].sz) + vbo_exec_fixup_vertex( ctx, i, exec->eval.map2[i].sz ); + } + + if (ctx->Eval.AutoNormal) + if (exec->vtx.active_sz[VBO_ATTRIB_NORMAL] != 3) + vbo_exec_fixup_vertex( ctx, VBO_ATTRIB_NORMAL, 3 ); + } + + _mesa_memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, + exec->vtx.vertex_size * sizeof(GLfloat)); + + vbo_exec_do_EvalCoord2f( exec, u, v ); + + _mesa_memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, + exec->vtx.vertex_size * sizeof(GLfloat)); +} + +static void GLAPIENTRY vbo_exec_EvalCoord1fv( const GLfloat *u ) +{ + vbo_exec_EvalCoord1f( u[0] ); +} + +static void GLAPIENTRY vbo_exec_EvalCoord2fv( const GLfloat *u ) +{ + vbo_exec_EvalCoord2f( u[0], u[1] ); +} + +static void GLAPIENTRY vbo_exec_EvalPoint1( GLint i ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLfloat du = ((ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1) / + (GLfloat) ctx->Eval.MapGrid1un); + GLfloat u = i * du + ctx->Eval.MapGrid1u1; + + vbo_exec_EvalCoord1f( u ); +} + + +static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) / + (GLfloat) ctx->Eval.MapGrid2un); + GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) / + (GLfloat) ctx->Eval.MapGrid2vn); + GLfloat u = i * du + ctx->Eval.MapGrid2u1; + GLfloat v = j * dv + ctx->Eval.MapGrid2v1; + + vbo_exec_EvalCoord2f( u, v ); +} + + +/* Build a list of primitives on the fly. Keep + * ctx->Driver.CurrentExecPrimitive uptodate as well. + */ +static void GLAPIENTRY vbo_exec_Begin( GLenum mode ) +{ + GET_CURRENT_CONTEXT( ctx ); + + if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) { + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + int i; + + if (ctx->NewState) { + _mesa_update_state( ctx ); + + CALL_Begin(ctx->Exec, (mode)); + return; + } + + if (!_mesa_valid_to_render(ctx, "glBegin")) { + return; + } + + /* Heuristic: attempt to isolate attributes occuring outside + * begin/end pairs. + */ + if (exec->vtx.vertex_size && !exec->vtx.attrsz[0]) + vbo_exec_FlushVertices_internal( ctx, GL_FALSE ); + + i = exec->vtx.prim_count++; + exec->vtx.prim[i].mode = mode; + exec->vtx.prim[i].begin = 1; + exec->vtx.prim[i].end = 0; + exec->vtx.prim[i].indexed = 0; + exec->vtx.prim[i].weak = 0; + exec->vtx.prim[i].pad = 0; + exec->vtx.prim[i].start = exec->vtx.vert_count; + exec->vtx.prim[i].count = 0; + + ctx->Driver.CurrentExecPrimitive = mode; + } + else + _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" ); + +} + +static void GLAPIENTRY vbo_exec_End( void ) +{ + GET_CURRENT_CONTEXT( ctx ); + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + int idx = exec->vtx.vert_count; + int i = exec->vtx.prim_count - 1; + + exec->vtx.prim[i].end = 1; + exec->vtx.prim[i].count = idx - exec->vtx.prim[i].start; + + ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; + + if (exec->vtx.prim_count == VBO_MAX_PRIM) + vbo_exec_vtx_flush( exec, GL_FALSE ); + } + else + _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" ); +} + + +static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) +{ + GLvertexformat *vfmt = &exec->vtxfmt; + + vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */ + vfmt->Begin = vbo_exec_Begin; +#if FEATURE_dlist + vfmt->CallList = _mesa_CallList; + vfmt->CallLists = _mesa_CallLists; +#endif + vfmt->End = vbo_exec_End; + vfmt->EvalCoord1f = vbo_exec_EvalCoord1f; + vfmt->EvalCoord1fv = vbo_exec_EvalCoord1fv; + vfmt->EvalCoord2f = vbo_exec_EvalCoord2f; + vfmt->EvalCoord2fv = vbo_exec_EvalCoord2fv; + vfmt->EvalPoint1 = vbo_exec_EvalPoint1; + vfmt->EvalPoint2 = vbo_exec_EvalPoint2; + + vfmt->Rectf = _mesa_noop_Rectf; + vfmt->EvalMesh1 = _mesa_noop_EvalMesh1; + vfmt->EvalMesh2 = _mesa_noop_EvalMesh2; + + + /* from attrib_tmp.h: + */ + vfmt->Color3f = vbo_Color3f; + vfmt->Color3fv = vbo_Color3fv; + vfmt->Color4f = vbo_Color4f; + vfmt->Color4fv = vbo_Color4fv; + vfmt->FogCoordfEXT = vbo_FogCoordfEXT; + vfmt->FogCoordfvEXT = vbo_FogCoordfvEXT; + vfmt->MultiTexCoord1fARB = vbo_MultiTexCoord1f; + vfmt->MultiTexCoord1fvARB = vbo_MultiTexCoord1fv; + vfmt->MultiTexCoord2fARB = vbo_MultiTexCoord2f; + vfmt->MultiTexCoord2fvARB = vbo_MultiTexCoord2fv; + vfmt->MultiTexCoord3fARB = vbo_MultiTexCoord3f; + vfmt->MultiTexCoord3fvARB = vbo_MultiTexCoord3fv; + vfmt->MultiTexCoord4fARB = vbo_MultiTexCoord4f; + vfmt->MultiTexCoord4fvARB = vbo_MultiTexCoord4fv; + vfmt->Normal3f = vbo_Normal3f; + vfmt->Normal3fv = vbo_Normal3fv; + vfmt->SecondaryColor3fEXT = vbo_SecondaryColor3fEXT; + vfmt->SecondaryColor3fvEXT = vbo_SecondaryColor3fvEXT; + vfmt->TexCoord1f = vbo_TexCoord1f; + vfmt->TexCoord1fv = vbo_TexCoord1fv; + vfmt->TexCoord2f = vbo_TexCoord2f; + vfmt->TexCoord2fv = vbo_TexCoord2fv; + vfmt->TexCoord3f = vbo_TexCoord3f; + vfmt->TexCoord3fv = vbo_TexCoord3fv; + vfmt->TexCoord4f = vbo_TexCoord4f; + vfmt->TexCoord4fv = vbo_TexCoord4fv; + vfmt->Vertex2f = vbo_Vertex2f; + vfmt->Vertex2fv = vbo_Vertex2fv; + vfmt->Vertex3f = vbo_Vertex3f; + vfmt->Vertex3fv = vbo_Vertex3fv; + vfmt->Vertex4f = vbo_Vertex4f; + vfmt->Vertex4fv = vbo_Vertex4fv; + + vfmt->VertexAttrib1fARB = vbo_VertexAttrib1fARB; + vfmt->VertexAttrib1fvARB = vbo_VertexAttrib1fvARB; + vfmt->VertexAttrib2fARB = vbo_VertexAttrib2fARB; + vfmt->VertexAttrib2fvARB = vbo_VertexAttrib2fvARB; + vfmt->VertexAttrib3fARB = vbo_VertexAttrib3fARB; + vfmt->VertexAttrib3fvARB = vbo_VertexAttrib3fvARB; + vfmt->VertexAttrib4fARB = vbo_VertexAttrib4fARB; + vfmt->VertexAttrib4fvARB = vbo_VertexAttrib4fvARB; + + vfmt->VertexAttrib1fNV = vbo_VertexAttrib1fNV; + vfmt->VertexAttrib1fvNV = vbo_VertexAttrib1fvNV; + vfmt->VertexAttrib2fNV = vbo_VertexAttrib2fNV; + vfmt->VertexAttrib2fvNV = vbo_VertexAttrib2fvNV; + vfmt->VertexAttrib3fNV = vbo_VertexAttrib3fNV; + vfmt->VertexAttrib3fvNV = vbo_VertexAttrib3fvNV; + vfmt->VertexAttrib4fNV = vbo_VertexAttrib4fNV; + vfmt->VertexAttrib4fvNV = vbo_VertexAttrib4fvNV; + + vfmt->Materialfv = vbo_Materialfv; + + vfmt->EdgeFlag = vbo_EdgeFlag; + vfmt->Indexf = vbo_Indexf; + vfmt->Indexfv = vbo_Indexfv; + +} + + +/** + * Tell the VBO module to use a real OpenGL vertex buffer object to + * store accumulated immediate-mode vertex data. + * This replaces the malloced buffer which was created in + * vb_exec_vtx_init() below. + */ +void vbo_use_buffer_objects(GLcontext *ctx) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + /* Any buffer name but 0 can be used here since this bufferobj won't + * go into the bufferobj hashtable. + */ + GLuint bufName = IMM_BUFFER_NAME; + GLenum target = GL_ARRAY_BUFFER_ARB; + GLenum usage = GL_STREAM_DRAW_ARB; + GLsizei size = VBO_VERT_BUFFER_SIZE; + + /* Make sure this func is only used once */ + assert(exec->vtx.bufferobj == ctx->Shared->NullBufferObj); + if (exec->vtx.buffer_map) { + _mesa_align_free(exec->vtx.buffer_map); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + } + + /* Allocate a real buffer object now */ + exec->vtx.bufferobj = ctx->Driver.NewBufferObject(ctx, bufName, target); + ctx->Driver.BufferData(ctx, target, size, NULL, usage, exec->vtx.bufferobj); +} + + + +void vbo_exec_vtx_init( struct vbo_exec_context *exec ) +{ + GLcontext *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLuint i; + + /* Allocate a buffer object. Will just reuse this object + * continuously, unless vbo_use_buffer_objects() is called to enable + * use of real VBOs. + */ + _mesa_reference_buffer_object(ctx, + &exec->vtx.bufferobj, + ctx->Shared->NullBufferObj); + + ASSERT(!exec->vtx.buffer_map); + exec->vtx.buffer_map = (GLfloat *)ALIGN_MALLOC(VBO_VERT_BUFFER_SIZE, 64); + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + + vbo_exec_vtxfmt_init( exec ); + + /* Hook our functions into the dispatch table. + */ + _mesa_install_exec_vtxfmt( exec->ctx, &exec->vtxfmt ); + + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + exec->vtx.attrsz[i] = 0; + exec->vtx.active_sz[i] = 0; + exec->vtx.inputs[i] = &exec->vtx.arrays[i]; + } + + { + struct gl_client_array *arrays = exec->vtx.arrays; + memcpy(arrays, vbo->legacy_currval, 16 * sizeof(arrays[0])); + memcpy(arrays + 16, vbo->generic_currval, 16 * sizeof(arrays[0])); + } + + exec->vtx.vertex_size = 0; +} + + +void vbo_exec_vtx_destroy( struct vbo_exec_context *exec ) +{ + /* using a real VBO for vertex data */ + GLcontext *ctx = exec->ctx; + unsigned i; + + /* True VBOs should already be unmapped + */ + if (exec->vtx.buffer_map) { + ASSERT(exec->vtx.bufferobj->Name == 0 || + exec->vtx.bufferobj->Name == IMM_BUFFER_NAME); + if (exec->vtx.bufferobj->Name == 0) { + ALIGN_FREE(exec->vtx.buffer_map); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + } + } + + /* Drop any outstanding reference to the vertex buffer + */ + for (i = 0; i < Elements(exec->vtx.arrays); i++) { + _mesa_reference_buffer_object(ctx, + &exec->vtx.arrays[i].BufferObj, + NULL); + } + + /* Free the vertex buffer: + */ + _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL); +} + +void vbo_exec_BeginVertices( GLcontext *ctx ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + if (0) _mesa_printf("%s\n", __FUNCTION__); + vbo_exec_vtx_map( exec ); + + assert((exec->ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0); + exec->ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT; +} + +void vbo_exec_FlushVertices_internal( GLcontext *ctx, GLboolean unmap ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + if (exec->vtx.vert_count || unmap) { + vbo_exec_vtx_flush( exec, unmap ); + } + + if (exec->vtx.vertex_size) { + vbo_exec_copy_to_current( exec ); + reset_attrfv( exec ); + } +} + + + +void vbo_exec_FlushVertices( GLcontext *ctx, GLuint flags ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + if (0) _mesa_printf("%s\n", __FUNCTION__); + + if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + if (0) _mesa_printf("%s - inside begin/end\n", __FUNCTION__); + return; + } + + vbo_exec_FlushVertices_internal( ctx, GL_TRUE ); + + /* Need to do this to ensure BeginVertices gets called again: + */ + if (exec->ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) { + _mesa_restore_exec_vtxfmt( ctx ); + exec->ctx->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT; + } + + exec->ctx->Driver.NeedFlush &= ~flags; +} + + +static void reset_attrfv( struct vbo_exec_context *exec ) +{ + GLuint i; + + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + exec->vtx.attrsz[i] = 0; + exec->vtx.active_sz[i] = 0; + } + + exec->vtx.vertex_size = 0; +} + + +void GLAPIENTRY +_vbo_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + vbo_Color4f(r, g, b, a); +} + + +void GLAPIENTRY +_vbo_Normal3f(GLfloat x, GLfloat y, GLfloat z) +{ + vbo_Normal3f(x, y, z); +} + + +void GLAPIENTRY +_vbo_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +{ + vbo_MultiTexCoord4f(target, s, t, r, q); +} + +void GLAPIENTRY +_vbo_Materialfv(GLenum face, GLenum pname, const GLfloat *params) +{ + vbo_Materialfv(face, pname, params); +} + + +void GLAPIENTRY +_vbo_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + vbo_VertexAttrib4fARB(index, x, y, z, w); +} diff --git a/mesalib/src/mesa/vbo/vbo_exec_array.c b/mesalib/src/mesa/vbo/vbo_exec_array.c new file mode 100644 index 000000000..39c295763 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_exec_array.c @@ -0,0 +1,965 @@ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2009 VMware, Inc. + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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. + * + **************************************************************************/ + +#include "main/glheader.h" +#include "main/context.h" +#include "main/state.h" +#include "main/api_validate.h" +#include "main/api_noop.h" +#include "main/varray.h" +#include "main/bufferobj.h" +#include "main/macros.h" +#include "glapi/dispatch.h" + +#include "vbo_context.h" + + +/** + * Compute min and max elements for glDraw[Range]Elements() calls. + */ +void +vbo_get_minmax_index(GLcontext *ctx, + const struct _mesa_prim *prim, + const struct _mesa_index_buffer *ib, + GLuint *min_index, GLuint *max_index) +{ + GLuint i; + GLsizei count = prim->count; + const void *indices; + + if (_mesa_is_bufferobj(ib->obj)) { + const GLvoid *map = ctx->Driver.MapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER_ARB, + GL_READ_ONLY, + ib->obj); + indices = ADD_POINTERS(map, ib->ptr); + } else { + indices = ib->ptr; + } + + switch (ib->type) { + case GL_UNSIGNED_INT: { + const GLuint *ui_indices = (const GLuint *)indices; + GLuint max_ui = ui_indices[count-1]; + GLuint min_ui = ui_indices[0]; + for (i = 0; i < count; i++) { + if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; + if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; + } + *min_index = min_ui; + *max_index = max_ui; + break; + } + case GL_UNSIGNED_SHORT: { + const GLushort *us_indices = (const GLushort *)indices; + GLuint max_us = us_indices[count-1]; + GLuint min_us = us_indices[0]; + for (i = 0; i < count; i++) { + if (us_indices[i] > max_us) max_us = us_indices[i]; + if (us_indices[i] < min_us) min_us = us_indices[i]; + } + *min_index = min_us; + *max_index = max_us; + break; + } + case GL_UNSIGNED_BYTE: { + const GLubyte *ub_indices = (const GLubyte *)indices; + GLuint max_ub = ub_indices[count-1]; + GLuint min_ub = ub_indices[0]; + for (i = 0; i < count; i++) { + if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; + if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; + } + *min_index = min_ub; + *max_index = max_ub; + break; + } + default: + assert(0); + break; + } + + if (_mesa_is_bufferobj(ib->obj)) { + ctx->Driver.UnmapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER_ARB, + ib->obj); + } +} + + +/** + * Check that element 'j' of the array has reasonable data. + * Map VBO if needed. + */ +static void +check_array_data(GLcontext *ctx, struct gl_client_array *array, + GLuint attrib, GLuint j) +{ + if (array->Enabled) { + const void *data = array->Ptr; + if (_mesa_is_bufferobj(array->BufferObj)) { + if (!array->BufferObj->Pointer) { + /* need to map now */ + array->BufferObj->Pointer = ctx->Driver.MapBuffer(ctx, + GL_ARRAY_BUFFER_ARB, + GL_READ_ONLY, + array->BufferObj); + } + data = ADD_POINTERS(data, array->BufferObj->Pointer); + } + switch (array->Type) { + case GL_FLOAT: + { + GLfloat *f = (GLfloat *) ((GLubyte *) data + array->StrideB * j); + GLuint k; + for (k = 0; k < array->Size; k++) { + if (IS_INF_OR_NAN(f[k]) || + f[k] >= 1.0e20 || f[k] <= -1.0e10) { + _mesa_printf("Bad array data:\n"); + _mesa_printf(" Element[%u].%u = %f\n", j, k, f[k]); + _mesa_printf(" Array %u at %p\n", attrib, (void* ) array); + _mesa_printf(" Type 0x%x, Size %d, Stride %d\n", + array->Type, array->Size, array->Stride); + _mesa_printf(" Address/offset %p in Buffer Object %u\n", + array->Ptr, array->BufferObj->Name); + f[k] = 1.0; /* XXX replace the bad value! */ + } + //assert(!IS_INF_OR_NAN(f[k])); + } + } + break; + default: + ; + } + } +} + + +/** + * Unmap the buffer object referenced by given array, if mapped. + */ +static void +unmap_array_buffer(GLcontext *ctx, struct gl_client_array *array) +{ + if (array->Enabled && + _mesa_is_bufferobj(array->BufferObj) && + _mesa_bufferobj_mapped(array->BufferObj)) { + ctx->Driver.UnmapBuffer(ctx, + GL_ARRAY_BUFFER_ARB, + array->BufferObj); + } +} + + +/** + * Examine the array's data for NaNs, etc. + */ +static void +check_draw_elements_data(GLcontext *ctx, GLsizei count, GLenum elemType, + const void *elements) +{ + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + const void *elemMap; + GLint i, k; + + if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { + elemMap = ctx->Driver.MapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER_ARB, + GL_READ_ONLY, + ctx->Array.ElementArrayBufferObj); + elements = ADD_POINTERS(elements, elemMap); + } + + for (i = 0; i < count; i++) { + GLuint j; + + /* j = element[i] */ + switch (elemType) { + case GL_UNSIGNED_BYTE: + j = ((const GLubyte *) elements)[i]; + break; + case GL_UNSIGNED_SHORT: + j = ((const GLushort *) elements)[i]; + break; + case GL_UNSIGNED_INT: + j = ((const GLuint *) elements)[i]; + break; + default: + assert(0); + } + + /* check element j of each enabled array */ + check_array_data(ctx, &arrayObj->Vertex, VERT_ATTRIB_POS, j); + check_array_data(ctx, &arrayObj->Normal, VERT_ATTRIB_NORMAL, j); + check_array_data(ctx, &arrayObj->Color, VERT_ATTRIB_COLOR0, j); + check_array_data(ctx, &arrayObj->SecondaryColor, VERT_ATTRIB_COLOR1, j); + for (k = 0; k < Elements(arrayObj->TexCoord); k++) { + check_array_data(ctx, &arrayObj->TexCoord[k], VERT_ATTRIB_TEX0 + k, j); + } + for (k = 0; k < Elements(arrayObj->VertexAttrib); k++) { + check_array_data(ctx, &arrayObj->VertexAttrib[k], VERT_ATTRIB_GENERIC0 + k, j); + } + } + + if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { + ctx->Driver.UnmapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER_ARB, + ctx->Array.ElementArrayBufferObj); + } + + unmap_array_buffer(ctx, &arrayObj->Vertex); + unmap_array_buffer(ctx, &arrayObj->Normal); + unmap_array_buffer(ctx, &arrayObj->Color); + for (k = 0; k < Elements(arrayObj->TexCoord); k++) { + unmap_array_buffer(ctx, &arrayObj->TexCoord[k]); + } + for (k = 0; k < Elements(arrayObj->VertexAttrib); k++) { + unmap_array_buffer(ctx, &arrayObj->VertexAttrib[k]); + } +} + + +/** + * Check array data, looking for NaNs, etc. + */ +static void +check_draw_arrays_data(GLcontext *ctx, GLint start, GLsizei count) +{ + /* TO DO */ +} + + +/** + * Print info/data for glDrawArrays(). + */ +static void +print_draw_arrays(GLcontext *ctx, struct vbo_exec_context *exec, + GLenum mode, GLint start, GLsizei count) +{ + int i; + + _mesa_printf("vbo_exec_DrawArrays(mode 0x%x, start %d, count %d):\n", + mode, start, count); + + for (i = 0; i < 32; i++) { + GLuint bufName = exec->array.inputs[i]->BufferObj->Name; + GLint stride = exec->array.inputs[i]->Stride; + _mesa_printf("attr %2d: size %d stride %d enabled %d " + "ptr %p Bufobj %u\n", + i, + exec->array.inputs[i]->Size, + stride, + /*exec->array.inputs[i]->Enabled,*/ + exec->array.legacy_array[i]->Enabled, + exec->array.inputs[i]->Ptr, + bufName); + + if (bufName) { + struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, bufName); + GLubyte *p = ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, + GL_READ_ONLY_ARB, buf); + int offset = (int) (GLintptr) exec->array.inputs[i]->Ptr; + float *f = (float *) (p + offset); + int *k = (int *) f; + int i; + int n = (count * stride) / 4; + if (n > 32) + n = 32; + _mesa_printf(" Data at offset %d:\n", offset); + for (i = 0; i < n; i++) { + _mesa_printf(" float[%d] = 0x%08x %f\n", i, k[i], f[i]); + } + ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, buf); + } + } +} + + +/** + * Just translate the arrayobj into a sane layout. + */ +static void +bind_array_obj(GLcontext *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + GLuint i; + + /* TODO: Fix the ArrayObj struct to keep legacy arrays in an array + * rather than as individual named arrays. Then this function can + * go away. + */ + exec->array.legacy_array[VERT_ATTRIB_POS] = &arrayObj->Vertex; + exec->array.legacy_array[VERT_ATTRIB_WEIGHT] = &arrayObj->Weight; + exec->array.legacy_array[VERT_ATTRIB_NORMAL] = &arrayObj->Normal; + exec->array.legacy_array[VERT_ATTRIB_COLOR0] = &arrayObj->Color; + exec->array.legacy_array[VERT_ATTRIB_COLOR1] = &arrayObj->SecondaryColor; + exec->array.legacy_array[VERT_ATTRIB_FOG] = &arrayObj->FogCoord; + exec->array.legacy_array[VERT_ATTRIB_COLOR_INDEX] = &arrayObj->Index; + if (arrayObj->PointSize.Enabled) { + /* this aliases COLOR_INDEX */ + exec->array.legacy_array[VERT_ATTRIB_POINT_SIZE] = &arrayObj->PointSize; + } + exec->array.legacy_array[VERT_ATTRIB_EDGEFLAG] = &arrayObj->EdgeFlag; + + for (i = 0; i < Elements(arrayObj->TexCoord); i++) + exec->array.legacy_array[VERT_ATTRIB_TEX0 + i] = &arrayObj->TexCoord[i]; + + for (i = 0; i < Elements(arrayObj->VertexAttrib); i++) { + assert(i < Elements(exec->array.generic_array)); + exec->array.generic_array[i] = &arrayObj->VertexAttrib[i]; + } + + exec->array.array_obj = arrayObj->Name; +} + + +static void +recalculate_input_bindings(GLcontext *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + const struct gl_client_array **inputs = &exec->array.inputs[0]; + GLbitfield const_inputs = 0x0; + GLuint i; + + exec->array.program_mode = get_program_mode(ctx); + exec->array.enabled_flags = ctx->Array.ArrayObj->_Enabled; + + switch (exec->array.program_mode) { + case VP_NONE: + /* When no vertex program is active (or the vertex program is generated + * from fixed-function state). We put the material values into the + * generic slots. This is the only situation where material values + * are available as per-vertex attributes. + */ + for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { + if (exec->array.legacy_array[i]->Enabled) + inputs[i] = exec->array.legacy_array[i]; + else { + inputs[i] = &vbo->legacy_currval[i]; + const_inputs |= 1 << i; + } + } + + for (i = 0; i < MAT_ATTRIB_MAX; i++) { + inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->mat_currval[i]; + const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); + } + + /* Could use just about anything, just to fill in the empty + * slots: + */ + for (i = MAT_ATTRIB_MAX; i < VERT_ATTRIB_MAX - VERT_ATTRIB_GENERIC0; i++) { + inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; + const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); + } + break; + + case VP_NV: + /* NV_vertex_program - attribute arrays alias and override + * conventional, legacy arrays. No materials, and the generic + * slots are vacant. + */ + for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { + if (exec->array.generic_array[i]->Enabled) + inputs[i] = exec->array.generic_array[i]; + else if (exec->array.legacy_array[i]->Enabled) + inputs[i] = exec->array.legacy_array[i]; + else { + inputs[i] = &vbo->legacy_currval[i]; + const_inputs |= 1 << i; + } + } + + /* Could use just about anything, just to fill in the empty + * slots: + */ + for (i = VERT_ATTRIB_GENERIC0; i < VERT_ATTRIB_MAX; i++) { + inputs[i] = &vbo->generic_currval[i - VERT_ATTRIB_GENERIC0]; + const_inputs |= 1 << i; + } + break; + + case VP_ARB: + /* GL_ARB_vertex_program or GLSL vertex shader - Only the generic[0] + * attribute array aliases and overrides the legacy position array. + * + * Otherwise, legacy attributes available in the legacy slots, + * generic attributes in the generic slots and materials are not + * available as per-vertex attributes. + */ + if (exec->array.generic_array[0]->Enabled) + inputs[0] = exec->array.generic_array[0]; + else if (exec->array.legacy_array[0]->Enabled) + inputs[0] = exec->array.legacy_array[0]; + else { + inputs[0] = &vbo->legacy_currval[0]; + const_inputs |= 1 << 0; + } + + for (i = 1; i <= VERT_ATTRIB_TEX7; i++) { + if (exec->array.legacy_array[i]->Enabled) + inputs[i] = exec->array.legacy_array[i]; + else { + inputs[i] = &vbo->legacy_currval[i]; + const_inputs |= 1 << i; + } + } + + for (i = 0; i < MAX_VERTEX_GENERIC_ATTRIBS; i++) { + if (exec->array.generic_array[i]->Enabled) + inputs[VERT_ATTRIB_GENERIC0 + i] = exec->array.generic_array[i]; + else { + inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; + const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); + } + + } + break; + } + + _mesa_set_varying_vp_inputs( ctx, ~const_inputs ); +} + + +static void +bind_arrays(GLcontext *ctx) +{ +#if 0 + if (ctx->Array.ArrayObj.Name != exec->array.array_obj) { + bind_array_obj(ctx); + recalculate_input_bindings(ctx); + } + else if (exec->array.program_mode != get_program_mode(ctx) || + exec->array.enabled_flags != ctx->Array.ArrayObj->_Enabled) { + + recalculate_input_bindings(ctx); + } +#else + bind_array_obj(ctx); + recalculate_input_bindings(ctx); +#endif +} + + + +/*********************************************************************** + * API functions. + */ + +static void GLAPIENTRY +vbo_exec_DrawArrays(GLenum mode, GLint start, GLsizei count) +{ + GET_CURRENT_CONTEXT(ctx); + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_prim prim[1]; + + if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) + return; + + FLUSH_CURRENT( ctx, 0 ); + + if (ctx->NewState) + _mesa_update_state( ctx ); + + if (!_mesa_valid_to_render(ctx, "glDrawArrays")) { + return; + } + +#if 0 + check_draw_arrays_data(ctx, start, count); +#else + (void) check_draw_arrays_data; +#endif + + bind_arrays( ctx ); + + /* Again... because we may have changed the bitmask of per-vertex varying + * attributes. If we regenerate the fixed-function vertex program now + * we may be able to prune down the number of vertex attributes which we + * need in the shader. + */ + if (ctx->NewState) + _mesa_update_state( ctx ); + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = start; + prim[0].count = count; + prim[0].indexed = 0; + + vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, + GL_TRUE, start, start + count - 1 ); + +#if 0 + print_draw_arrays(ctx, exec, mode, start, count); +#else + (void) print_draw_arrays; +#endif +} + + +/** + * Map GL_ELEMENT_ARRAY_BUFFER and print contents. + */ +static void +dump_element_buffer(GLcontext *ctx, GLenum type) +{ + const GLvoid *map = ctx->Driver.MapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER_ARB, + GL_READ_ONLY, + ctx->Array.ElementArrayBufferObj); + switch (type) { + case GL_UNSIGNED_BYTE: + { + const GLubyte *us = (const GLubyte *) map; + GLuint i; + for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size; i++) { + _mesa_printf("%02x ", us[i]); + if (i % 32 == 31) + _mesa_printf("\n"); + } + _mesa_printf("\n"); + } + break; + case GL_UNSIGNED_SHORT: + { + const GLushort *us = (const GLushort *) map; + GLuint i; + for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size / 2; i++) { + _mesa_printf("%04x ", us[i]); + if (i % 16 == 15) + _mesa_printf("\n"); + } + _mesa_printf("\n"); + } + break; + case GL_UNSIGNED_INT: + { + const GLuint *us = (const GLuint *) map; + GLuint i; + for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size / 4; i++) { + _mesa_printf("%08x ", us[i]); + if (i % 8 == 7) + _mesa_printf("\n"); + } + _mesa_printf("\n"); + } + break; + default: + ; + } + + ctx->Driver.UnmapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER_ARB, + ctx->Array.ElementArrayBufferObj); +} + +/* Inner support for both _mesa_DrawElements and _mesa_DrawRangeElements */ +static void +vbo_validated_drawrangeelements(GLcontext *ctx, GLenum mode, + GLboolean index_bounds_valid, + GLuint start, GLuint end, + GLsizei count, GLenum type, + const GLvoid *indices) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_index_buffer ib; + struct _mesa_prim prim[1]; + + FLUSH_CURRENT( ctx, 0 ); + + if (ctx->NewState) + _mesa_update_state( ctx ); + + if (!_mesa_valid_to_render(ctx, "glDraw[Range]Elements")) { + return; + } + + if (ctx->NewState) + _mesa_update_state( ctx ); + + bind_arrays( ctx ); + + ib.count = count; + ib.type = type; + ib.obj = ctx->Array.ElementArrayBufferObj; + ib.ptr = indices; + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = 0; + prim[0].count = count; + prim[0].indexed = 1; + + /* Need to give special consideration to rendering a range of + * indices starting somewhere above zero. Typically the + * application is issuing multiple DrawRangeElements() to draw + * successive primitives layed out linearly in the vertex arrays. + * Unless the vertex arrays are all in a VBO (or locked as with + * CVA), the OpenGL semantics imply that we need to re-read or + * re-upload the vertex data on each draw call. + * + * In the case of hardware tnl, we want to avoid starting the + * upload at zero, as it will mean every draw call uploads an + * increasing amount of not-used vertex data. Worse - in the + * software tnl module, all those vertices might be transformed and + * lit but never rendered. + * + * If we just upload or transform the vertices in start..end, + * however, the indices will be incorrect. + * + * At this level, we don't know exactly what the requirements of + * the backend are going to be, though it will likely boil down to + * either: + * + * 1) Do nothing, everything is in a VBO and is processed once + * only. + * + * 2) Adjust the indices and vertex arrays so that start becomes + * zero. + * + * Rather than doing anything here, I'll provide a helper function + * for the latter case elsewhere. + */ + + vbo->draw_prims( ctx, exec->array.inputs, prim, 1, &ib, + index_bounds_valid, start, end ); +} + +static void GLAPIENTRY +vbo_exec_DrawRangeElements(GLenum mode, + GLuint start, GLuint end, + GLsizei count, GLenum type, const GLvoid *indices) +{ + static GLuint warnCount = 0; + GET_CURRENT_CONTEXT(ctx); + + if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count, + type, indices )) + return; + + if (end >= ctx->Array.ArrayObj->_MaxElement) { + /* the max element is out of bounds of one or more enabled arrays */ + warnCount++; + + if (warnCount < 10) { + _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, count %d, " + "type 0x%x, indices=%p)\n" + "\tend is out of bounds (max=%u) " + "Element Buffer %u (size %d)\n" + "\tThis should probably be fixed in the application.", + start, end, count, type, indices, + ctx->Array.ArrayObj->_MaxElement - 1, + ctx->Array.ElementArrayBufferObj->Name, + ctx->Array.ElementArrayBufferObj->Size); + } + + if (0) + dump_element_buffer(ctx, type); + + if (0) + _mesa_print_arrays(ctx); + +#ifdef DEBUG + /* 'end' was out of bounds, but now let's check the actual array + * indexes to see if any of them are out of bounds. If so, warn + * and skip the draw to avoid potential segfault, etc. + */ + { + GLuint max = _mesa_max_buffer_index(ctx, count, type, indices, + ctx->Array.ElementArrayBufferObj); + if (max >= ctx->Array.ArrayObj->_MaxElement) { + if (warnCount < 10) { + _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, " + "count %d, type 0x%x, indices=%p)\n" + "\tindex=%u is out of bounds (max=%u) " + "Element Buffer %u (size %d)\n" + "\tSkipping the glDrawRangeElements() call", + start, end, count, type, indices, max, + ctx->Array.ArrayObj->_MaxElement - 1, + ctx->Array.ElementArrayBufferObj->Name, + ctx->Array.ElementArrayBufferObj->Size); + } + return; + } + /* XXX we could also find the min index and compare to 'start' + * to see if start is correct. But it's more likely to get the + * upper bound wrong. + */ + } +#endif + } + else if (0) { + _mesa_printf("glDraw[Range]Elements" + "(start %u, end %u, type 0x%x, count %d) ElemBuf %u\n", + start, end, type, count, + ctx->Array.ElementArrayBufferObj->Name); + } + +#if 0 + check_draw_elements_data(ctx, count, type, indices); +#else + (void) check_draw_elements_data; +#endif + + vbo_validated_drawrangeelements(ctx, mode, GL_TRUE, start, end, + count, type, indices); +} + + +static void GLAPIENTRY +vbo_exec_DrawElements(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + + if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices )) + return; + + vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, + count, type, indices); +} + +/* Inner support for both _mesa_DrawElements and _mesa_DrawRangeElements */ +static void +vbo_validated_multidrawelements(GLcontext *ctx, GLenum mode, + const GLsizei *count, GLenum type, + const GLvoid **indices, GLsizei primcount) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_index_buffer ib; + struct _mesa_prim *prim; + unsigned int index_type_size = 0; + uintptr_t min_index_ptr, max_index_ptr; + GLboolean fallback = GL_FALSE; + int i; + + if (primcount == 0) + return; + + FLUSH_CURRENT( ctx, 0 ); + + if (ctx->NewState) + _mesa_update_state( ctx ); + + if (!_mesa_valid_to_render(ctx, "glMultiDrawElements")) { + return; + } + + if (ctx->NewState) + _mesa_update_state( ctx ); + + prim = _mesa_calloc(primcount * sizeof(*prim)); + if (prim == NULL) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMultiDrawElements"); + return; + } + + /* Decide if we can do this all as one set of primitives sharing the + * same index buffer, or if we have to reset the index pointer per primitive. + */ + bind_arrays( ctx ); + + switch (type) { + case GL_UNSIGNED_INT: + index_type_size = 4; + break; + case GL_UNSIGNED_SHORT: + index_type_size = 2; + break; + case GL_UNSIGNED_BYTE: + index_type_size = 1; + break; + default: + assert(0); + } + + min_index_ptr = (uintptr_t)indices[0]; + max_index_ptr = 0; + for (i = 0; i < primcount; i++) { + min_index_ptr = MIN2(min_index_ptr, (uintptr_t)indices[i]); + max_index_ptr = MAX2(max_index_ptr, (uintptr_t)indices[i] + + index_type_size * count[i]); + } + + /* Check if we can handle this thing as a bunch of index offsets from the + * same index pointer. If we can't, then we have to fall back to doing + * a draw_prims per primitive. + */ + if (index_type_size != 1) { + for (i = 0; i < primcount; i++) { + if ((((uintptr_t)indices[i] - min_index_ptr) % index_type_size) != 0) { + fallback = GL_TRUE; + break; + } + } + } + + /* If the index buffer isn't in a VBO, then treating the application's + * subranges of the index buffer as one large index buffer may lead to + * us reading unmapped memory. + */ + if (!_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) + fallback = GL_TRUE; + + if (!fallback) { + ib.count = (max_index_ptr - min_index_ptr) / index_type_size; + ib.type = type; + ib.obj = ctx->Array.ElementArrayBufferObj; + ib.ptr = (void *)min_index_ptr; + + for (i = 0; i < primcount; i++) { + prim[i].begin = (i == 0); + prim[i].end = (i == primcount - 1); + prim[i].weak = 0; + prim[i].pad = 0; + prim[i].mode = mode; + prim[i].start = ((uintptr_t)indices[i] - min_index_ptr) / index_type_size; + prim[i].count = count[i]; + prim[i].indexed = 1; + } + + vbo->draw_prims(ctx, exec->array.inputs, prim, primcount, &ib, + GL_FALSE, ~0, ~0); + } else { + for (i = 0; i < primcount; i++) { + ib.count = count[i]; + ib.type = type; + ib.obj = ctx->Array.ElementArrayBufferObj; + ib.ptr = indices[i]; + + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = 0; + prim[0].count = count[i]; + prim[0].indexed = 1; + } + + vbo->draw_prims(ctx, exec->array.inputs, prim, 1, &ib, + GL_FALSE, ~0, ~0); + } + _mesa_free(prim); +} + +static void GLAPIENTRY +vbo_exec_MultiDrawElements(GLenum mode, + const GLsizei *count, GLenum type, + const GLvoid **indices, + GLsizei primcount) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + for (i = 0; i < primcount; i++) { + if (!_mesa_validate_DrawElements( ctx, mode, count[i], type, indices[i] )) + return; + } + + vbo_validated_multidrawelements(ctx, mode, count, type, indices, primcount); +} + + + +/*********************************************************************** + * Initialization + */ + +void +vbo_exec_array_init( struct vbo_exec_context *exec ) +{ +#if 1 + exec->vtxfmt.DrawArrays = vbo_exec_DrawArrays; + exec->vtxfmt.DrawElements = vbo_exec_DrawElements; + exec->vtxfmt.DrawRangeElements = vbo_exec_DrawRangeElements; + exec->vtxfmt.MultiDrawElementsEXT = vbo_exec_MultiDrawElements; +#else + exec->vtxfmt.DrawArrays = _mesa_noop_DrawArrays; + exec->vtxfmt.DrawElements = _mesa_noop_DrawElements; + exec->vtxfmt.DrawRangeElements = _mesa_noop_DrawRangeElements; + exec->vtxfmt.MultiDrawElementsEXT = _mesa_noop_MultiDrawElements; +#endif +} + + +void +vbo_exec_array_destroy( struct vbo_exec_context *exec ) +{ + /* nothing to do */ +} + + +/* This API entrypoint is not ordinarily used */ +void GLAPIENTRY +_mesa_DrawArrays(GLenum mode, GLint first, GLsizei count) +{ + vbo_exec_DrawArrays(mode, first, count); +} + + +/* This API entrypoint is not ordinarily used */ +void GLAPIENTRY +_mesa_DrawElements(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices) +{ + vbo_exec_DrawElements(mode, count, type, indices); +} + + +/* This API entrypoint is not ordinarily used */ +void GLAPIENTRY +_mesa_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, + GLenum type, const GLvoid *indices) +{ + vbo_exec_DrawRangeElements(mode, start, end, count, type, indices); +} + +/* GL_EXT_multi_draw_arrays */ +void GLAPIENTRY +_mesa_MultiDrawElementsEXT(GLenum mode, const GLsizei *count, GLenum type, + const GLvoid **indices, GLsizei primcount) +{ + vbo_exec_MultiDrawElements(mode, count, type, indices, primcount); +} diff --git a/mesalib/src/mesa/vbo/vbo_exec_draw.c b/mesalib/src/mesa/vbo/vbo_exec_draw.c new file mode 100644 index 000000000..0c258c535 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_exec_draw.c @@ -0,0 +1,413 @@ +/* + * Mesa 3-D graphics library + * Version: 7.2 + * + * Copyright (C) 1999-2008 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/context.h" +#include "main/enums.h" +#include "main/state.h" +#include "main/macros.h" + +#include "vbo_context.h" + + +static void +vbo_exec_debug_verts( struct vbo_exec_context *exec ) +{ + GLuint count = exec->vtx.vert_count; + GLuint i; + + _mesa_printf("%s: %u vertices %d primitives, %d vertsize\n", + __FUNCTION__, + count, + exec->vtx.prim_count, + exec->vtx.vertex_size); + + for (i = 0 ; i < exec->vtx.prim_count ; i++) { + struct _mesa_prim *prim = &exec->vtx.prim[i]; + _mesa_printf(" prim %d: %s%s %d..%d %s %s\n", + i, + _mesa_lookup_prim_by_nr(prim->mode), + prim->weak ? " (weak)" : "", + prim->start, + prim->start + prim->count, + prim->begin ? "BEGIN" : "(wrap)", + prim->end ? "END" : "(wrap)"); + } +} + + +/* + * NOTE: Need to have calculated primitives by this point -- do it on the fly. + * NOTE: Old 'parity' issue is gone. + */ +static GLuint +vbo_copy_vertices( struct vbo_exec_context *exec ) +{ + GLuint nr = exec->vtx.prim[exec->vtx.prim_count-1].count; + GLuint ovf, i; + GLuint sz = exec->vtx.vertex_size; + GLfloat *dst = exec->vtx.copied.buffer; + const GLfloat *src = (exec->vtx.buffer_map + + exec->vtx.prim[exec->vtx.prim_count-1].start * + exec->vtx.vertex_size); + + + switch (exec->ctx->Driver.CurrentExecPrimitive) { + case GL_POINTS: + return 0; + case GL_LINES: + ovf = nr&1; + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case GL_TRIANGLES: + ovf = nr%3; + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case GL_QUADS: + ovf = nr&3; + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case GL_LINE_STRIP: + if (nr == 0) { + return 0; + } + else { + _mesa_memcpy( dst, src+(nr-1)*sz, sz * sizeof(GLfloat) ); + return 1; + } + case GL_LINE_LOOP: + case GL_TRIANGLE_FAN: + case GL_POLYGON: + if (nr == 0) { + return 0; + } + else if (nr == 1) { + _mesa_memcpy( dst, src+0, sz * sizeof(GLfloat) ); + return 1; + } + else { + _mesa_memcpy( dst, src+0, sz * sizeof(GLfloat) ); + _mesa_memcpy( dst+sz, src+(nr-1)*sz, sz * sizeof(GLfloat) ); + return 2; + } + case GL_TRIANGLE_STRIP: + /* no parity issue, but need to make sure the tri is not drawn twice */ + if (nr & 1) { + exec->vtx.prim[exec->vtx.prim_count-1].count--; + } + /* fallthrough */ + case GL_QUAD_STRIP: + switch (nr) { + case 0: + ovf = 0; + break; + case 1: + ovf = 1; + break; + default: + ovf = 2 + (nr & 1); + break; + } + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case PRIM_OUTSIDE_BEGIN_END: + return 0; + default: + assert(0); + return 0; + } +} + + + +/* TODO: populate these as the vertex is defined: + */ +static void +vbo_exec_bind_arrays( GLcontext *ctx ) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct gl_client_array *arrays = exec->vtx.arrays; + const GLuint count = exec->vtx.vert_count; + const GLubyte *data = (GLubyte *) exec->vtx.buffer_map; + const GLuint *map; + GLuint attr; + GLbitfield varying_inputs = 0x0; + + /* Install the default (ie Current) attributes first, then overlay + * all active ones. + */ + switch (get_program_mode(exec->ctx)) { + case VP_NONE: + for (attr = 0; attr < 16; attr++) { + exec->vtx.inputs[attr] = &vbo->legacy_currval[attr]; + } + for (attr = 0; attr < MAT_ATTRIB_MAX; attr++) { + exec->vtx.inputs[attr + 16] = &vbo->mat_currval[attr]; + } + map = vbo->map_vp_none; + break; + case VP_NV: + case VP_ARB: + /* The aliasing of attributes for NV vertex programs has already + * occurred. NV vertex programs cannot access material values, + * nor attributes greater than VERT_ATTRIB_TEX7. + */ + for (attr = 0; attr < 16; attr++) { + exec->vtx.inputs[attr] = &vbo->legacy_currval[attr]; + exec->vtx.inputs[attr + 16] = &vbo->generic_currval[attr]; + } + map = vbo->map_vp_arb; + + /* check if VERT_ATTRIB_POS is not read but VERT_BIT_GENERIC0 is read. + * In that case we effectively need to route the data from + * glVertexAttrib(0, val) calls to feed into the GENERIC0 input. + */ + if ((ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_POS) == 0 && + (ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_GENERIC0)) { + exec->vtx.inputs[16] = exec->vtx.inputs[0]; + exec->vtx.attrsz[16] = exec->vtx.attrsz[0]; + exec->vtx.attrptr[16] = exec->vtx.attrptr[0]; + exec->vtx.attrsz[0] = 0; + } + break; + default: + assert(0); + } + + /* Make all active attributes (including edgeflag) available as + * arrays of floats. + */ + for (attr = 0; attr < VERT_ATTRIB_MAX ; attr++) { + const GLuint src = map[attr]; + + if (exec->vtx.attrsz[src]) { + /* override the default array set above */ + exec->vtx.inputs[attr] = &arrays[attr]; + + if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { + /* a real buffer obj: Ptr is an offset, not a pointer*/ + GLsizeiptr offset; + assert(exec->vtx.bufferobj->Pointer); /* buf should be mapped */ + offset = (GLbyte *) data - + (GLbyte *) exec->vtx.bufferobj->Pointer + + exec->vtx.bufferobj->Offset; + assert(offset >= 0); + arrays[attr].Ptr = (void *) offset; + } + else { + /* Ptr into ordinary app memory */ + arrays[attr].Ptr = (void *) data; + } + arrays[attr].Size = exec->vtx.attrsz[src]; + arrays[attr].StrideB = exec->vtx.vertex_size * sizeof(GLfloat); + arrays[attr].Stride = exec->vtx.vertex_size * sizeof(GLfloat); + arrays[attr].Type = GL_FLOAT; + arrays[attr].Format = GL_RGBA; + arrays[attr].Enabled = 1; + _mesa_reference_buffer_object(ctx, + &arrays[attr].BufferObj, + exec->vtx.bufferobj); + arrays[attr]._MaxElement = count; /* ??? */ + + data += exec->vtx.attrsz[src] * sizeof(GLfloat); + varying_inputs |= 1 << attr; + } + } + + _mesa_set_varying_vp_inputs( ctx, varying_inputs ); +} + + +static void +vbo_exec_vtx_unmap( struct vbo_exec_context *exec ) +{ + GLenum target = GL_ARRAY_BUFFER_ARB; + + if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { + GLcontext *ctx = exec->ctx; + + if (ctx->Driver.FlushMappedBufferRange) { + GLintptr offset = exec->vtx.buffer_used - exec->vtx.bufferobj->Offset; + GLsizeiptr length = (exec->vtx.buffer_ptr - exec->vtx.buffer_map) * sizeof(float); + + if (length) + ctx->Driver.FlushMappedBufferRange(ctx, target, + offset, length, + exec->vtx.bufferobj); + } + + exec->vtx.buffer_used += (exec->vtx.buffer_ptr - + exec->vtx.buffer_map) * sizeof(float); + + assert(exec->vtx.buffer_used <= VBO_VERT_BUFFER_SIZE); + assert(exec->vtx.buffer_ptr != NULL); + + ctx->Driver.UnmapBuffer(ctx, target, exec->vtx.bufferobj); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + exec->vtx.max_vert = 0; + } +} + + +void +vbo_exec_vtx_map( struct vbo_exec_context *exec ) +{ + GLcontext *ctx = exec->ctx; + const GLenum target = GL_ARRAY_BUFFER_ARB; + const GLenum access = GL_READ_WRITE_ARB; /* for MapBuffer */ + const GLenum accessRange = GL_MAP_WRITE_BIT | /* for MapBufferRange */ + GL_MAP_INVALIDATE_RANGE_BIT | + GL_MAP_UNSYNCHRONIZED_BIT | + GL_MAP_FLUSH_EXPLICIT_BIT | + MESA_MAP_NOWAIT_BIT; + const GLenum usage = GL_STREAM_DRAW_ARB; + + if (!_mesa_is_bufferobj(exec->vtx.bufferobj)) + return; + + if (exec->vtx.buffer_map != NULL) { + assert(0); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + } + + if (VBO_VERT_BUFFER_SIZE > exec->vtx.buffer_used + 1024 && + ctx->Driver.MapBufferRange) { + exec->vtx.buffer_map = + (GLfloat *)ctx->Driver.MapBufferRange(ctx, + target, + exec->vtx.buffer_used, + (VBO_VERT_BUFFER_SIZE - + exec->vtx.buffer_used), + accessRange, + exec->vtx.bufferobj); + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + } + + if (!exec->vtx.buffer_map) { + exec->vtx.buffer_used = 0; + + ctx->Driver.BufferData(ctx, target, + VBO_VERT_BUFFER_SIZE, + NULL, usage, exec->vtx.bufferobj); + + + if (ctx->Driver.MapBufferRange) + exec->vtx.buffer_map = + (GLfloat *)ctx->Driver.MapBufferRange(ctx, target, + 0, VBO_VERT_BUFFER_SIZE, + accessRange, + exec->vtx.bufferobj); + if (!exec->vtx.buffer_map) + exec->vtx.buffer_map = + (GLfloat *)ctx->Driver.MapBuffer(ctx, target, access, exec->vtx.bufferobj); + assert(exec->vtx.buffer_map); + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + } + + if (0) + _mesa_printf("map %d..\n", exec->vtx.buffer_used); +} + + + +/** + * Execute the buffer and save copied verts. + */ +void +vbo_exec_vtx_flush( struct vbo_exec_context *exec, GLboolean unmap ) +{ + if (0) + vbo_exec_debug_verts( exec ); + + if (exec->vtx.prim_count && + exec->vtx.vert_count) { + + exec->vtx.copied.nr = vbo_copy_vertices( exec ); + + if (exec->vtx.copied.nr != exec->vtx.vert_count) { + GLcontext *ctx = exec->ctx; + + /* Before the update_state() as this may raise _NEW_ARRAY + * from _mesa_set_varying_vp_inputs(). + */ + vbo_exec_bind_arrays( ctx ); + + if (ctx->NewState) + _mesa_update_state( ctx ); + + if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { + vbo_exec_vtx_unmap( exec ); + } + + if (0) + _mesa_printf("%s %d %d\n", __FUNCTION__, exec->vtx.prim_count, + exec->vtx.vert_count); + + vbo_context(ctx)->draw_prims( ctx, + exec->vtx.inputs, + exec->vtx.prim, + exec->vtx.prim_count, + NULL, + GL_TRUE, + 0, + exec->vtx.vert_count - 1); + + /* If using a real VBO, get new storage -- unless asked not to. + */ + if (_mesa_is_bufferobj(exec->vtx.bufferobj) && !unmap) { + vbo_exec_vtx_map( exec ); + } + } + } + + /* May have to unmap explicitly if we didn't draw: + */ + if (unmap && + _mesa_is_bufferobj(exec->vtx.bufferobj) && + exec->vtx.buffer_map) { + vbo_exec_vtx_unmap( exec ); + } + + + if (unmap || exec->vtx.vertex_size == 0) + exec->vtx.max_vert = 0; + else + exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / + (exec->vtx.vertex_size * sizeof(GLfloat))); + + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + exec->vtx.prim_count = 0; + exec->vtx.vert_count = 0; +} diff --git a/mesalib/src/mesa/vbo/vbo_exec_eval.c b/mesalib/src/mesa/vbo/vbo_exec_eval.c new file mode 100644 index 000000000..0c691b3a5 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_exec_eval.c @@ -0,0 +1,254 @@ +/* + * Mesa 3-D graphics library + * Version: 6.1 + * + * Copyright (C) 1999-2004 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +#include "main/glheader.h" +#include "main/context.h" +#include "main/macros.h" +#include "math/m_eval.h" +#include "glapi/dispatch.h" +#include "vbo_exec.h" + + +static void clear_active_eval1( struct vbo_exec_context *exec, GLuint attr ) +{ + exec->eval.map1[attr].map = NULL; +} + +static void clear_active_eval2( struct vbo_exec_context *exec, GLuint attr ) +{ + exec->eval.map2[attr].map = NULL; +} + +static void set_active_eval1( struct vbo_exec_context *exec, GLuint attr, GLuint dim, + struct gl_1d_map *map ) +{ + if (!exec->eval.map1[attr].map) { + exec->eval.map1[attr].map = map; + exec->eval.map1[attr].sz = dim; + } +} + +static void set_active_eval2( struct vbo_exec_context *exec, GLuint attr, GLuint dim, + struct gl_2d_map *map ) +{ + if (!exec->eval.map2[attr].map) { + exec->eval.map2[attr].map = map; + exec->eval.map2[attr].sz = dim; + } +} + +void vbo_exec_eval_update( struct vbo_exec_context *exec ) +{ + GLcontext *ctx = exec->ctx; + GLuint attr; + + /* Vertex program maps have priority over conventional attribs */ + + for (attr = 0; attr < VBO_ATTRIB_FIRST_MATERIAL; attr++) { + clear_active_eval1( exec, attr ); + clear_active_eval2( exec, attr ); + } + + /* _NEW_PROGRAM */ + if (ctx->VertexProgram._Enabled) { + for (attr = 0; attr < VBO_ATTRIB_FIRST_MATERIAL; attr++) { + /* _NEW_EVAL */ + if (ctx->Eval.Map1Attrib[attr]) + set_active_eval1( exec, attr, 4, &ctx->EvalMap.Map1Attrib[attr] ); + + if (ctx->Eval.Map2Attrib[attr]) + set_active_eval2( exec, attr, 4, &ctx->EvalMap.Map2Attrib[attr] ); + } + } + + if (ctx->Eval.Map1Color4) + set_active_eval1( exec, VBO_ATTRIB_COLOR0, 4, &ctx->EvalMap.Map1Color4 ); + + if (ctx->Eval.Map2Color4) + set_active_eval2( exec, VBO_ATTRIB_COLOR0, 4, &ctx->EvalMap.Map2Color4 ); + + if (ctx->Eval.Map1TextureCoord4) + set_active_eval1( exec, VBO_ATTRIB_TEX0, 4, &ctx->EvalMap.Map1Texture4 ); + else if (ctx->Eval.Map1TextureCoord3) + set_active_eval1( exec, VBO_ATTRIB_TEX0, 3, &ctx->EvalMap.Map1Texture3 ); + else if (ctx->Eval.Map1TextureCoord2) + set_active_eval1( exec, VBO_ATTRIB_TEX0, 2, &ctx->EvalMap.Map1Texture2 ); + else if (ctx->Eval.Map1TextureCoord1) + set_active_eval1( exec, VBO_ATTRIB_TEX0, 1, &ctx->EvalMap.Map1Texture1 ); + + if (ctx->Eval.Map2TextureCoord4) + set_active_eval2( exec, VBO_ATTRIB_TEX0, 4, &ctx->EvalMap.Map2Texture4 ); + else if (ctx->Eval.Map2TextureCoord3) + set_active_eval2( exec, VBO_ATTRIB_TEX0, 3, &ctx->EvalMap.Map2Texture3 ); + else if (ctx->Eval.Map2TextureCoord2) + set_active_eval2( exec, VBO_ATTRIB_TEX0, 2, &ctx->EvalMap.Map2Texture2 ); + else if (ctx->Eval.Map2TextureCoord1) + set_active_eval2( exec, VBO_ATTRIB_TEX0, 1, &ctx->EvalMap.Map2Texture1 ); + + if (ctx->Eval.Map1Normal) + set_active_eval1( exec, VBO_ATTRIB_NORMAL, 3, &ctx->EvalMap.Map1Normal ); + + if (ctx->Eval.Map2Normal) + set_active_eval2( exec, VBO_ATTRIB_NORMAL, 3, &ctx->EvalMap.Map2Normal ); + + if (ctx->Eval.Map1Vertex4) + set_active_eval1( exec, VBO_ATTRIB_POS, 4, &ctx->EvalMap.Map1Vertex4 ); + else if (ctx->Eval.Map1Vertex3) + set_active_eval1( exec, VBO_ATTRIB_POS, 3, &ctx->EvalMap.Map1Vertex3 ); + + if (ctx->Eval.Map2Vertex4) + set_active_eval2( exec, VBO_ATTRIB_POS, 4, &ctx->EvalMap.Map2Vertex4 ); + else if (ctx->Eval.Map2Vertex3) + set_active_eval2( exec, VBO_ATTRIB_POS, 3, &ctx->EvalMap.Map2Vertex3 ); + + exec->eval.recalculate_maps = 0; +} + + + +void vbo_exec_do_EvalCoord1f(struct vbo_exec_context *exec, GLfloat u) +{ + GLuint attr; + + for (attr = 1; attr <= VBO_ATTRIB_TEX7; attr++) { + struct gl_1d_map *map = exec->eval.map1[attr].map; + if (map) { + GLfloat uu = (u - map->u1) * map->du; + GLfloat data[4]; + + ASSIGN_4V(data, 0, 0, 0, 1); + + _math_horner_bezier_curve(map->Points, data, uu, + exec->eval.map1[attr].sz, + map->Order); + + COPY_SZ_4V( exec->vtx.attrptr[attr], + exec->vtx.attrsz[attr], + data ); + } + } + + /** Vertex -- EvalCoord1f is a noop if this map not enabled: + **/ + if (exec->eval.map1[0].map) { + struct gl_1d_map *map = exec->eval.map1[0].map; + GLfloat uu = (u - map->u1) * map->du; + GLfloat vertex[4]; + + ASSIGN_4V(vertex, 0, 0, 0, 1); + + _math_horner_bezier_curve(map->Points, vertex, uu, + exec->eval.map1[0].sz, + map->Order); + + if (exec->eval.map1[0].sz == 4) + CALL_Vertex4fv(GET_DISPATCH(), ( vertex )); + else + CALL_Vertex3fv(GET_DISPATCH(), ( vertex )); + } +} + + + +void vbo_exec_do_EvalCoord2f( struct vbo_exec_context *exec, + GLfloat u, GLfloat v ) +{ + GLuint attr; + + for (attr = 1; attr <= VBO_ATTRIB_TEX7; attr++) { + struct gl_2d_map *map = exec->eval.map2[attr].map; + if (map) { + GLfloat uu = (u - map->u1) * map->du; + GLfloat vv = (v - map->v1) * map->dv; + GLfloat data[4]; + + ASSIGN_4V(data, 0, 0, 0, 1); + + _math_horner_bezier_surf(map->Points, + data, + uu, vv, + exec->eval.map2[attr].sz, + map->Uorder, map->Vorder); + + COPY_SZ_4V( exec->vtx.attrptr[attr], + exec->vtx.attrsz[attr], + data ); + } + } + + /** Vertex -- EvalCoord2f is a noop if this map not enabled: + **/ + if (exec->eval.map2[0].map) { + struct gl_2d_map *map = exec->eval.map2[0].map; + GLfloat uu = (u - map->u1) * map->du; + GLfloat vv = (v - map->v1) * map->dv; + GLfloat vertex[4]; + + ASSIGN_4V(vertex, 0, 0, 0, 1); + + if (exec->ctx->Eval.AutoNormal) { + GLfloat normal[4]; + GLfloat du[4], dv[4]; + + _math_de_casteljau_surf(map->Points, vertex, du, dv, uu, vv, + exec->eval.map2[0].sz, + map->Uorder, map->Vorder); + + if (exec->eval.map2[0].sz == 4) { + du[0] = du[0]*vertex[3] - du[3]*vertex[0]; + du[1] = du[1]*vertex[3] - du[3]*vertex[1]; + du[2] = du[2]*vertex[3] - du[3]*vertex[2]; + + dv[0] = dv[0]*vertex[3] - dv[3]*vertex[0]; + dv[1] = dv[1]*vertex[3] - dv[3]*vertex[1]; + dv[2] = dv[2]*vertex[3] - dv[3]*vertex[2]; + } + + + CROSS3(normal, du, dv); + NORMALIZE_3FV(normal); + normal[3] = 1.0; + + COPY_SZ_4V( exec->vtx.attrptr[VBO_ATTRIB_NORMAL], + exec->vtx.attrsz[VBO_ATTRIB_NORMAL], + normal ); + + } + else { + _math_horner_bezier_surf(map->Points, vertex, uu, vv, + exec->eval.map2[0].sz, + map->Uorder, map->Vorder); + } + + if (exec->vtx.attrsz[0] == 4) + CALL_Vertex4fv(GET_DISPATCH(), ( vertex )); + else + CALL_Vertex3fv(GET_DISPATCH(), ( vertex )); + } +} + + diff --git a/mesalib/src/mesa/vbo/vbo_rebase.c b/mesalib/src/mesa/vbo/vbo_rebase.c new file mode 100644 index 000000000..3bf7ef580 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_rebase.c @@ -0,0 +1,223 @@ + +/* + * Mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +/* Helper for drivers which find themselves rendering a range of + * indices starting somewhere above zero. Typically the application + * is issuing multiple DrawArrays() or DrawElements() to draw + * successive primitives layed out linearly in the vertex arrays. + * Unless the vertex arrays are all in a VBO, the OpenGL semantics + * imply that we need to re-upload the vertex data on each draw call. + * In that case, we want to avoid starting the upload at zero, as it + * will mean every draw call uploads an increasing amount of not-used + * vertex data. Worse - in the software tnl module, all those + * vertices will be transformed and lit. + * + * If we just upload the new data, however, the indices will be + * incorrect as we tend to upload each set of vertex data to a new + * region. + * + * This file provides a helper to adjust the arrays, primitives and + * indices of a draw call so that it can be re-issued with a min_index + * of zero. + */ + +#include "main/glheader.h" +#include "main/imports.h" +#include "main/mtypes.h" + +#include "vbo.h" + + +#define REBASE(TYPE) \ +static void *rebase_##TYPE( const void *ptr, \ + GLuint count, \ + TYPE min_index ) \ +{ \ + const TYPE *in = (TYPE *)ptr; \ + TYPE *tmp_indices = malloc(count * sizeof(TYPE)); \ + GLuint i; \ + \ + for (i = 0; i < count; i++) \ + tmp_indices[i] = in[i] - min_index; \ + \ + return (void *)tmp_indices; \ +} + + +REBASE(GLuint) +REBASE(GLushort) +REBASE(GLubyte) + +GLboolean vbo_all_varyings_in_vbos( const struct gl_client_array *arrays[] ) +{ + GLuint i; + + for (i = 0; i < VERT_ATTRIB_MAX; i++) + if (arrays[i]->StrideB && + arrays[i]->BufferObj->Name == 0) + return GL_FALSE; + + return GL_TRUE; +} + +/* Adjust primitives, indices and vertex definitions so that min_index + * becomes zero. There are lots of reasons for wanting to do this, eg: + * + * Software tnl: + * - any time min_index != 0, otherwise unused vertices lower than + * min_index will be transformed. + * + * Hardware tnl: + * - if ib != NULL and min_index != 0, otherwise vertices lower than + * min_index will be uploaded. Requires adjusting index values. + * + * - if ib == NULL and min_index != 0, just for convenience so this doesn't + * have to be handled within the driver. + * + * Hardware tnl with VBO support: + * - as above, but only when vertices are not (all?) in VBO's. + * - can't save time by trying to upload half a vbo - typically it is + * all or nothing. + */ +void vbo_rebase_prims( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index, + vbo_draw_func draw ) +{ + struct gl_client_array tmp_arrays[VERT_ATTRIB_MAX]; + const struct gl_client_array *tmp_array_pointers[VERT_ATTRIB_MAX]; + + struct _mesa_index_buffer tmp_ib; + struct _mesa_prim *tmp_prims = NULL; + void *tmp_indices = NULL; + GLuint i; + + assert(min_index != 0); + + if (0) + _mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index); + + if (ib) { + /* Unfortunately need to adjust each index individually. + */ + GLboolean map_ib = ib->obj->Name && !ib->obj->Pointer; + void *ptr; + + if (map_ib) + ctx->Driver.MapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER, + GL_READ_ONLY_ARB, + ib->obj); + + + ptr = ADD_POINTERS(ib->obj->Pointer, ib->ptr); + + /* Some users might prefer it if we translated elements to + * GLuints here. Others wouldn't... + */ + switch (ib->type) { + case GL_UNSIGNED_INT: + tmp_indices = rebase_GLuint( ptr, ib->count, min_index ); + break; + case GL_UNSIGNED_SHORT: + tmp_indices = rebase_GLushort( ptr, ib->count, min_index ); + break; + case GL_UNSIGNED_BYTE: + tmp_indices = rebase_GLubyte( ptr, ib->count, min_index ); + break; + } + + if (map_ib) + ctx->Driver.UnmapBuffer(ctx, + GL_ELEMENT_ARRAY_BUFFER, + ib->obj); + + tmp_ib.obj = ctx->Shared->NullBufferObj; + tmp_ib.ptr = tmp_indices; + tmp_ib.count = ib->count; + tmp_ib.type = ib->type; + + ib = &tmp_ib; + } + else { + /* Otherwise the primitives need adjustment. + */ + tmp_prims = (struct _mesa_prim *)_mesa_malloc(sizeof(*prim) * nr_prims); + + for (i = 0; i < nr_prims; i++) { + /* If this fails, it could indicate an application error: + */ + assert(prim[i].start >= min_index); + + tmp_prims[i] = prim[i]; + tmp_prims[i].start -= min_index; + } + + prim = tmp_prims; + } + + /* Just need to adjust the pointer values on each incoming array. + * This works for VBO and non-vbo rendering and shouldn't pesimize + * VBO-based upload schemes. However this may still not be a fast + * path for hardware tnl for VBO based rendering as most machines + * will be happier if you just specify a starting vertex value in + * each primitive. + * + * For drivers with hardware tnl, you only want to do this if you + * are forced to, eg non-VBO indexed rendering with start != 0. + */ + for (i = 0; i < VERT_ATTRIB_MAX; i++) { + tmp_arrays[i] = *arrays[i]; + tmp_arrays[i].Ptr += min_index * tmp_arrays[i].StrideB; + tmp_array_pointers[i] = &tmp_arrays[i]; + } + + /* Re-issue the draw call. + */ + draw( ctx, + tmp_array_pointers, + prim, + nr_prims, + ib, + GL_TRUE, + 0, + max_index - min_index ); + + if (tmp_indices) + _mesa_free(tmp_indices); + + if (tmp_prims) + _mesa_free(tmp_prims); +} + + + diff --git a/mesalib/src/mesa/vbo/vbo_save.c b/mesalib/src/mesa/vbo/vbo_save.c new file mode 100644 index 000000000..9757c3d9f --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_save.c @@ -0,0 +1,110 @@ +/* + * Mesa 3-D graphics library + * Version: 7.2 + * + * Copyright (C) 1999-2008 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + + +#include "main/mtypes.h" +#include "main/bufferobj.h" +#include "main/dlist.h" +#include "main/vtxfmt.h" +#include "main/imports.h" + +#include "vbo_context.h" + + + +static void vbo_save_callback_init( GLcontext *ctx ) +{ + ctx->Driver.NewList = vbo_save_NewList; + ctx->Driver.EndList = vbo_save_EndList; + ctx->Driver.SaveFlushVertices = vbo_save_SaveFlushVertices; + ctx->Driver.BeginCallList = vbo_save_BeginCallList; + ctx->Driver.EndCallList = vbo_save_EndCallList; + ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin; +} + + + +void vbo_save_init( GLcontext *ctx ) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_save_context *save = &vbo->save; + + save->ctx = ctx; + + vbo_save_api_init( save ); + vbo_save_callback_init(ctx); + + { + struct gl_client_array *arrays = save->arrays; + memcpy(arrays, vbo->legacy_currval, 16 * sizeof(arrays[0])); + memcpy(arrays + 16, vbo->generic_currval, 16 * sizeof(arrays[0])); + } + + ctx->Driver.CurrentSavePrimitive = PRIM_UNKNOWN; +} + + +void vbo_save_destroy( GLcontext *ctx ) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_save_context *save = &vbo->save; + GLuint i; + + if (save->prim_store) { + if ( --save->prim_store->refcount == 0 ) { + FREE( save->prim_store ); + save->prim_store = NULL; + } + if ( --save->vertex_store->refcount == 0 ) { + _mesa_reference_buffer_object(ctx, + &save->vertex_store->bufferobj, NULL); + FREE( save->vertex_store ); + save->vertex_store = NULL; + } + } + + for (i = 0; i < VBO_ATTRIB_MAX; i++) { + _mesa_reference_buffer_object(ctx, &save->arrays[i].BufferObj, NULL); + } +} + + + + +/* Note that this can occur during the playback of a display list: + */ +void vbo_save_fallback( GLcontext *ctx, GLboolean fallback ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + if (fallback) + save->replay_flags |= VBO_SAVE_FALLBACK; + else + save->replay_flags &= ~VBO_SAVE_FALLBACK; +} + + diff --git a/mesalib/src/mesa/vbo/vbo_save.h b/mesalib/src/mesa/vbo/vbo_save.h new file mode 100644 index 000000000..86bbd24f7 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_save.h @@ -0,0 +1,184 @@ +/************************************************************************** + +Copyright 2002 Tungsten Graphics Inc., Cedar Park, Texas. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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. + +**************************************************************************/ + +/* + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + * + */ + +#ifndef VBO_SAVE_H +#define VBO_SAVE_H + +#include "main/mtypes.h" +#include "vbo.h" +#include "vbo_attrib.h" + + +struct vbo_save_copied_vtx { + GLfloat buffer[VBO_ATTRIB_MAX * 4 * VBO_MAX_COPIED_VERTS]; + GLuint nr; +}; + + +/* For display lists, this structure holds a run of vertices of the + * same format, and a strictly well-formed set of begin/end pairs, + * starting on the first vertex and ending at the last. Vertex + * copying on buffer breaks is precomputed according to these + * primitives, though there are situations where the copying will need + * correction at execute-time, perhaps by replaying the list as + * immediate mode commands. + * + * On executing this list, the 'current' values may be updated with + * the values of the final vertex, and often no fixup of the start of + * the vertex list is required. + * + * Eval and other commands that don't fit into these vertex lists are + * compiled using the fallback opcode mechanism provided by dlist.c. + */ +struct vbo_save_vertex_list { + GLubyte attrsz[VBO_ATTRIB_MAX]; + GLuint vertex_size; + + /* Copy of the final vertex from node->vertex_store->bufferobj. + * Keep this in regular (non-VBO) memory to avoid repeated + * map/unmap of the VBO when updating GL current data. + */ + GLfloat *current_data; + GLuint current_size; + + GLuint buffer_offset; + GLuint count; + GLuint wrap_count; /* number of copied vertices at start */ + GLboolean dangling_attr_ref; /* current attr implicitly referenced + outside the list */ + + struct _mesa_prim *prim; + GLuint prim_count; + + struct vbo_save_vertex_store *vertex_store; + struct vbo_save_primitive_store *prim_store; +}; + +/* These buffers should be a reasonable size to support upload to + * hardware. Current vbo implementation will re-upload on any + * changes, so don't make too big or apps which dynamically create + * dlists and use only a few times will suffer. + * + * Consider stategy of uploading regions from the VBO on demand in the + * case of dynamic vbos. Then make the dlist code signal that + * likelyhood as it occurs. No reason we couldn't change usage + * internally even though this probably isn't allowed for client VBOs? + */ +#define VBO_SAVE_BUFFER_SIZE (8*1024) /* dwords */ +#define VBO_SAVE_PRIM_SIZE 128 +#define VBO_SAVE_PRIM_WEAK 0x40 + +#define VBO_SAVE_FALLBACK 0x10000000 + +/* Storage to be shared among several vertex_lists. + */ +struct vbo_save_vertex_store { + struct gl_buffer_object *bufferobj; + GLfloat *buffer; + GLuint used; + GLuint refcount; +}; + +struct vbo_save_primitive_store { + struct _mesa_prim buffer[VBO_SAVE_PRIM_SIZE]; + GLuint used; + GLuint refcount; +}; + + +struct vbo_save_context { + GLcontext *ctx; + GLvertexformat vtxfmt; + struct gl_client_array arrays[VBO_ATTRIB_MAX]; + const struct gl_client_array *inputs[VBO_ATTRIB_MAX]; + + GLubyte attrsz[VBO_ATTRIB_MAX]; + GLubyte active_sz[VBO_ATTRIB_MAX]; + GLuint vertex_size; + + GLfloat *buffer; + GLuint count; + GLuint wrap_count; + GLuint replay_flags; + + struct _mesa_prim *prim; + GLuint prim_count, prim_max; + + struct vbo_save_vertex_store *vertex_store; + struct vbo_save_primitive_store *prim_store; + + GLfloat *buffer_ptr; /* cursor, points into buffer */ + GLfloat vertex[VBO_ATTRIB_MAX*4]; /* current values */ + GLfloat *attrptr[VBO_ATTRIB_MAX]; + GLuint vert_count; + GLuint max_vert; + GLboolean dangling_attr_ref; + GLboolean have_materials; + + GLuint opcode_vertex_list; + + struct vbo_save_copied_vtx copied; + + GLfloat *current[VBO_ATTRIB_MAX]; /* points into ctx->ListState */ + GLubyte *currentsz[VBO_ATTRIB_MAX]; +}; + + +void vbo_save_init( GLcontext *ctx ); +void vbo_save_destroy( GLcontext *ctx ); +void vbo_save_fallback( GLcontext *ctx, GLboolean fallback ); + +/* save_loopback.c: + */ +void vbo_loopback_vertex_list( GLcontext *ctx, + const GLfloat *buffer, + const GLubyte *attrsz, + const struct _mesa_prim *prim, + GLuint prim_count, + GLuint wrap_count, + GLuint vertex_size); + +/* Callbacks: + */ +void vbo_save_EndList( GLcontext *ctx ); +void vbo_save_NewList( GLcontext *ctx, GLuint list, GLenum mode ); +void vbo_save_EndCallList( GLcontext *ctx ); +void vbo_save_BeginCallList( GLcontext *ctx, struct gl_display_list *list ); +void vbo_save_SaveFlushVertices( GLcontext *ctx ); +GLboolean vbo_save_NotifyBegin( GLcontext *ctx, GLenum mode ); + +void vbo_save_playback_vertex_list( GLcontext *ctx, void *data ); + +void vbo_save_api_init( struct vbo_save_context *save ); + +#endif diff --git a/mesalib/src/mesa/vbo/vbo_save_api.c b/mesalib/src/mesa/vbo/vbo_save_api.c new file mode 100644 index 000000000..1771510d8 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_save_api.c @@ -0,0 +1,1238 @@ +/************************************************************************** + +Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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. + +**************************************************************************/ + +/* + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + + + +/* Display list compiler attempts to store lists of vertices with the + * same vertex layout. Additionally it attempts to minimize the need + * for execute-time fixup of these vertex lists, allowing them to be + * cached on hardware. + * + * There are still some circumstances where this can be thwarted, for + * example by building a list that consists of one very long primitive + * (eg Begin(Triangles), 1000 vertices, End), and calling that list + * from inside a different begin/end object (Begin(Lines), CallList, + * End). + * + * In that case the code will have to replay the list as individual + * commands through the Exec dispatch table, or fix up the copied + * vertices at execute-time. + * + * The other case where fixup is required is when a vertex attribute + * is introduced in the middle of a primitive. Eg: + * Begin(Lines) + * TexCoord1f() Vertex2f() + * TexCoord1f() Color3f() Vertex2f() + * End() + * + * If the current value of Color isn't known at compile-time, this + * primitive will require fixup. + * + * + * The list compiler currently doesn't attempt to compile lists + * containing EvalCoord or EvalPoint commands. On encountering one of + * these, compilation falls back to opcodes. + * + * This could be improved to fallback only when a mix of EvalCoord and + * Vertex commands are issued within a single primitive. + */ + + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/context.h" +#include "main/dlist.h" +#include "main/enums.h" +#include "main/macros.h" +#include "main/api_noop.h" +#include "main/api_validate.h" +#include "main/api_arrayelt.h" +#include "main/vtxfmt.h" +#include "glapi/dispatch.h" + +#include "vbo_context.h" + + +#ifdef ERROR +#undef ERROR +#endif + + +/* An interesting VBO number/name to help with debugging */ +#define VBO_BUF_ID 12345 + + +/* + * NOTE: Old 'parity' issue is gone, but copying can still be + * wrong-footed on replay. + */ +static GLuint _save_copy_vertices( GLcontext *ctx, + const struct vbo_save_vertex_list *node, + const GLfloat *src_buffer) +{ + struct vbo_save_context *save = &vbo_context( ctx )->save; + const struct _mesa_prim *prim = &node->prim[node->prim_count-1]; + GLuint nr = prim->count; + GLuint sz = save->vertex_size; + const GLfloat *src = src_buffer + prim->start * sz; + GLfloat *dst = save->copied.buffer; + GLuint ovf, i; + + if (prim->end) + return 0; + + switch( prim->mode ) + { + case GL_POINTS: + return 0; + case GL_LINES: + ovf = nr&1; + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); + return i; + case GL_TRIANGLES: + ovf = nr%3; + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); + return i; + case GL_QUADS: + ovf = nr&3; + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); + return i; + case GL_LINE_STRIP: + if (nr == 0) + return 0; + else { + _mesa_memcpy( dst, src+(nr-1)*sz, sz*sizeof(GLfloat) ); + return 1; + } + case GL_LINE_LOOP: + case GL_TRIANGLE_FAN: + case GL_POLYGON: + if (nr == 0) + return 0; + else if (nr == 1) { + _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) ); + return 1; + } else { + _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) ); + _mesa_memcpy( dst+sz, src+(nr-1)*sz, sz*sizeof(GLfloat) ); + return 2; + } + case GL_TRIANGLE_STRIP: + case GL_QUAD_STRIP: + switch (nr) { + case 0: ovf = 0; break; + case 1: ovf = 1; break; + default: ovf = 2 + (nr&1); break; + } + for (i = 0 ; i < ovf ; i++) + _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); + return i; + default: + assert(0); + return 0; + } +} + + +static struct vbo_save_vertex_store *alloc_vertex_store( GLcontext *ctx ) +{ + struct vbo_save_vertex_store *vertex_store = CALLOC_STRUCT(vbo_save_vertex_store); + + /* obj->Name needs to be non-zero, but won't ever be examined more + * closely than that. In particular these buffers won't be entered + * into the hash and can never be confused with ones visible to the + * user. Perhaps there could be a special number for internal + * buffers: + */ + vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx, + VBO_BUF_ID, + GL_ARRAY_BUFFER_ARB); + + ctx->Driver.BufferData( ctx, + GL_ARRAY_BUFFER_ARB, + VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat), + NULL, + GL_STATIC_DRAW_ARB, + vertex_store->bufferobj); + + vertex_store->buffer = NULL; + vertex_store->used = 0; + vertex_store->refcount = 1; + + return vertex_store; +} + +static void free_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) +{ + assert(!vertex_store->buffer); + + if (vertex_store->bufferobj) { + _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL); + } + + FREE( vertex_store ); +} + +static GLfloat *map_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) +{ + assert(vertex_store->bufferobj); + assert(!vertex_store->buffer); + vertex_store->buffer = (GLfloat *)ctx->Driver.MapBuffer(ctx, + GL_ARRAY_BUFFER_ARB, /* not used */ + GL_WRITE_ONLY, /* not used */ + vertex_store->bufferobj); + + assert(vertex_store->buffer); + return vertex_store->buffer + vertex_store->used; +} + +static void unmap_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) +{ + ctx->Driver.UnmapBuffer( ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj ); + vertex_store->buffer = NULL; +} + + +static struct vbo_save_primitive_store *alloc_prim_store( GLcontext *ctx ) +{ + struct vbo_save_primitive_store *store = CALLOC_STRUCT(vbo_save_primitive_store); + (void) ctx; + store->used = 0; + store->refcount = 1; + return store; +} + +static void _save_reset_counters( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + save->prim = save->prim_store->buffer + save->prim_store->used; + save->buffer = (save->vertex_store->buffer + + save->vertex_store->used); + + assert(save->buffer == save->buffer_ptr); + + if (save->vertex_size) + save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / + save->vertex_size); + else + save->max_vert = 0; + + save->vert_count = 0; + save->prim_count = 0; + save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used; + save->dangling_attr_ref = 0; +} + + +/* Insert the active immediate struct onto the display list currently + * being built. + */ +static void _save_compile_vertex_list( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + struct vbo_save_vertex_list *node; + + /* Allocate space for this structure in the display list currently + * being compiled. + */ + node = (struct vbo_save_vertex_list *) + _mesa_alloc_instruction(ctx, save->opcode_vertex_list, sizeof(*node)); + + if (!node) + return; + + /* Duplicate our template, increment refcounts to the storage structs: + */ + _mesa_memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz)); + node->vertex_size = save->vertex_size; + node->buffer_offset = (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat); + node->count = save->vert_count; + node->wrap_count = save->copied.nr; + node->dangling_attr_ref = save->dangling_attr_ref; + node->prim = save->prim; + node->prim_count = save->prim_count; + node->vertex_store = save->vertex_store; + node->prim_store = save->prim_store; + + node->vertex_store->refcount++; + node->prim_store->refcount++; + + + node->current_size = node->vertex_size - node->attrsz[0]; + node->current_data = NULL; + + if (node->current_size) { + /* If the malloc fails, we just pull the data out of the VBO + * later instead. + */ + node->current_data = MALLOC( node->current_size * sizeof(GLfloat) ); + if (node->current_data) { + const char *buffer = (const char *)save->vertex_store->buffer; + unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat); + unsigned vertex_offset = 0; + + if (node->count) + vertex_offset = (node->count-1) * node->vertex_size * sizeof(GLfloat); + + memcpy( node->current_data, + buffer + node->buffer_offset + vertex_offset + attr_offset, + node->current_size * sizeof(GLfloat) ); + } + } + + + + assert(node->attrsz[VBO_ATTRIB_POS] != 0 || + node->count == 0); + + if (save->dangling_attr_ref) + ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS; + + save->vertex_store->used += save->vertex_size * node->count; + save->prim_store->used += node->prim_count; + + + /* Copy duplicated vertices + */ + save->copied.nr = _save_copy_vertices( ctx, node, save->buffer ); + + + /* Deal with GL_COMPILE_AND_EXECUTE: + */ + if (ctx->ExecuteFlag) { + struct _glapi_table *dispatch = GET_DISPATCH(); + + _glapi_set_dispatch(ctx->Exec); + + vbo_loopback_vertex_list( ctx, + (const GLfloat *)((const char *)save->vertex_store->buffer + + node->buffer_offset), + node->attrsz, + node->prim, + node->prim_count, + node->wrap_count, + node->vertex_size); + + _glapi_set_dispatch(dispatch); + } + + + /* Decide whether the storage structs are full, or can be used for + * the next vertex lists as well. + */ + if (save->vertex_store->used > + VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) { + + /* Unmap old store: + */ + unmap_vertex_store( ctx, save->vertex_store ); + + /* Release old reference: + */ + save->vertex_store->refcount--; + assert(save->vertex_store->refcount != 0); + save->vertex_store = NULL; + + /* Allocate and map new store: + */ + save->vertex_store = alloc_vertex_store( ctx ); + save->buffer_ptr = map_vertex_store( ctx, save->vertex_store ); + } + + if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) { + save->prim_store->refcount--; + assert(save->prim_store->refcount != 0); + save->prim_store = alloc_prim_store( ctx ); + } + + /* Reset our structures for the next run of vertices: + */ + _save_reset_counters( ctx ); +} + + +/* TODO -- If no new vertices have been stored, don't bother saving + * it. + */ +static void _save_wrap_buffers( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLint i = save->prim_count - 1; + GLenum mode; + GLboolean weak; + + assert(i < (GLint) save->prim_max); + assert(i >= 0); + + /* Close off in-progress primitive. + */ + save->prim[i].count = (save->vert_count - + save->prim[i].start); + mode = save->prim[i].mode; + weak = save->prim[i].weak; + + /* store the copied vertices, and allocate a new list. + */ + _save_compile_vertex_list( ctx ); + + /* Restart interrupted primitive + */ + save->prim[0].mode = mode; + save->prim[0].weak = weak; + save->prim[0].begin = 0; + save->prim[0].end = 0; + save->prim[0].pad = 0; + save->prim[0].start = 0; + save->prim[0].count = 0; + save->prim_count = 1; +} + + + +/* Called only when buffers are wrapped as the result of filling the + * vertex_store struct. + */ +static void _save_wrap_filled_vertex( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLfloat *data = save->copied.buffer; + GLuint i; + + /* Emit a glEnd to close off the last vertex list. + */ + _save_wrap_buffers( ctx ); + + /* Copy stored stored vertices to start of new list. + */ + assert(save->max_vert - save->vert_count > save->copied.nr); + + for (i = 0 ; i < save->copied.nr ; i++) { + _mesa_memcpy( save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat)); + data += save->vertex_size; + save->buffer_ptr += save->vertex_size; + save->vert_count++; + } +} + + +static void _save_copy_to_current( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLuint i; + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + if (save->attrsz[i]) { + save->currentsz[i][0] = save->attrsz[i]; + COPY_CLEAN_4V(save->current[i], + save->attrsz[i], + save->attrptr[i]); + } + } +} + + +static void _save_copy_from_current( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLint i; + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + switch (save->attrsz[i]) { + case 4: save->attrptr[i][3] = save->current[i][3]; + case 3: save->attrptr[i][2] = save->current[i][2]; + case 2: save->attrptr[i][1] = save->current[i][1]; + case 1: save->attrptr[i][0] = save->current[i][0]; + case 0: break; + } + } +} + + + + +/* Flush existing data, set new attrib size, replay copied vertices. + */ +static void _save_upgrade_vertex( GLcontext *ctx, + GLuint attr, + GLuint newsz ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLuint oldsz; + GLuint i; + GLfloat *tmp; + + /* Store the current run of vertices, and emit a GL_END. Emit a + * BEGIN in the new buffer. + */ + if (save->vert_count) + _save_wrap_buffers( ctx ); + else + assert( save->copied.nr == 0 ); + + /* Do a COPY_TO_CURRENT to ensure back-copying works for the case + * when the attribute already exists in the vertex and is having + * its size increased. + */ + _save_copy_to_current( ctx ); + + /* Fix up sizes: + */ + oldsz = save->attrsz[attr]; + save->attrsz[attr] = newsz; + + save->vertex_size += newsz - oldsz; + save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / + save->vertex_size); + save->vert_count = 0; + + /* Recalculate all the attrptr[] values: + */ + for (i = 0, tmp = save->vertex ; i < VBO_ATTRIB_MAX ; i++) { + if (save->attrsz[i]) { + save->attrptr[i] = tmp; + tmp += save->attrsz[i]; + } + else + save->attrptr[i] = NULL; /* will not be dereferenced. */ + } + + /* Copy from current to repopulate the vertex with correct values. + */ + _save_copy_from_current( ctx ); + + /* Replay stored vertices to translate them to new format here. + * + * If there are copied vertices and the new (upgraded) attribute + * has not been defined before, this list is somewhat degenerate, + * and will need fixup at runtime. + */ + if (save->copied.nr) + { + GLfloat *data = save->copied.buffer; + GLfloat *dest = save->buffer; + GLuint j; + + /* Need to note this and fix up at runtime (or loopback): + */ + if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) { + assert(oldsz == 0); + save->dangling_attr_ref = GL_TRUE; + } + + for (i = 0 ; i < save->copied.nr ; i++) { + for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) { + if (save->attrsz[j]) { + if (j == attr) { + if (oldsz) { + COPY_CLEAN_4V( dest, oldsz, data ); + data += oldsz; + dest += newsz; + } + else { + COPY_SZ_4V( dest, newsz, save->current[attr] ); + dest += newsz; + } + } + else { + GLint sz = save->attrsz[j]; + COPY_SZ_4V( dest, sz, data ); + data += sz; + dest += sz; + } + } + } + } + + save->buffer_ptr = dest; + save->vert_count += save->copied.nr; + } +} + +static void save_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + if (sz > save->attrsz[attr]) { + /* New size is larger. Need to flush existing vertices and get + * an enlarged vertex format. + */ + _save_upgrade_vertex( ctx, attr, sz ); + } + else if (sz < save->active_sz[attr]) { + static GLfloat id[4] = { 0, 0, 0, 1 }; + GLuint i; + + /* New size is equal or smaller - just need to fill in some + * zeros. + */ + for (i = sz ; i <= save->attrsz[attr] ; i++) + save->attrptr[attr][i-1] = id[i-1]; + } + + save->active_sz[attr] = sz; +} + +static void _save_reset_vertex( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLuint i; + + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + save->attrsz[i] = 0; + save->active_sz[i] = 0; + } + + save->vertex_size = 0; +} + + + +#define ERROR() _mesa_compile_error( ctx, GL_INVALID_ENUM, __FUNCTION__ ); + + +/* Only one size for each attribute may be active at once. Eg. if + * Color3f is installed/active, then Color4f may not be, even if the + * vertex actually contains 4 color coordinates. This is because the + * 3f version won't otherwise set color[3] to 1.0 -- this is the job + * of the chooser function when switching between Color4f and Color3f. + */ +#define ATTR( A, N, V0, V1, V2, V3 ) \ +do { \ + struct vbo_save_context *save = &vbo_context(ctx)->save; \ + \ + if (save->active_sz[A] != N) \ + save_fixup_vertex(ctx, A, N); \ + \ + { \ + GLfloat *dest = save->attrptr[A]; \ + if (N>0) dest[0] = V0; \ + if (N>1) dest[1] = V1; \ + if (N>2) dest[2] = V2; \ + if (N>3) dest[3] = V3; \ + } \ + \ + if ((A) == 0) { \ + GLuint i; \ + \ + for (i = 0; i < save->vertex_size; i++) \ + save->buffer_ptr[i] = save->vertex[i]; \ + \ + save->buffer_ptr += save->vertex_size; \ + \ + if (++save->vert_count >= save->max_vert) \ + _save_wrap_filled_vertex( ctx ); \ + } \ +} while (0) + +#define TAG(x) _save_##x + +#include "vbo_attrib_tmp.h" + + + + +/* Cope with EvalCoord/CallList called within a begin/end object: + * -- Flush current buffer + * -- Fallback to opcodes for the rest of the begin/end object. + */ +static void DO_FALLBACK( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + if (save->vert_count || save->prim_count) { + GLint i = save->prim_count - 1; + + /* Close off in-progress primitive. + */ + save->prim[i].count = (save->vert_count - + save->prim[i].start); + + /* Need to replay this display list with loopback, + * unfortunately, otherwise this primitive won't be handled + * properly: + */ + save->dangling_attr_ref = 1; + + _save_compile_vertex_list( ctx ); + } + + _save_copy_to_current( ctx ); + _save_reset_vertex( ctx ); + _save_reset_counters( ctx ); + _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); + ctx->Driver.SaveNeedFlush = 0; +} + +static void GLAPIENTRY _save_EvalCoord1f( GLfloat u ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->EvalCoord1f( u ); +} + +static void GLAPIENTRY _save_EvalCoord1fv( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->EvalCoord1fv( v ); +} + +static void GLAPIENTRY _save_EvalCoord2f( GLfloat u, GLfloat v ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->EvalCoord2f( u, v ); +} + +static void GLAPIENTRY _save_EvalCoord2fv( const GLfloat *v ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->EvalCoord2fv( v ); +} + +static void GLAPIENTRY _save_EvalPoint1( GLint i ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->EvalPoint1( i ); +} + +static void GLAPIENTRY _save_EvalPoint2( GLint i, GLint j ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->EvalPoint2( i, j ); +} + +static void GLAPIENTRY _save_CallList( GLuint l ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->CallList( l ); +} + +static void GLAPIENTRY _save_CallLists( GLsizei n, GLenum type, const GLvoid *v ) +{ + GET_CURRENT_CONTEXT(ctx); + DO_FALLBACK(ctx); + ctx->Save->CallLists( n, type, v ); +} + + + + +/* This begin is hooked into ... Updating of + * ctx->Driver.CurrentSavePrimitive is already taken care of. + */ +GLboolean vbo_save_NotifyBegin( GLcontext *ctx, GLenum mode ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + GLuint i = save->prim_count++; + + assert(i < save->prim_max); + save->prim[i].mode = mode & ~VBO_SAVE_PRIM_WEAK; + save->prim[i].begin = 1; + save->prim[i].end = 0; + save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0; + save->prim[i].pad = 0; + save->prim[i].start = save->vert_count; + save->prim[i].count = 0; + + _mesa_install_save_vtxfmt( ctx, &save->vtxfmt ); + ctx->Driver.SaveNeedFlush = 1; + return GL_TRUE; +} + + + +static void GLAPIENTRY _save_End( void ) +{ + GET_CURRENT_CONTEXT( ctx ); + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLint i = save->prim_count - 1; + + ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END; + save->prim[i].end = 1; + save->prim[i].count = (save->vert_count - + save->prim[i].start); + + if (i == (GLint) save->prim_max - 1) { + _save_compile_vertex_list( ctx ); + assert(save->copied.nr == 0); + } + + /* Swap out this vertex format while outside begin/end. Any color, + * etc. received between here and the next begin will be compiled + * as opcodes. + */ + _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); +} + + +/* These are all errors as this vtxfmt is only installed inside + * begin/end pairs. + */ +static void GLAPIENTRY _save_DrawElements(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; (void) count; (void) type; (void) indices; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" ); +} + + +static void GLAPIENTRY _save_DrawRangeElements(GLenum mode, + GLuint start, GLuint end, + GLsizei count, GLenum type, + const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; (void) start; (void) end; (void) count; (void) type; (void) indices; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" ); +} + +static void GLAPIENTRY _save_DrawArrays(GLenum mode, GLint start, GLsizei count) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; (void) start; (void) count; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" ); +} + +static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) +{ + GET_CURRENT_CONTEXT(ctx); + (void) x1; (void) y1; (void) x2; (void) y2; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" ); +} + +static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 ) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; (void) i1; (void) i2; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" ); +} + +static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2, + GLint j1, GLint j2 ) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; (void) i1; (void) i2; (void) j1; (void) j2; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" ); +} + +static void GLAPIENTRY _save_Begin( GLenum mode ) +{ + GET_CURRENT_CONTEXT( ctx ); + (void) mode; + _mesa_compile_error( ctx, GL_INVALID_OPERATION, "Recursive glBegin" ); +} + + +/* Unlike the functions above, these are to be hooked into the vtxfmt + * maintained in ctx->ListState, active when the list is known or + * suspected to be outside any begin/end primitive. + */ +static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) +{ + GET_CURRENT_CONTEXT(ctx); + vbo_save_NotifyBegin( ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK ); + CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 )); + CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 )); + CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 )); + CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 )); + CALL_End(GET_DISPATCH(), ()); +} + + +static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) + return; + + _ae_map_vbos( ctx ); + + vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK ); + + for (i = 0; i < count; i++) + CALL_ArrayElement(GET_DISPATCH(), (start + i)); + CALL_End(GET_DISPATCH(), ()); + + _ae_unmap_vbos( ctx ); +} + +/* Could do better by copying the arrays and element list intact and + * then emitting an indexed prim at runtime. + */ +static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices )) + return; + + _ae_map_vbos( ctx ); + + if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) + indices = ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices); + + vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK ); + + switch (type) { + case GL_UNSIGNED_BYTE: + for (i = 0 ; i < count ; i++) + CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] )); + break; + case GL_UNSIGNED_SHORT: + for (i = 0 ; i < count ; i++) + CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] )); + break; + case GL_UNSIGNED_INT: + for (i = 0 ; i < count ; i++) + CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] )); + break; + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" ); + break; + } + + CALL_End(GET_DISPATCH(), ()); + + _ae_unmap_vbos( ctx ); +} + +static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode, + GLuint start, GLuint end, + GLsizei count, GLenum type, + const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + if (_mesa_validate_DrawRangeElements( ctx, mode, + start, end, + count, type, indices )) + _save_OBE_DrawElements( mode, count, type, indices ); +} + + + + + +static void _save_vtxfmt_init( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLvertexformat *vfmt = &save->vtxfmt; + + vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */ + vfmt->Begin = _save_Begin; + vfmt->Color3f = _save_Color3f; + vfmt->Color3fv = _save_Color3fv; + vfmt->Color4f = _save_Color4f; + vfmt->Color4fv = _save_Color4fv; + vfmt->EdgeFlag = _save_EdgeFlag; + vfmt->End = _save_End; + vfmt->FogCoordfEXT = _save_FogCoordfEXT; + vfmt->FogCoordfvEXT = _save_FogCoordfvEXT; + vfmt->Indexf = _save_Indexf; + vfmt->Indexfv = _save_Indexfv; + vfmt->Materialfv = _save_Materialfv; + vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f; + vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv; + vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f; + vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv; + vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f; + vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv; + vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f; + vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv; + vfmt->Normal3f = _save_Normal3f; + vfmt->Normal3fv = _save_Normal3fv; + vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT; + vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT; + vfmt->TexCoord1f = _save_TexCoord1f; + vfmt->TexCoord1fv = _save_TexCoord1fv; + vfmt->TexCoord2f = _save_TexCoord2f; + vfmt->TexCoord2fv = _save_TexCoord2fv; + vfmt->TexCoord3f = _save_TexCoord3f; + vfmt->TexCoord3fv = _save_TexCoord3fv; + vfmt->TexCoord4f = _save_TexCoord4f; + vfmt->TexCoord4fv = _save_TexCoord4fv; + vfmt->Vertex2f = _save_Vertex2f; + vfmt->Vertex2fv = _save_Vertex2fv; + vfmt->Vertex3f = _save_Vertex3f; + vfmt->Vertex3fv = _save_Vertex3fv; + vfmt->Vertex4f = _save_Vertex4f; + vfmt->Vertex4fv = _save_Vertex4fv; + vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB; + vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB; + vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB; + vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB; + vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB; + vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB; + vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB; + vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB; + + vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV; + vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV; + vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV; + vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV; + vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV; + vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV; + vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV; + vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV; + + /* This will all require us to fallback to saving the list as opcodes: + */ + vfmt->CallList = _save_CallList; /* inside begin/end */ + vfmt->CallLists = _save_CallLists; /* inside begin/end */ + vfmt->EvalCoord1f = _save_EvalCoord1f; + vfmt->EvalCoord1fv = _save_EvalCoord1fv; + vfmt->EvalCoord2f = _save_EvalCoord2f; + vfmt->EvalCoord2fv = _save_EvalCoord2fv; + vfmt->EvalPoint1 = _save_EvalPoint1; + vfmt->EvalPoint2 = _save_EvalPoint2; + + /* These are all errors as we at least know we are in some sort of + * begin/end pair: + */ + vfmt->EvalMesh1 = _save_EvalMesh1; + vfmt->EvalMesh2 = _save_EvalMesh2; + vfmt->Begin = _save_Begin; + vfmt->Rectf = _save_Rectf; + vfmt->DrawArrays = _save_DrawArrays; + vfmt->DrawElements = _save_DrawElements; + vfmt->DrawRangeElements = _save_DrawRangeElements; + /* Loops back into vfmt->DrawElements */ + vfmt->MultiDrawElementsEXT = _mesa_noop_MultiDrawElements; + +} + + +void vbo_save_SaveFlushVertices( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + /* Noop when we are actually active: + */ + if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM || + ctx->Driver.CurrentSavePrimitive <= GL_POLYGON) + return; + + if (save->vert_count || + save->prim_count) + _save_compile_vertex_list( ctx ); + + _save_copy_to_current( ctx ); + _save_reset_vertex( ctx ); + _save_reset_counters( ctx ); + ctx->Driver.SaveNeedFlush = 0; +} + +void vbo_save_NewList( GLcontext *ctx, GLuint list, GLenum mode ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + (void) list; (void) mode; + + if (!save->prim_store) + save->prim_store = alloc_prim_store( ctx ); + + if (!save->vertex_store) + save->vertex_store = alloc_vertex_store( ctx ); + + save->buffer_ptr = map_vertex_store( ctx, save->vertex_store ); + + _save_reset_vertex( ctx ); + _save_reset_counters( ctx ); + ctx->Driver.SaveNeedFlush = 0; +} + +void vbo_save_EndList( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + /* EndList called inside a (saved) Begin/End pair? + */ + if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) { + + if (save->prim_count > 0) { + GLint i = save->prim_count - 1; + ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END; + save->prim[i].end = 0; + save->prim[i].count = (save->vert_count - + save->prim[i].start); + } + + /* Make sure this vertex list gets replayed by the "loopback" + * mechanism: + */ + save->dangling_attr_ref = 1; + vbo_save_SaveFlushVertices( ctx ); + + /* Swap out this vertex format while outside begin/end. Any color, + * etc. received between here and the next begin will be compiled + * as opcodes. + */ + _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); + } + + unmap_vertex_store( ctx, save->vertex_store ); + + assert(save->vertex_size == 0); +} + +void vbo_save_BeginCallList( GLcontext *ctx, struct gl_display_list *dlist ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + save->replay_flags |= dlist->Flags; +} + +void vbo_save_EndCallList( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + if (ctx->ListState.CallDepth == 1) { + /* This is correct: want to keep only the VBO_SAVE_FALLBACK + * flag, if it is set: + */ + save->replay_flags &= VBO_SAVE_FALLBACK; + } +} + + +static void vbo_destroy_vertex_list( GLcontext *ctx, void *data ) +{ + struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data; + (void) ctx; + + if ( --node->vertex_store->refcount == 0 ) + free_vertex_store( ctx, node->vertex_store ); + + if ( --node->prim_store->refcount == 0 ) + FREE( node->prim_store ); + + if (node->current_data) { + FREE(node->current_data); + node->current_data = NULL; + } +} + + +static void vbo_print_vertex_list( GLcontext *ctx, void *data ) +{ + struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data; + GLuint i; + (void) ctx; + + _mesa_printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n", + node->count, + node->prim_count, + node->vertex_size); + + for (i = 0 ; i < node->prim_count ; i++) { + struct _mesa_prim *prim = &node->prim[i]; + _mesa_debug(NULL, " prim %d: %s%s %d..%d %s %s\n", + i, + _mesa_lookup_prim_by_nr(prim->mode), + prim->weak ? " (weak)" : "", + prim->start, + prim->start + prim->count, + (prim->begin) ? "BEGIN" : "(wrap)", + (prim->end) ? "END" : "(wrap)"); + } +} + + +static void _save_current_init( GLcontext *ctx ) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLint i; + + for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) { + const GLuint j = i - VBO_ATTRIB_POS; + ASSERT(j < VERT_ATTRIB_MAX); + save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j]; + save->current[i] = ctx->ListState.CurrentAttrib[j]; + } + + for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) { + const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL; + ASSERT(j < MAT_ATTRIB_MAX); + save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j]; + save->current[i] = ctx->ListState.CurrentMaterial[j]; + } +} + +/** + * Initialize the display list compiler + */ +void vbo_save_api_init( struct vbo_save_context *save ) +{ + GLcontext *ctx = save->ctx; + GLuint i; + + save->opcode_vertex_list = + _mesa_alloc_opcode( ctx, + sizeof(struct vbo_save_vertex_list), + vbo_save_playback_vertex_list, + vbo_destroy_vertex_list, + vbo_print_vertex_list ); + + ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin; + + _save_vtxfmt_init( ctx ); + _save_current_init( ctx ); + + /* These will actually get set again when binding/drawing */ + for (i = 0; i < VBO_ATTRIB_MAX; i++) + save->inputs[i] = &save->arrays[i]; + + /* Hook our array functions into the outside-begin-end vtxfmt in + * ctx->ListState. + */ + ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf; + ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays; + ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements; + ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements; + /* loops back into _save_OBE_DrawElements */ + ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = _mesa_noop_MultiDrawElements; + _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); +} + diff --git a/mesalib/src/mesa/vbo/vbo_save_draw.c b/mesalib/src/mesa/vbo/vbo_save_draw.c new file mode 100644 index 000000000..d834fa1f2 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_save_draw.c @@ -0,0 +1,290 @@ +/* + * Mesa 3-D graphics library + * Version: 7.2 + * + * Copyright (C) 1999-2008 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 "main/glheader.h" +#include "main/bufferobj.h" +#include "main/context.h" +#include "main/imports.h" +#include "main/mtypes.h" +#include "main/macros.h" +#include "main/light.h" +#include "main/state.h" + +#include "vbo_context.h" + + +/* + * After playback, copy everything but the position from the + * last vertex to the saved state + */ +static void _playback_copy_to_current( GLcontext *ctx, + const struct vbo_save_vertex_list *node ) +{ + struct vbo_context *vbo = vbo_context(ctx); + GLfloat vertex[VBO_ATTRIB_MAX * 4]; + GLfloat *data; + GLuint i, offset; + + if (node->current_size == 0) + return; + + if (node->current_data) { + data = node->current_data; + } + else { + data = vertex; + + if (node->count) + offset = (node->buffer_offset + + (node->count-1) * node->vertex_size * sizeof(GLfloat)); + else + offset = node->buffer_offset; + + ctx->Driver.GetBufferSubData( ctx, 0, offset, + node->vertex_size * sizeof(GLfloat), + data, node->vertex_store->bufferobj ); + + data += node->attrsz[0]; /* skip vertex position */ + } + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + if (node->attrsz[i]) { + GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; + GLfloat tmp[4]; + + COPY_CLEAN_4V(tmp, + node->attrsz[i], + data); + + if (memcmp(current, tmp, 4 * sizeof(GLfloat)) != 0) + { + memcpy(current, tmp, 4 * sizeof(GLfloat)); + + vbo->currval[i].Size = node->attrsz[i]; + + if (i >= VBO_ATTRIB_FIRST_MATERIAL && + i <= VBO_ATTRIB_LAST_MATERIAL) + ctx->NewState |= _NEW_LIGHT; + + ctx->NewState |= _NEW_CURRENT_ATTRIB; + } + + data += node->attrsz[i]; + } + } + + /* Colormaterial -- this kindof sucks. + */ + if (ctx->Light.ColorMaterialEnabled) { + _mesa_update_color_material(ctx, ctx->Current.Attrib[VBO_ATTRIB_COLOR0]); + } + + /* CurrentExecPrimitive + */ + if (node->prim_count) { + const struct _mesa_prim *prim = &node->prim[node->prim_count - 1]; + if (prim->end) + ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; + else + ctx->Driver.CurrentExecPrimitive = prim->mode; + } +} + + + +/* Treat the vertex storage as a VBO, define vertex arrays pointing + * into it: + */ +static void vbo_bind_vertex_list( GLcontext *ctx, + const struct vbo_save_vertex_list *node ) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_save_context *save = &vbo->save; + struct gl_client_array *arrays = save->arrays; + GLuint buffer_offset = node->buffer_offset; + const GLuint *map; + GLuint attr; + GLubyte node_attrsz[VBO_ATTRIB_MAX]; /* copy of node->attrsz[] */ + GLbitfield varying_inputs = 0x0; + + memcpy(node_attrsz, node->attrsz, sizeof(node->attrsz)); + + /* Install the default (ie Current) attributes first, then overlay + * all active ones. + */ + switch (get_program_mode(ctx)) { + case VP_NONE: + for (attr = 0; attr < 16; attr++) { + save->inputs[attr] = &vbo->legacy_currval[attr]; + } + for (attr = 0; attr < MAT_ATTRIB_MAX; attr++) { + save->inputs[attr + 16] = &vbo->mat_currval[attr]; + } + map = vbo->map_vp_none; + break; + case VP_NV: + case VP_ARB: + /* The aliasing of attributes for NV vertex programs has already + * occurred. NV vertex programs cannot access material values, + * nor attributes greater than VERT_ATTRIB_TEX7. + */ + for (attr = 0; attr < 16; attr++) { + save->inputs[attr] = &vbo->legacy_currval[attr]; + save->inputs[attr + 16] = &vbo->generic_currval[attr]; + } + map = vbo->map_vp_arb; + + /* check if VERT_ATTRIB_POS is not read but VERT_BIT_GENERIC0 is read. + * In that case we effectively need to route the data from + * glVertexAttrib(0, val) calls to feed into the GENERIC0 input. + */ + if ((ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_POS) == 0 && + (ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_GENERIC0)) { + save->inputs[16] = save->inputs[0]; + node_attrsz[16] = node_attrsz[0]; + node_attrsz[0] = 0; + } + break; + default: + assert(0); + } + + for (attr = 0; attr < VERT_ATTRIB_MAX; attr++) { + GLuint src = map[attr]; + + if (node_attrsz[src]) { + /* override the default array set above */ + save->inputs[attr] = &arrays[attr]; + + arrays[attr].Ptr = (const GLubyte *) NULL + buffer_offset; + arrays[attr].Size = node->attrsz[src]; + arrays[attr].StrideB = node->vertex_size * sizeof(GLfloat); + arrays[attr].Stride = node->vertex_size * sizeof(GLfloat); + arrays[attr].Type = GL_FLOAT; + arrays[attr].Format = GL_RGBA; + arrays[attr].Enabled = 1; + _mesa_reference_buffer_object(ctx, + &arrays[attr].BufferObj, + node->vertex_store->bufferobj); + arrays[attr]._MaxElement = node->count; /* ??? */ + + assert(arrays[attr].BufferObj->Name); + + buffer_offset += node->attrsz[src] * sizeof(GLfloat); + varying_inputs |= 1<<attr; + } + } + + _mesa_set_varying_vp_inputs( ctx, varying_inputs ); +} + +static void vbo_save_loopback_vertex_list( GLcontext *ctx, + const struct vbo_save_vertex_list *list ) +{ + const char *buffer = ctx->Driver.MapBuffer(ctx, + GL_ARRAY_BUFFER_ARB, + GL_READ_ONLY, /* ? */ + list->vertex_store->bufferobj); + + vbo_loopback_vertex_list( ctx, + (const GLfloat *)(buffer + list->buffer_offset), + list->attrsz, + list->prim, + list->prim_count, + list->wrap_count, + list->vertex_size); + + ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, + list->vertex_store->bufferobj); +} + + +/** + * Execute the buffer and save copied verts. + */ +void vbo_save_playback_vertex_list( GLcontext *ctx, void *data ) +{ + const struct vbo_save_vertex_list *node = (const struct vbo_save_vertex_list *) data; + struct vbo_save_context *save = &vbo_context(ctx)->save; + + FLUSH_CURRENT(ctx, 0); + + if (node->prim_count > 0 && node->count > 0) { + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END && + node->prim[0].begin) { + + /* Degenerate case: list is called inside begin/end pair and + * includes operations such as glBegin or glDrawArrays. + */ + if (0) + _mesa_printf("displaylist recursive begin"); + + vbo_save_loopback_vertex_list( ctx, node ); + return; + } + else if (save->replay_flags) { + /* Various degnerate cases: translate into immediate mode + * calls rather than trying to execute in place. + */ + vbo_save_loopback_vertex_list( ctx, node ); + return; + } + + if (ctx->NewState) + _mesa_update_state( ctx ); + + /* XXX also need to check if shader enabled, but invalid */ + if ((ctx->VertexProgram.Enabled && !ctx->VertexProgram._Enabled) || + (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBegin (invalid vertex/fragment program)"); + return; + } + + vbo_bind_vertex_list( ctx, node ); + + /* Again... + */ + if (ctx->NewState) + _mesa_update_state( ctx ); + + vbo_context(ctx)->draw_prims( ctx, + save->inputs, + node->prim, + node->prim_count, + NULL, + GL_TRUE, + 0, /* Node is a VBO, so this is ok */ + node->count - 1); + } + + /* Copy to current? + */ + _playback_copy_to_current( ctx, node ); +} diff --git a/mesalib/src/mesa/vbo/vbo_save_loopback.c b/mesalib/src/mesa/vbo/vbo_save_loopback.c new file mode 100644 index 000000000..b7a74e453 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_save_loopback.c @@ -0,0 +1,191 @@ +/************************************************************************** + * + * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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. + * + **************************************************************************/ + +#include "main/context.h" +#include "main/glheader.h" +#include "main/enums.h" +#include "main/imports.h" +#include "main/macros.h" +#include "main/mtypes.h" +#include "glapi/dispatch.h" +#include "glapi/glapi.h" + +#include "vbo_context.h" + + + +typedef void (*attr_func)( GLcontext *ctx, GLint target, const GLfloat * ); + + +/* This file makes heavy use of the aliasing of NV vertex attributes + * with the legacy attributes, and also with ARB and Material + * attributes as currently implemented. + */ +static void VertexAttrib1fvNV(GLcontext *ctx, GLint target, const GLfloat *v) +{ + CALL_VertexAttrib1fvNV(ctx->Exec, (target, v)); +} + +static void VertexAttrib2fvNV(GLcontext *ctx, GLint target, const GLfloat *v) +{ + CALL_VertexAttrib2fvNV(ctx->Exec, (target, v)); +} + +static void VertexAttrib3fvNV(GLcontext *ctx, GLint target, const GLfloat *v) +{ + CALL_VertexAttrib3fvNV(ctx->Exec, (target, v)); +} + +static void VertexAttrib4fvNV(GLcontext *ctx, GLint target, const GLfloat *v) +{ + CALL_VertexAttrib4fvNV(ctx->Exec, (target, v)); +} + +static attr_func vert_attrfunc[4] = { + VertexAttrib1fvNV, + VertexAttrib2fvNV, + VertexAttrib3fvNV, + VertexAttrib4fvNV +}; + +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, + const GLfloat *buffer, + const struct _mesa_prim *prim, + GLuint wrap_count, + GLuint vertex_size, + const struct loopback_attr *la, GLuint nr ) +{ + GLint start = prim->start; + GLint end = start + prim->count; + const GLfloat *data; + GLint j; + GLuint k; + + if (0) + _mesa_printf("loopback prim %s(%s,%s) verts %d..%d\n", + _mesa_lookup_prim_by_nr(prim->mode), + prim->begin ? "begin" : "..", + prim->end ? "end" : "..", + start, + end); + + if (prim->begin) { + CALL_Begin(GET_DISPATCH(), ( prim->mode )); + } + else { + assert(start == 0); + start += wrap_count; + } + + data = buffer + start * vertex_size; + + for (j = start ; j < end ; j++) { + const 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, VBO_ATTRIB_POS, data ); + data = tmp; + } + + if (prim->end) { + CALL_End(GET_DISPATCH(), ()); + } +} + +/* 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, + const struct _mesa_prim *prim ) +{ + /* 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->begin) + ctx->Driver.CurrentExecPrimitive |= VBO_SAVE_PRIM_WEAK; + if (prim->end) + ctx->Driver.CurrentExecPrimitive &= ~VBO_SAVE_PRIM_WEAK; +} + + +void vbo_loopback_vertex_list( GLcontext *ctx, + const GLfloat *buffer, + const GLubyte *attrsz, + const struct _mesa_prim *prim, + GLuint prim_count, + GLuint wrap_count, + GLuint vertex_size) +{ + struct loopback_attr la[VBO_ATTRIB_MAX]; + GLuint i, nr = 0; + + /* All Legacy, NV, ARB and Material attributes are routed through + * the NV attributes entrypoints: + */ + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + if (attrsz[i]) { + la[nr].target = i; + la[nr].sz = attrsz[i]; + la[nr].func = vert_attrfunc[attrsz[i]-1]; + nr++; + } + } + + for (i = 0 ; i < prim_count ; i++) { + if ((prim[i].mode & VBO_SAVE_PRIM_WEAK) && + (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END)) + { + loopback_weak_prim( ctx, &prim[i] ); + } + else + { + loopback_prim( ctx, buffer, &prim[i], wrap_count, vertex_size, la, nr ); + } + } +} diff --git a/mesalib/src/mesa/vbo/vbo_split.c b/mesalib/src/mesa/vbo/vbo_split.c new file mode 100644 index 000000000..58e879628 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_split.c @@ -0,0 +1,161 @@ + +/* + * Mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +/* Deal with hardware and/or swtnl maximums: + * - maximum number of vertices in buffer + * - maximum number of elements (maybe zero) + * + * The maximums may vary with opengl state (eg if a larger hardware + * vertex is required in this state, the maximum number of vertices + * may be smaller than in another state). + * + * We want buffer splitting to be a convenience function for the code + * actually drawing the primitives rather than a system-wide maximum, + * otherwise it is hard to avoid pessimism. + * + * For instance, if a driver has no hardware limits on vertex buffer + * dimensions, it would not ordinarily want to split vbos. But if + * there is an unexpected fallback, eg memory manager fails to upload + * textures, it will want to pass the drawing commands onto swtnl, + * which does have limitations. A convenience function allows swtnl + * to split the drawing and vbos internally without imposing its + * limitations on drivers which want to use it as a fallback path. + */ + +#include "main/glheader.h" +#include "main/imports.h" +#include "main/mtypes.h" + +#include "vbo_split.h" +#include "vbo.h" + +/* True if a primitive can be split without copying of vertices, false + * otherwise. + */ +GLboolean split_prim_inplace(GLenum mode, GLuint *first, GLuint *incr) +{ + switch (mode) { + case GL_POINTS: + *first = 1; + *incr = 1; + return GL_TRUE; + case GL_LINES: + *first = 2; + *incr = 2; + return GL_TRUE; + case GL_LINE_STRIP: + *first = 2; + *incr = 1; + return GL_TRUE; + case GL_TRIANGLES: + *first = 3; + *incr = 3; + return GL_TRUE; + case GL_TRIANGLE_STRIP: + *first = 3; + *incr = 1; + return GL_TRUE; + case GL_QUADS: + *first = 4; + *incr = 4; + return GL_TRUE; + case GL_QUAD_STRIP: + *first = 4; + *incr = 2; + return GL_TRUE; + default: + *first = 0; + *incr = 1; /* so that count % incr works */ + return GL_FALSE; + } +} + + + +void vbo_split_prims( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index, + vbo_draw_func draw, + const struct split_limits *limits ) +{ + + if (ib) { + if (limits->max_indices == 0) { + /* Could traverse the indices, re-emitting vertices in turn. + * But it's hard to see why this case would be needed - for + * software tnl, it is better to convert to non-indexed + * rendering after transformation is complete, as is done in + * the t_dd_rendertmp.h templates. Are there any devices + * with hardware tnl that cannot do indexed rendering? + * + * For now, this path is disabled. + */ + assert(0); + } + else if (max_index - min_index >= limits->max_verts) { + /* The vertex buffers are too large for hardware (or the + * swtnl module). Traverse the indices, re-emitting vertices + * in turn. Use a vertex cache to preserve some of the + * sharing from the original index list. + */ + vbo_split_copy(ctx, arrays, prim, nr_prims, ib, + draw, limits ); + } + else if (ib->count > limits->max_indices) { + /* The index buffer is too large for hardware. Try to split + * on whole-primitive boundaries, otherwise try to split the + * individual primitives. + */ + vbo_split_inplace(ctx, arrays, prim, nr_prims, ib, + min_index, max_index, draw, limits ); + } + else { + /* Why were we called? */ + assert(0); + } + } + else { + if (max_index - min_index >= limits->max_verts) { + /* The vertex buffer is too large for hardware (or the swtnl + * module). Try to split on whole-primitive boundaries, + * otherwise try to split the individual primitives. + */ + vbo_split_inplace(ctx, arrays, prim, nr_prims, ib, + min_index, max_index, draw, limits ); + } + else { + /* Why were we called? */ + assert(0); + } + } +} + diff --git a/mesalib/src/mesa/vbo/vbo_split.h b/mesalib/src/mesa/vbo/vbo_split.h new file mode 100644 index 000000000..05888d048 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_split.h @@ -0,0 +1,72 @@ +/* + * mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + */ + +/** + * \file vbo_context.h + * \brief VBO builder module datatypes and definitions. + * \author Keith Whitwell + */ + + +/** + * \mainpage The VBO splitter + * + * This is the private data used internally to the vbo_split_prims() + * helper function. Nobody outside the vbo_split* files needs to + * include or know about this structure. + */ + + +#ifndef _VBO_SPLIT_H +#define _VBO_SPLIT_H + +#include "vbo.h" + + +/* True if a primitive can be split without copying of vertices, false + * otherwise. + */ +GLboolean split_prim_inplace(GLenum mode, GLuint *first, GLuint *incr); + +void vbo_split_inplace( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index, + vbo_draw_func draw, + const struct split_limits *limits ); + +/* Requires ib != NULL: + */ +void vbo_split_copy( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + vbo_draw_func draw, + const struct split_limits *limits ); + +#endif diff --git a/mesalib/src/mesa/vbo/vbo_split_copy.c b/mesalib/src/mesa/vbo/vbo_split_copy.c new file mode 100644 index 000000000..8ec180d55 --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_split_copy.c @@ -0,0 +1,616 @@ + +/* + * Mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + +/* Split indexed primitives with per-vertex copying. + */ + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/imports.h" +#include "main/image.h" +#include "main/macros.h" +#include "main/enums.h" +#include "main/mtypes.h" + +#include "vbo_split.h" +#include "vbo.h" + + +#define ELT_TABLE_SIZE 16 + +/** + * Used for vertex-level splitting of indexed buffers. Note that + * non-indexed primitives may be converted to indexed in some cases + * (eg loops, fans) in order to use this splitting path. + */ +struct copy_context { + + GLcontext *ctx; + const struct gl_client_array **array; + const struct _mesa_prim *prim; + GLuint nr_prims; + const struct _mesa_index_buffer *ib; + vbo_draw_func draw; + + const struct split_limits *limits; + + struct { + GLuint attr; + GLuint size; + const struct gl_client_array *array; + const GLubyte *src_ptr; + + struct gl_client_array dstarray; + + } varying[VERT_ATTRIB_MAX]; + GLuint nr_varying; + + const struct gl_client_array *dstarray_ptr[VERT_ATTRIB_MAX]; + struct _mesa_index_buffer dstib; + + GLuint *translated_elt_buf; + const GLuint *srcelt; + + /** A baby hash table to avoid re-emitting (some) duplicate + * vertices when splitting indexed primitives. + */ + struct { + GLuint in; + GLuint out; + } vert_cache[ELT_TABLE_SIZE]; + + GLuint vertex_size; + GLubyte *dstbuf; + GLubyte *dstptr; /**< dstptr == dstbuf + dstelt_max * vertsize */ + GLuint dstbuf_size; /**< in vertices */ + GLuint dstbuf_nr; /**< count of emitted vertices, also the largest value + * in dstelt. Our MaxIndex. + */ + + GLuint *dstelt; + GLuint dstelt_nr; + GLuint dstelt_size; + +#define MAX_PRIM 32 + struct _mesa_prim dstprim[MAX_PRIM]; + GLuint dstprim_nr; + +}; + + +static GLuint attr_size( const struct gl_client_array *array ) +{ + return array->Size * _mesa_sizeof_type(array->Type); +} + + +/** + * Starts returning true slightly before the buffer fills, to ensure + * that there is sufficient room for any remaining vertices to finish + * off the prim: + */ +static GLboolean +check_flush( struct copy_context *copy ) +{ + GLenum mode = copy->dstprim[copy->dstprim_nr].mode; + + if (GL_TRIANGLE_STRIP == mode && + copy->dstelt_nr & 1) { /* see bug9962 */ + return GL_FALSE; + } + + if (copy->dstbuf_nr + 4 > copy->dstbuf_size) + return GL_TRUE; + + if (copy->dstelt_nr + 4 > copy->dstelt_size) + return GL_TRUE; + + return GL_FALSE; +} + + +/** + * Dump the parameters/info for a vbo->draw() call. + */ +static void +dump_draw_info(GLcontext *ctx, + const struct gl_client_array **arrays, + const struct _mesa_prim *prims, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index) +{ + GLuint i, j; + + _mesa_printf("VBO Draw:\n"); + for (i = 0; i < nr_prims; i++) { + _mesa_printf("Prim %u of %u\n", i, nr_prims); + _mesa_printf(" Prim mode 0x%x\n", prims[i].mode); + _mesa_printf(" IB: %p\n", (void*) ib); + for (j = 0; j < VERT_ATTRIB_MAX; j++) { + _mesa_printf(" array %d at %p:\n", j, (void*) arrays[j]); + _mesa_printf(" enabled %d, ptr %p, size %d, type 0x%x, stride %d\n", + arrays[j]->Enabled, arrays[j]->Ptr, + arrays[j]->Size, arrays[j]->Type, arrays[j]->StrideB); + if (0) { + GLint k = prims[i].start + prims[i].count - 1; + GLfloat *last = (GLfloat *) (arrays[j]->Ptr + arrays[j]->Stride * k); + _mesa_printf(" last: %f %f %f\n", + last[0], last[1], last[2]); + } + } + } +} + + +static void +flush( struct copy_context *copy ) +{ + GLuint i; + + /* Set some counters: + */ + copy->dstib.count = copy->dstelt_nr; + +#if 0 + dump_draw_info(copy->ctx, + copy->dstarray_ptr, + copy->dstprim, + copy->dstprim_nr, + ©->dstib, + 0, + copy->dstbuf_nr); +#else + (void) dump_draw_info; +#endif + + copy->draw( copy->ctx, + copy->dstarray_ptr, + copy->dstprim, + copy->dstprim_nr, + ©->dstib, + GL_TRUE, + 0, + copy->dstbuf_nr ); + + /* Reset all pointers: + */ + copy->dstprim_nr = 0; + copy->dstelt_nr = 0; + copy->dstbuf_nr = 0; + copy->dstptr = copy->dstbuf; + + /* Clear the vertex cache: + */ + for (i = 0; i < ELT_TABLE_SIZE; i++) + copy->vert_cache[i].in = ~0; +} + + +/** + * Called at begin of each primitive during replay. + */ +static void +begin( struct copy_context *copy, GLenum mode, GLboolean begin_flag ) +{ + struct _mesa_prim *prim = ©->dstprim[copy->dstprim_nr]; + +/* _mesa_printf("begin %s (%d)\n", _mesa_lookup_prim_by_nr(mode), begin_flag); */ + + prim->mode = mode; + prim->begin = begin_flag; +} + + +/** + * Use a hashtable to attempt to identify recently-emitted vertices + * and avoid re-emitting them. + */ +static GLuint +elt(struct copy_context *copy, GLuint elt_idx) +{ + GLuint elt = copy->srcelt[elt_idx]; + GLuint slot = elt & (ELT_TABLE_SIZE-1); + +/* _mesa_printf("elt %d\n", elt); */ + + /* Look up the incoming element in the vertex cache. Re-emit if + * necessary. + */ + if (copy->vert_cache[slot].in != elt) { + GLubyte *csr = copy->dstptr; + GLuint i; + +/* _mesa_printf(" --> emit to dstelt %d\n", copy->dstbuf_nr); */ + + for (i = 0; i < copy->nr_varying; i++) { + const struct gl_client_array *srcarray = copy->varying[i].array; + const GLubyte *srcptr = copy->varying[i].src_ptr + elt * srcarray->StrideB; + + memcpy(csr, srcptr, copy->varying[i].size); + csr += copy->varying[i].size; + +#ifdef NAN_CHECK + if (srcarray->Type == GL_FLOAT) { + GLuint k; + GLfloat *f = (GLfloat *) srcptr; + for (k = 0; k < srcarray->Size; k++) { + assert(!IS_INF_OR_NAN(f[k])); + assert(f[k] <= 1.0e20 && f[k] >= -1.0e20); + } + } +#endif + + if (0) + { + const GLuint *f = (const GLuint *)srcptr; + GLuint j; + _mesa_printf(" varying %d: ", i); + for(j = 0; j < copy->varying[i].size / 4; j++) + _mesa_printf("%x ", f[j]); + _mesa_printf("\n"); + } + } + + copy->vert_cache[slot].in = elt; + copy->vert_cache[slot].out = copy->dstbuf_nr++; + copy->dstptr += copy->vertex_size; + + assert(csr == copy->dstptr); + assert(copy->dstptr == (copy->dstbuf + + copy->dstbuf_nr * copy->vertex_size)); + } +/* else */ +/* _mesa_printf(" --> reuse vertex\n"); */ + +/* _mesa_printf(" --> emit %d\n", copy->vert_cache[slot].out); */ + copy->dstelt[copy->dstelt_nr++] = copy->vert_cache[slot].out; + return check_flush(copy); +} + + +/** + * Called at end of each primitive during replay. + */ +static void +end( struct copy_context *copy, GLboolean end_flag ) +{ + struct _mesa_prim *prim = ©->dstprim[copy->dstprim_nr]; + +/* _mesa_printf("end (%d)\n", end_flag); */ + + prim->end = end_flag; + prim->count = copy->dstelt_nr - prim->start; + + if (++copy->dstprim_nr == MAX_PRIM || + check_flush(copy)) + flush(copy); +} + + +static void +replay_elts( struct copy_context *copy ) +{ + GLuint i, j, k; + GLboolean split; + + for (i = 0; i < copy->nr_prims; i++) { + const struct _mesa_prim *prim = ©->prim[i]; + const GLuint start = prim->start; + GLuint first, incr; + + switch (prim->mode) { + + case GL_LINE_LOOP: + /* Convert to linestrip and emit the final vertex explicitly, + * but only in the resultant strip that requires it. + */ + j = 0; + while (j != prim->count) { + begin(copy, GL_LINE_STRIP, prim->begin && j == 0); + + for (split = GL_FALSE; j != prim->count && !split; j++) + split = elt(copy, start + j); + + if (j == prim->count) { + /* Done, emit final line. Split doesn't matter as + * it is always raised a bit early so we can emit + * the last verts if necessary! + */ + if (prim->end) + (void)elt(copy, start + 0); + + end(copy, prim->end); + } + else { + /* Wrap + */ + assert(split); + end(copy, 0); + j--; + } + } + break; + + case GL_TRIANGLE_FAN: + case GL_POLYGON: + j = 2; + while (j != prim->count) { + begin(copy, prim->mode, prim->begin && j == 0); + + split = elt(copy, start+0); + assert(!split); + + split = elt(copy, start+j-1); + assert(!split); + + for (; j != prim->count && !split; j++) + split = elt(copy, start+j); + + end(copy, prim->end && j == prim->count); + + if (j != prim->count) { + /* Wrapped the primitive, need to repeat some vertices: + */ + j -= 1; + } + } + break; + + default: + (void)split_prim_inplace(prim->mode, &first, &incr); + + j = 0; + while (j != prim->count) { + + begin(copy, prim->mode, prim->begin && j == 0); + + split = 0; + for (k = 0; k < first; k++, j++) + split |= elt(copy, start+j); + + assert(!split); + + for (; j != prim->count && !split; ) + for (k = 0; k < incr; k++, j++) + split |= elt(copy, start+j); + + end(copy, prim->end && j == prim->count); + + if (j != prim->count) { + /* Wrapped the primitive, need to repeat some vertices: + */ + assert(j > first - incr); + j -= (first - incr); + } + } + break; + } + } + + if (copy->dstprim_nr) + flush(copy); +} + + +static void +replay_init( struct copy_context *copy ) +{ + GLcontext *ctx = copy->ctx; + GLuint i; + GLuint offset; + const GLvoid *srcptr; + + /* Make a list of varying attributes and their vbo's. Also + * calculate vertex size. + */ + copy->vertex_size = 0; + for (i = 0; i < VERT_ATTRIB_MAX; i++) { + struct gl_buffer_object *vbo = copy->array[i]->BufferObj; + + if (copy->array[i]->StrideB == 0) { + copy->dstarray_ptr[i] = copy->array[i]; + } + else { + GLuint j = copy->nr_varying++; + + copy->varying[j].attr = i; + copy->varying[j].array = copy->array[i]; + copy->varying[j].size = attr_size(copy->array[i]); + copy->vertex_size += attr_size(copy->array[i]); + + if (_mesa_is_bufferobj(vbo) && !_mesa_bufferobj_mapped(vbo)) + ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER, GL_READ_ONLY, vbo); + + copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer, + copy->array[i]->Ptr); + + copy->dstarray_ptr[i] = ©->varying[j].dstarray; + } + } + + /* There must always be an index buffer. Currently require the + * caller convert non-indexed prims to indexed. Could alternately + * do it internally. + */ + if (_mesa_is_bufferobj(copy->ib->obj) && + !_mesa_bufferobj_mapped(copy->ib->obj)) + ctx->Driver.MapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY, + copy->ib->obj); + + srcptr = (const GLubyte *) ADD_POINTERS(copy->ib->obj->Pointer, + copy->ib->ptr); + + switch (copy->ib->type) { + case GL_UNSIGNED_BYTE: + copy->translated_elt_buf = _mesa_malloc(sizeof(GLuint) * copy->ib->count); + copy->srcelt = copy->translated_elt_buf; + + for (i = 0; i < copy->ib->count; i++) + copy->translated_elt_buf[i] = ((const GLubyte *)srcptr)[i]; + break; + + case GL_UNSIGNED_SHORT: + copy->translated_elt_buf = _mesa_malloc(sizeof(GLuint) * copy->ib->count); + copy->srcelt = copy->translated_elt_buf; + + for (i = 0; i < copy->ib->count; i++) + copy->translated_elt_buf[i] = ((const GLushort *)srcptr)[i]; + break; + + case GL_UNSIGNED_INT: + copy->translated_elt_buf = NULL; + copy->srcelt = (const GLuint *)srcptr; + break; + } + + /* Figure out the maximum allowed vertex buffer size: + */ + if (copy->vertex_size * copy->limits->max_verts <= copy->limits->max_vb_size) { + copy->dstbuf_size = copy->limits->max_verts; + } + else { + copy->dstbuf_size = copy->limits->max_vb_size / copy->vertex_size; + } + + /* Allocate an output vertex buffer: + * + * XXX: This should be a VBO! + */ + copy->dstbuf = _mesa_malloc(copy->dstbuf_size * copy->vertex_size); + copy->dstptr = copy->dstbuf; + + /* Setup new vertex arrays to point into the output buffer: + */ + for (offset = 0, i = 0; i < copy->nr_varying; i++) { + const struct gl_client_array *src = copy->varying[i].array; + struct gl_client_array *dst = ©->varying[i].dstarray; + + dst->Size = src->Size; + dst->Type = src->Type; + dst->Format = GL_RGBA; + dst->Stride = copy->vertex_size; + dst->StrideB = copy->vertex_size; + dst->Ptr = copy->dstbuf + offset; + dst->Enabled = GL_TRUE; + dst->Normalized = src->Normalized; + dst->BufferObj = ctx->Shared->NullBufferObj; + dst->_MaxElement = copy->dstbuf_size; /* may be less! */ + + offset += copy->varying[i].size; + } + + /* Allocate an output element list: + */ + copy->dstelt_size = MIN2(65536, + copy->ib->count * 2 + 3); + copy->dstelt_size = MIN2(copy->dstelt_size, + copy->limits->max_indices); + copy->dstelt = _mesa_malloc(sizeof(GLuint) * copy->dstelt_size); + copy->dstelt_nr = 0; + + /* Setup the new index buffer to point to the allocated element + * list: + */ + copy->dstib.count = 0; /* duplicates dstelt_nr */ + copy->dstib.type = GL_UNSIGNED_INT; + copy->dstib.obj = ctx->Shared->NullBufferObj; + copy->dstib.ptr = copy->dstelt; +} + + +/** + * Free up everything allocated during split/replay. + */ +static void +replay_finish( struct copy_context *copy ) +{ + GLcontext *ctx = copy->ctx; + GLuint i; + + /* Free our vertex and index buffers: + */ + _mesa_free(copy->translated_elt_buf); + _mesa_free(copy->dstbuf); + _mesa_free(copy->dstelt); + + /* Unmap VBO's + */ + for (i = 0; i < copy->nr_varying; i++) { + struct gl_buffer_object *vbo = copy->varying[i].array->BufferObj; + if (_mesa_is_bufferobj(vbo) && _mesa_bufferobj_mapped(vbo)) + ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER, vbo); + } + + /* Unmap index buffer: + */ + if (_mesa_is_bufferobj(copy->ib->obj) && + _mesa_bufferobj_mapped(copy->ib->obj)) { + ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, copy->ib->obj); + } +} + + +/** + * Split VBO into smaller pieces, draw the pieces. + */ +void vbo_split_copy( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + vbo_draw_func draw, + const struct split_limits *limits ) +{ + struct copy_context copy; + GLuint i; + + memset(©, 0, sizeof(copy)); + + /* Require indexed primitives: + */ + assert(ib); + + copy.ctx = ctx; + copy.array = arrays; + copy.prim = prim; + copy.nr_prims = nr_prims; + copy.ib = ib; + copy.draw = draw; + copy.limits = limits; + + /* Clear the vertex cache: + */ + for (i = 0; i < ELT_TABLE_SIZE; i++) + copy.vert_cache[i].in = ~0; + + replay_init(©); + replay_elts(©); + replay_finish(©); +} diff --git a/mesalib/src/mesa/vbo/vbo_split_inplace.c b/mesalib/src/mesa/vbo/vbo_split_inplace.c new file mode 100644 index 000000000..da84eaa6e --- /dev/null +++ b/mesalib/src/mesa/vbo/vbo_split_inplace.c @@ -0,0 +1,271 @@ + +/* + * Mesa 3-D graphics library + * Version: 6.5 + * + * Copyright (C) 1999-2006 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. + * + * Authors: + * Keith Whitwell <keith@tungstengraphics.com> + */ + + +#include "main/mtypes.h" +#include "main/macros.h" +#include "main/enums.h" +#include "vbo_split.h" + + +#define MAX_PRIM 32 + +/* Used for splitting without copying. + */ +struct split_context { + GLcontext *ctx; + const struct gl_client_array **array; + const struct _mesa_prim *prim; + GLuint nr_prims; + const struct _mesa_index_buffer *ib; + GLuint min_index; + GLuint max_index; + vbo_draw_func draw; + + const struct split_limits *limits; + + struct _mesa_prim dstprim[MAX_PRIM]; + GLuint dstprim_nr; +}; + + + + +static void flush_vertex( struct split_context *split ) +{ + GLuint min_index, max_index; + GLuint i; + + if (!split->dstprim_nr) + return; + + min_index = split->dstprim[0].start; + max_index = min_index + split->dstprim[0].count - 1; + + for (i = 1; i < split->dstprim_nr; i++) { + GLuint tmp_min = split->dstprim[i].start; + GLuint tmp_max = tmp_min + split->dstprim[i].count - 1; + + if (tmp_min < min_index) + min_index = tmp_min; + + if (tmp_max > max_index) + max_index = tmp_max; + } + + assert(max_index >= min_index); + + split->draw( split->ctx, + split->array, + split->dstprim, + split->dstprim_nr, + NULL, + GL_TRUE, + min_index, + max_index); + + split->dstprim_nr = 0; +} + + +static struct _mesa_prim *next_outprim( struct split_context *split ) +{ + if (split->dstprim_nr == MAX_PRIM-1) { + flush_vertex(split); + } + + { + struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++]; + memset(prim, 0, sizeof(*prim)); + return prim; + } +} + +static int align(int value, int alignment) +{ + return (value + alignment - 1) & ~(alignment - 1); +} + + + +/* Break large primitives into smaller ones. If not possible, convert + * the primitive to indexed and pass to split_elts(). + */ +static void split_prims( struct split_context *split) +{ + GLuint csr = 0; + GLuint i; + + for (i = 0; i < split->nr_prims; i++) { + const struct _mesa_prim *prim = &split->prim[i]; + GLuint first, incr; + GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr); + GLuint count; + + /* Always wrap on an even numbered vertex to avoid problems with + * triangle strips. + */ + GLuint available = align(split->limits->max_verts - csr - 1, 2); + assert(split->limits->max_verts >= csr); + + if (prim->count < first) + continue; + + count = prim->count - (prim->count - first) % incr; + + + if ((available < count && !split_inplace) || + (available < first && split_inplace)) { + flush_vertex(split); + csr = 0; + available = align(split->limits->max_verts - csr - 1, 2); + } + + if (available >= count) { + struct _mesa_prim *outprim = next_outprim(split); + *outprim = *prim; + csr += prim->count; + available = align(split->limits->max_verts - csr - 1, 2); + } + else if (split_inplace) { + GLuint j, nr; + + + for (j = 0 ; j < count ; ) { + GLuint remaining = count - j; + struct _mesa_prim *outprim = next_outprim(split); + + nr = MIN2( available, remaining ); + + nr -= (nr - first) % incr; + + outprim->mode = prim->mode; + outprim->begin = (j == 0 && prim->begin); + outprim->end = (nr == remaining && prim->end); + outprim->start = prim->start + j; + outprim->count = nr; + + if (nr == remaining) { + /* Finished. + */ + j += nr; + csr += nr; + available = align(split->limits->max_verts - csr - 1, 2); + } + else { + /* Wrapped the primitive: + */ + j += nr - (first - incr); + flush_vertex(split); + csr = 0; + available = align(split->limits->max_verts - csr - 1, 2); + } + } + } + else if (split->ib == NULL) { + /* XXX: could at least send the first max_verts off from the + * inplace buffers. + */ + + /* else convert to indexed primitive and pass to split_elts, + * which will do the necessary copying and turn it back into a + * vertex primitive for rendering... + */ + struct _mesa_index_buffer ib; + struct _mesa_prim tmpprim; + GLuint *elts = malloc(count * sizeof(GLuint)); + GLuint j; + + for (j = 0; j < count; j++) + elts[j] = prim->start + j; + + ib.count = count; + ib.type = GL_UNSIGNED_INT; + ib.obj = split->ctx->Shared->NullBufferObj; + ib.ptr = elts; + + tmpprim = *prim; + tmpprim.indexed = 1; + tmpprim.start = 0; + tmpprim.count = count; + + flush_vertex(split); + + vbo_split_copy(split->ctx, + split->array, + &tmpprim, 1, + &ib, + split->draw, + split->limits); + + free(elts); + } + else { + flush_vertex(split); + + vbo_split_copy(split->ctx, + split->array, + prim, 1, + split->ib, + split->draw, + split->limits); + } + } + + flush_vertex(split); +} + + +void vbo_split_inplace( GLcontext *ctx, + const struct gl_client_array *arrays[], + const struct _mesa_prim *prim, + GLuint nr_prims, + const struct _mesa_index_buffer *ib, + GLuint min_index, + GLuint max_index, + vbo_draw_func draw, + const struct split_limits *limits ) +{ + struct split_context split; + + memset(&split, 0, sizeof(split)); + + split.ctx = ctx; + split.array = arrays; + split.prim = prim; + split.nr_prims = nr_prims; + split.ib = ib; + split.min_index = min_index; + split.max_index = max_index; + split.draw = draw; + split.limits = limits; + + split_prims( &split ); +} + + |