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