1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 /* Author:
26 * Keith Whitwell <keithw@vmware.com>
27 */
28
29 #include <stdbool.h>
30 #include "main/arrayobj.h"
31 #include "main/glheader.h"
32 #include "main/bufferobj.h"
33 #include "main/context.h"
34 #include "main/enable.h"
35 #include "main/mesa_private.h"
36 #include "main/macros.h"
37 #include "main/light.h"
38 #include "main/state.h"
39 #include "main/varray.h"
40 #include "util/bitscan.h"
41
42 #include "vbo_private.h"
43
44 static void
copy_vao(struct gl_context * ctx,const struct gl_vertex_array_object * vao,GLbitfield mask,GLbitfield state,GLbitfield pop_state,int shift,fi_type ** data,bool * color0_changed)45 copy_vao(struct gl_context *ctx, const struct gl_vertex_array_object *vao,
46 GLbitfield mask, GLbitfield state, GLbitfield pop_state,
47 int shift, fi_type **data, bool *color0_changed)
48 {
49 struct vbo_context *vbo = vbo_context(ctx);
50
51 mask &= vao->Enabled;
52 while (mask) {
53 const int i = u_bit_scan(&mask);
54 const struct gl_array_attributes *attrib = &vao->VertexAttrib[i];
55 unsigned current_index = shift + i;
56 struct gl_array_attributes *currval = &vbo->current[current_index];
57 const GLubyte size = attrib->Format.Size;
58 const GLenum16 type = attrib->Format.Type;
59 fi_type tmp[8];
60 int dmul_shift = 0;
61
62 if (type == GL_DOUBLE ||
63 type == GL_UNSIGNED_INT64_ARB) {
64 dmul_shift = 1;
65 memcpy(tmp, *data, size * 2 * sizeof(GLfloat));
66 } else {
67 COPY_CLEAN_4V_TYPE_AS_UNION(tmp, size, *data, type);
68 }
69
70 if (memcmp(currval->Ptr, tmp, 4 * sizeof(GLfloat) << dmul_shift) != 0) {
71 memcpy((fi_type*)currval->Ptr, tmp, 4 * sizeof(GLfloat) << dmul_shift);
72
73 if (current_index == VBO_ATTRIB_COLOR0)
74 *color0_changed = true;
75
76 /* The fixed-func vertex program uses this. */
77 if (current_index == VBO_ATTRIB_MAT_FRONT_SHININESS ||
78 current_index == VBO_ATTRIB_MAT_BACK_SHININESS)
79 ctx->NewState |= _NEW_FF_VERT_PROGRAM;
80
81 ctx->NewState |= state;
82 ctx->PopAttribState |= pop_state;
83 }
84
85 if (type != currval->Format.Type ||
86 (size >> dmul_shift) != currval->Format.Size)
87 vbo_set_vertex_format(&currval->Format, size >> dmul_shift, type);
88
89 *data += size;
90 }
91 }
92
93 /**
94 * After playback, copy everything but the position from the
95 * last vertex to the saved state
96 */
97 static void
playback_copy_to_current(struct gl_context * ctx,const struct vbo_save_vertex_list * node)98 playback_copy_to_current(struct gl_context *ctx,
99 const struct vbo_save_vertex_list *node)
100 {
101 if (!node->cold->current_data)
102 return;
103
104 fi_type *data = node->cold->current_data;
105 bool color0_changed = false;
106
107 /* Copy conventional attribs and generics except pos */
108 copy_vao(ctx, node->cold->VAO[VP_MODE_SHADER], ~VERT_BIT_POS & VERT_BIT_ALL,
109 _NEW_CURRENT_ATTRIB, GL_CURRENT_BIT, 0, &data, &color0_changed);
110 /* Copy materials */
111 copy_vao(ctx, node->cold->VAO[VP_MODE_FF], VERT_BIT_MAT_ALL,
112 _NEW_MATERIAL, GL_LIGHTING_BIT,
113 VBO_MATERIAL_SHIFT, &data, &color0_changed);
114
115 if (color0_changed && ctx->Light.ColorMaterialEnabled) {
116 _mesa_update_color_material(ctx, ctx->Current.Attrib[VBO_ATTRIB_COLOR0]);
117 }
118
119 /* CurrentExecPrimitive
120 */
121 if (node->cold->prim_count) {
122 const struct _mesa_prim *prim = &node->cold->prims[node->cold->prim_count - 1];
123 if (prim->end)
124 ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
125 else
126 ctx->Driver.CurrentExecPrimitive = prim->mode;
127 }
128 }
129
130
131
132 /**
133 * Set the appropriate VAO to draw.
134 */
135 static void
bind_vertex_list(struct gl_context * ctx,const struct vbo_save_vertex_list * node)136 bind_vertex_list(struct gl_context *ctx,
137 const struct vbo_save_vertex_list *node)
138 {
139 const gl_vertex_processing_mode mode = ctx->VertexProgram._VPMode;
140 _mesa_set_draw_vao(ctx, node->cold->VAO[mode], _vbo_get_vao_filter(mode));
141 }
142
143
144 static void
loopback_vertex_list(struct gl_context * ctx,const struct vbo_save_vertex_list * list)145 loopback_vertex_list(struct gl_context *ctx,
146 const struct vbo_save_vertex_list *list)
147 {
148 struct gl_buffer_object *bo = list->cold->VAO[0]->BufferBinding[0].BufferObj;
149 void *buffer = _mesa_bufferobj_map_range(ctx, 0, bo->Size, GL_MAP_READ_BIT, /* ? */
150 bo, MAP_INTERNAL);
151
152 /* TODO: in this case, we shouldn't create a bo at all and instead keep
153 * the in-RAM buffer. */
154 _vbo_loopback_vertex_list(ctx, list, buffer);
155
156 _mesa_bufferobj_unmap(ctx, bo, MAP_INTERNAL);
157 }
158
159
160 void
vbo_save_playback_vertex_list_loopback(struct gl_context * ctx,void * data)161 vbo_save_playback_vertex_list_loopback(struct gl_context *ctx, void *data)
162 {
163 const struct vbo_save_vertex_list *node =
164 (const struct vbo_save_vertex_list *) data;
165
166 FLUSH_FOR_DRAW(ctx);
167
168 if (_mesa_inside_begin_end(ctx) && node->draw_begins) {
169 /* Error: we're about to begin a new primitive but we're already
170 * inside a glBegin/End pair.
171 */
172 _mesa_error(ctx, GL_INVALID_OPERATION,
173 "draw operation inside glBegin/End");
174 return;
175 }
176 /* Various degenerate cases: translate into immediate mode
177 * calls rather than trying to execute in place.
178 */
179 loopback_vertex_list(ctx, node);
180 }
181
182 enum vbo_save_status {
183 DONE,
184 USE_SLOW_PATH,
185 };
186
187 static enum vbo_save_status
vbo_save_playback_vertex_list_gallium(struct gl_context * ctx,const struct vbo_save_vertex_list * node,bool copy_to_current)188 vbo_save_playback_vertex_list_gallium(struct gl_context *ctx,
189 const struct vbo_save_vertex_list *node,
190 bool copy_to_current)
191 {
192 /* Don't use this if selection or feedback mode is enabled. st/mesa can't
193 * handle it.
194 */
195 if (!ctx->Driver.DrawGalliumVertexState || ctx->RenderMode != GL_RENDER)
196 return USE_SLOW_PATH;
197
198 const gl_vertex_processing_mode mode = ctx->VertexProgram._VPMode;
199
200 /* This sets which vertex arrays are enabled, which determines
201 * which attribs have stride = 0 and whether edge flags are enabled.
202 */
203 const GLbitfield enabled = node->enabled_attribs[mode];
204 ctx->Array._DrawVAOEnabledAttribs = enabled;
205 _mesa_set_varying_vp_inputs(ctx, enabled);
206
207 if (ctx->NewState)
208 _mesa_update_state(ctx);
209
210 /* Return precomputed GL errors such as invalid shaders. */
211 if (!ctx->ValidPrimMask) {
212 _mesa_error(ctx, ctx->DrawGLError, "glCallList");
213 return DONE;
214 }
215
216 /* Use the slow path when there are vertex inputs without vertex
217 * elements. This happens with zero-stride attribs and non-fixed-func
218 * shaders.
219 *
220 * Dual-slot inputs are also unsupported because the higher slot is
221 * always missing in vertex elements.
222 *
223 * TODO: Add support for zero-stride attribs.
224 */
225 struct gl_program *vp = ctx->VertexProgram._Current;
226
227 if (vp->info.inputs_read & ~enabled || vp->DualSlotInputs)
228 return USE_SLOW_PATH;
229
230 struct pipe_vertex_state *state = node->state[mode];
231 struct pipe_draw_vertex_state_info info;
232
233 info.mode = node->mode;
234 info.take_vertex_state_ownership = false;
235
236 if (node->ctx == ctx) {
237 /* This mechanism allows passing references to the driver without
238 * using atomics to increase the reference count.
239 *
240 * This private refcount can be decremented without atomics but only
241 * one context (ctx above) can use this counter (so that it's only
242 * used by 1 thread).
243 *
244 * This number is atomically added to reference.count at
245 * initialization. If it's never used, the same number is atomically
246 * subtracted from reference.count before destruction. If this number
247 * is decremented, we can pass one reference to the driver without
248 * touching reference.count with atomics. At destruction we only
249 * subtract the number of references we have not returned. This can
250 * possibly turn a million atomic increments into 1 add and 1 subtract
251 * atomic op over the whole lifetime of an app.
252 */
253 int16_t * const private_refcount = (int16_t*)&node->private_refcount[mode];
254 assert(*private_refcount >= 0);
255
256 if (unlikely(*private_refcount == 0)) {
257 /* pipe_vertex_state can be reused through util_vertex_state_cache,
258 * and there can be many display lists over-incrementing this number,
259 * causing it to overflow.
260 *
261 * Guess that the same state can never be used by N=500000 display
262 * lists, so one display list can only increment it by
263 * INT_MAX / N.
264 */
265 const int16_t add_refs = INT_MAX / 500000;
266 p_atomic_add(&state->reference.count, add_refs);
267 *private_refcount = add_refs;
268 }
269
270 (*private_refcount)--;
271 info.take_vertex_state_ownership = true;
272 }
273
274 /* Fast path using a pre-built gallium vertex buffer state. */
275 if (node->modes || node->num_draws > 1) {
276 ctx->Driver.DrawGalliumVertexState(ctx, state, info,
277 node->start_counts,
278 node->modes,
279 node->num_draws,
280 enabled & VERT_ATTRIB_EDGEFLAG);
281 } else if (node->num_draws) {
282 ctx->Driver.DrawGalliumVertexState(ctx, state, info,
283 &node->start_count,
284 NULL, 1,
285 enabled & VERT_ATTRIB_EDGEFLAG);
286 }
287
288 if (copy_to_current)
289 playback_copy_to_current(ctx, node);
290 return DONE;
291 }
292
293 /**
294 * Execute the buffer and save copied verts.
295 * This is called from the display list code when executing
296 * a drawing command.
297 */
298 void
vbo_save_playback_vertex_list(struct gl_context * ctx,void * data,bool copy_to_current)299 vbo_save_playback_vertex_list(struct gl_context *ctx, void *data, bool copy_to_current)
300 {
301 const struct vbo_save_vertex_list *node =
302 (const struct vbo_save_vertex_list *) data;
303
304 FLUSH_FOR_DRAW(ctx);
305
306 if (_mesa_inside_begin_end(ctx) && node->draw_begins) {
307 /* Error: we're about to begin a new primitive but we're already
308 * inside a glBegin/End pair.
309 */
310 _mesa_error(ctx, GL_INVALID_OPERATION,
311 "draw operation inside glBegin/End");
312 return;
313 }
314
315 if (vbo_save_playback_vertex_list_gallium(ctx, node, copy_to_current) == DONE)
316 return;
317
318 bind_vertex_list(ctx, node);
319
320 /* Need that at least one time. */
321 if (ctx->NewState)
322 _mesa_update_state(ctx);
323
324 /* Return precomputed GL errors such as invalid shaders. */
325 if (!ctx->ValidPrimMask) {
326 _mesa_error(ctx, ctx->DrawGLError, "glCallList");
327 return;
328 }
329
330 assert(ctx->NewState == 0);
331
332 struct pipe_draw_info *info = (struct pipe_draw_info *) &node->cold->info;
333 void *gl_bo = info->index.gl_bo;
334 if (node->modes) {
335 ctx->Driver.DrawGalliumMultiMode(ctx, info,
336 node->start_counts,
337 node->modes,
338 node->num_draws);
339 } else if (node->num_draws == 1) {
340 ctx->Driver.DrawGallium(ctx, info, 0, &node->start_count, 1);
341 } else if (node->num_draws) {
342 ctx->Driver.DrawGallium(ctx, info, 0, node->start_counts,
343 node->num_draws);
344 }
345 info->index.gl_bo = gl_bo;
346
347 if (copy_to_current)
348 playback_copy_to_current(ctx, node);
349 }
350