[Mesa-dev] [PATCH 4/4] radeonsi: do compilation from si_create_shader_selector asynchronously
Nicolai Hähnle
nhaehnle at gmail.com
Sat Jul 2 08:56:31 UTC 2016
Apart from the comment on patch 3 (which may just be me being confused
about the e-mailed diffs), this series is
Reviewed-by: Nicolai Hähnle <nicolai.haehnle at amd.com>
On 29.06.2016 18:32, Marek Olšák wrote:
> From: Marek Olšák <marek.olsak at amd.com>
>
> Main shader parts and geometry shaders are compiled asynchronously
> by util_queue. si_create_shader_selector doesn't wait and returns.
> si_draw_vbo(si_shader_select) waits for completion.
>
> This has the best effect when shaders are compiled at app-loading time.
> It doesn't help much for shaders compiled on demand, even though
> VS+PS compilation should take as much as time as the bigger one of the two.
>
> If an app creates more shaders, at most 4 threads will be used to compile
> them.
>
> Debug output disables this for shader stats to be printed in the correct
> order.
>
> (We could go even further and build variants asynchronously too, then emit
> draw calls without waiting and emit incomplete shader states, then force IB
> chaining to give the compiler more time, then sync the compilation at the IB
> flush and patch the IB with correct shader states. This is great for
> compilation before draw calls, but there are some difficulties such as
> scratch and tess states requiring the compiler output, and an on-disk shader
> cache will likely be a much better and simpler solution.)
> ---
> src/gallium/drivers/radeonsi/si_pipe.c | 18 ++++++++++++
> src/gallium/drivers/radeonsi/si_pipe.h | 7 ++++-
> src/gallium/drivers/radeonsi/si_shader.h | 1 +
> src/gallium/drivers/radeonsi/si_state_shaders.c | 39 +++++++++++++++++++++----
> 4 files changed, 58 insertions(+), 7 deletions(-)
>
> diff --git a/src/gallium/drivers/radeonsi/si_pipe.c b/src/gallium/drivers/radeonsi/si_pipe.c
> index 2e8d846..0abf01b 100644
> --- a/src/gallium/drivers/radeonsi/si_pipe.c
> +++ b/src/gallium/drivers/radeonsi/si_pipe.c
> @@ -639,6 +639,13 @@ static void si_destroy_screen(struct pipe_screen* pscreen)
> if (!sscreen->b.ws->unref(sscreen->b.ws))
> return;
>
> + if (util_queue_is_initialized(&sscreen->shader_compiler_queue))
> + util_queue_destroy(&sscreen->shader_compiler_queue);
> +
> + for (i = 0; i < ARRAY_SIZE(sscreen->tm); i++)
> + if (sscreen->tm[i])
> + LLVMDisposeTargetMachine(sscreen->tm[i]);
> +
> /* Free shader parts. */
> for (i = 0; i < ARRAY_SIZE(parts); i++) {
> while (parts[i]) {
> @@ -686,6 +693,7 @@ static bool si_init_gs_info(struct si_screen *sscreen)
> struct pipe_screen *radeonsi_screen_create(struct radeon_winsys *ws)
> {
> struct si_screen *sscreen = CALLOC_STRUCT(si_screen);
> + unsigned num_cpus, num_compiler_threads, i;
>
> if (!sscreen) {
> return NULL;
> @@ -730,6 +738,16 @@ struct pipe_screen *radeonsi_screen_create(struct radeon_winsys *ws)
> if (debug_get_bool_option("RADEON_DUMP_SHADERS", false))
> sscreen->b.debug_flags |= DBG_FS | DBG_VS | DBG_GS | DBG_PS | DBG_CS;
>
> + /* Only enable as many threads as we have target machines and CPUs. */
> + num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
> + num_compiler_threads = MIN2(num_cpus, ARRAY_SIZE(sscreen->tm));
> +
> + for (i = 0; i < num_compiler_threads; i++)
> + sscreen->tm[i] = si_create_llvm_target_machine(sscreen);
> +
> + util_queue_init(&sscreen->shader_compiler_queue, "si_shader",
> + 32, num_compiler_threads);
> +
> /* Create the auxiliary context. This must be done last. */
> sscreen->b.aux_context = sscreen->b.b.context_create(&sscreen->b.b, NULL, 0);
>
> diff --git a/src/gallium/drivers/radeonsi/si_pipe.h b/src/gallium/drivers/radeonsi/si_pipe.h
> index 3aff0ac..542d6a8 100644
> --- a/src/gallium/drivers/radeonsi/si_pipe.h
> +++ b/src/gallium/drivers/radeonsi/si_pipe.h
> @@ -27,6 +27,7 @@
> #define SI_PIPE_H
>
> #include "si_state.h"
> +#include "util/u_queue.h"
>
> #include <llvm-c/TargetMachine.h>
>
> @@ -109,6 +110,10 @@ struct si_screen {
> */
> pipe_mutex shader_cache_mutex;
> struct hash_table *shader_cache;
> +
> + /* Shader compiler queue for multithreaded compilation. */
> + struct util_queue shader_compiler_queue;
> + LLVMTargetMachineRef tm[4]; /* used by the queue only */
> };
>
> struct si_blend_color {
> @@ -206,7 +211,7 @@ struct si_context {
>
> struct pipe_fence_handle *last_gfx_fence;
> struct si_shader_ctx_state fixed_func_tcs_shader;
> - LLVMTargetMachineRef tm;
> + LLVMTargetMachineRef tm; /* only non-threaded compilation */
> bool gfx_flush_in_progress;
>
> /* Atoms (direct states). */
> diff --git a/src/gallium/drivers/radeonsi/si_shader.h b/src/gallium/drivers/radeonsi/si_shader.h
> index 41c6091..0570907 100644
> --- a/src/gallium/drivers/radeonsi/si_shader.h
> +++ b/src/gallium/drivers/radeonsi/si_shader.h
> @@ -234,6 +234,7 @@ struct si_shader;
> */
> struct si_shader_selector {
> struct si_screen *screen;
> + struct util_queue_fence ready;
>
> /* Should only be used by si_init_shader_selector_async
> * if thread_index == -1 (non-threaded). */
> diff --git a/src/gallium/drivers/radeonsi/si_state_shaders.c b/src/gallium/drivers/radeonsi/si_state_shaders.c
> index e433055..2de5707 100644
> --- a/src/gallium/drivers/radeonsi/si_state_shaders.c
> +++ b/src/gallium/drivers/radeonsi/si_state_shaders.c
> @@ -965,7 +965,8 @@ static int si_shader_select_with_key(struct si_screen *sscreen,
> struct si_shader_ctx_state *state,
> union si_shader_key *key,
> LLVMTargetMachineRef tm,
> - struct pipe_debug_callback *debug)
> + struct pipe_debug_callback *debug,
> + bool wait)
> {
> struct si_shader_selector *sel = state->cso;
> struct si_shader *current = state->current;
> @@ -979,6 +980,13 @@ static int si_shader_select_with_key(struct si_screen *sscreen,
> if (likely(current && memcmp(¤t->key, key, sizeof(*key)) == 0))
> return 0;
>
> + /* This must be done before the mutex is locked, because async GS
> + * compilation calls this function too, and therefore must enter
> + * the mutex first.
> + */
> + if (wait)
> + util_queue_job_wait(&sel->ready);
> +
> pipe_mutex_lock(sel->mutex);
>
> /* Find the shader variant. */
> @@ -1031,7 +1039,7 @@ static int si_shader_select(struct pipe_context *ctx,
>
> si_shader_selector_key(ctx, state->cso, &key);
> return si_shader_select_with_key(sctx->screen, state, &key,
> - sctx->tm, &sctx->b.debug);
> + sctx->tm, &sctx->b.debug, true);
> }
>
> static void si_parse_next_shader_property(const struct tgsi_shader_info *info,
> @@ -1068,10 +1076,19 @@ void si_init_shader_selector_async(void *job, int thread_index)
> {
> struct si_shader_selector *sel = (struct si_shader_selector *)job;
> struct si_screen *sscreen = sel->screen;
> - LLVMTargetMachineRef tm = sel->tm;
> - struct pipe_debug_callback *debug = &sel->debug;
> + LLVMTargetMachineRef tm;
> + struct pipe_debug_callback *debug;
> unsigned i;
>
> + if (thread_index >= 0) {
> + assert(thread_index < ARRAY_SIZE(sscreen->tm));
> + tm = sscreen->tm[thread_index];
> + debug = NULL;
> + } else {
> + tm = sel->tm;
> + debug = &sel->debug;
> + }
> +
> /* Compile the main shader part for use with a prolog and/or epilog.
> * If this fails, the driver will try to compile a monolithic shader
> * on demand.
> @@ -1147,7 +1164,8 @@ void si_init_shader_selector_async(void *job, int thread_index)
> break;
> }
>
> - if (si_shader_select_with_key(sscreen, &state, &key, tm, debug))
> + if (si_shader_select_with_key(sscreen, &state, &key, tm, debug,
> + false))
> fprintf(stderr, "radeonsi: can't create a monolithic shader\n");
> }
> }
> @@ -1279,8 +1297,14 @@ static void *si_create_shader_selector(struct pipe_context *ctx,
> sel->db_shader_control |= S_02880C_EXEC_ON_HIER_FAIL(1) |
> S_02880C_EXEC_ON_NOOP(1);
> pipe_mutex_init(sel->mutex);
> + util_queue_fence_init(&sel->ready);
>
> - si_init_shader_selector_async(sel, -1);
> + if (sctx->b.debug.debug_message ||
> + !util_queue_is_initialized(&sscreen->shader_compiler_queue))
> + si_init_shader_selector_async(sel, -1);
> + else
> + util_queue_add_job(&sscreen->shader_compiler_queue, sel,
> + &sel->ready, si_init_shader_selector_async);
>
> return sel;
> }
> @@ -1417,6 +1441,8 @@ static void si_delete_shader_selector(struct pipe_context *ctx, void *state)
> [PIPE_SHADER_FRAGMENT] = &sctx->ps_shader,
> };
>
> + util_queue_job_wait(&sel->ready);
> +
> if (current_shader[sel->type]->cso == sel) {
> current_shader[sel->type]->cso = NULL;
> current_shader[sel->type]->current = NULL;
> @@ -1431,6 +1457,7 @@ static void si_delete_shader_selector(struct pipe_context *ctx, void *state)
> if (sel->main_shader_part)
> si_delete_shader(sctx, sel->main_shader_part);
>
> + util_queue_fence_destroy(&sel->ready);
> pipe_mutex_destroy(sel->mutex);
> free(sel->tokens);
> free(sel);
>
More information about the mesa-dev
mailing list