From 6a8d5ab932e525f16e744b4f13c4f6a7fa636ecf Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 14 Apr 2017 09:44:27 +0200 Subject: [PATCH] etnaviv: SINGLE_BUFFER support on GC3000 This patch adds support for the SINGLE_BUFFER feature on GC3000 GPUs, which allows rendering to a single buffer using multiple pixel pipes. This feature is always used when it is available, which means that multi-tiled formats are no longer being used in that case, and all buffers will be normal (super)tiled. This mimics the behavior of the blob on GC3000. - Because the same format can be used to render to and texture from, this avoids an extra resolve pass when rendering to texture. - i.MX6qp includes a PRE which can scan-out directly from tiled formats, avoiding untiling overhead. Signed-off-by: Wladimir J. van der Laan Reviewed-by: Christian Gmeiner --- src/gallium/drivers/etnaviv/etnaviv_context.c | 3 ++ src/gallium/drivers/etnaviv/etnaviv_emit.c | 6 ++-- src/gallium/drivers/etnaviv/etnaviv_internal.h | 3 ++ src/gallium/drivers/etnaviv/etnaviv_resource.c | 8 +++++- src/gallium/drivers/etnaviv/etnaviv_rs.c | 39 +++++++++++++++----------- src/gallium/drivers/etnaviv/etnaviv_screen.c | 4 +++ src/gallium/drivers/etnaviv/etnaviv_state.c | 10 +++++-- src/gallium/drivers/etnaviv/etnaviv_surface.c | 18 ++++++++---- 8 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/gallium/drivers/etnaviv/etnaviv_context.c b/src/gallium/drivers/etnaviv/etnaviv_context.c index 555aa12..f2f709c 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_context.c +++ b/src/gallium/drivers/etnaviv/etnaviv_context.c @@ -293,6 +293,9 @@ etna_cmd_stream_reset_notify(struct etna_cmd_stream *stream, void *priv) etna_set_state(stream, VIVS_RA_EARLY_DEPTH, 0x00000031); etna_set_state(stream, VIVS_PA_W_CLIP_LIMIT, 0x34000001); + /* Enable SINGLE_BUFFER for resolve, if supported */ + etna_set_state(stream, VIVS_RS_SINGLE_BUFFER, COND(ctx->specs.single_buffer, VIVS_RS_SINGLE_BUFFER_ENABLE)); + ctx->dirty = ~0L; /* go through all the used resources and clear their status flag */ diff --git a/src/gallium/drivers/etnaviv/etnaviv_emit.c b/src/gallium/drivers/etnaviv/etnaviv_emit.c index af74cbb..7ced5fc 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_emit.c +++ b/src/gallium/drivers/etnaviv/etnaviv_emit.c @@ -603,10 +603,12 @@ etna_emit_state(struct etna_context *ctx) if (unlikely(dirty & (ETNA_DIRTY_STENCIL_REF))) { /*014A0*/ EMIT_STATE(PE_STENCIL_CONFIG_EXT, ctx->stencil_ref.PE_STENCIL_CONFIG_EXT); } + if (unlikely(dirty & (ETNA_DIRTY_BLEND | ETNA_DIRTY_FRAMEBUFFER))) { + struct etna_blend_state *blend = etna_blend_state(ctx->blend); + /*014A4*/ EMIT_STATE(PE_LOGIC_OP, blend->PE_LOGIC_OP | ctx->framebuffer.PE_LOGIC_OP); + } if (unlikely(dirty & (ETNA_DIRTY_BLEND))) { struct etna_blend_state *blend = etna_blend_state(ctx->blend); - - /*014A4*/ EMIT_STATE(PE_LOGIC_OP, blend->PE_LOGIC_OP); for (int x = 0; x < 2; ++x) { /*014A8*/ EMIT_STATE(PE_DITHER(x), blend->PE_DITHER[x]); } diff --git a/src/gallium/drivers/etnaviv/etnaviv_internal.h b/src/gallium/drivers/etnaviv/etnaviv_internal.h index 2f09d55..2f8dacb 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_internal.h +++ b/src/gallium/drivers/etnaviv/etnaviv_internal.h @@ -72,6 +72,8 @@ struct etna_specs { unsigned has_shader_range_registers : 1; /* has the new sin/cos functions */ unsigned has_new_sin_cos : 1; + /* supports single-buffer rendering with multiple pixel pipes */ + unsigned single_buffer : 1; /* can use any kind of wrapping mode on npot textures */ unsigned npot_tex_any_wrap; /* number of bits per TS tile */ @@ -191,6 +193,7 @@ struct compiled_framebuffer_state { uint32_t TS_COLOR_CLEAR_VALUE; struct etna_reloc TS_COLOR_STATUS_BASE; struct etna_reloc TS_COLOR_SURFACE_BASE; + uint32_t PE_LOGIC_OP; bool msaa_mode; /* adds input (and possible temp) to PS */ }; diff --git a/src/gallium/drivers/etnaviv/etnaviv_resource.c b/src/gallium/drivers/etnaviv/etnaviv_resource.c index 5f2b63f..db9acac 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_resource.c +++ b/src/gallium/drivers/etnaviv/etnaviv_resource.c @@ -249,9 +249,15 @@ etna_resource_create(struct pipe_screen *pscreen, if (util_format_is_compressed(templat->format)) layout = ETNA_LAYOUT_LINEAR; } else if (templat->target != PIPE_BUFFER) { - bool want_multitiled = screen->specs.pixel_pipes > 1; + bool want_multitiled = false; bool want_supertiled = screen->specs.can_supertile && !DBG_ENABLED(ETNA_DBG_NO_SUPERTILE); + /* When this GPU supports single-buffer rendering, don't ever enable + * multi-tiling. This replicates the blob behavior on GC3000. + */ + if (!screen->specs.single_buffer) + want_multitiled = screen->specs.pixel_pipes > 1; + /* Keep single byte blocksized resources as tiled, since we * are unable to use the RS blit to de-tile them. However, * if they're used as a render target or depth/stencil, they diff --git a/src/gallium/drivers/etnaviv/etnaviv_rs.c b/src/gallium/drivers/etnaviv/etnaviv_rs.c index 295ca10..5c108a6 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_rs.c +++ b/src/gallium/drivers/etnaviv/etnaviv_rs.c @@ -70,44 +70,45 @@ etna_compile_rs_state(struct etna_context *ctx, struct compiled_rs_state *cs, COND(rs->source_tiling & 2, VIVS_RS_SOURCE_STRIDE_TILING) | COND(source_multi, VIVS_RS_SOURCE_STRIDE_MULTI); - cs->source[0].bo = rs->source; - cs->source[0].offset = rs->source_offset; - cs->source[0].flags = ETNA_RELOC_READ; + /* Initially all pipes are set to the base address of the source and + * destination buffer respectively. This will be overridden below as + * necessary for the multi-pipe, multi-tiled case. + */ + for (unsigned pipe = 0; pipe < ctx->specs.pixel_pipes; ++pipe) { + cs->source[pipe].bo = rs->source; + cs->source[pipe].offset = rs->source_offset; + cs->source[pipe].flags = ETNA_RELOC_READ; + + cs->dest[pipe].bo = rs->dest; + cs->dest[pipe].offset = rs->dest_offset; + cs->dest[pipe].flags = ETNA_RELOC_WRITE; - cs->dest[0].bo = rs->dest; - cs->dest[0].offset = rs->dest_offset; - cs->dest[0].flags = ETNA_RELOC_WRITE; + cs->RS_PIPE_OFFSET[pipe] = VIVS_RS_PIPE_OFFSET_X(0) | VIVS_RS_PIPE_OFFSET_Y(0); + } cs->RS_DEST_STRIDE = (rs->dest_stride << dest_stride_shift) | COND(rs->dest_tiling & 2, VIVS_RS_DEST_STRIDE_TILING) | COND(dest_multi, VIVS_RS_DEST_STRIDE_MULTI); - if (ctx->specs.pixel_pipes == 1) { + if (ctx->specs.pixel_pipes == 1 || ctx->specs.single_buffer) { cs->RS_WINDOW_SIZE = VIVS_RS_WINDOW_SIZE_WIDTH(rs->width) | VIVS_RS_WINDOW_SIZE_HEIGHT(rs->height); } else if (ctx->specs.pixel_pipes == 2) { assert((rs->height & 7) == 0); /* GPU hangs happen if height not 8-aligned */ - if (source_multi) { - cs->source[1].bo = rs->source; + if (source_multi) cs->source[1].offset = rs->source_offset + rs->source_stride * rs->source_padded_height / 2; - cs->source[1].flags = ETNA_RELOC_READ; - } - if (dest_multi) { - cs->dest[1].bo = rs->dest; + if (dest_multi) cs->dest[1].offset = rs->dest_offset + rs->dest_stride * rs->dest_padded_height / 2; - cs->dest[1].flags = ETNA_RELOC_WRITE; - } cs->RS_WINDOW_SIZE = VIVS_RS_WINDOW_SIZE_WIDTH(rs->width) | VIVS_RS_WINDOW_SIZE_HEIGHT(rs->height / 2); + cs->RS_PIPE_OFFSET[1] = VIVS_RS_PIPE_OFFSET_X(0) | VIVS_RS_PIPE_OFFSET_Y(rs->height / 2); } else { abort(); } - cs->RS_PIPE_OFFSET[0] = VIVS_RS_PIPE_OFFSET_X(0) | VIVS_RS_PIPE_OFFSET_Y(0); - cs->RS_PIPE_OFFSET[1] = VIVS_RS_PIPE_OFFSET_X(0) | VIVS_RS_PIPE_OFFSET_Y(rs->height / 2); cs->RS_DITHER[0] = rs->dither[0]; cs->RS_DITHER[1] = rs->dither[1]; cs->RS_CLEAR_CONTROL = VIVS_RS_CLEAR_CONTROL_BITS(rs->clear_bits) | rs->clear_mode; @@ -117,6 +118,10 @@ etna_compile_rs_state(struct etna_context *ctx, struct compiled_rs_state *cs, cs->RS_FILL_VALUE[3] = rs->clear_value[3]; cs->RS_EXTRA_CONFIG = VIVS_RS_EXTRA_CONFIG_AA(rs->aa) | VIVS_RS_EXTRA_CONFIG_ENDIAN(rs->endian_mode); + /* TODO: cs->RS_UNK016B0 = s->size / 64 ? + * The blob does this consistently but there seems to be no currently supported + * model that needs it. + */ } void diff --git a/src/gallium/drivers/etnaviv/etnaviv_screen.c b/src/gallium/drivers/etnaviv/etnaviv_screen.c index d4e4c7f..f61b7f6 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_screen.c +++ b/src/gallium/drivers/etnaviv/etnaviv_screen.c @@ -675,6 +675,10 @@ etna_get_specs(struct etna_screen *screen) screen->specs.max_rendertarget_size = VIV_FEATURE(screen, chipMinorFeatures0, RENDERTARGET_8K) ? 8192 : 2048; + screen->specs.single_buffer = VIV_FEATURE(screen, chipMinorFeatures4, SINGLE_BUFFER); + if (screen->specs.single_buffer) + DBG("etnaviv: Single buffer mode enabled with %d pixel pipes\n", screen->specs.pixel_pipes); + return true; fail: diff --git a/src/gallium/drivers/etnaviv/etnaviv_state.c b/src/gallium/drivers/etnaviv/etnaviv_state.c index dbb6c54..d96b445 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_state.c +++ b/src/gallium/drivers/etnaviv/etnaviv_state.c @@ -168,8 +168,8 @@ etna_set_framebuffer_state(struct pipe_context *pctx, cs->PE_COLOR_ADDR = cbuf->reloc[0]; cs->PE_COLOR_ADDR.flags = ETNA_RELOC_READ | ETNA_RELOC_WRITE; } else { - /* Rendered textures must always be multi-tiled */ - assert(res->layout & ETNA_LAYOUT_BIT_MULTI); + /* Rendered textures must always be multi-tiled, or single-buffer mode must be supported */ + assert((res->layout & ETNA_LAYOUT_BIT_MULTI) || ctx->specs.single_buffer); for (int i = 0; i < ctx->specs.pixel_pipes; i++) { cs->PE_PIPE_COLOR_ADDR[i] = cbuf->reloc[i]; cs->PE_PIPE_COLOR_ADDR[i].flags = ETNA_RELOC_READ | ETNA_RELOC_WRITE; @@ -331,6 +331,12 @@ etna_set_framebuffer_state(struct pipe_context *pctx, cs->TS_MEM_CONFIG = ts_mem_config; + /* Single buffer setup. There is only one switch for this, not a separate + * one per color buffer / depth buffer. To keep the logic simple always use + * single buffer when this feature is available. + */ + cs->PE_LOGIC_OP = VIVS_PE_LOGIC_OP_SINGLE_BUFFER(ctx->specs.single_buffer ? 2 : 0); + ctx->framebuffer_s = *sv; /* keep copy of original structure */ ctx->dirty |= ETNA_DIRTY_FRAMEBUFFER; } diff --git a/src/gallium/drivers/etnaviv/etnaviv_surface.c b/src/gallium/drivers/etnaviv/etnaviv_surface.c index 7ac2862..1db9b40 100644 --- a/src/gallium/drivers/etnaviv/etnaviv_surface.c +++ b/src/gallium/drivers/etnaviv/etnaviv_surface.c @@ -91,12 +91,18 @@ etna_create_surface(struct pipe_context *pctx, struct pipe_resource *prsc, struct etna_resource_level *lev = &rsc->levels[level]; /* Setup template relocations for this surface */ - surf->reloc[0].bo = rsc->bo; - surf->reloc[0].offset = surf->surf.offset; - surf->reloc[0].flags = 0; - surf->reloc[1].bo = rsc->bo; - surf->reloc[1].offset = surf->surf.offset + lev->stride * lev->padded_height / 2; - surf->reloc[1].flags = 0; + for (unsigned pipe = 0; pipe < ctx->specs.pixel_pipes; ++pipe) { + surf->reloc[pipe].bo = rsc->bo; + surf->reloc[pipe].offset = surf->surf.offset; + surf->reloc[pipe].flags = 0; + } + + /* In single buffer mode, both pixel pipes must point to the same address, + * for multi-tiled surfaces on the other hand the second pipe is expected to + * point halfway the image vertically. + */ + if (rsc->layout & ETNA_LAYOUT_BIT_MULTI) + surf->reloc[1].offset = surf->surf.offset + lev->stride * lev->padded_height / 2; if (surf->surf.ts_size) { unsigned int layer_offset = layer * surf->surf.ts_layer_stride; -- 2.7.4