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 
45 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)46 copy_vao(struct gl_context *ctx, const struct gl_vertex_array_object *vao,
47          GLbitfield mask, GLbitfield state, GLbitfield pop_state,
48          int shift, fi_type **data, bool *color0_changed)
49 {
50    struct vbo_context *vbo = vbo_context(ctx);
51 
52    mask &= vao->Enabled;
53    while (mask) {
54       const int i = u_bit_scan(&mask);
55       const struct gl_array_attributes *attrib = &vao->VertexAttrib[i];
56       unsigned current_index = shift + i;
57       struct gl_array_attributes *currval = &vbo->current[current_index];
58       const GLubyte size = attrib->Format.Size;
59       const GLenum16 type = attrib->Format.Type;
60       fi_type tmp[8];
61       int dmul_shift = 0;
62 
63       if (type == GL_DOUBLE ||
64           type == GL_UNSIGNED_INT64_ARB) {
65          dmul_shift = 1;
66          memcpy(tmp, *data, size * 2 * sizeof(GLfloat));
67       } else {
68          COPY_CLEAN_4V_TYPE_AS_UNION(tmp, size, *data, type);
69       }
70 
71       if (memcmp(currval->Ptr, tmp, 4 * sizeof(GLfloat) << dmul_shift) != 0) {
72          memcpy((fi_type*)currval->Ptr, tmp, 4 * sizeof(GLfloat) << dmul_shift);
73 
74          if (current_index == VBO_ATTRIB_COLOR0)
75             *color0_changed = true;
76 
77          /* The fixed-func vertex program uses this. */
78          if (current_index == VBO_ATTRIB_MAT_FRONT_SHININESS ||
79              current_index == VBO_ATTRIB_MAT_BACK_SHININESS)
80             ctx->NewState |= _NEW_FF_VERT_PROGRAM;
81 
82          ctx->NewState |= state;
83          ctx->PopAttribState |= pop_state;
84       }
85 
86       if (type != currval->Format.Type ||
87           (size >> dmul_shift) != currval->Format.Size)
88          vbo_set_vertex_format(&currval->Format, size >> dmul_shift, type);
89 
90       *data += size;
91    }
92 }
93 
94 /**
95  * After playback, copy everything but the position from the
96  * last vertex to the saved state
97  */
98 static void
playback_copy_to_current(struct gl_context * ctx,const struct vbo_save_vertex_list * node)99 playback_copy_to_current(struct gl_context *ctx,
100                          const struct vbo_save_vertex_list *node)
101 {
102    if (!node->cold->current_data)
103       return;
104 
105    fi_type *data = node->cold->current_data;
106    bool color0_changed = false;
107 
108    /* Copy conventional attribs and generics except pos */
109    copy_vao(ctx, node->VAO[VP_MODE_SHADER], ~VERT_BIT_POS & VERT_BIT_ALL,
110             _NEW_CURRENT_ATTRIB, GL_CURRENT_BIT, 0, &data, &color0_changed);
111    /* Copy materials */
112    copy_vao(ctx, node->VAO[VP_MODE_FF], VERT_BIT_MAT_ALL,
113             _NEW_MATERIAL, GL_LIGHTING_BIT,
114             VBO_MATERIAL_SHIFT, &data, &color0_changed);
115 
116    if (color0_changed && ctx->Light.ColorMaterialEnabled) {
117       _mesa_update_color_material(ctx, ctx->Current.Attrib[VBO_ATTRIB_COLOR0]);
118    }
119 
120    /* CurrentExecPrimitive
121     */
122    if (node->cold->prim_count) {
123       const struct _mesa_prim *prim = &node->cold->prims[node->cold->prim_count - 1];
124       if (prim->end)
125          ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
126       else
127          ctx->Driver.CurrentExecPrimitive = prim->mode;
128    }
129 }
130 
131 
132 
133 /**
134  * Set the appropriate VAO to draw.
135  */
136 static void
bind_vertex_list(struct gl_context * ctx,const struct vbo_save_vertex_list * node)137 bind_vertex_list(struct gl_context *ctx,
138                  const struct vbo_save_vertex_list *node)
139 {
140    const gl_vertex_processing_mode mode = ctx->VertexProgram._VPMode;
141    _mesa_set_draw_vao(ctx, node->VAO[mode], _vbo_get_vao_filter(mode));
142 }
143 
144 
145 static void
loopback_vertex_list(struct gl_context * ctx,const struct vbo_save_vertex_list * list)146 loopback_vertex_list(struct gl_context *ctx,
147                      const struct vbo_save_vertex_list *list)
148 {
149    struct gl_buffer_object *bo = list->VAO[0]->BufferBinding[0].BufferObj;
150    void *buffer = ctx->Driver.MapBufferRange(ctx, 0, bo->Size, GL_MAP_READ_BIT, /* ? */
151                                              bo, MAP_INTERNAL);
152 
153    /* TODO: in this case, we shouldn't create a bo at all and instead keep
154     * the in-RAM buffer. */
155    _vbo_loopback_vertex_list(ctx, list, buffer);
156 
157    ctx->Driver.UnmapBuffer(ctx, bo, MAP_INTERNAL);
158 }
159 
160 
161 void
vbo_save_playback_vertex_list_loopback(struct gl_context * ctx,void * data)162 vbo_save_playback_vertex_list_loopback(struct gl_context *ctx, void *data)
163 {
164    const struct vbo_save_vertex_list *node =
165       (const struct vbo_save_vertex_list *) data;
166 
167    FLUSH_FOR_DRAW(ctx);
168 
169    if (_mesa_inside_begin_end(ctx) && node->cold->prims[0].begin) {
170       /* Error: we're about to begin a new primitive but we're already
171        * inside a glBegin/End pair.
172        */
173       _mesa_error(ctx, GL_INVALID_OPERATION,
174                   "draw operation inside glBegin/End");
175       return;
176    }
177    /* Various degenerate cases: translate into immediate mode
178     * calls rather than trying to execute in place.
179     */
180    loopback_vertex_list(ctx, node);
181 }
182 
183 enum vbo_save_status {
184    DONE,
185    USE_SLOW_PATH,
186 };
187 
188 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)189 vbo_save_playback_vertex_list_gallium(struct gl_context *ctx,
190                                       const struct vbo_save_vertex_list *node,
191                                       bool copy_to_current)
192 {
193    /* Don't use this if selection or feedback mode is enabled. st/mesa can't
194     * handle it.
195     */
196    if (!ctx->Driver.DrawGalliumVertexState || ctx->RenderMode != GL_RENDER)
197       return USE_SLOW_PATH;
198 
199    const gl_vertex_processing_mode mode = ctx->VertexProgram._VPMode;
200 
201    /* This sets which vertex arrays are enabled, which determines
202     * which attribs have stride = 0 and whether edge flags are enabled.
203     */
204    const GLbitfield enabled = node->merged.gallium.enabled_attribs[mode];
205    ctx->Array._DrawVAOEnabledAttribs = enabled;
206    _mesa_set_varying_vp_inputs(ctx, enabled);
207 
208    if (ctx->NewState)
209       _mesa_update_state(ctx);
210 
211    /* Use the slow path when there are vertex inputs without vertex
212     * elements. This happens with zero-stride attribs and non-fixed-func
213     * shaders.
214     *
215     * Dual-slot inputs are also unsupported because the higher slot is
216     * always missing in vertex elements.
217     *
218     * TODO: Add support for zero-stride attribs.
219     */
220    struct gl_program *vp = ctx->VertexProgram._Current;
221 
222    if (vp->info.inputs_read & ~enabled || vp->DualSlotInputs)
223       return USE_SLOW_PATH;
224 
225    struct pipe_vertex_state *state = node->merged.gallium.state[mode];
226    struct pipe_draw_vertex_state_info info = node->merged.gallium.info;
227 
228    /* Return precomputed GL errors such as invalid shaders. */
229    if (!ctx->ValidPrimMask) {
230       _mesa_error(ctx, ctx->DrawGLError, "glCallList");
231       return DONE;
232    }
233 
234    if (node->merged.gallium.ctx == ctx) {
235       /* This mechanism allows passing references to the driver without
236        * using atomics to increase the reference count.
237        *
238        * This private refcount can be decremented without atomics but only
239        * one context (ctx above) can use this counter (so that it's only
240        * used by 1 thread).
241        *
242        * This number is atomically added to reference.count at
243        * initialization. If it's never used, the same number is atomically
244        * subtracted from reference.count before destruction. If this number
245        * is decremented, we can pass one reference to the driver without
246        * touching reference.count with atomics. At destruction we only
247        * subtract the number of references we have not returned. This can
248        * possibly turn a million atomic increments into 1 add and 1 subtract
249        * atomic op over the whole lifetime of an app.
250        */
251       int * const private_refcount = (int*)&node->merged.gallium.private_refcount[mode];
252       assert(*private_refcount >= 0);
253 
254       if (unlikely(*private_refcount == 0)) {
255          /* pipe_vertex_state can be reused through util_vertex_state_cache,
256           * and there can be many display lists over-incrementing this number,
257           * causing it to overflow.
258           *
259           * Guess that the same state can never be used by N=500000 display
260           * lists, so one display list can only increment it by
261           * INT_MAX / N.
262           */
263          const int add_refs = INT_MAX / 500000;
264          p_atomic_add(&state->reference.count, add_refs);
265          *private_refcount = add_refs;
266       }
267 
268       (*private_refcount)--;
269       info.take_vertex_state_ownership = true;
270    }
271 
272    /* Fast path using a pre-built gallium vertex buffer state. */
273    if (node->merged.mode || node->merged.num_draws > 1) {
274       ctx->Driver.DrawGalliumVertexState(ctx, state, info,
275                                          node->merged.start_counts,
276                                          node->merged.mode,
277                                          node->merged.num_draws,
278                                          enabled & VERT_ATTRIB_EDGEFLAG);
279    } else if (node->merged.num_draws) {
280       ctx->Driver.DrawGalliumVertexState(ctx, state, info,
281                                          &node->merged.start_count,
282                                          NULL, 1,
283                                          enabled & VERT_ATTRIB_EDGEFLAG);
284    }
285 
286    if (copy_to_current)
287       playback_copy_to_current(ctx, node);
288    return DONE;
289 }
290 
291 /**
292  * Execute the buffer and save copied verts.
293  * This is called from the display list code when executing
294  * a drawing command.
295  */
296 void
vbo_save_playback_vertex_list(struct gl_context * ctx,void * data,bool copy_to_current)297 vbo_save_playback_vertex_list(struct gl_context *ctx, void *data, bool copy_to_current)
298 {
299    const struct vbo_save_vertex_list *node =
300       (const struct vbo_save_vertex_list *) data;
301 
302    FLUSH_FOR_DRAW(ctx);
303 
304    if (_mesa_inside_begin_end(ctx) && node->cold->prims[0].begin) {
305       /* Error: we're about to begin a new primitive but we're already
306        * inside a glBegin/End pair.
307        */
308       _mesa_error(ctx, GL_INVALID_OPERATION,
309                   "draw operation inside glBegin/End");
310       return;
311    }
312 
313    if (vbo_save_playback_vertex_list_gallium(ctx, node, copy_to_current) == DONE)
314       return;
315 
316    bind_vertex_list(ctx, node);
317 
318    /* Need that at least one time. */
319    if (ctx->NewState)
320       _mesa_update_state(ctx);
321 
322    /* Return precomputed GL errors such as invalid shaders. */
323    if (!ctx->ValidPrimMask) {
324       _mesa_error(ctx, ctx->DrawGLError, "glCallList");
325       return;
326    }
327 
328    assert(ctx->NewState == 0);
329 
330    struct pipe_draw_info *info = (struct pipe_draw_info *) &node->merged.info;
331    void *gl_bo = info->index.gl_bo;
332    if (node->merged.mode) {
333       ctx->Driver.DrawGalliumMultiMode(ctx, info,
334                                        node->merged.start_counts,
335                                        node->merged.mode,
336                                        node->merged.num_draws);
337    } else if (node->merged.num_draws == 1) {
338       ctx->Driver.DrawGallium(ctx, info, 0, &node->merged.start_count, 1);
339    } else if (node->merged.num_draws) {
340       ctx->Driver.DrawGallium(ctx, info, 0, node->merged.start_counts,
341                               node->merged.num_draws);
342    }
343    info->index.gl_bo = gl_bo;
344 
345    if (copy_to_current)
346       playback_copy_to_current(ctx, node);
347 }
348