From 93379748f7e4f5ab22040cdb7a4cccdcfb7954c1 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 10 Jun 2015 20:02:55 -0400 Subject: util/blitter (and friends): generate appropriate SVIEW decls Some hardware needs to know the sampler type. Update the blit related shaders to include SVIEW decl. Signed-off-by: Rob Clark Reviewed-by: Jose Fonseca --- src/gallium/auxiliary/util/u_blit.c | 35 +++++++++--- src/gallium/auxiliary/util/u_blitter.c | 57 +++++++++++++++----- src/gallium/auxiliary/util/u_simple_shaders.c | 78 ++++++++++++++++++++------- src/gallium/auxiliary/util/u_simple_shaders.h | 16 +++--- src/gallium/auxiliary/util/u_tests.c | 3 +- src/gallium/tests/trivial/quad-tex.c | 4 +- 6 files changed, 146 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/gallium/auxiliary/util/u_blit.c b/src/gallium/auxiliary/util/u_blit.c index 3f3b5fe63e4..e3f30557a03 100644 --- a/src/gallium/auxiliary/util/u_blit.c +++ b/src/gallium/auxiliary/util/u_blit.c @@ -65,7 +65,7 @@ struct blit_state struct pipe_vertex_element velem[2]; void *vs; - void *fs[PIPE_MAX_TEXTURE_TYPES][TGSI_WRITEMASK_XYZW + 1]; + void *fs[PIPE_MAX_TEXTURE_TYPES][TGSI_WRITEMASK_XYZW + 1][3]; struct pipe_resource *vbuf; /**< quad vertices */ unsigned vbuf_slot; @@ -135,15 +135,17 @@ void util_destroy_blit(struct blit_state *ctx) { struct pipe_context *pipe = ctx->pipe; - unsigned i, j; + unsigned i, j, k; if (ctx->vs) pipe->delete_vs_state(pipe, ctx->vs); for (i = 0; i < Elements(ctx->fs); i++) { for (j = 0; j < Elements(ctx->fs[i]); j++) { - if (ctx->fs[i][j]) - pipe->delete_fs_state(pipe, ctx->fs[i][j]); + for (k = 0; k < Elements(ctx->fs[i][j]); k++) { + if (ctx->fs[i][j][k]) + pipe->delete_fs_state(pipe, ctx->fs[i][j][k]); + } } } @@ -158,18 +160,34 @@ util_destroy_blit(struct blit_state *ctx) */ static INLINE void set_fragment_shader(struct blit_state *ctx, uint writemask, + enum pipe_format format, enum pipe_texture_target pipe_tex) { - if (!ctx->fs[pipe_tex][writemask]) { + enum tgsi_return_type stype; + unsigned idx; + + if (util_format_is_pure_uint(format)) { + stype = TGSI_RETURN_TYPE_UINT; + idx = 0; + } else if (util_format_is_pure_sint(format)) { + stype = TGSI_RETURN_TYPE_SINT; + idx = 1; + } else { + stype = TGSI_RETURN_TYPE_FLOAT; + idx = 2; + } + + if (!ctx->fs[pipe_tex][writemask][idx]) { unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex, 0); - ctx->fs[pipe_tex][writemask] = + ctx->fs[pipe_tex][writemask][idx] = util_make_fragment_tex_shader_writemask(ctx->pipe, tgsi_tex, TGSI_INTERPOLATE_LINEAR, - writemask); + writemask, + stype); } - cso_set_fragment_shader_handle(ctx->cso, ctx->fs[pipe_tex][writemask]); + cso_set_fragment_shader_handle(ctx->cso, ctx->fs[pipe_tex][writemask][idx]); } @@ -571,6 +589,7 @@ util_blit_pixels_tex(struct blit_state *ctx, /* shaders */ set_fragment_shader(ctx, TGSI_WRITEMASK_XYZW, + src_sampler_view->format, src_sampler_view->texture->target); set_vertex_shader(ctx); cso_set_tessctrl_shader_handle(ctx->cso, NULL); diff --git a/src/gallium/auxiliary/util/u_blitter.c b/src/gallium/auxiliary/util/u_blitter.c index 16bf90fc9d6..b5ef9a23966 100644 --- a/src/gallium/auxiliary/util/u_blitter.c +++ b/src/gallium/auxiliary/util/u_blitter.c @@ -81,6 +81,8 @@ struct blitter_context_priv /* FS which outputs a color from a texture, where the index is PIPE_TEXTURE_* to be sampled. */ void *fs_texfetch_col[PIPE_MAX_TEXTURE_TYPES]; + void *fs_texfetch_col_uint[PIPE_MAX_TEXTURE_TYPES]; + void *fs_texfetch_col_sint[PIPE_MAX_TEXTURE_TYPES]; /* FS which outputs a depth from a texture, where the index is PIPE_TEXTURE_* to be sampled. */ @@ -90,6 +92,8 @@ struct blitter_context_priv /* FS which outputs one sample from a multisample texture. */ void *fs_texfetch_col_msaa[PIPE_MAX_TEXTURE_TYPES]; + void *fs_texfetch_col_msaa_uint[PIPE_MAX_TEXTURE_TYPES]; + void *fs_texfetch_col_msaa_sint[PIPE_MAX_TEXTURE_TYPES]; void *fs_texfetch_depth_msaa[PIPE_MAX_TEXTURE_TYPES]; void *fs_texfetch_depthstencil_msaa[PIPE_MAX_TEXTURE_TYPES]; void *fs_texfetch_stencil_msaa[PIPE_MAX_TEXTURE_TYPES]; @@ -438,6 +442,10 @@ void util_blitter_destroy(struct blitter_context *blitter) for (i = 0; i < PIPE_MAX_TEXTURE_TYPES; i++) { if (ctx->fs_texfetch_col[i]) ctx->delete_fs_state(pipe, ctx->fs_texfetch_col[i]); + if (ctx->fs_texfetch_col_sint[i]) + ctx->delete_fs_state(pipe, ctx->fs_texfetch_col_sint[i]); + if (ctx->fs_texfetch_col_uint[i]) + ctx->delete_fs_state(pipe, ctx->fs_texfetch_col_uint[i]); if (ctx->fs_texfetch_depth[i]) ctx->delete_fs_state(pipe, ctx->fs_texfetch_depth[i]); if (ctx->fs_texfetch_depthstencil[i]) @@ -447,6 +455,10 @@ void util_blitter_destroy(struct blitter_context *blitter) if (ctx->fs_texfetch_col_msaa[i]) ctx->delete_fs_state(pipe, ctx->fs_texfetch_col_msaa[i]); + if (ctx->fs_texfetch_col_msaa_sint[i]) + ctx->delete_fs_state(pipe, ctx->fs_texfetch_col_msaa_sint[i]); + if (ctx->fs_texfetch_col_msaa_uint[i]) + ctx->delete_fs_state(pipe, ctx->fs_texfetch_col_msaa_uint[i]); if (ctx->fs_texfetch_depth_msaa[i]) ctx->delete_fs_state(pipe, ctx->fs_texfetch_depth_msaa[i]); if (ctx->fs_texfetch_depthstencil_msaa[i]) @@ -844,25 +856,29 @@ static void *blitter_get_fs_texfetch_col(struct blitter_context_priv *ctx, { struct pipe_context *pipe = ctx->base.pipe; unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(target, src_nr_samples); + enum tgsi_return_type stype; assert(target < PIPE_MAX_TEXTURE_TYPES); + if (util_format_is_pure_uint(format)) + stype = TGSI_RETURN_TYPE_UINT; + else if (util_format_is_pure_sint(format)) + stype = TGSI_RETURN_TYPE_SINT; + else + stype = TGSI_RETURN_TYPE_FLOAT; + if (src_nr_samples > 1) { void **shader; if (dst_nr_samples <= 1) { /* The destination has one sample, so we'll do color resolve. */ - boolean is_uint, is_sint; unsigned index = GET_MSAA_RESOLVE_FS_IDX(src_nr_samples); - is_uint = util_format_is_pure_uint(format); - is_sint = util_format_is_pure_sint(format); - assert(filter < 2); - if (is_uint) + if (stype == TGSI_RETURN_TYPE_UINT) shader = &ctx->fs_resolve_uint[target][index][filter]; - else if (is_sint) + else if (stype == TGSI_RETURN_TYPE_SINT) shader = &ctx->fs_resolve_sint[target][index][filter]; else shader = &ctx->fs_resolve[target][index][filter]; @@ -872,12 +888,12 @@ static void *blitter_get_fs_texfetch_col(struct blitter_context_priv *ctx, if (filter == PIPE_TEX_FILTER_LINEAR) { *shader = util_make_fs_msaa_resolve_bilinear(pipe, tgsi_tex, src_nr_samples, - is_uint, is_sint); + stype); } else { *shader = util_make_fs_msaa_resolve(pipe, tgsi_tex, src_nr_samples, - is_uint, is_sint); + stype); } } } @@ -885,24 +901,37 @@ static void *blitter_get_fs_texfetch_col(struct blitter_context_priv *ctx, /* The destination has multiple samples, we'll do * an MSAA->MSAA copy. */ - shader = &ctx->fs_texfetch_col_msaa[target]; + if (stype == TGSI_RETURN_TYPE_UINT) + shader = &ctx->fs_texfetch_col_msaa_uint[target]; + else if (stype == TGSI_RETURN_TYPE_SINT) + shader = &ctx->fs_texfetch_col_msaa_sint[target]; + else + shader = &ctx->fs_texfetch_col_msaa[target]; /* Create the fragment shader on-demand. */ if (!*shader) { assert(!ctx->cached_all_shaders); - *shader = util_make_fs_blit_msaa_color(pipe, tgsi_tex); + *shader = util_make_fs_blit_msaa_color(pipe, tgsi_tex, stype); } } return *shader; } else { - void **shader = &ctx->fs_texfetch_col[target]; + void **shader; + + if (stype == TGSI_RETURN_TYPE_UINT) + shader = &ctx->fs_texfetch_col_uint[target]; + else if (stype == TGSI_RETURN_TYPE_SINT) + shader = &ctx->fs_texfetch_col_sint[target]; + else + shader = &ctx->fs_texfetch_col[target]; /* Create the fragment shader on-demand. */ if (!*shader) { assert(!ctx->cached_all_shaders); *shader = util_make_fragment_tex_shader(pipe, tgsi_tex, - TGSI_INTERPOLATE_LINEAR); + TGSI_INTERPOLATE_LINEAR, + stype); } return *shader; @@ -1066,6 +1095,10 @@ void util_blitter_cache_all_shaders(struct blitter_context *blitter) */ blitter_get_fs_texfetch_col(ctx, PIPE_FORMAT_R32_FLOAT, target, samples, samples, 0); + blitter_get_fs_texfetch_col(ctx, PIPE_FORMAT_R32_UINT, target, + samples, samples, 0); + blitter_get_fs_texfetch_col(ctx, PIPE_FORMAT_R32_SINT, target, + samples, samples, 0); blitter_get_fs_texfetch_depth(ctx, target, samples); if (ctx->has_stencil_export) { blitter_get_fs_texfetch_depthstencil(ctx, target, samples); diff --git a/src/gallium/auxiliary/util/u_simple_shaders.c b/src/gallium/auxiliary/util/u_simple_shaders.c index c612b67e284..6d29cab9207 100644 --- a/src/gallium/auxiliary/util/u_simple_shaders.c +++ b/src/gallium/auxiliary/util/u_simple_shaders.c @@ -216,7 +216,8 @@ void * util_make_fragment_tex_shader_writemask(struct pipe_context *pipe, unsigned tex_target, unsigned interp_mode, - unsigned writemask ) + unsigned writemask, + enum tgsi_return_type stype) { struct ureg_program *ureg; struct ureg_src sampler; @@ -232,6 +233,8 @@ util_make_fragment_tex_shader_writemask(struct pipe_context *pipe, sampler = ureg_DECL_sampler( ureg, 0 ); + ureg_DECL_sampler_view(ureg, 0, tex_target, stype, stype, stype, stype); + tex = ureg_DECL_fs_input( ureg, TGSI_SEMANTIC_GENERIC, 0, interp_mode ); @@ -268,12 +271,14 @@ util_make_fragment_tex_shader_writemask(struct pipe_context *pipe, */ void * util_make_fragment_tex_shader(struct pipe_context *pipe, unsigned tex_target, - unsigned interp_mode) + unsigned interp_mode, + enum tgsi_return_type stype) { return util_make_fragment_tex_shader_writemask( pipe, tex_target, interp_mode, - TGSI_WRITEMASK_XYZW ); + TGSI_WRITEMASK_XYZW, + stype ); } @@ -298,6 +303,12 @@ util_make_fragment_tex_shader_writedepth(struct pipe_context *pipe, sampler = ureg_DECL_sampler( ureg, 0 ); + ureg_DECL_sampler_view(ureg, 0, tex_target, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_FLOAT); + tex = ureg_DECL_fs_input( ureg, TGSI_SEMANTIC_GENERIC, 0, interp_mode ); @@ -343,7 +354,17 @@ util_make_fragment_tex_shader_writedepthstencil(struct pipe_context *pipe, return NULL; depth_sampler = ureg_DECL_sampler( ureg, 0 ); + ureg_DECL_sampler_view(ureg, 0, tex_target, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_FLOAT); stencil_sampler = ureg_DECL_sampler( ureg, 1 ); + ureg_DECL_sampler_view(ureg, 0, tex_target, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_UINT); tex = ureg_DECL_fs_input( ureg, TGSI_SEMANTIC_GENERIC, 0, @@ -398,6 +419,12 @@ util_make_fragment_tex_shader_writestencil(struct pipe_context *pipe, stencil_sampler = ureg_DECL_sampler( ureg, 0 ); + ureg_DECL_sampler_view(ureg, 0, tex_target, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_UINT); + tex = ureg_DECL_fs_input( ureg, TGSI_SEMANTIC_GENERIC, 0, interp_mode ); @@ -512,6 +539,7 @@ util_make_fragment_cloneinput_shader(struct pipe_context *pipe, int num_cbufs, static void * util_make_fs_blit_msaa_gen(struct pipe_context *pipe, unsigned tgsi_tex, + const char *samp_type, const char *output_semantic, const char *output_mask) { @@ -519,6 +547,7 @@ util_make_fs_blit_msaa_gen(struct pipe_context *pipe, "FRAG\n" "DCL IN[0], GENERIC[0], LINEAR\n" "DCL SAMP[0]\n" + "DCL SVIEW[0], %s, %s\n" "DCL OUT[0], %s\n" "DCL TEMP[0]\n" @@ -534,7 +563,8 @@ util_make_fs_blit_msaa_gen(struct pipe_context *pipe, assert(tgsi_tex == TGSI_TEXTURE_2D_MSAA || tgsi_tex == TGSI_TEXTURE_2D_ARRAY_MSAA); - sprintf(text, shader_templ, output_semantic, output_mask, type); + sprintf(text, shader_templ, type, samp_type, + output_semantic, output_mask, type); if (!tgsi_text_translate(text, tokens, Elements(tokens))) { puts(text); @@ -556,9 +586,19 @@ util_make_fs_blit_msaa_gen(struct pipe_context *pipe, */ void * util_make_fs_blit_msaa_color(struct pipe_context *pipe, - unsigned tgsi_tex) + unsigned tgsi_tex, + enum tgsi_return_type stype) { - return util_make_fs_blit_msaa_gen(pipe, tgsi_tex, + const char *samp_type; + + if (stype == TGSI_RETURN_TYPE_UINT) + samp_type = "UINT"; + else if (stype == TGSI_RETURN_TYPE_SINT) + samp_type = "SINT"; + else + samp_type = "FLOAT"; + + return util_make_fs_blit_msaa_gen(pipe, tgsi_tex, samp_type, "COLOR[0]", ""); } @@ -572,7 +612,7 @@ void * util_make_fs_blit_msaa_depth(struct pipe_context *pipe, unsigned tgsi_tex) { - return util_make_fs_blit_msaa_gen(pipe, tgsi_tex, + return util_make_fs_blit_msaa_gen(pipe, tgsi_tex, "FLOAT", "POSITION", ".z"); } @@ -586,7 +626,7 @@ void * util_make_fs_blit_msaa_stencil(struct pipe_context *pipe, unsigned tgsi_tex) { - return util_make_fs_blit_msaa_gen(pipe, tgsi_tex, + return util_make_fs_blit_msaa_gen(pipe, tgsi_tex, "UINT", "STENCIL", ".y"); } @@ -640,7 +680,7 @@ util_make_fs_blit_msaa_depthstencil(struct pipe_context *pipe, void * util_make_fs_msaa_resolve(struct pipe_context *pipe, unsigned tgsi_tex, unsigned nr_samples, - boolean is_uint, boolean is_sint) + enum tgsi_return_type stype) { struct ureg_program *ureg; struct ureg_src sampler, coord; @@ -653,6 +693,7 @@ util_make_fs_msaa_resolve(struct pipe_context *pipe, /* Declarations. */ sampler = ureg_DECL_sampler(ureg, 0); + ureg_DECL_sampler_view(ureg, 0, tgsi_tex, stype, stype, stype, stype); coord = ureg_DECL_fs_input(ureg, TGSI_SEMANTIC_GENERIC, 0, TGSI_INTERPOLATE_LINEAR); out = ureg_DECL_output(ureg, TGSI_SEMANTIC_COLOR, 0); @@ -670,9 +711,9 @@ util_make_fs_msaa_resolve(struct pipe_context *pipe, ureg_imm1u(ureg, i)); ureg_TXF(ureg, tmp, tgsi_tex, ureg_src(tmp_coord), sampler); - if (is_uint) + if (stype == TGSI_RETURN_TYPE_UINT) ureg_U2F(ureg, tmp, ureg_src(tmp)); - else if (is_sint) + else if (stype == TGSI_RETURN_TYPE_SINT) ureg_I2F(ureg, tmp, ureg_src(tmp)); /* Add it to the sum.*/ @@ -683,9 +724,9 @@ util_make_fs_msaa_resolve(struct pipe_context *pipe, ureg_MUL(ureg, tmp_sum, ureg_src(tmp_sum), ureg_imm1f(ureg, 1.0 / nr_samples)); - if (is_uint) + if (stype == TGSI_RETURN_TYPE_UINT) ureg_F2U(ureg, out, ureg_src(tmp_sum)); - else if (is_sint) + else if (stype == TGSI_RETURN_TYPE_SINT) ureg_F2I(ureg, out, ureg_src(tmp_sum)); else ureg_MOV(ureg, out, ureg_src(tmp_sum)); @@ -699,7 +740,7 @@ util_make_fs_msaa_resolve(struct pipe_context *pipe, void * util_make_fs_msaa_resolve_bilinear(struct pipe_context *pipe, unsigned tgsi_tex, unsigned nr_samples, - boolean is_uint, boolean is_sint) + enum tgsi_return_type stype) { struct ureg_program *ureg; struct ureg_src sampler, coord; @@ -713,6 +754,7 @@ util_make_fs_msaa_resolve_bilinear(struct pipe_context *pipe, /* Declarations. */ sampler = ureg_DECL_sampler(ureg, 0); + ureg_DECL_sampler_view(ureg, 0, tgsi_tex, stype, stype, stype, stype); coord = ureg_DECL_fs_input(ureg, TGSI_SEMANTIC_GENERIC, 0, TGSI_INTERPOLATE_LINEAR); out = ureg_DECL_output(ureg, TGSI_SEMANTIC_COLOR, 0); @@ -744,9 +786,9 @@ util_make_fs_msaa_resolve_bilinear(struct pipe_context *pipe, ureg_imm1u(ureg, i)); ureg_TXF(ureg, tmp, tgsi_tex, ureg_src(tmp_coord[c]), sampler); - if (is_uint) + if (stype == TGSI_RETURN_TYPE_UINT) ureg_U2F(ureg, tmp, ureg_src(tmp)); - else if (is_sint) + else if (stype == TGSI_RETURN_TYPE_SINT) ureg_I2F(ureg, tmp, ureg_src(tmp)); /* Add it to the sum.*/ @@ -778,9 +820,9 @@ util_make_fs_msaa_resolve_bilinear(struct pipe_context *pipe, ureg_src(top)); /* Convert to the texture format and return. */ - if (is_uint) + if (stype == TGSI_RETURN_TYPE_UINT) ureg_F2U(ureg, out, ureg_src(tmp)); - else if (is_sint) + else if (stype == TGSI_RETURN_TYPE_SINT) ureg_F2I(ureg, out, ureg_src(tmp)); else ureg_MOV(ureg, out, ureg_src(tmp)); diff --git a/src/gallium/auxiliary/util/u_simple_shaders.h b/src/gallium/auxiliary/util/u_simple_shaders.h index dd282e02a13..08d798ef541 100644 --- a/src/gallium/auxiliary/util/u_simple_shaders.h +++ b/src/gallium/auxiliary/util/u_simple_shaders.h @@ -68,15 +68,16 @@ extern void * util_make_layered_clear_geometry_shader(struct pipe_context *pipe); extern void * -util_make_fragment_tex_shader_writemask(struct pipe_context *pipe, +util_make_fragment_tex_shader_writemask(struct pipe_context *pipe, unsigned tex_target, unsigned interp_mode, - unsigned writemask); + unsigned writemask, + enum tgsi_return_type stype); extern void * util_make_fragment_tex_shader(struct pipe_context *pipe, unsigned tex_target, - unsigned interp_mode); - + unsigned interp_mode, + enum tgsi_return_type stype); extern void * util_make_fragment_tex_shader_writedepth(struct pipe_context *pipe, @@ -115,7 +116,8 @@ util_make_fragment_cloneinput_shader(struct pipe_context *pipe, int num_cbufs, extern void * util_make_fs_blit_msaa_color(struct pipe_context *pipe, - unsigned tgsi_tex); + unsigned tgsi_tex, + enum tgsi_return_type stype); extern void * @@ -136,13 +138,13 @@ util_make_fs_blit_msaa_stencil(struct pipe_context *pipe, void * util_make_fs_msaa_resolve(struct pipe_context *pipe, unsigned tgsi_tex, unsigned nr_samples, - boolean is_uint, boolean is_sint); + enum tgsi_return_type stype); void * util_make_fs_msaa_resolve_bilinear(struct pipe_context *pipe, unsigned tgsi_tex, unsigned nr_samples, - boolean is_uint, boolean is_sint); + enum tgsi_return_type stype); #ifdef __cplusplus } diff --git a/src/gallium/auxiliary/util/u_tests.c b/src/gallium/auxiliary/util/u_tests.c index fe549723c33..6a489d63c09 100644 --- a/src/gallium/auxiliary/util/u_tests.c +++ b/src/gallium/auxiliary/util/u_tests.c @@ -373,7 +373,8 @@ null_sampler_view(struct pipe_context *ctx, unsigned tgsi_tex_target) /* Fragment shader. */ fs = util_make_fragment_tex_shader(ctx, tgsi_tex_target, - TGSI_INTERPOLATE_LINEAR); + TGSI_INTERPOLATE_LINEAR, + TGSI_RETURN_TYPE_FLOAT); cso_set_fragment_shader_handle(cso, fs); /* Vertex shader. */ diff --git a/src/gallium/tests/trivial/quad-tex.c b/src/gallium/tests/trivial/quad-tex.c index abecedbd594..daae577ec4b 100644 --- a/src/gallium/tests/trivial/quad-tex.c +++ b/src/gallium/tests/trivial/quad-tex.c @@ -270,7 +270,9 @@ static void init_prog(struct program *p) } /* fragment shader */ - p->fs = util_make_fragment_tex_shader(p->pipe, TGSI_TEXTURE_2D, TGSI_INTERPOLATE_LINEAR); + p->fs = util_make_fragment_tex_shader(p->pipe, TGSI_TEXTURE_2D, + TGSI_INTERPOLATE_LINEAR, + TGSI_RETURN_TYPE_FLOAT); } static void close_prog(struct program *p) -- cgit v1.2.3