1b8e80941Smrg
2b8e80941Smrg/*
3b8e80941Smrg * Mesa 3-D graphics library
4b8e80941Smrg *
5b8e80941Smrg * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
6b8e80941Smrg *
7b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
8b8e80941Smrg * copy of this software and associated documentation files (the "Software"),
9b8e80941Smrg * to deal in the Software without restriction, including without limitation
10b8e80941Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11b8e80941Smrg * and/or sell copies of the Software, and to permit persons to whom the
12b8e80941Smrg * Software is furnished to do so, subject to the following conditions:
13b8e80941Smrg *
14b8e80941Smrg * The above copyright notice and this permission notice shall be included
15b8e80941Smrg * in all copies or substantial portions of the Software.
16b8e80941Smrg *
17b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18b8e80941Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20b8e80941Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21b8e80941Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22b8e80941Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23b8e80941Smrg * OTHER DEALINGS IN THE SOFTWARE.
24b8e80941Smrg *
25b8e80941Smrg * Authors:
26b8e80941Smrg *    Keith Whitwell <keithw@vmware.com>
27b8e80941Smrg */
28b8e80941Smrg
29b8e80941Smrg/* Helper for drivers which find themselves rendering a range of
30b8e80941Smrg * indices starting somewhere above zero.  Typically the application
31b8e80941Smrg * is issuing multiple DrawArrays() or DrawElements() to draw
32b8e80941Smrg * successive primitives layed out linearly in the vertex arrays.
33b8e80941Smrg * Unless the vertex arrays are all in a VBO, the OpenGL semantics
34b8e80941Smrg * imply that we need to re-upload the vertex data on each draw call.
35b8e80941Smrg * In that case, we want to avoid starting the upload at zero, as it
36b8e80941Smrg * will mean every draw call uploads an increasing amount of not-used
37b8e80941Smrg * vertex data.  Worse - in the software tnl module, all those
38b8e80941Smrg * vertices will be transformed and lit.
39b8e80941Smrg *
40b8e80941Smrg * If we just upload the new data, however, the indices will be
41b8e80941Smrg * incorrect as we tend to upload each set of vertex data to a new
42b8e80941Smrg * region.
43b8e80941Smrg *
44b8e80941Smrg * This file provides a helper to adjust the arrays, primitives and
45b8e80941Smrg * indices of a draw call so that it can be re-issued with a min_index
46b8e80941Smrg * of zero.
47b8e80941Smrg */
48b8e80941Smrg
49b8e80941Smrg#include <stdio.h>
50b8e80941Smrg#include "main/bufferobj.h"
51b8e80941Smrg#include "main/errors.h"
52b8e80941Smrg#include "main/glheader.h"
53b8e80941Smrg#include "main/imports.h"
54b8e80941Smrg#include "main/mtypes.h"
55b8e80941Smrg#include "vbo/vbo.h"
56b8e80941Smrg
57b8e80941Smrg#include "t_rebase.h"
58b8e80941Smrg
59b8e80941Smrg
60b8e80941Smrg#define REBASE(TYPE) 						\
61b8e80941Smrgstatic void *rebase_##TYPE( const void *ptr,			\
62b8e80941Smrg			  GLuint count, 			\
63b8e80941Smrg			  TYPE min_index )			\
64b8e80941Smrg{								\
65b8e80941Smrg   GLuint i;							\
66b8e80941Smrg   const TYPE *in = (TYPE *)ptr;				\
67b8e80941Smrg   TYPE *tmp_indices = malloc(count * sizeof(TYPE));		\
68b8e80941Smrg								\
69b8e80941Smrg   if (tmp_indices == NULL) {                                   \
70b8e80941Smrg      _mesa_error_no_memory(__func__);                          \
71b8e80941Smrg      return NULL;                                              \
72b8e80941Smrg   }                                                            \
73b8e80941Smrg								\
74b8e80941Smrg   for (i = 0; i < count; i++)  				\
75b8e80941Smrg      tmp_indices[i] = in[i] - min_index;			\
76b8e80941Smrg								\
77b8e80941Smrg   return (void *)tmp_indices;					\
78b8e80941Smrg}
79b8e80941Smrg
80b8e80941Smrg
81b8e80941SmrgREBASE(GLuint)
82b8e80941SmrgREBASE(GLushort)
83b8e80941SmrgREBASE(GLubyte)
84b8e80941Smrg
85b8e80941Smrg
86b8e80941Smrg
87b8e80941Smrg/* Adjust primitives, indices and vertex definitions so that min_index
88b8e80941Smrg * becomes zero. There are lots of reasons for wanting to do this, eg:
89b8e80941Smrg *
90b8e80941Smrg * Software tnl:
91b8e80941Smrg *    - any time min_index != 0, otherwise unused vertices lower than
92b8e80941Smrg *      min_index will be transformed.
93b8e80941Smrg *
94b8e80941Smrg * Hardware tnl:
95b8e80941Smrg *    - if ib != NULL and min_index != 0, otherwise vertices lower than
96b8e80941Smrg *      min_index will be uploaded.  Requires adjusting index values.
97b8e80941Smrg *
98b8e80941Smrg *    - if ib == NULL and min_index != 0, just for convenience so this doesn't
99b8e80941Smrg *      have to be handled within the driver.
100b8e80941Smrg *
101b8e80941Smrg * Hardware tnl with VBO support:
102b8e80941Smrg *    - as above, but only when vertices are not (all?) in VBO's.
103b8e80941Smrg *    - can't save time by trying to upload half a vbo - typically it is
104b8e80941Smrg *      all or nothing.
105b8e80941Smrg */
106b8e80941Smrgvoid t_rebase_prims( struct gl_context *ctx,
107b8e80941Smrg                     const struct tnl_vertex_array *arrays,
108b8e80941Smrg                     const struct _mesa_prim *prim,
109b8e80941Smrg                     GLuint nr_prims,
110b8e80941Smrg                     const struct _mesa_index_buffer *ib,
111b8e80941Smrg                     GLuint min_index,
112b8e80941Smrg                     GLuint max_index,
113b8e80941Smrg                     tnl_draw_func draw )
114b8e80941Smrg{
115b8e80941Smrg   struct gl_array_attributes tmp_attribs[VERT_ATTRIB_MAX];
116b8e80941Smrg   struct tnl_vertex_array tmp_arrays[VERT_ATTRIB_MAX];
117b8e80941Smrg
118b8e80941Smrg   struct _mesa_index_buffer tmp_ib;
119b8e80941Smrg   struct _mesa_prim *tmp_prims = NULL;
120b8e80941Smrg   void *tmp_indices = NULL;
121b8e80941Smrg   GLuint i;
122b8e80941Smrg
123b8e80941Smrg   assert(min_index != 0);
124b8e80941Smrg
125b8e80941Smrg   if (0)
126b8e80941Smrg      printf("%s %d..%d\n", __func__, min_index, max_index);
127b8e80941Smrg
128b8e80941Smrg
129b8e80941Smrg   /* XXX this path is disabled for now.
130b8e80941Smrg    * There's rendering corruption in some apps when it's enabled.
131b8e80941Smrg    */
132b8e80941Smrg   if (0 && ib && ctx->Extensions.ARB_draw_elements_base_vertex) {
133b8e80941Smrg      /* If we can just tell the hardware or the TNL to interpret our
134b8e80941Smrg       * indices with a different base, do so.
135b8e80941Smrg       */
136b8e80941Smrg      tmp_prims = malloc(sizeof(*prim) * nr_prims);
137b8e80941Smrg
138b8e80941Smrg      if (tmp_prims == NULL) {
139b8e80941Smrg         _mesa_error_no_memory(__func__);
140b8e80941Smrg         return;
141b8e80941Smrg      }
142b8e80941Smrg
143b8e80941Smrg      for (i = 0; i < nr_prims; i++) {
144b8e80941Smrg	 tmp_prims[i] = prim[i];
145b8e80941Smrg	 tmp_prims[i].basevertex -= min_index;
146b8e80941Smrg      }
147b8e80941Smrg
148b8e80941Smrg      prim = tmp_prims;
149b8e80941Smrg   } else if (ib) {
150b8e80941Smrg      /* Unfortunately need to adjust each index individually.
151b8e80941Smrg       */
152b8e80941Smrg      GLboolean map_ib = ib->obj->Name &&
153b8e80941Smrg                         !ib->obj->Mappings[MAP_INTERNAL].Pointer;
154b8e80941Smrg      void *ptr;
155b8e80941Smrg
156b8e80941Smrg      if (map_ib)
157b8e80941Smrg	 ctx->Driver.MapBufferRange(ctx, 0, ib->obj->Size, GL_MAP_READ_BIT,
158b8e80941Smrg				    ib->obj, MAP_INTERNAL);
159b8e80941Smrg
160b8e80941Smrg
161b8e80941Smrg      ptr = ADD_POINTERS(ib->obj->Mappings[MAP_INTERNAL].Pointer, ib->ptr);
162b8e80941Smrg
163b8e80941Smrg      /* Some users might prefer it if we translated elements to
164b8e80941Smrg       * GLuints here.  Others wouldn't...
165b8e80941Smrg       */
166b8e80941Smrg      switch (ib->index_size) {
167b8e80941Smrg      case 4:
168b8e80941Smrg	 tmp_indices = rebase_GLuint( ptr, ib->count, min_index );
169b8e80941Smrg	 break;
170b8e80941Smrg      case 2:
171b8e80941Smrg	 tmp_indices = rebase_GLushort( ptr, ib->count, min_index );
172b8e80941Smrg	 break;
173b8e80941Smrg      case 1:
174b8e80941Smrg	 tmp_indices = rebase_GLubyte( ptr, ib->count, min_index );
175b8e80941Smrg	 break;
176b8e80941Smrg      }
177b8e80941Smrg
178b8e80941Smrg      if (map_ib)
179b8e80941Smrg	 ctx->Driver.UnmapBuffer(ctx, ib->obj, MAP_INTERNAL);
180b8e80941Smrg
181b8e80941Smrg      if (tmp_indices == NULL) {
182b8e80941Smrg         return;
183b8e80941Smrg      }
184b8e80941Smrg
185b8e80941Smrg      tmp_ib.obj = ctx->Shared->NullBufferObj;
186b8e80941Smrg      tmp_ib.ptr = tmp_indices;
187b8e80941Smrg      tmp_ib.count = ib->count;
188b8e80941Smrg      tmp_ib.index_size = ib->index_size;
189b8e80941Smrg
190b8e80941Smrg      ib = &tmp_ib;
191b8e80941Smrg   }
192b8e80941Smrg   else {
193b8e80941Smrg      /* Otherwise the primitives need adjustment.
194b8e80941Smrg       */
195b8e80941Smrg      tmp_prims = malloc(sizeof(*prim) * nr_prims);
196b8e80941Smrg
197b8e80941Smrg      if (tmp_prims == NULL) {
198b8e80941Smrg         _mesa_error_no_memory(__func__);
199b8e80941Smrg         return;
200b8e80941Smrg      }
201b8e80941Smrg
202b8e80941Smrg      for (i = 0; i < nr_prims; i++) {
203b8e80941Smrg	 /* If this fails, it could indicate an application error:
204b8e80941Smrg	  */
205b8e80941Smrg	 assert(prim[i].start >= min_index);
206b8e80941Smrg
207b8e80941Smrg	 tmp_prims[i] = prim[i];
208b8e80941Smrg	 tmp_prims[i].start -= min_index;
209b8e80941Smrg      }
210b8e80941Smrg
211b8e80941Smrg      prim = tmp_prims;
212b8e80941Smrg   }
213b8e80941Smrg
214b8e80941Smrg   /* Just need to adjust the pointer values on each incoming array.
215b8e80941Smrg    * This works for VBO and non-vbo rendering and shouldn't pesimize
216b8e80941Smrg    * VBO-based upload schemes.  However this may still not be a fast
217b8e80941Smrg    * path for hardware tnl for VBO based rendering as most machines
218b8e80941Smrg    * will be happier if you just specify a starting vertex value in
219b8e80941Smrg    * each primitive.
220b8e80941Smrg    *
221b8e80941Smrg    * For drivers with hardware tnl, you only want to do this if you
222b8e80941Smrg    * are forced to, eg non-VBO indexed rendering with start != 0.
223b8e80941Smrg    */
224b8e80941Smrg   for (i = 0; i < VERT_ATTRIB_MAX; i++) {
225b8e80941Smrg      tmp_attribs[i] = *(arrays[i].VertexAttrib);
226b8e80941Smrg      tmp_arrays[i].BufferBinding = arrays[i].BufferBinding;
227b8e80941Smrg      tmp_arrays[i].VertexAttrib = &tmp_attribs[i];
228b8e80941Smrg      if (_mesa_is_bufferobj(arrays[i].BufferBinding->BufferObj))
229b8e80941Smrg         tmp_attribs[i].RelativeOffset +=
230b8e80941Smrg            min_index * arrays[i].BufferBinding->Stride;
231b8e80941Smrg      else
232b8e80941Smrg         tmp_attribs[i].Ptr += min_index * arrays[i].BufferBinding->Stride;
233b8e80941Smrg   }
234b8e80941Smrg
235b8e80941Smrg   /* Re-issue the draw call.
236b8e80941Smrg    */
237b8e80941Smrg   draw( ctx,
238b8e80941Smrg         tmp_arrays,
239b8e80941Smrg	 prim,
240b8e80941Smrg	 nr_prims,
241b8e80941Smrg	 ib,
242b8e80941Smrg	 GL_TRUE,
243b8e80941Smrg	 0,
244b8e80941Smrg	 max_index - min_index,
245b8e80941Smrg	 NULL, 0, NULL );
246b8e80941Smrg
247b8e80941Smrg   free(tmp_indices);
248b8e80941Smrg
249b8e80941Smrg   free(tmp_prims);
250b8e80941Smrg}
251b8e80941Smrg
252b8e80941Smrg
253b8e80941Smrg
254