On 07/09/2014 12:47 AM, Chia-I Wu wrote: > Add _mesa_enable_glsl_threadpool to enable the thread pool for a context, and > add ctx->Const.DeferCompileShader and ctx->Const.DeferLinkProgram to > fine-control what gets threaded. > > Setting DeferCompileShader to true will make _mesa_glsl_compile_shader be > executed in a worker thread. The function is thread-safe so there is no > restriction on DeferCompileShader. > > Setting DeferLinkProgram to true will make _mesa_glsl_link_shader be executed > in a worker thread. The function is thread-safe only when certain driver > functions (as documented in struct gl_constants) are thread-safe. It is > drivers' responsibility to fix those driver functions before setting > DeferLinkProgram. > > When DeferLinkProgram is set, drivers are not supposed to inspect the context > in their LinkShader callbacks. Instead, NotifyLinkShader is added. Drivers > should inspect the context in NotifyLinkShader and save what they need for > LinkShader in gl_shader_program. > > As a final note, most applications will not benefit from threaded shader > compilation because they check GL_COMPILE_STATUS/GL_LINK_STATUS immediately, > giving the worker threads no time to do their jobs. A possible improvement is > to split LinkShader into two parts: the first part links and error checks > while the second part optimizes and generates the machine code. With the > split, we can always defer the second part to the thread pool. > > Signed-off-by: Chia-I Wu <o...@lunarg.com> > --- > src/mesa/main/context.c | 29 +++++++++++ > src/mesa/main/context.h | 3 ++ > src/mesa/main/dd.h | 8 +++ > src/mesa/main/mtypes.h | 34 ++++++++++++ > src/mesa/main/pipelineobj.c | 18 +++++++ > src/mesa/main/shaderapi.c | 122 > +++++++++++++++++++++++++++++++++++++++----- > src/mesa/main/shaderobj.c | 74 +++++++++++++++++++++++++-- > src/mesa/main/shaderobj.h | 55 ++++++++++++++++++-- > 8 files changed, 322 insertions(+), 21 deletions(-) > > diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c > index b082159..e27450c 100644 > --- a/src/mesa/main/context.c > +++ b/src/mesa/main/context.c > @@ -112,6 +112,7 @@ > #include "points.h" > #include "polygon.h" > #include "queryobj.h" > +#include "shaderapi.h" > #include "syncobj.h" > #include "rastpos.h" > #include "remap.h" > @@ -139,6 +140,7 @@ > #endif > > #include "glsl_parser_extras.h" > +#include "threadpool.h" > #include <stdbool.h> > > > @@ -1187,6 +1189,27 @@ _mesa_create_context(gl_api api, > } > } > > +void > +_mesa_enable_glsl_threadpool(struct gl_context *ctx, int max_threads) > +{ > + if (!ctx->ThreadPool) > + ctx->ThreadPool = _mesa_glsl_get_threadpool(max_threads); > +} > + > +static void > +wait_shader_object_cb(GLuint id, void *data, void *userData) > +{ > + struct gl_context *ctx = (struct gl_context *) userData; > + struct gl_shader *sh = (struct gl_shader *) data; > + > + if (_mesa_validate_shader_target(ctx, sh->Type)) { > + _mesa_wait_shaders(ctx, &sh, 1); > + } > + else { > + struct gl_shader_program *shProg = (struct gl_shader_program *) data; > + _mesa_wait_shader_program(ctx, shProg); > + } > +} > > /** > * Free the data associated with the given context. > @@ -1205,6 +1228,12 @@ _mesa_free_context_data( struct gl_context *ctx ) > _mesa_make_current(ctx, NULL, NULL); > } > > + if (ctx->ThreadPool) { > + _mesa_HashWalk(ctx->Shared->ShaderObjects, wait_shader_object_cb, ctx); > + _mesa_threadpool_unref(ctx->ThreadPool); > + ctx->ThreadPool = NULL; > + } > + > /* unreference WinSysDraw/Read buffers */ > _mesa_reference_framebuffer(&ctx->WinSysDrawBuffer, NULL); > _mesa_reference_framebuffer(&ctx->WinSysReadBuffer, NULL); > diff --git a/src/mesa/main/context.h b/src/mesa/main/context.h > index 792ab4c..b23f9fa 100644 > --- a/src/mesa/main/context.h > +++ b/src/mesa/main/context.h > @@ -118,6 +118,9 @@ _mesa_create_context(gl_api api, > const struct dd_function_table *driverFunctions); > > extern void > +_mesa_enable_glsl_threadpool(struct gl_context *ctx, int max_threads); > + > +extern void > _mesa_free_context_data( struct gl_context *ctx ); > > extern void > diff --git a/src/mesa/main/dd.h b/src/mesa/main/dd.h > index 633ea2c..38f8c68 100644 > --- a/src/mesa/main/dd.h > +++ b/src/mesa/main/dd.h > @@ -447,6 +447,14 @@ struct dd_function_table { > */ > /*@{*/ > /** > + * Called when a shader program is to be linked. > + * > + * This is optional and gives drivers an opportunity to inspect the > context > + * and prepare for LinkShader, which may be deferred to another thread. > + */ > + void (*NotifyLinkShader)(struct gl_context *ctx, > + struct gl_shader_program *shader); > + /** > * Called when a shader program is linked. > * > * This gives drivers an opportunity to clone the IR and make their > diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h > index 5964576..316da23 100644 > --- a/src/mesa/main/mtypes.h > +++ b/src/mesa/main/mtypes.h > @@ -71,6 +71,8 @@ typedef GLuint64 GLbitfield64; > */ > /*@{*/ > struct _mesa_HashTable; > +struct _mesa_threadpool; > +struct _mesa_threadpool_task; > struct gl_attrib_node; > struct gl_list_extensions; > struct gl_meta_state; > @@ -2510,6 +2512,14 @@ struct gl_shader > */ > unsigned LocalSize[3]; > } Comp; > + > + /** > + * Deferred task of glCompileShader. We should extend the mutex, not only > + * to protect the deferred task, but to protect the entire gl_shader. > + */ > + mtx_t Mutex; > + struct _mesa_threadpool_task *Task; > + void *TaskData; > }; > > > @@ -2770,6 +2780,15 @@ struct gl_shader_program > * #extension ARB_fragment_coord_conventions: enable > */ > GLboolean ARB_fragment_coord_conventions_enable; > + > + /** > + * Deferred task of glLinkProgram. We should extend the mutex, not only > + * to protect the deferred task, but to protect the entire > + * gl_shader_program. > + */ > + mtx_t Mutex; > + struct _mesa_threadpool_task *Task; > + void *TaskData; > }; > > > @@ -3489,6 +3508,18 @@ struct gl_constants > GLfloat MaxFragmentInterpolationOffset; > > GLboolean FakeSWMSAA; > + > + /* > + * Defer certain operations to a thread pool. > + * > + * When DeferLinkProgram is set, these functions must be thread-safe > + * > + * ctx->Driver.NewShader > + * ctx->Driver.DeleteShader > + * ctx->Driver.LinkShader > + */ > + GLboolean DeferCompileShader; > + GLboolean DeferLinkProgram; > }; > > > @@ -4262,6 +4293,9 @@ struct gl_context > * Once this field becomes true, it is never reset to false. > */ > GLboolean ShareGroupReset; > + > + /* A thread pool for threaded shader compilation */ > + struct _mesa_threadpool *ThreadPool; > }; > > > diff --git a/src/mesa/main/pipelineobj.c b/src/mesa/main/pipelineobj.c > index 90c1d00..ae4f80a 100644 > --- a/src/mesa/main/pipelineobj.c > +++ b/src/mesa/main/pipelineobj.c > @@ -211,6 +211,24 @@ _mesa_reference_pipeline_object_(struct gl_context *ctx, > } > else { > obj->RefCount++; > + > + /* > + * The current pipeline object (as set by glUseProgram or > + * glBindProgramPipeline) cannot have unfinished tasks. > + */ > + if (ptr == &ctx->_Shader) { > + if (obj->ActiveProgram) > + _mesa_wait_shader_program(ctx, obj->ActiveProgram); > + if (obj->Name) { > + int i; > + > + for (i = 0; i < MESA_SHADER_STAGES; i++) { > + if (obj->CurrentProgram[i]) > + _mesa_wait_shader_program(ctx, obj->CurrentProgram[i]); > + } > + } > + } > + > *ptr = obj; > } > mtx_unlock(&obj->Mutex); > diff --git a/src/mesa/main/shaderapi.c b/src/mesa/main/shaderapi.c > index 2bbef35..339d7a2 100644 > --- a/src/mesa/main/shaderapi.c > +++ b/src/mesa/main/shaderapi.c > @@ -53,6 +53,7 @@ > #include "program/prog_print.h" > #include "program/prog_parameter.h" > #include "ralloc.h" > +#include "threadpool.h" > #include <stdbool.h> > #include "../glsl/glsl_parser_extras.h" > #include "../glsl/ir.h" > @@ -210,7 +211,8 @@ _mesa_validate_shader_target(const struct gl_context > *ctx, GLenum type) > static GLboolean > is_program(struct gl_context *ctx, GLuint name) > { > - struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, name); > + struct gl_shader_program *shProg = > + _mesa_lookup_shader_program_no_wait(ctx, name); > return shProg ? GL_TRUE : GL_FALSE; > } > > @@ -218,7 +220,7 @@ is_program(struct gl_context *ctx, GLuint name) > static GLboolean > is_shader(struct gl_context *ctx, GLuint name) > { > - struct gl_shader *shader = _mesa_lookup_shader(ctx, name); > + struct gl_shader *shader = _mesa_lookup_shader_no_wait(ctx, name); > return shader ? GL_TRUE : GL_FALSE; > } > > @@ -239,7 +241,7 @@ attach_shader(struct gl_context *ctx, GLuint program, > GLuint shader) > if (!shProg) > return; > > - sh = _mesa_lookup_shader_err(ctx, shader, "glAttachShader"); > + sh = _mesa_lookup_shader_err_no_wait(ctx, shader, "glAttachShader"); > if (!sh) { > return; > } > @@ -341,7 +343,9 @@ delete_shader_program(struct gl_context *ctx, GLuint name) > */ > struct gl_shader_program *shProg; > > - shProg = _mesa_lookup_shader_program_err(ctx, name, "glDeleteProgram"); > + /* no waiting until _mesa_delete_shader_program() */ > + shProg = _mesa_lookup_shader_program_err_no_wait(ctx, name, > + "glDeleteProgram"); > if (!shProg) > return; > > @@ -359,7 +363,8 @@ delete_shader(struct gl_context *ctx, GLuint shader) > { > struct gl_shader *sh; > > - sh = _mesa_lookup_shader_err(ctx, shader, "glDeleteShader"); > + /* no waiting until _mesa_delete_shader() */ > + sh = _mesa_lookup_shader_err_no_wait(ctx, shader, "glDeleteShader"); > if (!sh) > return; > > @@ -812,6 +817,48 @@ shader_source(struct gl_context *ctx, GLuint shader, > const GLchar *source) > #endif > } > > +static bool > +can_queue_task(struct gl_context *ctx) > +{ > + if (!ctx->ThreadPool) > + return false; > + > + /* MESA_GLSL is set */ > + if (ctx->_Shader->Flags) > + return false; > + > + /* context requires synchronized compiler warnings and errors */ > + if (_mesa_get_debug_state_int(ctx, GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB)) > + return false; > + > + return true; > +} > + > +static void > +deferred_compile_shader(void *data) > +{ > + struct gl_shader *sh = (struct gl_shader *) data; > + struct gl_context *ctx = (struct gl_context *) sh->TaskData; > + > + _mesa_glsl_compile_shader(ctx, sh, false, false); > +} > + > +static bool > +queue_compile_shader(struct gl_context *ctx, struct gl_shader *sh) > +{ > + if (!can_queue_task(ctx)) > + return false; > + if (!ctx->Const.DeferCompileShader) > + return false; > + > + sh->TaskData = (void *) ctx; > + sh->Task = _mesa_threadpool_queue_task(ctx->ThreadPool, > + deferred_compile_shader, (void *) sh); > + if (!sh->Task) > + sh->TaskData = NULL; > + > + return (sh->Task != NULL); > +} > > /** > * Compile a shader. > @@ -844,10 +891,12 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj) > fflush(stderr); > } > > - /* this call will set the shader->CompileStatus field to indicate if > - * compilation was successful. > + /* This call will set the shader->CompileStatus field to indicate if > + * compilation was successful. But if queueing succeeded, the field > + * will be set at a later point. > */ > - _mesa_glsl_compile_shader(ctx, sh, false, false); > + if (!queue_compile_shader(ctx, sh)) > + _mesa_glsl_compile_shader(ctx, sh, false, false); > > if (ctx->_Shader->Flags & GLSL_LOG) { > _mesa_write_shader_to_file(sh); > @@ -870,7 +919,7 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj) > > } > > - if (!sh->CompileStatus) { > + if (ctx->_Shader->Flags && !sh->CompileStatus) {
I don't understand this change. Is this because when Flags is set it won't use the background compile? Aren't there other things that could cause queue_compile_shader to fail that would result in GLSL_DUMP_ON_ERROR being ignored here? > if (ctx->_Shader->Flags & GLSL_DUMP_ON_ERROR) { > fprintf(stderr, "GLSL source for %s shader %d:\n", > _mesa_shader_stage_to_string(sh->Stage), sh->Name); > @@ -887,6 +936,49 @@ compile_shader(struct gl_context *ctx, GLuint shaderObj) > } > > > +static void > +deferred_link_program(void *data) > +{ > + struct gl_shader_program *shProg = (struct gl_shader_program *) data; > + struct gl_context *ctx = (struct gl_context *) shProg->TaskData; > + > + _mesa_wait_shaders(ctx, shProg->Shaders, shProg->NumShaders); > + _mesa_glsl_link_shader(ctx, shProg); > +} > + > +static bool > +queue_link_program(struct gl_context *ctx, struct gl_shader_program *shProg) > +{ > + int i; > + > + if (!can_queue_task(ctx)) > + return false; > + if (!ctx->Const.DeferLinkProgram) > + return false; > + > + /* > + * Rather than adding _mesa_wait_shader_program calls here and there, we > + * simply disallow threaded linking when the program is current or active. > + */ > + if (ctx->_Shader->ActiveProgram == shProg) > + return false; > + /* be careful for separate programs */ > + if (ctx->_Shader->Name != 0) { > + for (i = 0; i < MESA_SHADER_STAGES; i++) { > + if (ctx->_Shader->CurrentProgram[i] == shProg) > + return false; > + } > + } > + > + shProg->TaskData = (void *) ctx; > + shProg->Task = _mesa_threadpool_queue_task(ctx->ThreadPool, > + deferred_link_program, (void *) shProg); > + if (!shProg->Task) > + shProg->TaskData = NULL; > + > + return (shProg->Task != NULL); > +} > + > /** > * Link a program's shaders. > */ > @@ -912,10 +1004,16 @@ link_program(struct gl_context *ctx, GLuint program) > > FLUSH_VERTICES(ctx, _NEW_PROGRAM); > > - _mesa_glsl_link_shader(ctx, shProg); > + if (ctx->Driver.NotifyLinkShader) > + ctx->Driver.NotifyLinkShader(ctx, shProg); > + > + if (!queue_link_program(ctx, shProg)) { > + _mesa_wait_shaders(ctx, shProg->Shaders, shProg->NumShaders); > + _mesa_glsl_link_shader(ctx, shProg); > + } > > - if (shProg->LinkStatus == GL_FALSE && > - (ctx->_Shader->Flags & GLSL_REPORT_ERRORS)) { > + if ((ctx->_Shader->Flags & GLSL_REPORT_ERRORS) && > + shProg->LinkStatus == GL_FALSE) { > _mesa_debug(ctx, "Error linking program %u:\n%s\n", > shProg->Name, shProg->InfoLog); > } > diff --git a/src/mesa/main/shaderobj.c b/src/mesa/main/shaderobj.c > index b9feff4..ab94d14 100644 > --- a/src/mesa/main/shaderobj.c > +++ b/src/mesa/main/shaderobj.c > @@ -41,6 +41,7 @@ > #include "program/prog_parameter.h" > #include "program/hash_table.h" > #include "ralloc.h" > +#include "threadpool.h" > > /**********************************************************************/ > /*** Shader object functions ***/ > @@ -95,6 +96,7 @@ _mesa_reference_shader(struct gl_context *ctx, struct > gl_shader **ptr, > void > _mesa_init_shader(struct gl_context *ctx, struct gl_shader *shader) > { > + mtx_init(&shader->Mutex, mtx_plain); > shader->RefCount = 1; > } > > @@ -125,18 +127,51 @@ _mesa_new_shader(struct gl_context *ctx, GLuint name, > GLenum type) > static void > _mesa_delete_shader(struct gl_context *ctx, struct gl_shader *sh) > { > + _mesa_wait_shaders(ctx, &sh, 1); > + > free((void *)sh->Source); > free(sh->Label); > _mesa_reference_program(ctx, &sh->Program, NULL); > + mtx_destroy(&sh->Mutex); > ralloc_free(sh); > } > > > /** > + * Wait for the threaded compile tasks to complete. > + */ > +void > +_mesa_wait_shaders(struct gl_context *ctx, > + struct gl_shader **shaders, > + int num_shaders) > +{ > + int i; > + > + for (i = 0; i < num_shaders; i++) { > + struct gl_shader *sh = shaders[i]; > + > + mtx_lock(&sh->Mutex); > + > + if (sh->Task) { > + struct gl_context *task_ctx = (struct gl_context *) sh->TaskData; > + > + if (!_mesa_threadpool_complete_task(task_ctx->ThreadPool, sh->Task)) > + sh->CompileStatus = GL_FALSE; > + > + sh->Task = NULL; > + sh->TaskData = NULL; > + } > + > + mtx_unlock(&sh->Mutex); > + } > +} > + > + > +/** > * Lookup a GLSL shader object. > */ > struct gl_shader * > -_mesa_lookup_shader(struct gl_context *ctx, GLuint name) > +_mesa_lookup_shader_no_wait(struct gl_context *ctx, GLuint name) > { > if (name) { > struct gl_shader *sh = (struct gl_shader *) > @@ -158,7 +193,8 @@ _mesa_lookup_shader(struct gl_context *ctx, GLuint name) > * As above, but record an error if shader is not found. > */ > struct gl_shader * > -_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char > *caller) > +_mesa_lookup_shader_err_no_wait(struct gl_context *ctx, GLuint name, > + const char *caller) > { > if (!name) { > _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller); > @@ -238,6 +274,8 @@ _mesa_reference_shader_program(struct gl_context *ctx, > void > _mesa_init_shader_program(struct gl_context *ctx, struct gl_shader_program > *prog) > { > + mtx_init(&prog->Mutex, mtx_plain); > + > prog->Type = GL_SHADER_PROGRAM_MESA; > prog->RefCount = 1; > > @@ -373,17 +411,43 @@ _mesa_free_shader_program_data(struct gl_context *ctx, > static void > _mesa_delete_shader_program(struct gl_context *ctx, struct gl_shader_program > *shProg) > { > + _mesa_wait_shader_program(ctx, shProg); > + > _mesa_free_shader_program_data(ctx, shProg); > + mtx_destroy(&shProg->Mutex); > > ralloc_free(shProg); > } > > > /** > + * Wait for the threaded linking task to complete. > + */ > +void > +_mesa_wait_shader_program(struct gl_context *ctx, > + struct gl_shader_program *shProg) > +{ > + mtx_lock(&shProg->Mutex); > + > + if (shProg->Task) { > + struct gl_context *task_ctx = (struct gl_context *) shProg->TaskData; > + > + if (!_mesa_threadpool_complete_task(task_ctx->ThreadPool, > + shProg->Task)) > + shProg->LinkStatus = GL_FALSE; > + shProg->Task = NULL; > + shProg->TaskData = NULL; > + } > + > + mtx_unlock(&shProg->Mutex); > +} > + > + > +/** > * Lookup a GLSL program object. > */ > struct gl_shader_program * > -_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name) > +_mesa_lookup_shader_program_no_wait(struct gl_context *ctx, GLuint name) > { > struct gl_shader_program *shProg; > if (name) { > @@ -406,8 +470,8 @@ _mesa_lookup_shader_program(struct gl_context *ctx, > GLuint name) > * As above, but record an error if program is not found. > */ > struct gl_shader_program * > -_mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name, > - const char *caller) > +_mesa_lookup_shader_program_err_no_wait(struct gl_context *ctx, GLuint name, > + const char *caller) > { > if (!name) { > _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller); > diff --git a/src/mesa/main/shaderobj.h b/src/mesa/main/shaderobj.h > index fae8be8..c2f8a0d 100644 > --- a/src/mesa/main/shaderobj.h > +++ b/src/mesa/main/shaderobj.h > @@ -53,13 +53,35 @@ extern void > _mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr, > struct gl_shader *sh); > > +extern void > +_mesa_wait_shaders(struct gl_context *ctx, > + struct gl_shader **shaders, > + int num_shaders); > + > extern struct gl_shader * > -_mesa_lookup_shader(struct gl_context *ctx, GLuint name); > +_mesa_lookup_shader_no_wait(struct gl_context *ctx, GLuint name); > > extern struct gl_shader * > -_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char > *caller); > +_mesa_lookup_shader_err_no_wait(struct gl_context *ctx, GLuint name, > + const char *caller); > > +static inline struct gl_shader * > +_mesa_lookup_shader(struct gl_context *ctx, GLuint name) > +{ > + struct gl_shader *sh = _mesa_lookup_shader_no_wait(ctx, name); > + if (sh) > + _mesa_wait_shaders(ctx, &sh, 1); > + return sh; > +} > > +static inline struct gl_shader * > +_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char > *caller) > +{ > + struct gl_shader *sh = _mesa_lookup_shader_err_no_wait(ctx, name, caller); > + if (sh) > + _mesa_wait_shaders(ctx, &sh, 1); > + return sh; > +} > > extern void > _mesa_reference_shader_program(struct gl_context *ctx, > @@ -74,12 +96,37 @@ _mesa_new_shader(struct gl_context *ctx, GLuint name, > GLenum type); > extern void > _mesa_init_shader_program(struct gl_context *ctx, struct gl_shader_program > *prog); > > +extern void > +_mesa_wait_shader_program(struct gl_context *ctx, > + struct gl_shader_program *shProg); > + > extern struct gl_shader_program * > -_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name); > +_mesa_lookup_shader_program_no_wait(struct gl_context *ctx, GLuint name); > > extern struct gl_shader_program * > +_mesa_lookup_shader_program_err_no_wait(struct gl_context *ctx, GLuint name, > + const char *caller); > + > +static inline struct gl_shader_program * > +_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name) > +{ > + struct gl_shader_program *shProg = > + _mesa_lookup_shader_program_no_wait(ctx, name); > + if (shProg) > + _mesa_wait_shader_program(ctx, shProg); > + return shProg; > +} > + > +static inline struct gl_shader_program * > _mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name, > - const char *caller); > + const char *caller) > +{ > + struct gl_shader_program *shProg = > + _mesa_lookup_shader_program_err_no_wait(ctx, name, caller); > + if (shProg) > + _mesa_wait_shader_program(ctx, shProg); > + return shProg; > +} > > extern void > _mesa_clear_shader_program_data(struct gl_context *ctx, > _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev