[Mesa-dev] [PATCH 12/25] radeonsi: add VS prolog

Nicolai Hähnle nhaehnle at gmail.com
Tue Feb 16 16:05:00 UTC 2016


On 15.02.2016 18:59, Marek Olšák wrote:
> From: Marek Olšák <marek.olsak at amd.com>
>
> This is disabled with use_monolithic_shaders = true.
> ---
>   src/gallium/drivers/radeonsi/si_pipe.c   |  19 +++
>   src/gallium/drivers/radeonsi/si_pipe.h   |   3 +
>   src/gallium/drivers/radeonsi/si_shader.c | 236 ++++++++++++++++++++++++++++++-
>   src/gallium/drivers/radeonsi/si_shader.h |   9 ++
>   4 files changed, 266 insertions(+), 1 deletion(-)
>
> diff --git a/src/gallium/drivers/radeonsi/si_pipe.c b/src/gallium/drivers/radeonsi/si_pipe.c
> index 448fe88..7ce9570 100644
> --- a/src/gallium/drivers/radeonsi/si_pipe.c
> +++ b/src/gallium/drivers/radeonsi/si_pipe.c
> @@ -22,6 +22,7 @@
>    */
>
>   #include "si_pipe.h"
> +#include "si_shader.h"
>   #include "si_public.h"
>   #include "sid.h"
>
> @@ -536,6 +537,11 @@ static int si_get_shader_param(struct pipe_screen* pscreen, unsigned shader, enu
>   static void si_destroy_screen(struct pipe_screen* pscreen)
>   {
>   	struct si_screen *sscreen = (struct si_screen *)pscreen;
> +	struct si_shader_part *parts[] = {
> +		sscreen->vs_prologs,
> +		/* this will be filled with other shader parts */
> +	};
> +	unsigned i;
>
>   	if (!sscreen)
>   		return;
> @@ -543,6 +549,18 @@ static void si_destroy_screen(struct pipe_screen* pscreen)
>   	if (!sscreen->b.ws->unref(sscreen->b.ws))
>   		return;
>
> +	/* Free shader parts. */
> +	for (i = 0; i < ARRAY_SIZE(parts); i++) {
> +		while (parts[i]) {
> +			struct si_shader_part *part = parts[i];
> +
> +			parts[i] = part->next;
> +			radeon_shader_binary_clean(&part->binary);
> +			FREE(part);
> +		}
> +	}
> +	pipe_mutex_destroy(sscreen->shader_parts_mutex);
> +
>   	r600_destroy_common_screen(&sscreen->b);
>   }
>
> @@ -600,6 +618,7 @@ struct pipe_screen *radeonsi_screen_create(struct radeon_winsys *ws)
>
>   	sscreen->b.has_cp_dma = true;
>   	sscreen->b.has_streamout = true;
> +	pipe_mutex_init(sscreen->shader_parts_mutex);
>   	sscreen->use_monolithic_shaders = true;
>
>   	if (debug_get_bool_option("RADEON_DUMP_SHADERS", FALSE))
> diff --git a/src/gallium/drivers/radeonsi/si_pipe.h b/src/gallium/drivers/radeonsi/si_pipe.h
> index 2a2455c..f4bafc2 100644
> --- a/src/gallium/drivers/radeonsi/si_pipe.h
> +++ b/src/gallium/drivers/radeonsi/si_pipe.h
> @@ -87,6 +87,9 @@ struct si_screen {
>
>   	/* Whether shaders are monolithic (1-part) or separate (3-part). */
>   	bool				use_monolithic_shaders;
> +
> +	pipe_mutex			shader_parts_mutex;
> +	struct si_shader_part		*vs_prologs;
>   };
>
>   struct si_blend_color {
> diff --git a/src/gallium/drivers/radeonsi/si_shader.c b/src/gallium/drivers/radeonsi/si_shader.c
> index b74ed1e..fbb8394 100644
> --- a/src/gallium/drivers/radeonsi/si_shader.c
> +++ b/src/gallium/drivers/radeonsi/si_shader.c
> @@ -83,6 +83,7 @@ struct si_shader_context
>   	int param_rel_auto_id;
>   	int param_vs_prim_id;
>   	int param_instance_id;
> +	int param_vertex_index0;
>   	int param_tes_u;
>   	int param_tes_v;
>   	int param_tes_rel_patch_id;
> @@ -432,7 +433,11 @@ static void declare_input_vs(
>   	/* Build the attribute offset */
>   	attribute_offset = lp_build_const_int32(gallivm, 0);
>
> -	if (divisor) {
> +	if (!ctx->is_monolithic) {
> +		buffer_index = LLVMGetParam(radeon_bld->main_fn,
> +					    ctx->param_vertex_index0 +
> +					    input_index);
> +	} else if (divisor) {
>   		/* Build index from instance ID, start instance and divisor */
>   		ctx->shader->uses_instanceid = true;
>   		buffer_index = get_instance_index_for_fetch(&ctx->radeon_bld,
> @@ -3711,6 +3716,15 @@ static void create_function(struct si_shader_context *ctx)
>   		params[ctx->param_rel_auto_id = num_params++] = ctx->i32;
>   		params[ctx->param_vs_prim_id = num_params++] = ctx->i32;
>   		params[ctx->param_instance_id = num_params++] = ctx->i32;
> +
> +		if (!ctx->is_monolithic &&
> +		    !ctx->is_gs_copy_shader) {
> +			/* Vertex load indices. */
> +			ctx->param_vertex_index0 = num_params;
> +
> +			for (i = 0; i < shader->selector->info.num_inputs; i++)
> +				params[num_params++] = ctx->i32;
> +		}
>   		break;
>
>   	case TGSI_PROCESSOR_TESS_CTRL:
> @@ -4678,6 +4692,203 @@ out:
>   	return r;
>   }
>
> +/**
> + * Create, compile and return a shader part (prolog or epilog).
> + *
> + * \param sscreen	screen
> + * \param list		list of shader parts of the same category
> + * \param key		shader part key
> + * \param tm		LLVM target machine
> + * \param debug		debug callback
> + * \param compile	the callback responsible for compilation
> + * \return		non-NULL on success
> + */
> +static struct si_shader_part *
> +si_get_shader_part(struct si_screen *sscreen,
> +		   struct si_shader_part **list,
> +		   union si_shader_part_key *key,
> +		   LLVMTargetMachineRef tm,
> +		   struct pipe_debug_callback *debug,
> +		   bool (*compile)(struct si_screen *,
> +				   LLVMTargetMachineRef,
> +				   struct pipe_debug_callback *,
> +				   struct si_shader_part *))
> +{
> +	struct si_shader_part *result;
> +
> +	pipe_mutex_lock(sscreen->shader_parts_mutex);
> +
> +	/* Find existing. */
> +	for (result = *list; result; result = result->next)
> +		if (memcmp(&result->key, key, sizeof(*key)) == 0) {
> +			pipe_mutex_unlock(sscreen->shader_parts_mutex);
> +			return result;
> +		}

Bike-shedding: I generally prefer braces around multi-line blocks even 
when they consist only of a single statement.

> +
> +	/* Compile a new one. */
> +	result = CALLOC_STRUCT(si_shader_part);
> +	result->key = *key;
> +	if (!compile(sscreen, tm, debug, result)) {
> +		FREE(result);
> +		pipe_mutex_unlock(sscreen->shader_parts_mutex);
> +		return NULL;
> +	}
> +
> +	result->next = *list;
> +	*list = result;
> +	pipe_mutex_unlock(sscreen->shader_parts_mutex);
> +	return result;
> +}
> +
> +/**
> + * Create a vertex shader prolog.
> + *
> + * The inputs are the same as VS (a lot of SGPRs and 4 VGPR system values).
> + * All inputs are returned unmodified. The vertex load indices are
> + * stored after them, which will used by the API VS for fetching inputs.
> + *
> + * For example, the expected outputs for instance_divisors[] = {0, 1, 2} are:
> + *   input_v0,
> + *   input_v1,
> + *   input_v2,
> + *   input_v3,
> + *   (VertexID + BaseVertex),
> + *   (InstanceID + StartInstance),
> + *   (InstanceID / 2 + StartInstance)
> + */
> +static bool si_compile_vs_prolog(struct si_screen *sscreen,
> +				 LLVMTargetMachineRef tm,
> +				 struct pipe_debug_callback *debug,
> +				 struct si_shader_part *out)
> +{
> +	union si_shader_part_key *key = &out->key;
> +	struct si_shader shader = {};
> +	struct si_shader_context ctx;
> +	struct gallivm_state *gallivm = &ctx.radeon_bld.gallivm;
> +	LLVMTypeRef *params, *returns;
> +	LLVMValueRef ret, func;
> +	int last_sgpr, num_params, num_returns, i;
> +	bool status = true;
> +
> +	si_init_shader_ctx(&ctx, sscreen, &shader, tm, NULL);
> +	ctx.type = TGSI_PROCESSOR_VERTEX;
> +	ctx.param_vertex_id = key->vs_prolog.num_input_sgprs;
> +	ctx.param_instance_id = key->vs_prolog.num_input_sgprs + 3;
> +
> +	/* 4 preloaded VGPRs + vertex load indices as prolog outputs */
> +	params = alloca((key->vs_prolog.num_input_sgprs + 4) *
> +			sizeof(LLVMTypeRef));
> +	returns = alloca((key->vs_prolog.num_input_sgprs + 4 +
> +			  key->vs_prolog.last_input + 1) *
> +			 sizeof(LLVMTypeRef));
> +	num_params = 0;
> +	num_returns = 0;
> +
> +	/* Declare input and output SGPRs. */
> +	num_params = 0;
> +	for (i = 0; i < key->vs_prolog.num_input_sgprs; i++) {
> +		params[num_params++] = ctx.i32;
> +		returns[num_returns++] = ctx.i32;
> +	}
> +	last_sgpr = num_params - 1;
> +
> +	/* 4 preloaded VGPRs (outputs must be floats) */
> +	for (i = 0; i < 4; i++) {
> +		params[num_params++] = ctx.i32;
> +		returns[num_returns++] = ctx.f32;
> +	}
> +
> +	/* Vertex load indices. */
> +	for (i = 0; i <= key->vs_prolog.last_input; i++)
> +		returns[num_returns++] = ctx.f32;
> +
> +	/* Create the function. */
> +	si_create_function(&ctx, returns, num_returns, params,
> +			   num_params, -1, last_sgpr);
> +	func = ctx.radeon_bld.main_fn;
> +
> +	/* Copy inputs to outputs. This should be no-op, as the registers match,
> +	 * but it will prevent the compiler from overwriting them unintentionally.
> +	 */
> +	ret = ctx.return_value;
> +	for (i = 0; i < key->vs_prolog.num_input_sgprs; i++) {
> +		LLVMValueRef p = LLVMGetParam(func, i);
> +		ret = LLVMBuildInsertValue(gallivm->builder, ret, p, i, "");
> +	}
> +	for (i = num_params - 4; i < num_params; i++) {
> +		LLVMValueRef p = LLVMGetParam(func, i);
> +		p = LLVMBuildBitCast(gallivm->builder, p, ctx.f32, "");
> +		ret = LLVMBuildInsertValue(gallivm->builder, ret, p, i, "");
> +	}
> +
> +	/* Compute vertex load indices from instance divisors. */
> +	for (i = 0; i <= key->vs_prolog.last_input; i++) {
> +		unsigned divisor = key->vs_prolog.states.instance_divisors[i];
> +		LLVMValueRef index;
> +
> +		if (divisor) {
> +			/* InstanceID / Divisor + StartInstance */
> +			index = get_instance_index_for_fetch(&ctx.radeon_bld,
> +							     SI_SGPR_START_INSTANCE,
> +							     divisor);
> +		} else {
> +			/* VertexID + BaseVertex */
> +			index = LLVMBuildAdd(gallivm->builder,
> +					     LLVMGetParam(func, ctx.param_vertex_id),
> +					     LLVMGetParam(func, SI_SGPR_BASE_VERTEX), "");
> +		}
> +
> +		index = LLVMBuildBitCast(gallivm->builder, index, ctx.f32, "");
> +		ret = LLVMBuildInsertValue(gallivm->builder, ret, index,
> +					   num_params++, "");
> +	}
> +
> +	/* Compile. */
> +	LLVMBuildRet(gallivm->builder, ret);
> +	radeon_llvm_finalize_module(&ctx.radeon_bld);
> +
> +	if (si_compile_llvm(sscreen, &out->binary, &out->config, tm,
> +			    gallivm->module, debug, ctx.type,
> +			    "Vertex Shader Prolog"))
> +		status = false;
> +
> +	radeon_llvm_dispose(&ctx.radeon_bld);
> +	return status;
> +}
> +
> +static bool si_shader_select_vs_parts(struct si_screen *sscreen,
> +				      LLVMTargetMachineRef tm,
> +				      struct si_shader *shader,
> +				      struct pipe_debug_callback *debug)
> +{
> +	struct tgsi_shader_info *info = &shader->selector->info;
> +	union si_shader_part_key prolog_key;
> +	unsigned i;
> +
> +	/* Get the prolog. */
> +	memset(&prolog_key, 0, sizeof(prolog_key));
> +	prolog_key.vs_prolog.states = shader->key.vs.prolog;
> +	prolog_key.vs_prolog.num_input_sgprs = shader->num_input_sgprs;
> +	prolog_key.vs_prolog.last_input = MAX2(1, info->num_inputs) - 1;
> +
> +	/* The prolog is a no-op if these are no inputs. */

s/these/there/

Nicolai

> +	if (info->num_inputs) {
> +		shader->prolog =
> +			si_get_shader_part(sscreen, &sscreen->vs_prologs,
> +					   &prolog_key, tm, debug,
> +					   si_compile_vs_prolog);
> +		if (!shader->prolog)
> +			return false;
> +	}
> +
> +	/* Set the instanceID flag. */
> +	for (i = 0; i < info->num_inputs; i++)
> +		if (prolog_key.vs_prolog.states.instance_divisors[i])
> +			shader->uses_instanceid = true;
> +
> +	return true;
> +}
> +
>   int si_shader_create(struct si_screen *sscreen, LLVMTargetMachineRef tm,
>   		     struct si_shader *shader,
>   		     struct pipe_debug_callback *debug)
> @@ -4690,6 +4901,29 @@ int si_shader_create(struct si_screen *sscreen, LLVMTargetMachineRef tm,
>   	if (r)
>   		return r;
>
> +	if (!sscreen->use_monolithic_shaders) {
> +		switch (shader->selector->type) {
> +		case PIPE_SHADER_VERTEX:
> +			if (!si_shader_select_vs_parts(sscreen, tm, shader, debug))
> +				return -1;
> +			break;
> +		}
> +
> +		/* Update SGPR and VGPR counts. */
> +		if (shader->prolog) {
> +			shader->config.num_sgprs = MAX2(shader->config.num_sgprs,
> +							shader->prolog->config.num_sgprs);
> +			shader->config.num_vgprs = MAX2(shader->config.num_vgprs,
> +							shader->prolog->config.num_vgprs);
> +		}
> +		if (shader->epilog) {
> +			shader->config.num_sgprs = MAX2(shader->config.num_sgprs,
> +							shader->epilog->config.num_sgprs);
> +			shader->config.num_vgprs = MAX2(shader->config.num_vgprs,
> +							shader->epilog->config.num_vgprs);
> +		}
> +	}
> +
>   	si_shader_dump(sscreen, shader, debug, shader->selector->info.processor);
>
>   	/* Upload. */
> diff --git a/src/gallium/drivers/radeonsi/si_shader.h b/src/gallium/drivers/radeonsi/si_shader.h
> index 66b3156..e3ba4c7 100644
> --- a/src/gallium/drivers/radeonsi/si_shader.h
> +++ b/src/gallium/drivers/radeonsi/si_shader.h
> @@ -268,6 +268,14 @@ struct si_ps_epilog_bits {
>   	unsigned	clamp_color:1;
>   };
>
> +union si_shader_part_key {
> +	struct {
> +		struct si_vs_prolog_bits states;
> +		unsigned	num_input_sgprs:5;
> +		unsigned	last_input:4;
> +	} vs_prolog;
> +};
> +
>   union si_shader_key {
>   	struct {
>   		struct si_ps_prolog_bits prolog;
> @@ -327,6 +335,7 @@ struct si_shader {
>
>   struct si_shader_part {
>   	struct si_shader_part *next;
> +	union si_shader_part_key key;
>   	struct radeon_shader_binary binary;
>   	struct si_shader_config config;
>   };
>


More information about the mesa-dev mailing list