From 78354011f99c4103345f8f32e10b0b4b884ebdaf Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Sun, 26 Aug 2012 22:38:35 +0200 Subject: [PATCH] r600g: implement color resolve for r600 The blend state is different and the resolve single-sample buffer must have FMASK and CMASK enabled. I decided to have one CMASK and one FMASK per context instead of per resource. There are new FMASK and CMASK allocation helpers and a new buffer_create helper for that. --- src/gallium/drivers/r600/r600_buffer.c | 5 +- src/gallium/drivers/r600/r600_pipe.c | 6 +- src/gallium/drivers/r600/r600_pipe.h | 11 +- src/gallium/drivers/r600/r600_resource.c | 2 +- src/gallium/drivers/r600/r600_resource.h | 21 ++++ src/gallium/drivers/r600/r600_state.c | 126 +++++++++++++++++++++-- src/gallium/drivers/r600/r600_texture.c | 54 +++++++--- 7 files changed, 199 insertions(+), 26 deletions(-) diff --git a/src/gallium/drivers/r600/r600_buffer.c b/src/gallium/drivers/r600/r600_buffer.c index 907ac9691ba..88d67dfaf49 100644 --- a/src/gallium/drivers/r600/r600_buffer.c +++ b/src/gallium/drivers/r600/r600_buffer.c @@ -237,12 +237,11 @@ bool r600_init_resource(struct r600_screen *rscreen, } struct pipe_resource *r600_buffer_create(struct pipe_screen *screen, - const struct pipe_resource *templ) + const struct pipe_resource *templ, + unsigned alignment) { struct r600_screen *rscreen = (struct r600_screen*)screen; struct r600_resource *rbuffer; - /* XXX We probably want a different alignment for buffers and textures. */ - unsigned alignment = 4096; rbuffer = MALLOC_STRUCT(r600_resource); diff --git a/src/gallium/drivers/r600/r600_pipe.c b/src/gallium/drivers/r600/r600_pipe.c index 77adeaef8bc..7856e624ea2 100644 --- a/src/gallium/drivers/r600/r600_pipe.c +++ b/src/gallium/drivers/r600/r600_pipe.c @@ -156,6 +156,9 @@ static void r600_destroy_context(struct pipe_context *context) { struct r600_context *rctx = (struct r600_context *)context; + pipe_resource_reference((struct pipe_resource**)&rctx->dummy_cmask, NULL); + pipe_resource_reference((struct pipe_resource**)&rctx->dummy_fmask, NULL); + if (rctx->no_blend) { rctx->context.delete_blend_state(&rctx->context, rctx->no_blend); } @@ -250,7 +253,8 @@ static struct pipe_context *r600_create_context(struct pipe_screen *screen, void if (r600_context_init(rctx)) goto fail; rctx->custom_dsa_flush = r600_create_db_flush_dsa(rctx); - rctx->custom_blend_resolve = r600_create_resolve_blend(rctx); + rctx->custom_blend_resolve = rctx->chip_class == R700 ? r700_create_resolve_blend(rctx) + : r600_create_resolve_blend(rctx); rctx->custom_blend_decompress = r600_create_decompress_blend(rctx); rctx->has_vertex_cache = !(rctx->family == CHIP_RV610 || rctx->family == CHIP_RV620 || diff --git a/src/gallium/drivers/r600/r600_pipe.h b/src/gallium/drivers/r600/r600_pipe.h index 9cbf0c88e64..8c718a7d202 100644 --- a/src/gallium/drivers/r600/r600_pipe.h +++ b/src/gallium/drivers/r600/r600_pipe.h @@ -450,6 +450,13 @@ struct r600_context { /* Index buffer. */ struct pipe_index_buffer index_buffer; + + /* Dummy CMASK and FMASK buffers used to get around the R6xx hardware + * bug where valid CMASK and FMASK are required to be present to avoid + * a hardlock in certain operations but aren't actually used + * for anything useful. */ + struct r600_resource *dummy_fmask; + struct r600_resource *dummy_cmask; }; static INLINE void r600_emit_atom(struct r600_context *rctx, struct r600_atom *atom) @@ -518,7 +525,8 @@ bool r600_init_resource(struct r600_screen *rscreen, unsigned size, unsigned alignment, unsigned bind, unsigned usage); struct pipe_resource *r600_buffer_create(struct pipe_screen *screen, - const struct pipe_resource *templ); + const struct pipe_resource *templ, + unsigned alignment); /* r600_pipe.c */ void r600_flush(struct pipe_context *ctx, struct pipe_fence_handle **fence, @@ -552,6 +560,7 @@ void r600_pipe_shader_vs(struct pipe_context *ctx, struct r600_pipe_shader *shad void r600_fetch_shader(struct pipe_context *ctx, struct r600_vertex_element *ve); void *r600_create_db_flush_dsa(struct r600_context *rctx); void *r600_create_resolve_blend(struct r600_context *rctx); +void *r700_create_resolve_blend(struct r600_context *rctx); void *r600_create_decompress_blend(struct r600_context *rctx); void r600_polygon_offset_update(struct r600_context *rctx); void r600_adjust_gprs(struct r600_context *rctx); diff --git a/src/gallium/drivers/r600/r600_resource.c b/src/gallium/drivers/r600/r600_resource.c index 0c14a2dc6bc..1a91d5d5755 100644 --- a/src/gallium/drivers/r600/r600_resource.c +++ b/src/gallium/drivers/r600/r600_resource.c @@ -31,7 +31,7 @@ static struct pipe_resource *r600_resource_create(struct pipe_screen *screen, return r600_compute_global_buffer_create(screen, templ); } else { - return r600_buffer_create(screen, templ); + return r600_buffer_create(screen, templ, 4096); } } else { return r600_texture_create(screen, templ); diff --git a/src/gallium/drivers/r600/r600_resource.h b/src/gallium/drivers/r600/r600_resource.h index da60d373c80..a5a540439e6 100644 --- a/src/gallium/drivers/r600/r600_resource.h +++ b/src/gallium/drivers/r600/r600_resource.h @@ -64,6 +64,18 @@ struct r600_texture { #define R600_TEX_IS_TILED(tex, level) ((tex)->array_mode[level] != V_038000_ARRAY_LINEAR_GENERAL && (tex)->array_mode[level] != V_038000_ARRAY_LINEAR_ALIGNED) +struct r600_fmask_info { + unsigned size; + unsigned alignment; + unsigned bank_height; +}; + +struct r600_cmask_info { + unsigned size; + unsigned alignment; + unsigned slice_tile_max; +}; + struct r600_surface { struct pipe_surface base; @@ -88,6 +100,8 @@ struct r600_surface { unsigned cb_color_cmask; /* CB_COLORn_CMASK (EG) or CB_COLORn_TILE (r600) */ unsigned cb_color_cmask_slice; /* EG only */ unsigned cb_color_mask; /* R600 only */ + struct r600_resource *cb_buffer_fmask; /* Used for FMASK relocations. R600 only */ + struct r600_resource *cb_buffer_cmask; /* Used for CMASK relocations. R600 only */ /* DB registers. */ unsigned db_depth_info; /* DB_Z_INFO (EG) or DB_DEPTH_INFO (r600) */ @@ -104,6 +118,13 @@ void r600_resource_destroy(struct pipe_screen *screen, struct pipe_resource *res void r600_init_screen_resource_functions(struct pipe_screen *screen); /* r600_texture */ +void r600_texture_get_fmask_info(struct r600_screen *rscreen, + struct r600_texture *rtex, + unsigned nr_samples, + struct r600_fmask_info *out); +void r600_texture_get_cmask_info(struct r600_screen *rscreen, + struct r600_texture *rtex, + struct r600_cmask_info *out); struct pipe_resource *r600_texture_create(struct pipe_screen *screen, const struct pipe_resource *templ); struct pipe_resource *r600_texture_from_handle(struct pipe_screen *screen, diff --git a/src/gallium/drivers/r600/r600_state.c b/src/gallium/drivers/r600/r600_state.c index 3e5958af2a3..e7ed239fe34 100644 --- a/src/gallium/drivers/r600/r600_state.c +++ b/src/gallium/drivers/r600/r600_state.c @@ -1232,9 +1232,31 @@ static void r600_set_viewport_state(struct pipe_context *ctx, r600_context_pipe_state_set(rctx, rstate); } +static struct r600_resource *r600_buffer_create_helper(struct r600_screen *rscreen, + unsigned size, unsigned alignment) +{ + struct pipe_resource buffer; + + memset(&buffer, 0, sizeof buffer); + buffer.target = PIPE_BUFFER; + buffer.format = PIPE_FORMAT_R8_UNORM; + buffer.bind = PIPE_BIND_CUSTOM; + buffer.usage = PIPE_USAGE_STATIC; + buffer.flags = 0; + buffer.width0 = size; + buffer.height0 = 1; + buffer.depth0 = 1; + buffer.array_size = 1; + + return (struct r600_resource*) + r600_buffer_create(&rscreen->screen, &buffer, alignment); +} + static void r600_init_color_surface(struct r600_context *rctx, - struct r600_surface *surf) + struct r600_surface *surf, + bool force_cmask_fmask) { + struct r600_screen *rscreen = rctx->screen; struct r600_texture *rtex = (struct r600_texture*)surf->base.texture; unsigned level = surf->base.u.tex.level; unsigned pitch, slice; @@ -1366,11 +1388,18 @@ static void r600_init_color_surface(struct r600_context *rctx, } } + /* These might not always be initialized to zero. */ surf->cb_color_base = offset >> 8; surf->cb_color_size = S_028060_PITCH_TILE_MAX(pitch) | S_028060_SLICE_TILE_MAX(slice); surf->cb_color_fmask = surf->cb_color_base; surf->cb_color_cmask = surf->cb_color_base; + surf->cb_color_mask = 0; + + pipe_resource_reference((struct pipe_resource**)&surf->cb_buffer_cmask, + &rtex->resource.b.b); + pipe_resource_reference((struct pipe_resource**)&surf->cb_buffer_fmask, + &rtex->resource.b.b); if (rtex->cmask_size) { surf->cb_color_cmask = rtex->cmask_offset >> 8; @@ -1383,7 +1412,56 @@ static void r600_init_color_surface(struct r600_context *rctx, } else { /* cmask only */ color_info |= S_0280A0_TILE_MODE(V_0280A0_CLEAR_ENABLE); } + } else if (force_cmask_fmask) { + /* Allocate dummy FMASK and CMASK if they aren't allocated already. + * + * R6xx needs FMASK and CMASK for the destination buffer of color resolve, + * otherwise it hangs. We don't have FMASK and CMASK pre-allocated, + * because it's not an MSAA buffer. + */ + struct r600_cmask_info cmask; + struct r600_fmask_info fmask; + + r600_texture_get_cmask_info(rscreen, rtex, &cmask); + r600_texture_get_fmask_info(rscreen, rtex, 8, &fmask); + + /* CMASK. */ + if (!rctx->dummy_cmask || + rctx->dummy_cmask->buf->size < cmask.size || + rctx->dummy_cmask->buf->alignment % cmask.alignment != 0) { + struct pipe_transfer *transfer; + void *ptr; + + pipe_resource_reference((struct pipe_resource**)&rctx->dummy_cmask, NULL); + rctx->dummy_cmask = r600_buffer_create_helper(rscreen, cmask.size, cmask.alignment); + + /* Set the contents to 0xCC. */ + ptr = pipe_buffer_map(&rctx->context, &rctx->dummy_cmask->b.b, PIPE_TRANSFER_WRITE, &transfer); + memset(ptr, 0xCC, cmask.size); + pipe_buffer_unmap(&rctx->context, transfer); + } + pipe_resource_reference((struct pipe_resource**)&surf->cb_buffer_cmask, + &rctx->dummy_cmask->b.b); + + /* FMASK. */ + if (!rctx->dummy_fmask || + rctx->dummy_fmask->buf->size < fmask.size || + rctx->dummy_fmask->buf->alignment % fmask.alignment != 0) { + pipe_resource_reference((struct pipe_resource**)&rctx->dummy_fmask, NULL); + rctx->dummy_fmask = r600_buffer_create_helper(rscreen, fmask.size, fmask.alignment); + + } + pipe_resource_reference((struct pipe_resource**)&surf->cb_buffer_fmask, + &rctx->dummy_fmask->b.b); + + /* Init the registers. */ + color_info |= S_0280A0_TILE_MODE(V_0280A0_FRAG_ENABLE); + surf->cb_color_cmask = 0; + surf->cb_color_fmask = 0; + surf->cb_color_mask = S_028100_CMASK_BLOCK_MAX(cmask.slice_tile_max) | + S_028100_FMASK_TILE_MAX(slice); } + surf->cb_color_info = color_info; if (rtex->surface.level[level].mode < RADEON_SURF_MODE_1D) { @@ -1509,6 +1587,11 @@ static void r600_set_framebuffer_state(struct pipe_context *ctx, struct r600_resource *res; struct r600_texture *rtex; uint32_t tl, br, i, nr_samples, max_dist; + bool is_resolve = state->nr_cbufs == 2 && + state->cbufs[0]->texture->nr_samples > 1 && + state->cbufs[1]->texture->nr_samples <= 1; + /* The resolve buffer must have CMASK and FMASK to prevent hardlocks on R6xx. */ + bool cb1_force_cmask_fmask = rctx->chip_class == R600 && is_resolve; if (rstate == NULL) return; @@ -1528,12 +1611,17 @@ static void r600_set_framebuffer_state(struct pipe_context *ctx, rctx->compressed_cb_mask = 0; for (i = 0; i < state->nr_cbufs; i++) { + bool force_cmask_fmask = cb1_force_cmask_fmask && i == 1; surf = (struct r600_surface*)state->cbufs[i]; res = (struct r600_resource*)surf->base.texture; rtex = (struct r600_texture*)res; - if (!surf->color_initialized) { - r600_init_color_surface(rctx, surf); + if (!surf->color_initialized || force_cmask_fmask) { + r600_init_color_surface(rctx, surf, force_cmask_fmask); + if (force_cmask_fmask) { + /* re-initialize later without compression */ + surf->color_initialized = false; + } } if (!surf->export_16bpc) { @@ -1549,9 +1637,11 @@ static void r600_set_framebuffer_state(struct pipe_context *ctx, r600_pipe_state_add_reg(rstate, R_028080_CB_COLOR0_VIEW + i * 4, surf->cb_color_view); r600_pipe_state_add_reg_bo(rstate, R_0280E0_CB_COLOR0_FRAG + i * 4, - surf->cb_color_fmask, res, RADEON_USAGE_READWRITE); + surf->cb_color_fmask, surf->cb_buffer_fmask, + RADEON_USAGE_READWRITE); r600_pipe_state_add_reg_bo(rstate, R_0280C0_CB_COLOR0_TILE + i * 4, - surf->cb_color_cmask, res, RADEON_USAGE_READWRITE); + surf->cb_color_cmask, surf->cb_buffer_cmask, + RADEON_USAGE_READWRITE); r600_pipe_state_add_reg(rstate, R_028100_CB_COLOR0_MASK + i * 4, surf->cb_color_mask); @@ -1607,9 +1697,7 @@ static void r600_set_framebuffer_state(struct pipe_context *ctx, R_028208_PA_SC_WINDOW_SCISSOR_BR, br); /* If we're doing MSAA resolve... */ - if (state->nr_cbufs == 2 && - state->cbufs[0]->texture->nr_samples > 1 && - state->cbufs[1]->texture->nr_samples <= 1) { + if (is_resolve) { r600_pipe_state_add_reg(rstate, R_0287A0_CB_SHADER_CONTROL, 1); } else { /* Always enable the first colorbuffer in CB_SHADER_CONTROL. This @@ -2551,6 +2639,28 @@ void r600_fetch_shader(struct pipe_context *ctx, } void *r600_create_resolve_blend(struct r600_context *rctx) +{ + struct pipe_blend_state blend; + struct r600_pipe_state *rstate; + unsigned i; + + memset(&blend, 0, sizeof(blend)); + blend.independent_blend_enable = true; + for (i = 0; i < 2; i++) { + blend.rt[i].colormask = 0xf; + blend.rt[i].blend_enable = 1; + blend.rt[i].rgb_func = PIPE_BLEND_ADD; + blend.rt[i].alpha_func = PIPE_BLEND_ADD; + blend.rt[i].rgb_src_factor = PIPE_BLENDFACTOR_ZERO; + blend.rt[i].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO; + blend.rt[i].alpha_src_factor = PIPE_BLENDFACTOR_ZERO; + blend.rt[i].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO; + } + rstate = r600_create_blend_state_mode(&rctx->context, &blend, V_028808_SPECIAL_RESOLVE_BOX); + return rstate; +} + +void *r700_create_resolve_blend(struct r600_context *rctx) { struct pipe_blend_state blend; struct r600_pipe_state *rstate; diff --git a/src/gallium/drivers/r600/r600_texture.c b/src/gallium/drivers/r600/r600_texture.c index 0b2fdda53f7..6de3d6a8645 100644 --- a/src/gallium/drivers/r600/r600_texture.c +++ b/src/gallium/drivers/r600/r600_texture.c @@ -252,13 +252,15 @@ static const struct u_resource_vtbl r600_texture_vtbl = NULL /* transfer_inline_write */ }; -static void r600_texture_allocate_fmask(struct r600_screen *rscreen, - struct r600_texture *rtex) +/* The number of samples can be specified independently of the texture. */ +void r600_texture_get_fmask_info(struct r600_screen *rscreen, + struct r600_texture *rtex, + unsigned nr_samples, + struct r600_fmask_info *out) { /* FMASK is allocated pretty much like an ordinary texture. * Here we use bpe in the units of bits, not bytes. */ struct radeon_surface fmask = rtex->surface; - unsigned nr_samples = rtex->resource.b.b.nr_samples; switch (nr_samples) { case 2: @@ -297,10 +299,23 @@ static void r600_texture_allocate_fmask(struct r600_screen *rscreen, } assert(fmask.level[0].mode == RADEON_SURF_MODE_2D); + out->bank_height = fmask.bankh; + out->alignment = MAX2(256, fmask.bo_alignment); + out->size = (fmask.bo_size + 7) / 8; +} + +static void r600_texture_allocate_fmask(struct r600_screen *rscreen, + struct r600_texture *rtex) +{ + struct r600_fmask_info fmask; + + r600_texture_get_fmask_info(rscreen, rtex, + rtex->resource.b.b.nr_samples, &fmask); + /* Reserve space for FMASK while converting bits back to bytes. */ - rtex->fmask_bank_height = fmask.bankh; - rtex->fmask_offset = align(rtex->size, MAX2(256, fmask.bo_alignment)); - rtex->fmask_size = (fmask.bo_size + 7) / 8; + rtex->fmask_bank_height = fmask.bank_height; + rtex->fmask_offset = align(rtex->size, fmask.alignment); + rtex->fmask_size = fmask.size; rtex->size = rtex->fmask_offset + rtex->fmask_size; #if 0 printf("FMASK width=%u, height=%i, bits=%u, size=%u\n", @@ -308,8 +323,9 @@ static void r600_texture_allocate_fmask(struct r600_screen *rscreen, #endif } -static void r600_texture_allocate_cmask(struct r600_screen *rscreen, - struct r600_texture *rtex) +void r600_texture_get_cmask_info(struct r600_screen *rscreen, + struct r600_texture *rtex, + struct r600_cmask_info *out) { unsigned cmask_tile_width = 8; unsigned cmask_tile_height = 8; @@ -331,14 +347,25 @@ static void r600_texture_allocate_cmask(struct r600_screen *rscreen, unsigned base_align = num_pipes * pipe_interleave_bytes; unsigned slice_bytes = ((pitch_elements * height * element_bits + 7) / 8) / cmask_tile_elements; - unsigned size = rtex->surface.array_size * align(slice_bytes, base_align); assert(macro_tile_width % 128 == 0); assert(macro_tile_height % 128 == 0); - rtex->cmask_slice_tile_max = ((pitch_elements * height) / (128*128)) - 1; - rtex->cmask_offset = align(rtex->size, MAX2(256, base_align)); - rtex->cmask_size = size; + out->slice_tile_max = ((pitch_elements * height) / (128*128)) - 1; + out->alignment = MAX2(256, base_align); + out->size = rtex->surface.array_size * align(slice_bytes, base_align); +} + +static void r600_texture_allocate_cmask(struct r600_screen *rscreen, + struct r600_texture *rtex) +{ + struct r600_cmask_info cmask; + + r600_texture_get_cmask_info(rscreen, rtex, &cmask); + + rtex->cmask_slice_tile_max = cmask.slice_tile_max; + rtex->cmask_offset = align(rtex->size, cmask.alignment); + rtex->cmask_size = cmask.size; rtex->size = rtex->cmask_offset + rtex->cmask_size; #if 0 printf("CMASK: macro tile width = %u, macro tile height = %u, " @@ -479,6 +506,9 @@ static struct pipe_surface *r600_create_surface(struct pipe_context *pipe, static void r600_surface_destroy(struct pipe_context *pipe, struct pipe_surface *surface) { + struct r600_surface *surf = (struct r600_surface*)surface; + pipe_resource_reference((struct pipe_resource**)&surf->cb_buffer_fmask, NULL); + pipe_resource_reference((struct pipe_resource**)&surf->cb_buffer_cmask, NULL); pipe_resource_reference(&surface->texture, NULL); FREE(surface); } -- 2.30.2