[Mesa-dev] [PATCH 3/5] r600g: implement color resolve for r600

Marek Olšák maraeo at gmail.com
Sun Aug 26 19:00:33 PDT 2012


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 907ac96..88d67df 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 77adeae..7856e62 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 9cbf0c8..8c718a7 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 0c14a2d..1a91d5d 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 da60d37..a5a5404 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 3e5958a..e7ed239 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
@@ -2554,6 +2642,28 @@ 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;
 
 	memset(&blend, 0, sizeof(blend));
 	blend.independent_blend_enable = true;
diff --git a/src/gallium/drivers/r600/r600_texture.c b/src/gallium/drivers/r600/r600_texture.c
index 0b2fdda..6de3d6a 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);
 }
-- 
1.7.9.5



More information about the mesa-dev mailing list