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
45static void
46copy_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 */
98static void
99playback_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 */
136static void
137bind_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
145static void
146loopback_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
161void
162vbo_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
183enum vbo_save_status {
184   DONE,
185   USE_SLOW_PATH,
186};
187
188static enum vbo_save_status
189vbo_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 */
296void
297vbo_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