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 44static void 45copy_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 */ 97static void 98playback_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 */ 135static void 136bind_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 144static void 145loopback_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 160void 161vbo_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 182enum vbo_save_status { 183 DONE, 184 USE_SLOW_PATH, 185}; 186 187static enum vbo_save_status 188vbo_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 */ 298void 299vbo_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