vbo_exec_vtx_flush定義在vbo_exec_draw.c第353行處,具體代碼如下:
vbo_exec_vtx_flush// vbo_exec_draw.c 從第349行至417行/** * Execute the buffer and save copied verts. */voidvbo_exec_vtx_flush( struct vbo_exec_context *exec, GLboolean unmap ){ if (0) vbo_exec_debug_verts( exec ); if (exec->vtx.prim_count && exec->vtx.vert_count) { exec->vtx.copied.nr = vbo_copy_vertices( exec ); if (exec->vtx.copied.nr != exec->vtx.vert_count) { GLcontext *ctx = exec->ctx; /* Before the update_state() as this may raise _NEW_ARRAY * from _mesa_set_varying_vp_inputs(). */ vbo_exec_bind_arrays( ctx ); if (ctx->NewState) _mesa_update_state( ctx ); if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { vbo_exec_vtx_unmap( exec ); } if (0) _mesa_printf("%s %d %d\n", __FUNCTION__, exec->vtx.prim_count, exec->vtx.vert_count); vbo_context(ctx)->draw_prims( ctx, exec->vtx.inputs, exec->vtx.prim, exec->vtx.prim_count, NULL, GL_TRUE, 0, exec->vtx.vert_count - 1); /* If using a real VBO, get new storage -- unless asked not to. */ if (_mesa_is_bufferobj(exec->vtx.bufferobj) && !unmap) { vbo_exec_vtx_map( exec ); } } } /* May have to unmap explicitly if we didn't draw: */ if (unmap && _mesa_is_bufferobj(exec->vtx.bufferobj) && exec->vtx.buffer_map) { vbo_exec_vtx_unmap( exec ); } if (unmap || exec->vtx.vertex_size == 0) exec->vtx.max_vert = 0; else exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / (exec->vtx.vertex_size * sizeof(GLfloat))); exec->vtx.buffer_ptr = exec->vtx.buffer_map; exec->vtx.prim_count = 0; exec->vtx.vert_count = 0;}
第382行是開始準備進入管道繪製處的語句,具體對應t_draw.c第363行處的_tnl_vbo_draw_prims,然後到達_tnl_draw_prims函數;
_tnl_draw_prims// t_draw.c 從第378行到450行/* This is the main entrypoint into the slimmed-down software tnl * module. In a regular swtnl driver, this can be plugged straight * into the vbo->Driver.DrawPrims() callback. */void _tnl_draw_prims( GLcontext *ctx, const struct gl_client_array *arrays[], const struct _mesa_prim *prim, GLuint nr_prims, const struct _mesa_index_buffer *ib, GLuint min_index, GLuint max_index){ TNLcontext *tnl = TNL_CONTEXT(ctx); const GLuint TEST_SPLIT = 0; const GLint max = TEST_SPLIT ? 8 : tnl->vb.Size - MAX_CLIPPED_VERTICES; if (0) { GLuint i; _mesa_printf("%s %d..%d\n", __FUNCTION__, min_index, max_index); for (i = 0; i < nr_prims; i++) _mesa_printf("prim %d: %s start %d count %d\n", i, _mesa_lookup_enum_by_nr(prim[i].mode), prim[i].start, prim[i].count); } if (min_index) { /* We always translate away calls with min_index != 0. */ vbo_rebase_prims( ctx, arrays, prim, nr_prims, ib, min_index, max_index,_tnl_vbo_draw_prims ); return; } else if (max_index > max) { /* The software TNL pipeline has a fixed amount of storage for * vertices and it is necessary to split incoming drawing commands * if they exceed that limit. */ struct split_limits limits; limits.max_verts = max; limits.max_vb_size = ~0; limits.max_indices = ~0; /* This will split the buffers one way or another and * recursively call back into this function. */ vbo_split_prims( ctx, arrays, prim, nr_prims, ib, 0, max_index, _tnl_vbo_draw_prims, &limits ); } else { /* May need to map a vertex buffer object for every attribute plus * one for the index buffer. */ struct gl_buffer_object *bo[VERT_ATTRIB_MAX + 1]; GLuint nr_bo = 0; /* Binding inputs may imply mapping some vertex buffer objects. * They will need to be unmapped below. */ bind_inputs(ctx, arrays, max_index+1, bo, &nr_bo); bind_indices(ctx, ib, bo, &nr_bo); bind_prims(ctx, prim, nr_prims ); TNL_CONTEXT(ctx)->Driver.RunPipeline(ctx); unmap_vbos(ctx, bo, nr_bo); free_space(ctx); }}
實際執行時只會執行第431行起的else {}代碼塊,而第445行處的TNL_CONTEXT(ctx)->Driver.RunPipeline(ctx);是正式的進入管道的入口語句,它對應在t_pipeline.c第121行處的_tnl_run_pipeline。
_tnl_run_pipeline// t_pipeline.c 從第121行至162行void _tnl_run_pipeline( GLcontext *ctx ){ TNLcontext *tnl = TNL_CONTEXT(ctx); unsigned short __tmp; GLuint i; if (!tnl->vb.Count) return; /* Check for changed input sizes or change in stride to/from zero * (ie const or non-const). */ if (check_input_changes( ctx ) || tnl->pipeline.new_state) { if (ctx->VertexProgram._MaintainTnlProgram) _tnl_UpdateFixedFunctionProgram( ctx ); for (i = 0; i < tnl->pipeline.nr_stages ; i++) { struct tnl_pipeline_stage *s = &tnl->pipeline.stages[i]; if (s->validate) s->validate( ctx, s ); } tnl->pipeline.new_state = 0; tnl->pipeline.input_changes = 0; /* Pipeline can only change its output in response to either a * statechange or an input size/stride change. No other changes * are allowed. */ if (check_output_changes( ctx )) _tnl_notify_pipeline_output_change( ctx ); } START_FAST_MATH(__tmp); for (i = 0; i < tnl->pipeline.nr_stages ; i++) { struct tnl_pipeline_stage *s = &tnl->pipeline.stages[i]; if (!s->run( ctx, s )) break; } END_FAST_MATH(__tmp);}
其中對應的管道結構變數是:
const struct tnl_pipeline_stage *_tnl_default_pipeline[] = { &_tnl_vertex_transform_stage, &_tnl_normal_transform_stage, &_tnl_lighting_stage, &_tnl_texgen_stage, &_tnl_texture_transform_stage, &_tnl_point_attenuation_stage, &_tnl_vertex_program_stage, &_tnl_fog_coordinate_stage, &_tnl_render_stage, NULL };
由上可見首先計算頂點,然後變換,再光照等等,最後運行渲染。