[PATCH 3/3] etnaviv: Implement ICACHE

Christian Gmeiner christian.gmeiner at gmail.com
Sat Aug 5 16:13:30 UTC 2017


2017-07-24 10:28 GMT+02:00 Wladimir J. van der Laan <laanwj at gmail.com>:
> This patch adds support for large shaders on GC3000. For example the "terrain"
> glmark benchmark with a large fragment shader will work after this.
>
> If the GPU supports ICACHE, shaders larger than the available state area will
> be uploaded to a bo of their own and instructed to be loaded from memory on
> demand. Small shaders will be uploaded in the usual way. This mimics the
> behavior of the blob.
>
> On GPUs that don't support ICACHE, this patch should make no difference.
>
> Signed-off-by: Wladimir J. van der Laan <laanwj at gmail.com>

Reviewed-by: Christian Gmeiner <christian.gmeiner at gmail.com>

> ---
>  src/gallium/drivers/etnaviv/etnaviv_compiler.c |  3 +-
>  src/gallium/drivers/etnaviv/etnaviv_compiler.h |  5 +++
>  src/gallium/drivers/etnaviv/etnaviv_emit.c     | 52 ++++++++++++++++++--------
>  src/gallium/drivers/etnaviv/etnaviv_internal.h |  4 ++
>  src/gallium/drivers/etnaviv/etnaviv_screen.c   |  4 +-
>  src/gallium/drivers/etnaviv/etnaviv_shader.c   | 45 +++++++++++++++++++++-
>  6 files changed, 95 insertions(+), 18 deletions(-)
>
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_compiler.c b/src/gallium/drivers/etnaviv/etnaviv_compiler.c
> index fbe66d0..0664d52 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_compiler.c
> +++ b/src/gallium/drivers/etnaviv/etnaviv_compiler.c
> @@ -2277,7 +2277,7 @@ etna_compile_check_limits(struct etna_compile *c)
>     /* round up number of uniforms, including immediates, in units of four */
>     int num_uniforms = c->imm_base / 4 + (c->imm_size + 3) / 4;
>
> -   if (c->inst_ptr > c->specs->max_instructions) {
> +   if (!c->specs->has_icache && c->inst_ptr > c->specs->max_instructions) {
>        DBG("Number of instructions (%d) exceeds maximum %d", c->inst_ptr,
>            c->specs->max_instructions);
>        return false;
> @@ -2501,6 +2501,7 @@ etna_compile_shader(struct etna_shader_variant *v)
>     v->vs_pointsize_out_reg = -1;
>     v->ps_color_out_reg = -1;
>     v->ps_depth_out_reg = -1;
> +   v->needs_icache = c->inst_ptr > c->specs->max_instructions;
>     copy_uniform_state_to_shader(c, v);
>
>     if (c->info.processor == PIPE_SHADER_VERTEX) {
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_compiler.h b/src/gallium/drivers/etnaviv/etnaviv_compiler.h
> index 88a093f..f5c1689 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_compiler.h
> +++ b/src/gallium/drivers/etnaviv/etnaviv_compiler.h
> @@ -94,12 +94,17 @@ struct etna_shader_variant {
>     /* unknown input property (XX_INPUT_COUNT, field UNK8) */
>     uint32_t input_count_unk8;
>
> +   /* shader is larger than GPU instruction limit, thus needs icache */
> +   bool needs_icache;
> +
>     /* shader variants form a linked list */
>     struct etna_shader_variant *next;
>
>     /* replicated here to avoid passing extra ptrs everywhere */
>     struct etna_shader *shader;
>     struct etna_shader_key key;
> +
> +   struct etna_bo *bo; /* cached code memory bo handle (for icache) */
>  };
>
>  struct etna_varying {
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_emit.c b/src/gallium/drivers/etnaviv/etnaviv_emit.c
> index 273b3d0..c2117d5 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_emit.c
> +++ b/src/gallium/drivers/etnaviv/etnaviv_emit.c
> @@ -421,9 +421,6 @@ etna_emit_state(struct etna_context *ctx)
>     if (unlikely(dirty & (ETNA_DIRTY_SHADER))) {
>        /*00830*/ EMIT_STATE(VS_LOAD_BALANCING, ctx->shader_state.VS_LOAD_BALANCING);
>        /*00838*/ EMIT_STATE(VS_START_PC, ctx->shader_state.VS_START_PC);
> -      if (ctx->specs.has_shader_range_registers) {
> -         /*0085C*/ EMIT_STATE(VS_RANGE, (ctx->shader_state.vs_inst_mem_size / 4 - 1) << 16);
> -      }
>     }
>     if (unlikely(dirty & (ETNA_DIRTY_VIEWPORT))) {
>        /*00A00*/ EMIT_STATE_FIXP(PA_VIEWPORT_SCALE_X, ctx->viewport.PA_VIEWPORT_SCALE_X);
> @@ -534,10 +531,6 @@ etna_emit_state(struct etna_context *ctx)
>                                : ctx->shader_state.PS_TEMP_REGISTER_CONTROL);
>        /*01010*/ EMIT_STATE(PS_CONTROL, ctx->shader_state.PS_CONTROL);
>        /*01018*/ EMIT_STATE(PS_START_PC, ctx->shader_state.PS_START_PC);
> -      if (ctx->specs.has_shader_range_registers) {
> -         /*0101C*/ EMIT_STATE(PS_RANGE, ((ctx->shader_state.ps_inst_mem_size / 4 - 1 + 0x100) << 16) |
> -                                        0x100);
> -      }
>     }
>     if (unlikely(dirty & (ETNA_DIRTY_ZSA | ETNA_DIRTY_FRAMEBUFFER))) {
>        uint32_t val = etna_zsa_state(ctx->zsa)->PE_DEPTH_CONFIG;
> @@ -739,14 +732,43 @@ etna_emit_state(struct etna_context *ctx)
>     if (dirty & (ETNA_DIRTY_SHADER)) {
>        /* Special case: a new shader was loaded; simply re-load all uniforms and
>         * shader code at once */
> -      /*04000 or 0C000*/
> -      etna_set_state_multi(stream, ctx->specs.vs_offset,
> -                           ctx->shader_state.vs_inst_mem_size,
> -                           ctx->shader_state.VS_INST_MEM);
> -      /*06000 or 0D000*/
> -      etna_set_state_multi(stream, ctx->specs.ps_offset,
> -                           ctx->shader_state.ps_inst_mem_size,
> -                           ctx->shader_state.PS_INST_MEM);
> +      if (ctx->shader_state.VS_INST_ADDR.bo || ctx->shader_state.PS_INST_ADDR.bo) {
> +         assert(ctx->specs.has_icache && ctx->specs.has_shader_range_registers);
> +         /* Set icache (VS) */
> +         etna_set_state(stream, VIVS_VS_RANGE, (ctx->shader_state.vs_inst_mem_size / 4 - 1) << 16);
> +         etna_set_state(stream, VIVS_VS_ICACHE_CONTROL,
> +               VIVS_VS_ICACHE_CONTROL_ENABLE |
> +               VIVS_VS_ICACHE_CONTROL_FLUSH_VS);
> +         assert(ctx->shader_state.VS_INST_ADDR.bo);
> +         etna_set_state_reloc(stream, VIVS_VS_INST_ADDR, &ctx->shader_state.VS_INST_ADDR);
> +
> +         /* Set icache (PS) */
> +         etna_set_state(stream, VIVS_PS_RANGE, (ctx->shader_state.ps_inst_mem_size / 4 - 1) << 16);
> +         etna_set_state(stream, VIVS_VS_ICACHE_CONTROL,
> +               VIVS_VS_ICACHE_CONTROL_ENABLE |
> +               VIVS_VS_ICACHE_CONTROL_FLUSH_PS);
> +         assert(ctx->shader_state.PS_INST_ADDR.bo);
> +         etna_set_state_reloc(stream, VIVS_PS_INST_ADDR, &ctx->shader_state.PS_INST_ADDR);
> +      } else {
> +         /* Upload shader directly, first flushing and disabling icache if
> +          * supported on this hw */
> +         if (ctx->specs.has_icache) {
> +            etna_set_state(stream, VIVS_VS_ICACHE_CONTROL,
> +                  VIVS_VS_ICACHE_CONTROL_FLUSH_PS |
> +                  VIVS_VS_ICACHE_CONTROL_FLUSH_VS);
> +         }
> +         if (ctx->specs.has_shader_range_registers) {
> +            etna_set_state(stream, VIVS_VS_RANGE, (ctx->shader_state.vs_inst_mem_size / 4 - 1) << 16);
> +            etna_set_state(stream, VIVS_PS_RANGE, ((ctx->shader_state.ps_inst_mem_size / 4 - 1 + 0x100) << 16) |
> +                                        0x100);
> +         }
> +         etna_set_state_multi(stream, ctx->specs.vs_offset,
> +                              ctx->shader_state.vs_inst_mem_size,
> +                              ctx->shader_state.VS_INST_MEM);
> +         etna_set_state_multi(stream, ctx->specs.ps_offset,
> +                              ctx->shader_state.ps_inst_mem_size,
> +                              ctx->shader_state.PS_INST_MEM);
> +      }
>
>        if (ctx->specs.has_unified_uniforms) {
>           etna_set_state(stream, VIVS_VS_UNIFORM_BASE, 0);
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_internal.h b/src/gallium/drivers/etnaviv/etnaviv_internal.h
> index 5c13f23..a6544f6 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_internal.h
> +++ b/src/gallium/drivers/etnaviv/etnaviv_internal.h
> @@ -76,6 +76,8 @@ struct etna_specs {
>     unsigned single_buffer : 1;
>     /* has unified uniforms memory */
>     unsigned has_unified_uniforms : 1;
> +   /* can load shader instructions from memory */
> +   unsigned has_icache : 1;
>     /* can use any kind of wrapping mode on npot textures */
>     unsigned npot_tex_any_wrap;
>     /* number of bits per TS tile */
> @@ -250,6 +252,8 @@ struct compiled_shader_state {
>     uint32_t VS_UNIFORMS[ETNA_MAX_UNIFORMS * 4];
>     uint32_t *PS_INST_MEM;
>     uint32_t PS_UNIFORMS[ETNA_MAX_UNIFORMS * 4];
> +   struct etna_reloc PS_INST_ADDR;
> +   struct etna_reloc VS_INST_ADDR;
>  };
>
>  /* state of some 3d and common registers relevant to etna driver */
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_screen.c b/src/gallium/drivers/etnaviv/etnaviv_screen.c
> index 81480e9..4fcbe87 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_screen.c
> +++ b/src/gallium/drivers/etnaviv/etnaviv_screen.c
> @@ -665,7 +665,8 @@ etna_get_specs(struct etna_screen *screen)
>         * same.
>         */
>        screen->specs.ps_offset = 0x8000 + 0x1000;
> -      screen->specs.max_instructions = 256;
> +      screen->specs.max_instructions = 256; /* maximum number instructions for non-icache use */
> +      screen->specs.has_icache = true;
>     } else {
>        if (instruction_count > 256) { /* unified instruction memory? */
>           screen->specs.vs_offset = 0xC000;
> @@ -676,6 +677,7 @@ etna_get_specs(struct etna_screen *screen)
>           screen->specs.ps_offset = 0x6000;
>           screen->specs.max_instructions = instruction_count / 2;
>        }
> +      screen->specs.has_icache = false;
>     }
>
>     if (VIV_FEATURE(screen, chipMinorFeatures1, HALTI0)) {
> diff --git a/src/gallium/drivers/etnaviv/etnaviv_shader.c b/src/gallium/drivers/etnaviv/etnaviv_shader.c
> index b5256e4..6012680 100644
> --- a/src/gallium/drivers/etnaviv/etnaviv_shader.c
> +++ b/src/gallium/drivers/etnaviv/etnaviv_shader.c
> @@ -29,12 +29,30 @@
>  #include "etnaviv_compiler.h"
>  #include "etnaviv_context.h"
>  #include "etnaviv_debug.h"
> +#include "etnaviv_screen.h"
>  #include "etnaviv_util.h"
>
>  #include "tgsi/tgsi_parse.h"
>  #include "util/u_math.h"
>  #include "util/u_memory.h"
>
> +/* Upload shader code to bo, if not already done */
> +static bool etna_icache_upload_shader(struct etna_context *ctx, struct etna_shader_variant *v)
> +{
> +   if (v->bo)
> +      return true;
> +   v->bo = etna_bo_new(ctx->screen->dev, v->code_size*4, DRM_ETNA_GEM_CACHE_UNCACHED);
> +   if (!v->bo)
> +      return false;
> +
> +   void *buf = etna_bo_map(v->bo);
> +   etna_bo_cpu_prep(v->bo, DRM_ETNA_PREP_WRITE);
> +   memcpy(buf, v->code, v->code_size*4);
> +   etna_bo_cpu_fini(v->bo);
> +   DBG("Uploaded %s of %u words to bo %p", v->processor == PIPE_SHADER_FRAGMENT ? "fs":"vs", v->code_size, v->bo);
> +   return true;
> +}
> +
>  /* Link vs and fs together: fill in shader_state from vs and fs
>   * as this function is called every time a new fs or vs is bound, the goal is to
>   * do little processing as possible here, and to precompute as much as possible in
> @@ -45,7 +63,7 @@
>   */
>  static bool
>  etna_link_shaders(struct etna_context *ctx, struct compiled_shader_state *cs,
> -                  const struct etna_shader_variant *vs, const struct etna_shader_variant *fs)
> +                  struct etna_shader_variant *vs, struct etna_shader_variant *fs)
>  {
>     struct etna_shader_link_info link = { };
>
> @@ -164,9 +182,32 @@ etna_link_shaders(struct etna_context *ctx, struct compiled_shader_state *cs,
>     /* reference instruction memory */
>     cs->vs_inst_mem_size = vs->code_size;
>     cs->VS_INST_MEM = vs->code;
> +
>     cs->ps_inst_mem_size = fs->code_size;
>     cs->PS_INST_MEM = fs->code;
>
> +   if (vs->needs_icache | fs->needs_icache) {
> +      /* If either of the shaders needs ICACHE, we use it for both. It is
> +       * either switched on or off for the entire shader processor.
> +       */
> +      if (!etna_icache_upload_shader(ctx, vs) ||
> +          !etna_icache_upload_shader(ctx, fs)) {
> +         assert(0);
> +         return false;
> +      }
> +
> +      cs->VS_INST_ADDR.bo = vs->bo;
> +      cs->VS_INST_ADDR.offset = 0;
> +      cs->VS_INST_ADDR.flags = ETNA_RELOC_READ;
> +      cs->PS_INST_ADDR.bo = fs->bo;
> +      cs->PS_INST_ADDR.offset = 0;
> +      cs->PS_INST_ADDR.flags = ETNA_RELOC_READ;
> +   } else {
> +      /* clear relocs */
> +      memset(&cs->VS_INST_ADDR, 0, sizeof(cs->VS_INST_ADDR));
> +      memset(&cs->PS_INST_ADDR, 0, sizeof(cs->PS_INST_ADDR));
> +   }
> +
>     return true;
>  }
>
> @@ -352,6 +393,8 @@ etna_delete_shader_state(struct pipe_context *pctx, void *ss)
>     while (v) {
>        t = v;
>        v = v->next;
> +      if (t->bo)
> +         etna_bo_del(t->bo);
>        etna_destroy_shader(t);
>     }
>
> --
> 2.7.4
>

greets
--
Christian Gmeiner, MSc

https://christian-gmeiner.info


More information about the etnaviv mailing list