--- src/mapi/glapi/gen/ARB_draw_indirect.xml | 45 +++++ src/mapi/glapi/gen/Makefile.am | 1 + src/mapi/glapi/gen/gl_API.xml | 4 +- src/mesa/drivers/dri/i965/brw_draw.c | 3 +- src/mesa/drivers/dri/i965/brw_draw.h | 3 +- src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c | 9 +- src/mesa/main/api_validate.c | 159 ++++++++++++++++ src/mesa/main/api_validate.h | 26 +++ src/mesa/main/bufferobj.c | 9 + src/mesa/main/dd.h | 12 ++ src/mesa/main/dlist.c | 41 ++++ src/mesa/main/extensions.c | 2 + src/mesa/main/get.c | 5 + src/mesa/main/get_hash_params.py | 2 + src/mesa/main/mtypes.h | 4 + src/mesa/main/tests/dispatch_sanity.cpp | 8 +- src/mesa/main/vtxfmt.c | 7 + src/mesa/state_tracker/st_cb_rasterpos.c | 2 +- src/mesa/state_tracker/st_draw.c | 3 +- src/mesa/state_tracker/st_draw.h | 6 +- src/mesa/state_tracker/st_draw_feedback.c | 3 +- src/mesa/tnl/tnl.h | 3 +- src/mesa/vbo/vbo.h | 5 +- src/mesa/vbo/vbo_exec_array.c | 255 +++++++++++++++++++++++++- src/mesa/vbo/vbo_exec_draw.c | 2 +- src/mesa/vbo/vbo_primitive_restart.c | 4 +- src/mesa/vbo/vbo_rebase.c | 2 +- src/mesa/vbo/vbo_save_api.c | 53 ++++++ src/mesa/vbo/vbo_save_draw.c | 2 +- src/mesa/vbo/vbo_split_copy.c | 2 +- src/mesa/vbo/vbo_split_inplace.c | 2 +- 31 files changed, 656 insertions(+), 28 deletions(-) create mode 100644 src/mapi/glapi/gen/ARB_draw_indirect.xml
diff --git a/src/mapi/glapi/gen/ARB_draw_indirect.xml b/src/mapi/glapi/gen/ARB_draw_indirect.xml new file mode 100644 index 0000000..7de03cd --- /dev/null +++ b/src/mapi/glapi/gen/ARB_draw_indirect.xml @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd"> + +<OpenGLAPI> + +<category name="GL_ARB_draw_indirect" number="87"> + + <enum name="DRAW_INDIRECT_BUFFER" value="0x8F3F"/> + <enum name="DRAW_INDIRECT_BUFFER_BINDING" value="0x8F43"/> + + <function name="DrawArraysIndirect" offset="assign" exec="dynamic"> + <param name="mode" type="GLenum"/> + <param name="indirect" type="const GLvoid *"/> + </function> + + <function name="DrawElementsIndirect" offset="assign" exec="dynamic"> + <param name="mode" type="GLenum"/> + <param name="type" type="GLenum"/> + <param name="indirect" type="const GLvoid *"/> + </function> + +</category> + + +<category name="GL_ARB_multi_draw_indirect" number="133"> + + <function name="MultiDrawArraysIndirect" offset="assign" exec="dynamic"> + <param name="mode" type="GLenum"/> + <param name="indirect" type="const GLvoid *"/> + <param name="primcount" type="GLsizei"/> + <param name="stride" type="GLsizei"/> + </function> + + <function name="MultiDrawElementsIndirect" offset="assign" exec="dynamic"> + <param name="mode" type="GLenum"/> + <param name="type" type="GLenum"/> + <param name="indirect" type="const GLvoid *"/> + <param name="primcount" type="GLsizei"/> + <param name="stride" type="GLsizei"/> + </function> + +</category> + + +</OpenGLAPI> diff --git a/src/mapi/glapi/gen/Makefile.am b/src/mapi/glapi/gen/Makefile.am index 36e47e2..243c148 100644 --- a/src/mapi/glapi/gen/Makefile.am +++ b/src/mapi/glapi/gen/Makefile.am @@ -96,6 +96,7 @@ API_XML = \ ARB_depth_clamp.xml \ ARB_draw_buffers_blend.xml \ ARB_draw_elements_base_vertex.xml \ + ARB_draw_indirect.xml \ ARB_draw_instanced.xml \ ARB_ES2_compatibility.xml \ ARB_ES3_compatibility.xml \ diff --git a/src/mapi/glapi/gen/gl_API.xml b/src/mapi/glapi/gen/gl_API.xml index df95924..f22fdac 100644 --- a/src/mapi/glapi/gen/gl_API.xml +++ b/src/mapi/glapi/gen/gl_API.xml @@ -8240,6 +8240,8 @@ <!-- ARB extensions #86...#93 --> +<xi:include href="ARB_draw_indirect.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/> + <category name="GL_ARB_transform_feedback3" number="94"> <enum name="MAX_TRANSFORM_FEEDBACK_BUFFERS" value="0x8E70"/> <enum name="MAX_VERTEX_STREAMS" value="0x8E71"/> @@ -8317,7 +8319,7 @@ <xi:include href="ARB_invalidate_subdata.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/> -<!-- ARB extensions #133...#138 --> +<!-- ARB extensions #134...#138 --> <xi:include href="ARB_texture_buffer_range.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/> diff --git a/src/mesa/drivers/dri/i965/brw_draw.c b/src/mesa/drivers/dri/i965/brw_draw.c index 809bcc5..d0c8415 100644 --- a/src/mesa/drivers/dri/i965/brw_draw.c +++ b/src/mesa/drivers/dri/i965/brw_draw.c @@ -548,7 +548,8 @@ void brw_draw_prims( struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount ) + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect ) { struct intel_context *intel = intel_context(ctx); const struct gl_client_array **arrays = ctx->Array._DrawArrays; diff --git a/src/mesa/drivers/dri/i965/brw_draw.h b/src/mesa/drivers/dri/i965/brw_draw.h index d86a9e7..3dfac2e 100644 --- a/src/mesa/drivers/dri/i965/brw_draw.h +++ b/src/mesa/drivers/dri/i965/brw_draw.h @@ -41,7 +41,8 @@ void brw_draw_prims( struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount ); + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *tfb_vertcount ); void brw_draw_init( struct brw_context *brw ); void brw_draw_destroy( struct brw_context *brw ); diff --git a/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c b/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c index 436db32..4dee0b8 100644 --- a/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c +++ b/src/mesa/drivers/dri/nouveau/nouveau_vbo_t.c @@ -222,7 +222,8 @@ TAG(vbo_render_prims)(struct gl_context *ctx, const struct _mesa_index_buffer *ib, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount); + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect); static GLboolean vbo_maybe_split(struct gl_context *ctx, const struct gl_client_array **arrays, @@ -453,7 +454,8 @@ TAG(vbo_render_prims)(struct gl_context *ctx, const struct _mesa_index_buffer *ib, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount) + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect) { struct nouveau_render_state *render = to_render_state(ctx); const struct gl_client_array **arrays = ctx->Array._DrawArrays; @@ -489,7 +491,8 @@ TAG(vbo_check_render_prims)(struct gl_context *ctx, const struct _mesa_index_buffer *ib, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount) + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect) { struct nouveau_context *nctx = to_nouveau_context(ctx); diff --git a/src/mesa/main/api_validate.c b/src/mesa/main/api_validate.c index 53b0021..db9566c 100644 --- a/src/mesa/main/api_validate.c +++ b/src/mesa/main/api_validate.c @@ -737,3 +737,162 @@ _mesa_validate_DrawTransformFeedback(struct gl_context *ctx, return GL_TRUE; } + +static GLboolean +valid_draw_indirect(struct gl_context *ctx, + GLenum mode, const GLvoid *indirect, + GLsizei size, const char *name) +{ + const GLsizeiptr end = (GLsizeiptr)indirect + size; + + if (!_mesa_valid_prim_mode(ctx, mode, name)) + return GL_FALSE; + + if ((GLsizeiptr)indirect & (sizeof(GLuint) - 1)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(indirect is not aligned)", name); + return GL_FALSE; + } + + if (_mesa_is_bufferobj(ctx->DrawIndirectBuffer)) { + if (_mesa_bufferobj_mapped(ctx->DrawIndirectBuffer)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(DRAW_INDIRECT_BUFFER is mapped)", name); + return GL_FALSE; + } + if (ctx->DrawIndirectBuffer->Size < end) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(DRAW_INDIRECT_BUFFER too small)", name); + return GL_FALSE; + } + } else { + if (ctx->API != API_OPENGL_COMPAT) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s: no buffer bound to DRAW_INDIRECT_BUFFER", name); + return GL_FALSE; + } + } + + if (!check_valid_to_render(ctx, name)) + return GL_FALSE; + + return GL_TRUE; +} + +static inline GLboolean +valid_draw_indirect_elements(struct gl_context *ctx, + GLenum mode, GLenum type, const GLvoid *indirect, + GLsizeiptr size, const char *name) +{ + if (!valid_elements_type(ctx, type, name)) + return GL_FALSE; + + /* + * Unlike regular DrawElementsInstancedBaseVertex commands, the indices + * may not come from a client array and must come from an index buffer. + * If no element array buffer is bound, * an INVALID_OPERATION error is + * generated. + */ + if (!_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(no buffer bound to GL_ELEMENT_ARRAY_BUFFER)", name); + return GL_FALSE; + } + + return valid_draw_indirect(ctx, mode, indirect, size, name); +} + +static inline GLboolean +valid_draw_indirect_multi(struct gl_context *ctx, + GLsizei primcount, GLsizei stride, + const char *name) +{ + if (primcount < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(primcount < 0)", name); + return GL_FALSE; + } + + if (stride % 4) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(stride %% 4)", name); + return GL_FALSE; + } + + return GL_TRUE; +} + +extern GLboolean +_mesa_validate_DrawArraysIndirect(struct gl_context *ctx, + GLenum mode, + const GLvoid *indirect) +{ + FLUSH_CURRENT(ctx, 0); + + if (!valid_draw_indirect(ctx, mode, + indirect, 4 * sizeof(GLuint), + "glDrawArraysIndirect")) + return GL_FALSE; + + return GL_TRUE; +} + +extern GLboolean +_mesa_validate_DrawElementsIndirect(struct gl_context *ctx, + GLenum mode, GLenum type, + const GLvoid *indirect) +{ + FLUSH_CURRENT(ctx, 0); + + if (!valid_draw_indirect_elements(ctx, mode, type, + indirect, 5 * sizeof(GLuint), + "glDrawElementsIndirect")) + return GL_FALSE; + + return GL_TRUE; +} + +extern GLboolean +_mesa_validate_MultiDrawArraysIndirect(struct gl_context *ctx, + GLenum mode, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GLsizeiptr size = 0; + if (primcount) + size = (primcount - 1) * stride + 4 * sizeof(GLuint); + + FLUSH_CURRENT(ctx, 0); + + if (!valid_draw_indirect_multi(ctx, primcount, stride, + "glMultiDrawArraysIndirect")) + return GL_FALSE; + + if (!valid_draw_indirect(ctx, mode, indirect, size, + "glMultiDrawArraysIndirect")) + return GL_FALSE; + + return GL_TRUE; +} + +extern GLboolean +_mesa_validate_MultiDrawElementsIndirect(struct gl_context *ctx, + GLenum mode, GLenum type, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GLsizeiptr size = 0; + if (primcount) + size = (primcount - 1) * stride + 5 * sizeof(GLuint); + + FLUSH_CURRENT(ctx, 0); + + if (!valid_draw_indirect_multi(ctx, primcount, stride, + "glMultiDrawElementsIndirect")) + return GL_FALSE; + + if (!valid_draw_indirect_elements(ctx, mode, type, + indirect, size, + "glMultiDrawElementsIndirect")) + return GL_FALSE; + + return GL_TRUE; +} diff --git a/src/mesa/main/api_validate.h b/src/mesa/main/api_validate.h index 0ca9c90..38c6efd 100644 --- a/src/mesa/main/api_validate.h +++ b/src/mesa/main/api_validate.h @@ -85,5 +85,31 @@ _mesa_validate_DrawTransformFeedback(struct gl_context *ctx, GLuint stream, GLsizei numInstances); +extern GLboolean +_mesa_validate_DrawArraysIndirect(struct gl_context *ctx, + GLenum mode, + const GLvoid *indirect); + +extern GLboolean +_mesa_validate_DrawElementsIndirect(struct gl_context *ctx, + GLenum mode, + GLenum type, + const GLvoid *indirect); + +extern GLboolean +_mesa_validate_MultiDrawArraysIndirect(struct gl_context *ctx, + GLenum mode, + const GLvoid *indirect, + GLsizei primcount, + GLsizei stride); + +extern GLboolean +_mesa_validate_MultiDrawElementsIndirect(struct gl_context *ctx, + GLenum mode, + GLenum type, + const GLvoid *indirect, + GLsizei primcount, + GLsizei stride); + #endif diff --git a/src/mesa/main/bufferobj.c b/src/mesa/main/bufferobj.c index b82ba7b..0fe01d4 100644 --- a/src/mesa/main/bufferobj.c +++ b/src/mesa/main/bufferobj.c @@ -87,6 +87,10 @@ get_buffer_target(struct gl_context *ctx, GLenum target) return &ctx->CopyReadBuffer; case GL_COPY_WRITE_BUFFER: return &ctx->CopyWriteBuffer; + case GL_DRAW_INDIRECT_BUFFER: + if (ctx->Extensions.ARB_draw_indirect) + return &ctx->DrawIndirectBuffer; + break; case GL_TRANSFORM_FEEDBACK_BUFFER: if (ctx->Extensions.EXT_transform_feedback) { return &ctx->TransformFeedback.CurrentBuffer; @@ -875,6 +879,11 @@ _mesa_DeleteBuffers(GLsizei n, const GLuint *ids) _mesa_BindBuffer( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 ); } + /* unbind ARB_draw_indirect binding point */ + if (ctx->DrawIndirectBuffer == bufObj) { + _mesa_BindBuffer( GL_DRAW_INDIRECT_BUFFER, 0 ); + } + /* unbind ARB_copy_buffer binding points */ if (ctx->CopyReadBuffer == bufObj) { _mesa_BindBuffer( GL_COPY_READ_BUFFER, 0 ); diff --git a/src/mesa/main/dd.h b/src/mesa/main/dd.h index 8f3cd3d..51a78b4 100644 --- a/src/mesa/main/dd.h +++ b/src/mesa/main/dd.h @@ -1078,6 +1078,18 @@ typedef struct { GLuint name, GLuint stream, GLsizei primcount); + void (GLAPIENTRYP DrawArraysIndirect)(GLenum mode, + const GLvoid *indirect); + void (GLAPIENTRYP DrawElementsIndirect)(GLenum mode, GLenum type, + const GLvoid *indirect); + void (GLAPIENTRYP MultiDrawArraysIndirect)(GLenum mode, + const GLvoid *indirect, + GLsizei primcount, + GLsizei stride); + void (GLAPIENTRYP MultiDrawElementsIndirect)(GLenum mode, GLenum type, + const GLvoid *indirect, + GLsizei primcount, + GLsizei stride); /*@}*/ /** diff --git a/src/mesa/main/dlist.c b/src/mesa/main/dlist.c index 4b20d89..45e7439 100644 --- a/src/mesa/main/dlist.c +++ b/src/mesa/main/dlist.c @@ -1356,6 +1356,41 @@ save_DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, "glDrawElementsInstancedBaseVertexBaseInstance() during display list compile"); } +static void GLAPIENTRY +save_DrawArraysIndirect(GLenum mode, const GLvoid *indirect) +{ + GET_CURRENT_CONTEXT(ctx); + _mesa_error(ctx, GL_INVALID_OPERATION, + "glDrawArraysIndirect() during display list compile"); +} + +static void GLAPIENTRY +save_DrawElementsIndirect(GLenum mode, GLenum type, const GLvoid *indirect) +{ + GET_CURRENT_CONTEXT(ctx); + _mesa_error(ctx, GL_INVALID_OPERATION, + "glDrawElementsIndirect() during display list compile"); +} + +static void GLAPIENTRY +save_MultiDrawArraysIndirect(GLenum mode, const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GET_CURRENT_CONTEXT(ctx); + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMultiDrawArraysIndirect() during display list compile"); +} + +static void GLAPIENTRY +save_MultiDrawElementsIndirect(GLenum mode, GLenum type, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GET_CURRENT_CONTEXT(ctx); + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMultiDrawElementsIndirect() during display list compile"); +} + static void invalidate_saved_current_state( struct gl_context *ctx ) { GLint i; @@ -9620,6 +9655,12 @@ _mesa_save_vtxfmt_init(GLvertexformat * vfmt) vfmt->DrawElementsInstancedBaseInstance = save_DrawElementsInstancedBaseInstance; vfmt->DrawElementsInstancedBaseVertexBaseInstance = save_DrawElementsInstancedBaseVertexBaseInstance; + /* GL_ARB_draw_indirect and GL_ARB_multi_draw_indirect */ + vfmt->DrawArraysIndirect = save_DrawArraysIndirect; + vfmt->MultiDrawArraysIndirect = save_MultiDrawArraysIndirect; + vfmt->DrawElementsIndirect = save_DrawElementsIndirect; + vfmt->MultiDrawElementsIndirect = save_MultiDrawElementsIndirect; + /* The driver is required to implement these as * 1) They can probably do a better job. * 2) A lot of new mechanisms would have to be added to this module diff --git a/src/mesa/main/extensions.c b/src/mesa/main/extensions.c index c7f038b..0891d47 100644 --- a/src/mesa/main/extensions.c +++ b/src/mesa/main/extensions.c @@ -93,6 +93,7 @@ static const struct extension extension_table[] = { { "GL_ARB_draw_buffers", o(dummy_true), GL, 2002 }, { "GL_ARB_draw_buffers_blend", o(ARB_draw_buffers_blend), GL, 2009 }, { "GL_ARB_draw_elements_base_vertex", o(ARB_draw_elements_base_vertex), GL, 2009 }, + { "GL_ARB_draw_indirect", o(ARB_draw_indirect), GLC, 2010 }, { "GL_ARB_draw_instanced", o(ARB_draw_instanced), GL, 2008 }, { "GL_ARB_explicit_attrib_location", o(ARB_explicit_attrib_location), GL, 2009 }, { "GL_ARB_fragment_coord_conventions", o(ARB_fragment_coord_conventions), GL, 2009 }, @@ -109,6 +110,7 @@ static const struct extension extension_table[] = { { "GL_ARB_invalidate_subdata", o(dummy_true), GL, 2012 }, { "GL_ARB_map_buffer_alignment", o(ARB_map_buffer_alignment), GL, 2011 }, { "GL_ARB_map_buffer_range", o(ARB_map_buffer_range), GL, 2008 }, + { "GL_ARB_multi_draw_indirect", o(ARB_multi_draw_indirect), GLC, 2012 }, { "GL_ARB_multisample", o(dummy_true), GLL, 1994 }, { "GL_ARB_multitexture", o(dummy_true), GLL, 1998 }, { "GL_ARB_occlusion_query2", o(ARB_occlusion_query2), GL, 2003 }, diff --git a/src/mesa/main/get.c b/src/mesa/main/get.c index 582ef31..f0f44aa 100644 --- a/src/mesa/main/get.c +++ b/src/mesa/main/get.c @@ -356,6 +356,7 @@ EXTRA_EXT(ARB_map_buffer_alignment); EXTRA_EXT(ARB_texture_cube_map_array); EXTRA_EXT(ARB_texture_buffer_range); EXTRA_EXT(ARB_texture_multisample); +EXTRA_EXT(ARB_draw_indirect); static const int extra_NV_primitive_restart[] = { @@ -853,6 +854,10 @@ find_custom_value(struct gl_context *ctx, const struct value_desc *d, union valu _mesa_problem(ctx, "driver doesn't implement GetTimestamp"); } break; + /* GL_ARB_draw_indirect */ + case GL_DRAW_INDIRECT_BUFFER_BINDING: + v->value_int = ctx->DrawIndirectBuffer->Name; + break; } } diff --git a/src/mesa/main/get_hash_params.py b/src/mesa/main/get_hash_params.py index 7d4f7e2..68513dc 100644 --- a/src/mesa/main/get_hash_params.py +++ b/src/mesa/main/get_hash_params.py @@ -715,6 +715,8 @@ descriptor=[ { "apis": ["GL_CORE"], "params": [ # GL_ARB_texture_buffer_range [ "TEXTURE_BUFFER_OFFSET_ALIGNMENT", "CONTEXT_INT(Const.TextureBufferOffsetAlignment), extra_ARB_texture_buffer_range" ], +# GL_ARB_draw_indirect + [ "DRAW_INDIRECT_BUFFER_BINDING", "LOC_CUSTOM, TYPE_INT, 0, extra_ARB_draw_indirect" ], ]} ] diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index ace6938..4f64683 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -2953,6 +2953,7 @@ struct gl_extensions GLboolean ARB_depth_texture; GLboolean ARB_draw_buffers_blend; GLboolean ARB_draw_elements_base_vertex; + GLboolean ARB_draw_indirect; GLboolean ARB_draw_instanced; GLboolean ARB_fragment_coord_conventions; GLboolean ARB_fragment_program; @@ -2967,6 +2968,7 @@ struct gl_extensions GLboolean ARB_internalformat_query; GLboolean ARB_map_buffer_alignment; GLboolean ARB_map_buffer_range; + GLboolean ARB_multi_draw_indirect; GLboolean ARB_occlusion_query; GLboolean ARB_occlusion_query2; GLboolean ARB_point_sprite; @@ -3515,6 +3517,8 @@ struct gl_context struct gl_transform_feedback_state TransformFeedback; + struct gl_buffer_object *DrawIndirectBuffer; /** < GL_ARB_draw_indirect */ + struct gl_buffer_object *CopyReadBuffer; /**< GL_ARB_copy_buffer */ struct gl_buffer_object *CopyWriteBuffer; /**< GL_ARB_copy_buffer */ diff --git a/src/mesa/main/tests/dispatch_sanity.cpp b/src/mesa/main/tests/dispatch_sanity.cpp index ffd83fe..dedaef4 100644 --- a/src/mesa/main/tests/dispatch_sanity.cpp +++ b/src/mesa/main/tests/dispatch_sanity.cpp @@ -674,8 +674,8 @@ const struct function gl_core_functions_possible[] = { { "glVertexAttribP3uiv", 43, -1 }, { "glVertexAttribP4ui", 43, -1 }, { "glVertexAttribP4uiv", 43, -1 }, -// { "glDrawArraysIndirect", 43, -1 }, // XXX: Add to xml -// { "glDrawElementsIndirect", 43, -1 }, // XXX: Add to xml + { "glDrawArraysIndirect", 43, -1 }, + { "glDrawElementsIndirect", 43, -1 }, // { "glUniform1d", 43, -1 }, // XXX: Add to xml // { "glUniform2d", 43, -1 }, // XXX: Add to xml // { "glUniform3d", 43, -1 }, // XXX: Add to xml @@ -884,8 +884,8 @@ const struct function gl_core_functions_possible[] = { { "glInvalidateBufferData", 43, -1 }, { "glInvalidateFramebuffer", 43, -1 }, { "glInvalidateSubFramebuffer", 43, -1 }, -// { "glMultiDrawArraysIndirect", 43, -1 }, // XXX: Add to xml -// { "glMultiDrawElementsIndirect", 43, -1 }, // XXX: Add to xml + { "glMultiDrawArraysIndirect", 43, -1 }, + { "glMultiDrawElementsIndirect", 43, -1 }, // { "glGetProgramInterfaceiv", 43, -1 }, // XXX: Add to xml // { "glGetProgramResourceIndex", 43, -1 }, // XXX: Add to xml // { "glGetProgramResourceName", 43, -1 }, // XXX: Add to xml diff --git a/src/mesa/main/vtxfmt.c b/src/mesa/main/vtxfmt.c index 8669c40..92a9374 100644 --- a/src/mesa/main/vtxfmt.c +++ b/src/mesa/main/vtxfmt.c @@ -147,6 +147,13 @@ install_vtxfmt(struct gl_context *ctx, struct _glapi_table *tab, vfmt->DrawTransformFeedbackStreamInstanced); } + if (_mesa_is_desktop_gl(ctx)) { + SET_DrawArraysIndirect(tab, vfmt->DrawArraysIndirect); + SET_DrawElementsIndirect(tab, vfmt->DrawElementsIndirect); + SET_MultiDrawArraysIndirect(tab, vfmt->MultiDrawArraysIndirect); + SET_MultiDrawElementsIndirect(tab, vfmt->MultiDrawElementsIndirect); + } + /* Originally for GL_NV_vertex_program, this is also used by dlist.c */ if (ctx->API == API_OPENGL_COMPAT) { SET_VertexAttrib1fNV(tab, vfmt->VertexAttrib1fNV); diff --git a/src/mesa/state_tracker/st_cb_rasterpos.c b/src/mesa/state_tracker/st_cb_rasterpos.c index 4731f26..778218a1 100644 --- a/src/mesa/state_tracker/st_cb_rasterpos.c +++ b/src/mesa/state_tracker/st_cb_rasterpos.c @@ -255,7 +255,7 @@ st_RasterPos(struct gl_context *ctx, const GLfloat v[4]) * st_feedback_draw_vbo doesn't check for that flag. */ ctx->Array._DrawArrays = rs->arrays; st_feedback_draw_vbo(ctx, &rs->prim, 1, NULL, GL_TRUE, 0, 1, - NULL); + NULL, NULL); ctx->Array._DrawArrays = saved_arrays; /* restore draw's rasterization stage depending on rendermode */ diff --git a/src/mesa/state_tracker/st_draw.c b/src/mesa/state_tracker/st_draw.c index bff8d9b..ee1c902 100644 --- a/src/mesa/state_tracker/st_draw.c +++ b/src/mesa/state_tracker/st_draw.c @@ -195,7 +195,8 @@ st_draw_vbo(struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount) + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect) { struct st_context *st = st_context(ctx); struct pipe_index_buffer ibuffer = {0}; diff --git a/src/mesa/state_tracker/st_draw.h b/src/mesa/state_tracker/st_draw.h index 3313fc8..401992c 100644 --- a/src/mesa/state_tracker/st_draw.h +++ b/src/mesa/state_tracker/st_draw.h @@ -55,7 +55,8 @@ st_draw_vbo(struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount); + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect); extern void st_feedback_draw_vbo(struct gl_context *ctx, @@ -65,7 +66,8 @@ st_feedback_draw_vbo(struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount); + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect); /** * When drawing with VBOs, the addresses specified with diff --git a/src/mesa/state_tracker/st_draw_feedback.c b/src/mesa/state_tracker/st_draw_feedback.c index ae87fb2..5ecd7a5 100644 --- a/src/mesa/state_tracker/st_draw_feedback.c +++ b/src/mesa/state_tracker/st_draw_feedback.c @@ -95,7 +95,8 @@ st_feedback_draw_vbo(struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount) + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; diff --git a/src/mesa/tnl/tnl.h b/src/mesa/tnl/tnl.h index 9583228..ad361ea 100644 --- a/src/mesa/tnl/tnl.h +++ b/src/mesa/tnl/tnl.h @@ -92,7 +92,8 @@ _tnl_vbo_draw_prims( struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount ); + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect ); extern void _tnl_RasterPos(struct gl_context *ctx, const GLfloat vObj[4]); diff --git a/src/mesa/vbo/vbo.h b/src/mesa/vbo/vbo.h index 49dab23..7156c35 100644 --- a/src/mesa/vbo/vbo.h +++ b/src/mesa/vbo/vbo.h @@ -52,6 +52,8 @@ struct _mesa_prim { GLint basevertex; GLuint num_instances; GLuint base_instance; + + GLsizeiptr indirect_offset; }; /* Would like to call this a "vbo_index_buffer", but this would be @@ -79,7 +81,8 @@ typedef void (*vbo_draw_func)( struct gl_context *ctx, GLboolean index_bounds_valid, GLuint min_index, GLuint max_index, - struct gl_transform_feedback_object *tfb_vertcount ); + struct gl_transform_feedback_object *tfb_vertcount, + struct gl_buffer_object *indirect ); diff --git a/src/mesa/vbo/vbo_exec_array.c b/src/mesa/vbo/vbo_exec_array.c index 7e61f7b..cd6ca2c 100644 --- a/src/mesa/vbo/vbo_exec_array.c +++ b/src/mesa/vbo/vbo_exec_array.c @@ -542,7 +542,7 @@ vbo_handle_primitive_restart(struct gl_context *ctx, } else { /* Call driver directly for draw_prims */ vbo->draw_prims(ctx, prim, nr_prims, ib, - index_bounds_valid, min_index, max_index, NULL); + index_bounds_valid, min_index, max_index, NULL, NULL); } } @@ -607,7 +607,7 @@ vbo_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, /* draw one or two prims */ check_buffers_are_unmapped(exec->array.inputs); vbo->draw_prims(ctx, prim, primCount, NULL, - GL_TRUE, start, start + count - 1, NULL); + GL_TRUE, start, start + count - 1, NULL, NULL); } } else { @@ -618,7 +618,7 @@ vbo_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, check_buffers_are_unmapped(exec->array.inputs); vbo->draw_prims(ctx, prim, 1, NULL, GL_TRUE, start, start + count - 1, - NULL); + NULL, NULL); } if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) { @@ -1285,7 +1285,7 @@ vbo_draw_transform_feedback(struct gl_context *ctx, GLenum mode, check_buffers_are_unmapped(exec->array.inputs); vbo->draw_prims(ctx, prim, 1, NULL, - GL_TRUE, 0, 0, obj); + GL_TRUE, 0, 0, obj, NULL); if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) { _mesa_flush(ctx); @@ -1359,6 +1359,249 @@ vbo_exec_DrawTransformFeedbackStreamInstanced(GLenum mode, GLuint name, vbo_draw_transform_feedback(ctx, mode, obj, stream, primcount); } +static void +vbo_validated_drawarraysindirect(struct gl_context *ctx, + GLenum mode, const GLvoid *indirect) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_prim prim[1]; + + vbo_bind_arrays(ctx); + + memset(prim, 0, sizeof(prim)); + prim[0].begin = 1; + prim[0].end = 1; + prim[0].mode = mode; + prim[0].indirect_offset = (GLsizeiptr)indirect; + + /* NOTE: We do NOT want to handle primitive restart here, nor perform any + * other checks that require knowledge of the values in the command buffer. + * That would deafeat the whole purpose of this function. + */ + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, prim, 1, + NULL, GL_TRUE, 0, 0, + NULL, + ctx->DrawIndirectBuffer); + + if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) + _mesa_flush(ctx); +} + +static void +vbo_validated_multidrawarraysindirect(struct gl_context *ctx, + GLenum mode, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_prim *prim; + GLsizei i; + GLsizeiptr offset = (GLsizeiptr)indirect; + + if (primcount == 0) + return; + prim = calloc(1, primcount * sizeof(*prim)); + if (prim == NULL) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMultiDrawArraysIndirect"); + return; + } + + vbo_bind_arrays(ctx); + + memset(prim, 0, primcount * sizeof(*prim)); + prim[0].begin = 1; + prim[primcount - 1].end = 1; + for (i = 0; i < primcount; ++i, offset += stride) { + prim[i].mode = mode; + prim[i].indirect_offset = offset; + } + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, prim, primcount, + NULL, GL_TRUE, 0, 0, + NULL, + ctx->DrawIndirectBuffer); + + free(prim); + + if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) + _mesa_flush(ctx); +} + +static void +vbo_validated_drawelementsindirect(struct gl_context *ctx, + GLenum mode, GLenum type, + const GLvoid *indirect) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_index_buffer ib; + struct _mesa_prim prim[1]; + + vbo_bind_arrays(ctx); + + ib.count = 0; /* unknown */ + ib.type = type; + ib.obj = ctx->Array.ArrayObj->ElementArrayBufferObj; + ib.ptr = NULL; + + memset(prim, 0, sizeof(prim)); + prim[0].begin = 1; + prim[0].end = 1; + prim[0].mode = mode; + prim[0].indexed = 1; + prim[0].indirect_offset = (GLsizeiptr)indirect; + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, prim, 1, + &ib, GL_TRUE, 0, 0, + NULL, + ctx->DrawIndirectBuffer); + + if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) + _mesa_flush(ctx); +} + +static void +vbo_validated_multidrawelementsindirect(struct gl_context *ctx, + GLenum mode, GLenum type, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_index_buffer ib; + struct _mesa_prim *prim; + GLsizei i; + GLsizeiptr offset = (GLsizeiptr)indirect; + + if (primcount == 0) + return; + prim = calloc(1, primcount * sizeof(*prim)); + if (prim == NULL) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMultiDrawElementsIndirect"); + return; + } + + vbo_bind_arrays(ctx); + + /* NOTE: ElementArrayBufferObj is guaranteed to be a VBO. */ + + ib.count = 0; /* unknown */ + ib.type = type; + ib.obj = ctx->Array.ArrayObj->ElementArrayBufferObj; + ib.ptr = NULL; + + memset(prim, 0, primcount * sizeof(*prim)); + prim[0].begin = 1; + prim[primcount - 1].end = 1; + for (i = 0; i < primcount; ++i, offset += stride) { + prim[i].mode = mode; + prim[i].indexed = 1; + prim[i].indirect_offset = offset; + } + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, prim, primcount, + &ib, GL_TRUE, 0, 0, + NULL, + ctx->DrawIndirectBuffer); + + free(prim); + + if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) + _mesa_flush(ctx); +} + +/** + * Like [Multi]DrawArrays/Elements, but they take most arguments from + * a buffer object. + */ +static void GLAPIENTRY +vbo_exec_DrawArraysIndirect(GLenum mode, const GLvoid *indirect) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawArraysIndirect(%s, %p)\n", + _mesa_lookup_enum_by_nr(mode), indirect); + + if (!_mesa_validate_DrawArraysIndirect(ctx, mode, indirect)) + return; + + vbo_validated_drawarraysindirect(ctx, mode, indirect); +} + +static void GLAPIENTRY +vbo_exec_DrawElementsIndirect(GLenum mode, GLenum type, + const GLvoid *indirect) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawElementsIndirect(%s, %s, %p)\n", + _mesa_lookup_enum_by_nr(mode), + _mesa_lookup_enum_by_nr(type), indirect); + + if (!_mesa_validate_DrawElementsIndirect(ctx, mode, type, indirect)) + return; + + vbo_validated_drawelementsindirect(ctx, mode, type, indirect); +} + +static void GLAPIENTRY +vbo_exec_MultiDrawArraysIndirect(GLenum mode, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glMultiDrawArraysIndirect(%s, %p, %i, %i)\n", + _mesa_lookup_enum_by_nr(mode), indirect, primcount, stride); + + if (stride == 0) + stride = 4 * sizeof(GLuint); + + if (!_mesa_validate_MultiDrawArraysIndirect(ctx, mode, + indirect, + primcount, stride)) + return; + + vbo_validated_multidrawarraysindirect(ctx, mode, + indirect, + primcount, stride); +} + +static void GLAPIENTRY +vbo_exec_MultiDrawElementsIndirect(GLenum mode, GLenum type, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glMultiDrawElementsIndirect(%s, %s, %p, %i, %i)\n", + _mesa_lookup_enum_by_nr(mode), + _mesa_lookup_enum_by_nr(type), indirect, primcount, stride); + + if (stride == 0) + stride = 5 * sizeof(GLuint); + + if (!_mesa_validate_MultiDrawElementsIndirect(ctx, mode, type, + indirect, + primcount, stride)) + return; + + vbo_validated_multidrawelementsindirect(ctx, mode, type, + indirect, + primcount, stride); +} + /** * Plug in the immediate-mode vertex array drawing commands into the * givven vbo_exec_context object. @@ -1386,6 +1629,10 @@ vbo_exec_array_init( struct vbo_exec_context *exec ) vbo_exec_DrawTransformFeedbackInstanced; exec->vtxfmt.DrawTransformFeedbackStreamInstanced = vbo_exec_DrawTransformFeedbackStreamInstanced; + exec->vtxfmt.DrawArraysIndirect = vbo_exec_DrawArraysIndirect; + exec->vtxfmt.DrawElementsIndirect = vbo_exec_DrawElementsIndirect; + exec->vtxfmt.MultiDrawArraysIndirect = vbo_exec_MultiDrawArraysIndirect; + exec->vtxfmt.MultiDrawElementsIndirect = vbo_exec_MultiDrawElementsIndirect; } diff --git a/src/mesa/vbo/vbo_exec_draw.c b/src/mesa/vbo/vbo_exec_draw.c index 9529ce0..1122356 100644 --- a/src/mesa/vbo/vbo_exec_draw.c +++ b/src/mesa/vbo/vbo_exec_draw.c @@ -404,7 +404,7 @@ vbo_exec_vtx_flush(struct vbo_exec_context *exec, GLboolean keepUnmapped) GL_TRUE, 0, exec->vtx.vert_count - 1, - NULL); + NULL, NULL); /* If using a real VBO, get new storage -- unless asked not to. */ diff --git a/src/mesa/vbo/vbo_primitive_restart.c b/src/mesa/vbo/vbo_primitive_restart.c index a6a0149..38b4766 100644 --- a/src/mesa/vbo/vbo_primitive_restart.c +++ b/src/mesa/vbo/vbo_primitive_restart.c @@ -213,11 +213,11 @@ vbo_sw_primitive_restart(struct gl_context *ctx, (temp_prim.count == sub_prim->count)) { draw_prims_func(ctx, &temp_prim, 1, ib, GL_TRUE, sub_prim->min_index, sub_prim->max_index, - NULL); + NULL, NULL); } else { draw_prims_func(ctx, &temp_prim, 1, ib, GL_FALSE, -1, -1, - NULL); + NULL, NULL); } } if (sub_end_index >= end_index) { diff --git a/src/mesa/vbo/vbo_rebase.c b/src/mesa/vbo/vbo_rebase.c index d0bf6c5..828e667 100644 --- a/src/mesa/vbo/vbo_rebase.c +++ b/src/mesa/vbo/vbo_rebase.c @@ -237,7 +237,7 @@ void vbo_rebase_prims( struct gl_context *ctx, GL_TRUE, 0, max_index - min_index, - NULL ); + NULL, NULL ); ctx->Array._DrawArrays = saved_arrays; ctx->NewDriverState |= ctx->DriverFlags.NewArray; diff --git a/src/mesa/vbo/vbo_save_api.c b/src/mesa/vbo/vbo_save_api.c index 7490717..37e3342 100644 --- a/src/mesa/vbo/vbo_save_api.c +++ b/src/mesa/vbo/vbo_save_api.c @@ -1151,6 +1151,55 @@ _save_DrawTransformFeedbackStreamInstanced(GLenum mode, GLuint name, static void GLAPIENTRY +_save_DrawArraysIndirect(GLenum mode, const GLvoid *indirect) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; + (void) indirect; + _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawArraysIndirect"); +} + + +static void GLAPIENTRY +_save_DrawElementsIndirect(GLenum mode, GLenum type, const GLvoid *indirect) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; + (void) type; + (void) indirect; + _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElementsIndirect"); +} + + +static void GLAPIENTRY +_save_MultiDrawArraysIndirect(GLenum mode, const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; + (void) indirect; + (void) primcount; + (void) stride; + _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glMultiDrawArraysIndirect"); +} + + +static void GLAPIENTRY +_save_MultiDrawElementsIndirect(GLenum mode, GLenum type, + const GLvoid *indirect, + GLsizei primcount, GLsizei stride) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; + (void) type; + (void) indirect; + (void) primcount; + (void) stride; + _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glMultiDrawElementsIndirect"); +} + + +static void GLAPIENTRY _save_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) { GET_CURRENT_CONTEXT(ctx); @@ -1506,6 +1555,10 @@ _save_vtxfmt_init(struct gl_context *ctx) vfmt->DrawTransformFeedbackInstanced = _save_DrawTransformFeedbackInstanced; vfmt->DrawTransformFeedbackStreamInstanced = _save_DrawTransformFeedbackStreamInstanced; + vfmt->DrawArraysIndirect = _save_DrawArraysIndirect; + vfmt->DrawElementsIndirect = _save_DrawElementsIndirect; + vfmt->MultiDrawArraysIndirect = _save_MultiDrawArraysIndirect; + vfmt->MultiDrawElementsIndirect = _save_MultiDrawElementsIndirect; } diff --git a/src/mesa/vbo/vbo_save_draw.c b/src/mesa/vbo/vbo_save_draw.c index f5b5c41..84ff3f7 100644 --- a/src/mesa/vbo/vbo_save_draw.c +++ b/src/mesa/vbo/vbo_save_draw.c @@ -319,7 +319,7 @@ vbo_save_playback_vertex_list(struct gl_context *ctx, void *data) GL_TRUE, 0, /* Node is a VBO, so this is ok */ node->count - 1, - NULL); + NULL, NULL); } } diff --git a/src/mesa/vbo/vbo_split_copy.c b/src/mesa/vbo/vbo_split_copy.c index a917f39..51c4a3a 100644 --- a/src/mesa/vbo/vbo_split_copy.c +++ b/src/mesa/vbo/vbo_split_copy.c @@ -201,7 +201,7 @@ flush( struct copy_context *copy ) GL_TRUE, 0, copy->dstbuf_nr - 1, - NULL ); + NULL, NULL ); ctx->Array._DrawArrays = saved_arrays; ctx->NewDriverState |= ctx->DriverFlags.NewArray; diff --git a/src/mesa/vbo/vbo_split_inplace.c b/src/mesa/vbo/vbo_split_inplace.c index 8a38308..a9ae8f1 100644 --- a/src/mesa/vbo/vbo_split_inplace.c +++ b/src/mesa/vbo/vbo_split_inplace.c @@ -94,7 +94,7 @@ static void flush_vertex( struct split_context *split ) !split->ib, split->min_index, split->max_index, - NULL); + NULL, NULL); ctx->Array._DrawArrays = saved_arrays; ctx->NewDriverState |= ctx->DriverFlags.NewArray; -- 1.7.3.4 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev