xf86-video-intel: configure.ac src/sna/gen4_render.c src/sna/gen4_render.h src/sna/gen4_vertex.c src/sna/gen4_vertex.h src/sna/gen5_render.c src/sna/gen5_render.h src/sna/gen6_render.c src/sna/gen7_render.c src/sna/kgem.c src/sna/kgem_debug.c src/sna/kgem_debug_gen5.c src/sna/kgem_debug.h src/sna/kgem.h src/sna/Makefile.am src/sna/sna_render.c src/sna/sna_render.h src/sna/sna_render_inline.h src/sna/sna_trapezoids.c

Chris Wilson ickle at kemper.freedesktop.org
Thu Dec 20 11:15:08 PST 2012


 configure.ac                |    2 
 src/sna/Makefile.am         |    2 
 src/sna/gen4_render.c       |  803 +++++----------------------------------
 src/sna/gen4_render.h       |   17 
 src/sna/gen4_vertex.c       |  896 ++++++++++++++++++++++++++++++++++++++++++++
 src/sna/gen4_vertex.h       |   39 +
 src/sna/gen5_render.c       |  833 +++++-----------------------------------
 src/sna/gen5_render.h       |   17 
 src/sna/gen6_render.c       |  727 +----------------------------------
 src/sna/gen7_render.c       |  701 +---------------------------------
 src/sna/kgem.c              |    9 
 src/sna/kgem.h              |    7 
 src/sna/kgem_debug.c        |    9 
 src/sna/kgem_debug.h        |    2 
 src/sna/kgem_debug_gen5.c   |   21 -
 src/sna/sna_render.c        |    4 
 src/sna/sna_render.h        |   13 
 src/sna/sna_render_inline.h |   27 -
 src/sna/sna_trapezoids.c    |    2 
 19 files changed, 1297 insertions(+), 2834 deletions(-)

New commits:
commit 0f84ecfc3cd7dfe7f43ff99a6498d2ceccd90225
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Dec 20 12:00:00 2012 +0000

    sna/gen4+: Amalgamate all the gen4-7 vertex buffer emission
    
    Having reduced all the vb code for these generations to the same set of
    routines, we can refactor them into a single set of functions.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/configure.ac b/configure.ac
index 9354437..52db4e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -409,7 +409,7 @@ if test "x$UMS_ONLY" = xyes; then
 fi
 
 AM_CONDITIONAL(DEBUG, test x$DEBUG != xno)
-AM_CONDITIONAL(FULL_DEBUG, test x$FULL_DEBUG = xfull)
+AM_CONDITIONAL(FULL_DEBUG, test x$DEBUG = xfull)
 if test "x$DEBUG" = xno; then
 	AC_DEFINE(NDEBUG,1,[Disable internal debugging])
 fi
diff --git a/src/sna/Makefile.am b/src/sna/Makefile.am
index 306996b..8b654d7 100644
--- a/src/sna/Makefile.am
+++ b/src/sna/Makefile.am
@@ -73,6 +73,8 @@ libsna_la_SOURCES = \
 	gen3_render.h \
 	gen4_render.c \
 	gen4_render.h \
+	gen4_vertex.c \
+	gen4_vertex.h \
 	gen5_render.c \
 	gen5_render.h \
 	gen6_render.c \
diff --git a/src/sna/gen4_render.c b/src/sna/gen4_render.c
index 3f12062..8f4f1d4 100644
--- a/src/sna/gen4_render.c
+++ b/src/sna/gen4_render.c
@@ -42,6 +42,7 @@
 
 #include "brw/brw.h"
 #include "gen4_render.h"
+#include "gen4_vertex.h"
 
 /* gen4 has a serious issue with its shaders that we need to flush
  * after every rectangle... So until that is resolved, prefer
@@ -236,154 +237,6 @@ static void gen4_magic_ca_pass(struct sna *sna,
 	state->last_primitive = sna->kgem.nbatch;
 }
 
-static void gen4_vertex_flush(struct sna *sna)
-{
-	if (sna->render_state.gen4.vertex_offset == 0)
-		return;
-
-	DBG(("%s[%x] = %d\n", __FUNCTION__,
-	     4*sna->render_state.gen4.vertex_offset,
-	     sna->render.vertex_index - sna->render.vertex_start));
-	sna->kgem.batch[sna->render_state.gen4.vertex_offset] =
-		sna->render.vertex_index - sna->render.vertex_start;
-	sna->render_state.gen4.vertex_offset = 0;
-}
-
-static int gen4_vertex_finish(struct sna *sna)
-{
-	struct kgem_bo *bo;
-	unsigned int i;
-
-	assert(sna->render.vertex_used);
-	assert(sna->render.nvertex_reloc);
-
-	/* Note: we only need dword alignment (currently) */
-
-	bo = sna->render.vbo;
-	if (bo) {
-		gen4_vertex_flush(sna);
-
-		for (i = 0; i < sna->render.nvertex_reloc; i++) {
-			DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-			     i, sna->render.vertex_reloc[i]));
-
-			sna->kgem.batch[sna->render.vertex_reloc[i]] =
-				kgem_add_reloc(&sna->kgem,
-					       sna->render.vertex_reloc[i], bo,
-					       I915_GEM_DOMAIN_VERTEX << 16,
-					       0);
-		}
-
-		sna->render.vbo = NULL;
-		sna->render.nvertex_reloc = 0;
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-		sna->render_state.gen4.vb_id = 0;
-
-		kgem_bo_destroy(&sna->kgem, bo);
-	}
-
-	sna->render.vertices = NULL;
-	sna->render.vbo = kgem_create_linear(&sna->kgem,
-					     256*1024, CREATE_GTT_MAP);
-	if (sna->render.vbo)
-		sna->render.vertices = kgem_bo_map(&sna->kgem, sna->render.vbo);
-	if (sna->render.vertices == NULL) {
-		if (sna->render.vbo)
-			kgem_bo_destroy(&sna->kgem, sna->render.vbo);
-		sna->render.vbo = NULL;
-		return 0;
-	}
-
-	if (sna->render.vertex_used) {
-		memcpy(sna->render.vertices,
-		       sna->render.vertex_data,
-		       sizeof(float)*sna->render.vertex_used);
-	}
-	sna->render.vertex_size = 64 * 1024 - 1;
-	return sna->render.vertex_size - sna->render.vertex_used;
-}
-
-static void gen4_vertex_close(struct sna *sna)
-{
-	struct kgem_bo *bo, *free_bo = NULL;
-	unsigned int i, delta = 0;
-
-	assert(sna->render_state.gen4.vertex_offset == 0);
-	if (!sna->render_state.gen4.vb_id)
-		return;
-
-	DBG(("%s: used=%d, vbo active? %d\n",
-	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo != NULL));
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render.vertex_size - sna->render.vertex_used < 64) {
-			DBG(("%s: discarding full vbo\n", __FUNCTION__));
-			sna->render.vbo = NULL;
-			sna->render.vertices = sna->render.vertex_data;
-			sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
-			free_bo = bo;
-		} else if (IS_CPU_MAP(bo->map)) {
-			DBG(("%s: converting CPU map to GTT\n", __FUNCTION__));
-			sna->render.vertices =
-				kgem_bo_map__gtt(&sna->kgem, sna->render.vbo);
-			if (sna->render.vertices == NULL) {
-				sna->render.vbo = NULL;
-				sna->render.vertices = sna->render.vertex_data;
-				sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
-				free_bo = bo;
-			}
-		}
-	} else {
-		if (sna->kgem.nbatch + sna->render.vertex_used <= sna->kgem.surface) {
-			DBG(("%s: copy to batch: %d @ %d\n", __FUNCTION__,
-			     sna->render.vertex_used, sna->kgem.nbatch));
-			memcpy(sna->kgem.batch + sna->kgem.nbatch,
-			       sna->render.vertex_data,
-			       sna->render.vertex_used * 4);
-			delta = sna->kgem.nbatch * 4;
-			bo = NULL;
-			sna->kgem.nbatch += sna->render.vertex_used;
-		} else {
-			bo = kgem_create_linear(&sna->kgem,
-						4*sna->render.vertex_used,
-						CREATE_NO_THROTTLE);
-			if (bo && !kgem_bo_write(&sna->kgem, bo,
-						 sna->render.vertex_data,
-						 4*sna->render.vertex_used)) {
-				kgem_bo_destroy(&sna->kgem, bo);
-				bo = NULL;
-			}
-			DBG(("%s: new vbo: %d\n", __FUNCTION__,
-			     sna->render.vertex_used));
-			free_bo = bo;
-		}
-	}
-
-	assert(sna->render.nvertex_reloc);
-	for (i = 0; i < sna->render.nvertex_reloc; i++) {
-		DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-		     i, sna->render.vertex_reloc[i]));
-
-		sna->kgem.batch[sna->render.vertex_reloc[i]] =
-			kgem_add_reloc(&sna->kgem,
-				       sna->render.vertex_reloc[i], bo,
-				       I915_GEM_DOMAIN_VERTEX << 16,
-				       delta);
-	}
-	sna->render.nvertex_reloc = 0;
-
-	if (sna->render.vbo == NULL) {
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-	}
-
-	if (free_bo)
-		kgem_bo_destroy(&sna->kgem, free_bo);
-}
-
-
 static uint32_t gen4_get_blend(int op,
 			       bool has_component_alpha,
 			       uint32_t dst_format)
@@ -674,345 +527,6 @@ gen4_bind_bo(struct sna *sna,
 	return offset * sizeof(uint32_t);
 }
 
-fastcall static void
-gen4_emit_composite_primitive_solid(struct sna *sna,
-				    const struct sna_composite_op *op,
-				    const struct sna_composite_rectangles *r)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = 1.;
-	v[2] = 1.;
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	v[4] = 0.;
-	v[5] = 1.;
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	v[7] = 0.;
-	v[8] = 0.;
-}
-
-fastcall static void
-gen4_emit_composite_primitive_identity_source(struct sna *sna,
-					      const struct sna_composite_op *op,
-					      const struct sna_composite_rectangles *r)
-{
-	const float *sf = op->src.scale;
-	float sx, sy, *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	sx = r->src.x + op->src.offset[0];
-	sy = r->src.y + op->src.offset[1];
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = (sx + r->width) * sf[0];
-	v[2] = (sy + r->height) * sf[1];
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	v[4] = sx * sf[0];
-	v[5] = v[2];
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	v[7] = v[4];
-	v[8] = sy * sf[1];
-}
-
-fastcall static void
-gen4_emit_composite_primitive_affine_source(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float *v;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x + r->width,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[1], &v[2]);
-	v[1] *= op->src.scale[0];
-	v[2] *= op->src.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[4], &v[5]);
-	v[4] *= op->src.scale[0];
-	v[5] *= op->src.scale[1];
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y,
-					 op->src.transform,
-					 &v[7], &v[8]);
-	v[7] *= op->src.scale[0];
-	v[8] *= op->src.scale[1];
-}
-
-fastcall static void
-gen4_emit_composite_primitive_identity_mask(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[9] = v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[13] = v[8] = msk_x * op->mask.scale[0];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[14] = msk_y * op->mask.scale[1];
-
-	v[7] = v[2] = v[1] = 1;
-	v[12] = v[11] = v[6] = 0;
-}
-
-fastcall static void
-gen4_emit_composite_primitive_identity_source_mask(struct sna *sna,
-						   const struct sna_composite_op *op,
-						   const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float src_x, src_y;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	src_x = r->src.x + op->src.offset[0];
-	src_y = r->src.y + op->src.offset[1];
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = (src_x + w) * op->src.scale[0];
-	v[2] = (src_y + h) * op->src.scale[1];
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[6] = src_x * op->src.scale[0];
-	v[7] = v[2];
-	v[8] = msk_x * op->mask.scale[0];
-	v[9] = v[4];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[11] = v[6];
-	v[12] = src_y * op->src.scale[1];
-	v[13] = v[8];
-	v[14] = msk_y * op->mask.scale[1];
-}
-
-fastcall static void
-gen4_emit_composite_primitive(struct sna *sna,
-			      const struct sna_composite_op *op,
-			      const struct sna_composite_rectangles *r)
-{
-	float src_x[3], src_y[3], src_w[3], mask_x[3], mask_y[3], mask_w[3];
-	bool is_affine = op->is_affine;
-	const float *src_sf = op->src.scale;
-	const float *mask_sf = op->mask.scale;
-	bool has_mask = op->u.gen4.ve_id & 2;
-
-	if (op->src.is_solid) {
-		src_x[0] = 0;
-		src_y[0] = 0;
-		src_x[1] = 0;
-		src_y[1] = 1;
-		src_x[2] = 1;
-		src_y[2] = 1;
-		src_w[0] = src_w[1] = src_w[2] = 1;
-	} else if (is_affine) {
-		sna_get_transformed_coordinates(r->src.x + op->src.offset[0],
-						r->src.y + op->src.offset[1],
-						op->src.transform,
-						&src_x[0],
-						&src_y[0]);
-
-		sna_get_transformed_coordinates(r->src.x + op->src.offset[0],
-						r->src.y + op->src.offset[1] + r->height,
-						op->src.transform,
-						&src_x[1],
-						&src_y[1]);
-
-		sna_get_transformed_coordinates(r->src.x + op->src.offset[0] + r->width,
-						r->src.y + op->src.offset[1] + r->height,
-						op->src.transform,
-						&src_x[2],
-						&src_y[2]);
-	} else {
-		sna_get_transformed_coordinates_3d(r->src.x + op->src.offset[0],
-						   r->src.y + op->src.offset[1],
-						   op->src.transform,
-						   &src_x[0],
-						   &src_y[0],
-						   &src_w[0]);
-		sna_get_transformed_coordinates_3d(r->src.x + op->src.offset[0],
-						   r->src.y + op->src.offset[1] + r->height,
-						   op->src.transform,
-						   &src_x[1],
-						   &src_y[1],
-						   &src_w[1]);
-		sna_get_transformed_coordinates_3d(r->src.x + op->src.offset[0] + r->width,
-						   r->src.y + op->src.offset[1] + r->height,
-						   op->src.transform,
-						   &src_x[2],
-						   &src_y[2],
-						   &src_w[2]);
-	}
-
-	if (has_mask) {
-		if (op->mask.is_solid) {
-			mask_x[0] = 0;
-			mask_y[0] = 0;
-			mask_x[1] = 0;
-			mask_y[1] = 1;
-			mask_x[2] = 1;
-			mask_y[2] = 1;
-			mask_w[0] = mask_w[1] = mask_w[2] = 1;
-		} else if (is_affine) {
-			sna_get_transformed_coordinates(r->mask.x + op->mask.offset[0],
-							r->mask.y + op->mask.offset[1],
-							op->mask.transform,
-							&mask_x[0],
-							&mask_y[0]);
-
-			sna_get_transformed_coordinates(r->mask.x + op->mask.offset[0],
-							r->mask.y + op->mask.offset[1] + r->height,
-							op->mask.transform,
-							&mask_x[1],
-							&mask_y[1]);
-
-			sna_get_transformed_coordinates(r->mask.x + op->mask.offset[0] + r->width,
-							r->mask.y + op->mask.offset[1] + r->height,
-							op->mask.transform,
-							&mask_x[2],
-							&mask_y[2]);
-		} else {
-			sna_get_transformed_coordinates_3d(r->mask.x + op->mask.offset[0],
-							   r->mask.y + op->mask.offset[1],
-							   op->mask.transform,
-							   &mask_x[0],
-							   &mask_y[0],
-							   &mask_w[0]);
-			sna_get_transformed_coordinates_3d(r->mask.x + op->mask.offset[0],
-							   r->mask.y + op->mask.offset[1] + r->height,
-							   op->mask.transform,
-							   &mask_x[1],
-							   &mask_y[1],
-							   &mask_w[1]);
-			sna_get_transformed_coordinates_3d(r->mask.x + op->mask.offset[0] + r->width,
-							   r->mask.y + op->mask.offset[1] + r->height,
-							   op->mask.transform,
-							   &mask_x[2],
-							   &mask_y[2],
-							   &mask_w[2]);
-		}
-	}
-
-	OUT_VERTEX(r->dst.x + r->width, r->dst.y + r->height);
-	OUT_VERTEX_F(src_x[2] * src_sf[0]);
-	OUT_VERTEX_F(src_y[2] * src_sf[1]);
-	if (!is_affine)
-		OUT_VERTEX_F(src_w[2]);
-	if (has_mask) {
-		OUT_VERTEX_F(mask_x[2] * mask_sf[0]);
-		OUT_VERTEX_F(mask_y[2] * mask_sf[1]);
-		if (!is_affine)
-			OUT_VERTEX_F(mask_w[2]);
-	}
-
-	OUT_VERTEX(r->dst.x, r->dst.y + r->height);
-	OUT_VERTEX_F(src_x[1] * src_sf[0]);
-	OUT_VERTEX_F(src_y[1] * src_sf[1]);
-	if (!is_affine)
-		OUT_VERTEX_F(src_w[1]);
-	if (has_mask) {
-		OUT_VERTEX_F(mask_x[1] * mask_sf[0]);
-		OUT_VERTEX_F(mask_y[1] * mask_sf[1]);
-		if (!is_affine)
-			OUT_VERTEX_F(mask_w[1]);
-	}
-
-	OUT_VERTEX(r->dst.x, r->dst.y);
-	OUT_VERTEX_F(src_x[0] * src_sf[0]);
-	OUT_VERTEX_F(src_y[0] * src_sf[1]);
-	if (!is_affine)
-		OUT_VERTEX_F(src_w[0]);
-	if (has_mask) {
-		OUT_VERTEX_F(mask_x[0] * mask_sf[0]);
-		OUT_VERTEX_F(mask_y[0] * mask_sf[1]);
-		if (!is_affine)
-			OUT_VERTEX_F(mask_w[0]);
-	}
-}
-
 static void gen4_emit_vertex_buffer(struct sna *sna,
 				    const struct sna_composite_op *op)
 {
@@ -1026,13 +540,13 @@ static void gen4_emit_vertex_buffer(struct sna *sna,
 	OUT_BATCH(0);
 	OUT_BATCH(0);
 
-	sna->render_state.gen4.vb_id |= 1 << id;
+	sna->render.vb_id |= 1 << id;
 }
 
 static void gen4_emit_primitive(struct sna *sna)
 {
 	if (sna->kgem.nbatch == sna->render_state.gen4.last_primitive) {
-		sna->render_state.gen4.vertex_offset = sna->kgem.nbatch - 5;
+		sna->render.vertex_offset = sna->kgem.nbatch - 5;
 		return;
 	}
 
@@ -1041,7 +555,7 @@ static void gen4_emit_primitive(struct sna *sna)
 		  (_3DPRIM_RECTLIST << GEN4_3DPRIMITIVE_TOPOLOGY_SHIFT) |
 		  (0 << 9) |
 		  4);
-	sna->render_state.gen4.vertex_offset = sna->kgem.nbatch;
+	sna->render.vertex_offset = sna->kgem.nbatch;
 	OUT_BATCH(0);	/* vertex count, to be filled in later */
 	OUT_BATCH(sna->render.vertex_index);
 	OUT_BATCH(1);	/* single instance */
@@ -1060,15 +574,15 @@ static bool gen4_rectangle_begin(struct sna *sna,
 
 	/* 7xpipelined pointers + 6xprimitive + 1xflush */
 	ndwords = op->need_magic_ca_pass? 20 : 6;
-	if ((sna->render_state.gen4.vb_id & (1 << id)) == 0)
+	if ((sna->render.vb_id & (1 << id)) == 0)
 		ndwords += 5;
 
 	if (!kgem_check_batch(&sna->kgem, ndwords))
 		return false;
 
-	if ((sna->render_state.gen4.vb_id & (1 << id)) == 0)
+	if ((sna->render.vb_id & (1 << id)) == 0)
 		gen4_emit_vertex_buffer(sna, op);
-	if (sna->render_state.gen4.vertex_offset == 0)
+	if (sna->render.vertex_offset == 0)
 		gen4_emit_primitive(sna);
 
 	return true;
@@ -1105,7 +619,7 @@ start:
 			goto flush;
 	}
 
-	if (unlikely(sna->render_state.gen4.vertex_offset == 0 &&
+	if (unlikely(sna->render.vertex_offset == 0 &&
 		     !gen4_rectangle_begin(sna, op)))
 		goto flush;
 
@@ -1116,7 +630,7 @@ start:
 	return want;
 
 flush:
-	if (sna->render_state.gen4.vertex_offset) {
+	if (sna->render.vertex_offset) {
 		gen4_vertex_flush(sna);
 		gen4_magic_ca_pass(sna, op);
 	}
@@ -1346,26 +860,14 @@ gen4_emit_vertex_elements(struct sna *sna,
 	 *    texture coordinate 1 if (has_mask is true): same as above
 	 */
 	struct gen4_render_state *render = &sna->render_state.gen4;
+	uint32_t src_format, dw;
+	int src_offset, dst_offset;
 	int id = op->u.gen4.ve_id;
-	uint32_t w_component;
-	uint32_t src_format;
-	int selem;
 
 	if (render->ve_id == id)
 		return;
-
 	render->ve_id = id;
 
-	if (id & 1) {
-		src_format = GEN4_SURFACEFORMAT_R32G32_FLOAT;
-		w_component = GEN4_VFCOMPONENT_STORE_1_FLT;
-		selem = 2;
-	} else {
-		src_format = GEN4_SURFACEFORMAT_R32G32B32_FLOAT;
-		w_component = GEN4_VFCOMPONENT_STORE_SRC;
-		selem = 3;
-	}
-
 	/* The VUE layout
 	 *    dword 0-3: position (x, y, 1.0, 1.0),
 	 *    dword 4-7: texture coordinate 0 (u0, v0, w0, 1.0)
@@ -1376,39 +878,89 @@ gen4_emit_vertex_elements(struct sna *sna,
 	/* x,y */
 	OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
 		  GEN4_SURFACEFORMAT_R16G16_SSCALED << VE0_FORMAT_SHIFT |
-		  0 << VE0_OFFSET_SHIFT); /* offsets vb in bytes */
-	OUT_BATCH(GEN4_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT |
-		  GEN4_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT |
-		  GEN4_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT |
-		  GEN4_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT |
-		  (1*4) << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);       /* VUE offset in dwords */
+		  0 << VE0_OFFSET_SHIFT);
+	OUT_BATCH(VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT |
+		  VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT |
+		  VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT |
+		  VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT |
+		  (1*4) << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);
+	src_offset = 4;
+	dst_offset = 8;
 
 	/* u0, v0, w0 */
+	/* u0, v0, w0 */
+	DBG(("%s: first channel %d floats, offset=%d\n", __FUNCTION__,
+	     id & 3, src_offset));
+	dw = VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT;
+	switch (id & 3) {
+	case 1:
+		src_format = GEN4_SURFACEFORMAT_R32_FLOAT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+		dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT;
+		dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT;
+		break;
+	default:
+		assert(0);
+	case 2:
+		src_format = GEN4_SURFACEFORMAT_R32G32_FLOAT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+		dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT;
+		break;
+	case 3:
+		src_format = GEN4_SURFACEFORMAT_R32G32B32_FLOAT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_2_SHIFT;
+		break;
+	}
 	OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
 		  src_format << VE0_FORMAT_SHIFT |
-		  4 << VE0_OFFSET_SHIFT);	/* offset vb in bytes */
-	OUT_BATCH(GEN4_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT |
-		  GEN4_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT |
-		  w_component << VE1_VFCOMPONENT_2_SHIFT |
-		  GEN4_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT |
-		  (2*4) << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);       /* VUE offset in dwords */
+		  src_offset << VE0_OFFSET_SHIFT);
+	OUT_BATCH(dw | dst_offset << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);
+	src_offset += (id & 3) * sizeof(float);
+	dst_offset += 4;
 
 	/* u1, v1, w1 */
-	OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
-		  src_format << VE0_FORMAT_SHIFT |
-		  ((1 + selem) * 4) << VE0_OFFSET_SHIFT); /* vb offset in bytes */
-	if (id & 2) {
-		OUT_BATCH(GEN4_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT |
-			  GEN4_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT |
-			  w_component << VE1_VFCOMPONENT_2_SHIFT |
-			  GEN4_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT |
-			  (3*4) << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);       /* VUE offset in dwords */
+	if (id >> 2) {
+		DBG(("%s: second channel %d floats, offset=%d\n", __FUNCTION__,
+		     (id >> 2) & 3, src_offset));
+		dw = VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT;
+		switch ((id >> 2) & 3) {
+		case 1:
+			src_format = GEN4_SURFACEFORMAT_R32_FLOAT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+			dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT;
+			dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT;
+			break;
+		default:
+			assert(0);
+		case 2:
+			src_format = GEN4_SURFACEFORMAT_R32G32_FLOAT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+			dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT;
+			break;
+		case 3:
+			src_format = GEN4_SURFACEFORMAT_R32G32B32_FLOAT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_2_SHIFT;
+			break;
+		}
+		OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
+			  src_format << VE0_FORMAT_SHIFT |
+			  src_offset << VE0_OFFSET_SHIFT);
+		OUT_BATCH(dw | dst_offset << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);
 	} else {
-		OUT_BATCH(GEN4_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_0_SHIFT |
-			  GEN4_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT |
-			  GEN4_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT |
-			  GEN4_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT |
-			  (3*4) << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);       /* VUE offset in dwords */
+		OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
+			  GEN4_SURFACEFORMAT_R16G16_SSCALED << VE0_FORMAT_SHIFT |
+			  0 << VE0_OFFSET_SHIFT);
+		OUT_BATCH(VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_0_SHIFT |
+			  VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT |
+			  VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT |
+			  VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT |
+			  dst_offset << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT);
 	}
 }
 
@@ -1701,7 +1253,7 @@ gen4_render_video(struct sna *sna,
 	tmp.mask.bo = NULL;
 	tmp.u.gen4.wm_kernel =
 		is_planar_fourcc(frame->id) ? WM_KERNEL_VIDEO_PLANAR : WM_KERNEL_VIDEO_PACKED;
-	tmp.u.gen4.ve_id = 1;
+	tmp.u.gen4.ve_id = 2;
 	tmp.is_affine = true;
 	tmp.floats_per_vertex = 3;
 	tmp.floats_per_rect = 9;
@@ -2014,7 +1566,7 @@ gen4_render_composite_done(struct sna *sna,
 {
 	DBG(("%s()\n", __FUNCTION__));
 
-	if (sna->render_state.gen4.vertex_offset) {
+	if (sna->render.vertex_offset) {
 		gen4_vertex_flush(sna);
 		gen4_magic_ca_pass(sna, op);
 	}
@@ -2382,7 +1934,6 @@ gen4_render_composite(struct sna *sna,
 	tmp->need_magic_ca_pass = false;
 	tmp->u.gen4.sf = 0;
 
-	tmp->prim_emit = gen4_emit_composite_primitive;
 	if (mask) {
 		if (mask->componentAlpha && PICT_FORMAT_RGB(mask->format)) {
 			tmp->has_component_alpha = true;
@@ -2427,35 +1978,19 @@ gen4_render_composite(struct sna *sna,
 		}
 
 		tmp->is_affine &= tmp->mask.is_affine;
-
-		if (tmp->src.transform == NULL && tmp->mask.transform == NULL) {
-			if (tmp->src.is_solid)
-				tmp->prim_emit = gen4_emit_composite_primitive_identity_mask;
-			else
-				tmp->prim_emit = gen4_emit_composite_primitive_identity_source_mask;
-		}
-
-		tmp->floats_per_vertex = 5 + 2 * !tmp->is_affine;
-	} else {
-		if (tmp->src.is_solid) {
-			tmp->prim_emit = gen4_emit_composite_primitive_solid;
-		} else if (tmp->src.transform == NULL) {
-			tmp->prim_emit = gen4_emit_composite_primitive_identity_source;
-			/* XXX using more then one thread causes corruption? */
-			tmp->u.gen4.sf = 1;
-		} else if (tmp->src.is_affine)
-			tmp->prim_emit = gen4_emit_composite_primitive_affine_source;
-
-		tmp->floats_per_vertex = 3 + !tmp->is_affine;
 	}
-	tmp->floats_per_rect = 3*tmp->floats_per_vertex;
+	gen4_choose_composite_emitter(tmp);
+
+	if (tmp->mask.bo == NULL && tmp->src.transform == NULL)
+		/* XXX using more then one thread causes corruption? */
+		tmp->u.gen4.sf = 1;
 
 	tmp->u.gen4.wm_kernel =
 		gen4_choose_composite_kernel(tmp->op,
 					     tmp->mask.bo != NULL,
 					     tmp->has_component_alpha,
 					     tmp->is_affine);
-	tmp->u.gen4.ve_id = (tmp->mask.bo != NULL) << 1 | tmp->is_affine;
+	tmp->u.gen4.ve_id = gen4_choose_composite_vertex_buffer(tmp);
 
 	tmp->blt   = gen4_render_composite_blt;
 	tmp->box   = gen4_render_composite_box;
@@ -2490,122 +2025,6 @@ cleanup_dst:
 
 /* A poor man's span interface. But better than nothing? */
 #if !NO_COMPOSITE_SPANS
-inline static void
-gen4_emit_composite_texcoord(struct sna *sna,
-			     const struct sna_composite_channel *channel,
-			     int16_t x, int16_t y)
-{
-	float t[3];
-
-	if (channel->is_affine) {
-		sna_get_transformed_coordinates(x + channel->offset[0],
-						y + channel->offset[1],
-						channel->transform,
-						&t[0], &t[1]);
-		OUT_VERTEX_F(t[0] * channel->scale[0]);
-		OUT_VERTEX_F(t[1] * channel->scale[1]);
-	} else {
-		t[0] = t[1] = 0; t[2] = 1;
-		sna_get_transformed_coordinates_3d(x + channel->offset[0],
-						   y + channel->offset[1],
-						   channel->transform,
-						   &t[0], &t[1], &t[2]);
-		OUT_VERTEX_F(t[0] * channel->scale[0]);
-		OUT_VERTEX_F(t[1] * channel->scale[1]);
-		OUT_VERTEX_F(t[2]);
-	}
-}
-
-inline static void
-gen4_emit_composite_texcoord_affine(struct sna *sna,
-				    const struct sna_composite_channel *channel,
-				    int16_t x, int16_t y)
-{
-	float t[2];
-
-	sna_get_transformed_coordinates(x + channel->offset[0],
-					y + channel->offset[1],
-					channel->transform,
-					&t[0], &t[1]);
-	OUT_VERTEX_F(t[0] * channel->scale[0]);
-	OUT_VERTEX_F(t[1] * channel->scale[1]);
-}
-
-inline static void
-gen4_emit_composite_spans_vertex(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 int16_t x, int16_t y)
-{
-	OUT_VERTEX(x, y);
-	gen4_emit_composite_texcoord(sna, &op->base.src, x, y);
-}
-
-fastcall static void
-gen4_emit_composite_spans_primitive(struct sna *sna,
-				    const struct sna_composite_spans_op *op,
-				    const BoxRec *box,
-				    float opacity)
-{
-	gen4_emit_composite_spans_vertex(sna, op, box->x2, box->y2);
-	OUT_VERTEX_F(opacity);
-	OUT_VERTEX_F(1);
-	if (!op->base.is_affine)
-		OUT_VERTEX_F(1);
-
-	gen4_emit_composite_spans_vertex(sna, op, box->x1, box->y2);
-	OUT_VERTEX_F(opacity);
-	OUT_VERTEX_F(1);
-	if (!op->base.is_affine)
-		OUT_VERTEX_F(1);
-
-	gen4_emit_composite_spans_vertex(sna, op, box->x1, box->y1);
-	OUT_VERTEX_F(opacity);
-	OUT_VERTEX_F(0);
-	if (!op->base.is_affine)
-		OUT_VERTEX_F(1);
-}
-
-fastcall static void
-gen4_emit_composite_spans_solid(struct sna *sna,
-				const struct sna_composite_spans_op *op,
-				const BoxRec *box,
-				float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	OUT_VERTEX_F(1); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y2);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y1);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(0);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(0);
-}
-
-fastcall static void
-gen4_emit_composite_spans_affine(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 const BoxRec *box,
-				 float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	gen4_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x2, box->y2);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y2);
-	gen4_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y2);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y1);
-	gen4_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y1);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(0);
-}
-
 fastcall static void
 gen4_render_composite_spans_box(struct sna *sna,
 				const struct sna_composite_spans_op *op,
@@ -2758,23 +2177,11 @@ gen4_render_composite_spans(struct sna *sna,
 	tmp->base.has_component_alpha = false;
 	tmp->base.need_magic_ca_pass = false;
 
-	tmp->base.u.gen4.sf = 1;
-	if (tmp->base.src.is_solid) {
-		DBG(("%s: using solid fast emitter\n", __FUNCTION__));
-		tmp->prim_emit = gen4_emit_composite_spans_solid;
-		tmp->base.u.gen4.sf = 0;
-	} else if (tmp->base.is_affine) {
-		DBG(("%s: using affine fast emitter\n", __FUNCTION__));
-		tmp->prim_emit = gen4_emit_composite_spans_affine;
-	} else {
-		DBG(("%s: using general emitter\n", __FUNCTION__));
-		tmp->prim_emit = gen4_emit_composite_spans_primitive;
-	}
-	tmp->base.floats_per_vertex = 5 + 2*!tmp->base.is_affine;
-	tmp->base.floats_per_rect = 3 * tmp->base.floats_per_vertex;
+	tmp->base.u.gen4.sf = !tmp->base.src.is_solid;
+	gen4_choose_spans_emitter(tmp);
 
 	tmp->base.u.gen4.wm_kernel = WM_KERNEL_OPACITY | !tmp->base.is_affine;
-	tmp->base.u.gen4.ve_id = 1 << 1 | tmp->base.is_affine;
+	tmp->base.u.gen4.ve_id = gen4_choose_spans_vertex_buffer(&tmp->base);
 
 	tmp->box   = gen4_render_composite_spans_box;
 	tmp->boxes = gen4_render_composite_spans_boxes;
@@ -2984,7 +2391,7 @@ fallback_blt:
 	tmp.floats_per_vertex = 3;
 	tmp.floats_per_rect = 9;
 	tmp.u.gen4.wm_kernel = WM_KERNEL;
-	tmp.u.gen4.ve_id = 1;
+	tmp.u.gen4.ve_id = 2;
 	tmp.u.gen4.sf = 0;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, src_bo, NULL)) {
@@ -3117,7 +2524,7 @@ fallback:
 	op->base.floats_per_vertex = 3;
 	op->base.floats_per_rect = 9;
 	op->base.u.gen4.wm_kernel = WM_KERNEL;
-	op->base.u.gen4.ve_id = 1;
+	op->base.u.gen4.ve_id = 2;
 	op->base.u.gen4.sf = 0;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, src_bo, NULL)) {
@@ -3240,7 +2647,7 @@ gen4_render_fill_boxes(struct sna *sna,
 	tmp.floats_per_vertex = 3;
 	tmp.floats_per_rect = 9;
 	tmp.u.gen4.wm_kernel = WM_KERNEL;
-	tmp.u.gen4.ve_id = 1;
+	tmp.u.gen4.ve_id = 2;
 	tmp.u.gen4.sf = 0;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) {
@@ -3346,7 +2753,7 @@ gen4_render_fill(struct sna *sna, uint8_t alu,
 	op->base.floats_per_vertex = 3;
 	op->base.floats_per_rect = 9;
 	op->base.u.gen4.wm_kernel = WM_KERNEL;
-	op->base.u.gen4.ve_id = 1;
+	op->base.u.gen4.ve_id = 2;
 	op->base.u.gen4.sf = 0;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) {
@@ -3426,7 +2833,7 @@ gen4_render_fill_one(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo,
 	tmp.need_magic_ca_pass = false;
 
 	tmp.u.gen4.wm_kernel = WM_KERNEL;
-	tmp.u.gen4.ve_id = 1;
+	tmp.u.gen4.ve_id = 2;
 	tmp.u.gen4.sf = 0;
 
 	if (!kgem_check_bo(&sna->kgem, bo, NULL)) {
@@ -3449,6 +2856,9 @@ static void
 gen4_render_flush(struct sna *sna)
 {
 	gen4_vertex_close(sna);
+
+	assert(sna->render.vb_id == 0);
+	assert(sna->render.vertex_offset == 0);
 }
 
 static void
@@ -3491,7 +2901,6 @@ static void gen4_render_reset(struct sna *sna)
 {
 	sna->render_state.gen4.needs_invariant = true;
 	sna->render_state.gen4.needs_urb = true;
-	sna->render_state.gen4.vb_id = 0;
 	sna->render_state.gen4.ve_id = -1;
 	sna->render_state.gen4.last_primitive = -1;
 	sna->render_state.gen4.last_pipelined_pointers = -1;
diff --git a/src/sna/gen4_render.h b/src/sna/gen4_render.h
index 49d232e..2eae1ec 100644
--- a/src/sna/gen4_render.h
+++ b/src/sna/gen4_render.h
@@ -661,15 +661,14 @@
 #define GEN4_VERTEXBUFFER_ACCESS_VERTEXDATA     0
 #define GEN4_VERTEXBUFFER_ACCESS_INSTANCEDATA   1
 
-#define GEN4_VFCOMPONENT_NOSTORE      0
-#define GEN4_VFCOMPONENT_STORE_SRC    1
-#define GEN4_VFCOMPONENT_STORE_0      2
-#define GEN4_VFCOMPONENT_STORE_1_FLT  3
-#define GEN4_VFCOMPONENT_STORE_1_INT  4
-#define GEN4_VFCOMPONENT_STORE_VID    5
-#define GEN4_VFCOMPONENT_STORE_IID    6
-#define GEN4_VFCOMPONENT_STORE_PID    7
-
+#define VFCOMPONENT_NOSTORE      0
+#define VFCOMPONENT_STORE_SRC    1
+#define VFCOMPONENT_STORE_0      2
+#define VFCOMPONENT_STORE_1_FLT  3
+#define VFCOMPONENT_STORE_1_INT  4
+#define VFCOMPONENT_STORE_VID    5
+#define VFCOMPONENT_STORE_IID    6
+#define VFCOMPONENT_STORE_PID    7
 
 
 /* Execution Unit (EU) defines
diff --git a/src/sna/gen4_vertex.c b/src/sna/gen4_vertex.c
new file mode 100644
index 0000000..b302233
--- /dev/null
+++ b/src/sna/gen4_vertex.c
@@ -0,0 +1,896 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Chris Wilson <chris at chris-wilson.co.uk>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sna.h"
+#include "sna_render.h"
+#include "sna_render_inline.h"
+#include "gen4_vertex.h"
+
+void gen4_vertex_flush(struct sna *sna)
+{
+	assert(sna->render.vertex_offset);
+	assert(sna->render.vertex_index > sna->render.vertex_start);
+
+	DBG(("%s[%x] = %d\n", __FUNCTION__,
+	     4*sna->render.vertex_offset,
+	     sna->render.vertex_index - sna->render.vertex_start));
+	sna->kgem.batch[sna->render.vertex_offset] =
+		sna->render.vertex_index - sna->render.vertex_start;
+	sna->render.vertex_offset = 0;
+}
+
+int gen4_vertex_finish(struct sna *sna)
+{
+	struct kgem_bo *bo;
+	unsigned int i;
+	unsigned hint, size;
+
+	DBG(("%s: used=%d / %d\n", __FUNCTION__,
+	     sna->render.vertex_used, sna->render.vertex_size));
+	assert(sna->render.vertex_used);
+	assert(sna->render.nvertex_reloc);
+
+	/* Note: we only need dword alignment (currently) */
+
+	bo = sna->render.vbo;
+	if (bo) {
+		if (sna->render.vertex_offset)
+			gen4_vertex_flush(sna);
+
+		for (i = 0; i < sna->render.nvertex_reloc; i++) {
+			DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
+			     i, sna->render.vertex_reloc[i]));
+
+			sna->kgem.batch[sna->render.vertex_reloc[i]] =
+				kgem_add_reloc(&sna->kgem,
+					       sna->render.vertex_reloc[i], bo,
+					       I915_GEM_DOMAIN_VERTEX << 16,
+					       0);
+		}
+
+		sna->render.nvertex_reloc = 0;
+		sna->render.vertex_used = 0;
+		sna->render.vertex_index = 0;
+		sna->render.vbo = NULL;
+		sna->render.vb_id = 0;
+
+		kgem_bo_destroy(&sna->kgem, bo);
+	}
+
+	hint = CREATE_GTT_MAP;
+	if (bo)
+		hint |= CREATE_CACHED | CREATE_NO_THROTTLE;
+
+	size = 256*1024;
+	sna->render.vertices = NULL;
+	sna->render.vbo = kgem_create_linear(&sna->kgem, size, hint);
+	while (sna->render.vbo == NULL && size > 16*1024) {
+		size /= 2;
+		sna->render.vbo = kgem_create_linear(&sna->kgem, size, hint);
+	}
+	if (sna->render.vbo == NULL)
+		sna->render.vbo = kgem_create_linear(&sna->kgem,
+						     256*1024, CREATE_GTT_MAP);
+	if (sna->render.vbo)
+		sna->render.vertices = kgem_bo_map(&sna->kgem, sna->render.vbo);
+	if (sna->render.vertices == NULL) {
+		if (sna->render.vbo) {
+			kgem_bo_destroy(&sna->kgem, sna->render.vbo);
+			sna->render.vbo = NULL;
+		}
+		sna->render.vertices = sna->render.vertex_data;
+		sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
+		return 0;
+	}
+
+	if (sna->render.vertex_used) {
+		DBG(("%s: copying initial buffer x %d to handle=%d\n",
+		     __FUNCTION__,
+		     sna->render.vertex_used,
+		     sna->render.vbo->handle));
+		assert(sizeof(float)*sna->render.vertex_used <=
+		       __kgem_bo_size(sna->render.vbo));
+		memcpy(sna->render.vertices,
+		       sna->render.vertex_data,
+		       sizeof(float)*sna->render.vertex_used);
+	}
+
+	size = __kgem_bo_size(sna->render.vbo)/4;
+	if (size >= UINT16_MAX)
+		size = UINT16_MAX - 1;
+
+	DBG(("%s: create vbo handle=%d, size=%d\n",
+	     __FUNCTION__, sna->render.vbo->handle, size));
+
+	sna->render.vertex_size = size;
+	return sna->render.vertex_size - sna->render.vertex_used;
+}
+
+void gen4_vertex_close(struct sna *sna)
+{
+	struct kgem_bo *bo, *free_bo = NULL;
+	unsigned int i, delta = 0;
+
+	assert(sna->render.vertex_offset == 0);
+	if (!sna->render.vb_id)
+		return;
+
+	DBG(("%s: used=%d, vbo active? %d, vb=%x, nreloc=%d\n",
+	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo ? sna->render.vbo->handle : 0,
+	     sna->render.vb_id, sna->render.nvertex_reloc));
+
+	bo = sna->render.vbo;
+	if (bo) {
+		if (sna->render.vertex_size - sna->render.vertex_used < 64) {
+			DBG(("%s: discarding vbo (full), handle=%d\n", __FUNCTION__, sna->render.vbo->handle));
+			sna->render.vbo = NULL;
+			sna->render.vertices = sna->render.vertex_data;
+			sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
+			free_bo = bo;
+		} else if (IS_CPU_MAP(bo->map) && !sna->kgem.has_llc) {
+			DBG(("%s: converting CPU map to GTT\n", __FUNCTION__));
+			sna->render.vertices =
+				kgem_bo_map__gtt(&sna->kgem, sna->render.vbo);
+			if (sna->render.vertices == NULL) {
+				sna->render.vbo = NULL;
+				sna->render.vertices = sna->render.vertex_data;
+				sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
+				free_bo = bo;
+			}
+
+		}
+	} else {
+		if (sna->kgem.nbatch + sna->render.vertex_used <= sna->kgem.surface) {
+			DBG(("%s: copy to batch: %d @ %d\n", __FUNCTION__,
+			     sna->render.vertex_used, sna->kgem.nbatch));
+			memcpy(sna->kgem.batch + sna->kgem.nbatch,
+			       sna->render.vertex_data,
+			       sna->render.vertex_used * 4);
+			delta = sna->kgem.nbatch * 4;
+			bo = NULL;
+			sna->kgem.nbatch += sna->render.vertex_used;
+		} else {
+			bo = kgem_create_linear(&sna->kgem,
+						4*sna->render.vertex_used,
+						CREATE_NO_THROTTLE);
+			if (bo && !kgem_bo_write(&sna->kgem, bo,
+						 sna->render.vertex_data,
+						 4*sna->render.vertex_used)) {
+				kgem_bo_destroy(&sna->kgem, bo);
+				bo = NULL;
+			}
+			DBG(("%s: new vbo: %d\n", __FUNCTION__,
+			     sna->render.vertex_used));
+			free_bo = bo;
+		}
+	}
+
+	assert(sna->render.nvertex_reloc);
+	for (i = 0; i < sna->render.nvertex_reloc; i++) {
+		DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
+		     i, sna->render.vertex_reloc[i]));
+
+		sna->kgem.batch[sna->render.vertex_reloc[i]] =
+			kgem_add_reloc(&sna->kgem,
+				       sna->render.vertex_reloc[i], bo,
+				       I915_GEM_DOMAIN_VERTEX << 16,
+				       delta);
+	}
+	sna->render.nvertex_reloc = 0;
+	sna->render.vb_id = 0;
+
+	if (sna->render.vbo == NULL) {
+		sna->render.vertex_used = 0;
+		sna->render.vertex_index = 0;
+		assert(sna->render.vertices == sna->render.vertex_data);
+		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
+	}
+
+	if (free_bo)
+		kgem_bo_destroy(&sna->kgem, free_bo);
+}
+
+/* specialised vertex emission routines */
+
+#define OUT_VERTEX(x,y) vertex_emit_2s(sna, x,y) /* XXX assert(!too_large(x, y)); */
+#define OUT_VERTEX_F(v) vertex_emit(sna, v)
+
+inline static void
+emit_texcoord(struct sna *sna,
+	      const struct sna_composite_channel *channel,
+	      int16_t x, int16_t y)
+{
+	if (channel->is_solid) {
+		OUT_VERTEX_F(x);
+		OUT_VERTEX_F(y);
+		return;
+	}
+
+	x += channel->offset[0];
+	y += channel->offset[1];
+
+	if (channel->is_affine) {
+		float s, t;
+
+		sna_get_transformed_coordinates(x, y,
+						channel->transform,
+						&s, &t);
+		OUT_VERTEX_F(s * channel->scale[0]);
+		OUT_VERTEX_F(t * channel->scale[1]);
+	} else {
+		float s, t, w;
+
+		sna_get_transformed_coordinates_3d(x, y,
+						   channel->transform,
+						   &s, &t, &w);
+		OUT_VERTEX_F(s * channel->scale[0]);
+		OUT_VERTEX_F(t * channel->scale[1]);
+		OUT_VERTEX_F(w);
+	}
+}
+
+inline static void
+emit_vertex(struct sna *sna,
+		      const struct sna_composite_op *op,
+		      int16_t srcX, int16_t srcY,
+		      int16_t mskX, int16_t mskY,
+		      int16_t dstX, int16_t dstY)
+{
+	OUT_VERTEX(dstX, dstY);
+	emit_texcoord(sna, &op->src, srcX, srcY);
+}
+
+fastcall static void
+emit_primitive(struct sna *sna,
+	       const struct sna_composite_op *op,
+	       const struct sna_composite_rectangles *r)
+{
+	emit_vertex(sna, op,
+		    r->src.x + r->width,  r->src.y + r->height,
+		    r->mask.x + r->width, r->mask.y + r->height,
+		    r->dst.x + r->width, r->dst.y + r->height);
+	emit_vertex(sna, op,
+		    r->src.x,  r->src.y + r->height,
+		    r->mask.x, r->mask.y + r->height,
+		    r->dst.x,  r->dst.y + r->height);
+	emit_vertex(sna, op,
+		    r->src.x,  r->src.y,
+		    r->mask.x, r->mask.y,
+		    r->dst.x,  r->dst.y);
+}
+
+inline static void
+emit_vertex_mask(struct sna *sna,
+		 const struct sna_composite_op *op,
+		 int16_t srcX, int16_t srcY,
+		 int16_t mskX, int16_t mskY,
+		 int16_t dstX, int16_t dstY)
+{
+	OUT_VERTEX(dstX, dstY);
+	emit_texcoord(sna, &op->src, srcX, srcY);
+	emit_texcoord(sna, &op->mask, mskX, mskY);
+}
+
+fastcall static void
+emit_primitive_mask(struct sna *sna,
+		    const struct sna_composite_op *op,
+		    const struct sna_composite_rectangles *r)
+{
+	emit_vertex_mask(sna, op,
+			 r->src.x + r->width,  r->src.y + r->height,
+			 r->mask.x + r->width, r->mask.y + r->height,
+			 r->dst.x + r->width, r->dst.y + r->height);
+	emit_vertex_mask(sna, op,
+			 r->src.x,  r->src.y + r->height,
+			 r->mask.x, r->mask.y + r->height,
+			 r->dst.x,  r->dst.y + r->height);
+	emit_vertex_mask(sna, op,
+			 r->src.x,  r->src.y,
+			 r->mask.x, r->mask.y,
+			 r->dst.x,  r->dst.y);
+}
+
+fastcall static void
+emit_primitive_solid(struct sna *sna,
+		     const struct sna_composite_op *op,
+		     const struct sna_composite_rectangles *r)
+{
+	float *v;
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+
+	assert(op->floats_per_rect == 9);
+	assert((sna->render.vertex_used % 3) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 9;
+	assert(sna->render.vertex_used <= sna->render.vertex_size);
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	dst.p.x = r->dst.x;
+	v[3] = dst.f;
+	dst.p.y = r->dst.y;
+	v[6] = dst.f;
+
+	v[5] = v[2] = v[1] = 1.;
+	v[8] = v[7] = v[4] = 0.;
+}
+
+fastcall static void
+emit_primitive_identity_source(struct sna *sna,
+			       const struct sna_composite_op *op,
+			       const struct sna_composite_rectangles *r)
+{
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+	float *v;
+
+	assert(op->floats_per_rect == 9);
+	assert((sna->render.vertex_used % 3) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 9;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	dst.p.x = r->dst.x;
+	v[3] = dst.f;
+	dst.p.y = r->dst.y;
+	v[6] = dst.f;
+
+	v[7] = v[4] = (r->src.x + op->src.offset[0]) * op->src.scale[0];
+	v[1] = v[4] + r->width * op->src.scale[0];
+
+	v[8] = (r->src.y + op->src.offset[1]) * op->src.scale[1];
+	v[5] = v[2] = v[8] + r->height * op->src.scale[1];
+}
+
+fastcall static void
+emit_primitive_simple_source(struct sna *sna,
+			     const struct sna_composite_op *op,
+			     const struct sna_composite_rectangles *r)
+{
+	float *v;
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+
+	float xx = op->src.transform->matrix[0][0];
+	float x0 = op->src.transform->matrix[0][2];
+	float yy = op->src.transform->matrix[1][1];
+	float y0 = op->src.transform->matrix[1][2];
+	float sx = op->src.scale[0];
+	float sy = op->src.scale[1];
+	int16_t tx = op->src.offset[0];
+	int16_t ty = op->src.offset[1];
+
+	assert(op->floats_per_rect == 9);
+	assert((sna->render.vertex_used % 3) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 3*3;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	v[1] = ((r->src.x + r->width + tx) * xx + x0) * sx;
+	v[5] = v[2] = ((r->src.y + r->height + ty) * yy + y0) * sy;
+
+	dst.p.x = r->dst.x;
+	v[3] = dst.f;
+	v[7] = v[4] = ((r->src.x + tx) * xx + x0) * sx;
+
+	dst.p.y = r->dst.y;
+	v[6] = dst.f;
+	v[8] = ((r->src.y + ty) * yy + y0) * sy;
+}
+
+fastcall static void
+emit_primitive_affine_source(struct sna *sna,
+			     const struct sna_composite_op *op,
+			     const struct sna_composite_rectangles *r)
+{
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+	float *v;
+
+	assert(op->floats_per_rect == 9);
+	assert((sna->render.vertex_used % 3) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 9;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x + r->width,
+					 op->src.offset[1] + r->src.y + r->height,
+					 op->src.transform,
+					 &v[1], &v[2]);
+	v[1] *= op->src.scale[0];
+	v[2] *= op->src.scale[1];
+
+	dst.p.x = r->dst.x;
+	v[3] = dst.f;
+	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
+					 op->src.offset[1] + r->src.y + r->height,
+					 op->src.transform,
+					 &v[4], &v[5]);
+	v[4] *= op->src.scale[0];
+	v[5] *= op->src.scale[1];
+
+	dst.p.y = r->dst.y;
+	v[6] = dst.f;
+	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
+					 op->src.offset[1] + r->src.y,
+					 op->src.transform,
+					 &v[7], &v[8]);
+	v[7] *= op->src.scale[0];
+	v[8] *= op->src.scale[1];
+}
+
+fastcall static void
+emit_primitive_identity_mask(struct sna *sna,
+			     const struct sna_composite_op *op,
+			     const struct sna_composite_rectangles *r)
+{
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+	float msk_x, msk_y;
+	float w, h;
+	float *v;
+
+	msk_x = r->mask.x + op->mask.offset[0];
+	msk_y = r->mask.y + op->mask.offset[1];
+	w = r->width;
+	h = r->height;
+
+	DBG(("%s: dst=(%d, %d), mask=(%f, %f) x (%f, %f)\n",
+	     __FUNCTION__, r->dst.x, r->dst.y, msk_x, msk_y, w, h));
+
+	assert(op->floats_per_rect == 15);
+	assert((sna->render.vertex_used % 5) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 15;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	v[3] = (msk_x + w) * op->mask.scale[0];
+	v[9] = v[4] = (msk_y + h) * op->mask.scale[1];
+
+	dst.p.x = r->dst.x;
+	v[5] = dst.f;
+	v[13] = v[8] = msk_x * op->mask.scale[0];
+
+	dst.p.y = r->dst.y;
+	v[10] = dst.f;
+	v[14] = msk_y * op->mask.scale[1];
+
+	v[7] = v[2] = v[1] = 1;
+	v[12] = v[11] = v[6] = 0;
+}
+
+fastcall static void
+emit_primitive_identity_source_mask(struct sna *sna,
+				    const struct sna_composite_op *op,
+				    const struct sna_composite_rectangles *r)
+{
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+	float src_x, src_y;
+	float msk_x, msk_y;
+	float w, h;
+	float *v;
+
+	src_x = r->src.x + op->src.offset[0];
+	src_y = r->src.y + op->src.offset[1];
+	msk_x = r->mask.x + op->mask.offset[0];
+	msk_y = r->mask.y + op->mask.offset[1];
+	w = r->width;
+	h = r->height;
+
+	assert(op->floats_per_rect == 15);
+	assert((sna->render.vertex_used % 5) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 15;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	v[1] = (src_x + w) * op->src.scale[0];
+	v[2] = (src_y + h) * op->src.scale[1];
+	v[3] = (msk_x + w) * op->mask.scale[0];
+	v[4] = (msk_y + h) * op->mask.scale[1];
+
+	dst.p.x = r->dst.x;
+	v[5] = dst.f;
+	v[6] = src_x * op->src.scale[0];
+	v[7] = v[2];
+	v[8] = msk_x * op->mask.scale[0];
+	v[9] = v[4];
+
+	dst.p.y = r->dst.y;
+	v[10] = dst.f;
+	v[11] = v[6];
+	v[12] = src_y * op->src.scale[1];
+	v[13] = v[8];
+	v[14] = msk_y * op->mask.scale[1];
+}
+
+fastcall static void
+emit_primitive_simple_source_identity(struct sna *sna,
+				      const struct sna_composite_op *op,
+				      const struct sna_composite_rectangles *r)
+{
+	float *v;
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+
+	float xx = op->src.transform->matrix[0][0];
+	float x0 = op->src.transform->matrix[0][2];
+	float yy = op->src.transform->matrix[1][1];
+	float y0 = op->src.transform->matrix[1][2];
+	float sx = op->src.scale[0];
+	float sy = op->src.scale[1];
+	int16_t tx = op->src.offset[0];
+	int16_t ty = op->src.offset[1];
+	float msk_x = r->mask.x + op->mask.offset[0];
+	float msk_y = r->mask.y + op->mask.offset[1];
+	float w = r->width, h = r->height;
+
+	assert(op->floats_per_rect == 15);
+	assert((sna->render.vertex_used % 5) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 3*5;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	v[1] = ((r->src.x + r->width + tx) * xx + x0) * sx;
+	v[2] = ((r->src.y + r->height + ty) * yy + y0) * sy;
+	v[3] = (msk_x + w) * op->mask.scale[0];
+	v[4] = (msk_y + h) * op->mask.scale[1];
+
+	dst.p.x = r->dst.x;
+	v[5] = dst.f;
+	v[6] = ((r->src.x + tx) * xx + x0) * sx;
+	v[7] = v[2];
+	v[8] = msk_x * op->mask.scale[0];
+	v[9] = v[4];
+
+	dst.p.y = r->dst.y;
+	v[10] = dst.f;
+	v[11] = v[6];
+	v[12] = ((r->src.y + ty) * yy + y0) * sy;
+	v[13] = v[8];
+	v[14] = msk_y * op->mask.scale[1];
+}
+
+fastcall static void
+emit_primitive_affine_source_identity(struct sna *sna,
+				      const struct sna_composite_op *op,
+				      const struct sna_composite_rectangles *r)
+{
+	float *v;
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+	float msk_x = r->mask.x + op->mask.offset[0];
+	float msk_y = r->mask.y + op->mask.offset[1];
+	float w = r->width, h = r->height;
+
+	assert(op->floats_per_rect == 15);
+	assert((sna->render.vertex_used % 5) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 3*5;
+
+	dst.p.x = r->dst.x + r->width;
+	dst.p.y = r->dst.y + r->height;
+	v[0] = dst.f;
+	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x + r->width,
+					 op->src.offset[1] + r->src.y + r->height,
+					 op->src.transform,
+					 &v[1], &v[2]);
+	v[1] *= op->src.scale[0];
+	v[2] *= op->src.scale[1];
+	v[3] = (msk_x + w) * op->mask.scale[0];
+	v[4] = (msk_y + h) * op->mask.scale[1];
+
+	dst.p.x = r->dst.x;
+	v[5] = dst.f;
+	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
+					 op->src.offset[1] + r->src.y + r->height,
+					 op->src.transform,
+					 &v[6], &v[7]);
+	v[6] *= op->src.scale[0];
+	v[7] *= op->src.scale[1];
+	v[8] = msk_x * op->mask.scale[0];
+	v[9] = v[4];
+
+	dst.p.y = r->dst.y;
+	v[10] = dst.f;
+	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
+					 op->src.offset[1] + r->src.y,
+					 op->src.transform,
+					 &v[11], &v[12]);
+	v[11] *= op->src.scale[0];
+	v[12] *= op->src.scale[1];
+	v[13] = v[8];
+	v[14] = msk_y * op->mask.scale[1];
+}
+
+inline static void
+emit_composite_texcoord_affine(struct sna *sna,
+			       const struct sna_composite_channel *channel,
+			       int16_t x, int16_t y)
+{
+	float t[2];
+
+	sna_get_transformed_coordinates(x + channel->offset[0],
+					y + channel->offset[1],
+					channel->transform,
+					&t[0], &t[1]);
+	OUT_VERTEX_F(t[0] * channel->scale[0]);
+	OUT_VERTEX_F(t[1] * channel->scale[1]);
+}
+
+void gen4_choose_composite_emitter(struct sna_composite_op *tmp)
+{
+	tmp->prim_emit = emit_primitive;
+	tmp->floats_per_vertex = 1 + 2 + !tmp->src.is_affine;
+	if (tmp->mask.bo) {
+		tmp->floats_per_vertex += 2 + !tmp->mask.is_affine;
+		tmp->prim_emit = emit_primitive_mask;
+		if (tmp->mask.transform == NULL) {
+			if (tmp->src.is_solid) {
+				DBG(("%s: solid, identity mask\n", __FUNCTION__));
+				tmp->prim_emit = emit_primitive_identity_mask;
+			} else if (tmp->src.transform == NULL) {
+				DBG(("%s: identity source, identity mask\n", __FUNCTION__));
+				tmp->prim_emit = emit_primitive_identity_source_mask;
+			} else if (tmp->src.is_affine) {
+				if (tmp->src.transform->matrix[0][1] == 0 &&
+				    tmp->src.transform->matrix[1][0] == 0) {
+					DBG(("%s: simple src, identity mask\n", __FUNCTION__));
+					tmp->src.scale[0] /= tmp->src.transform->matrix[2][2];
+					tmp->src.scale[1] /= tmp->src.transform->matrix[2][2];
+					tmp->prim_emit = emit_primitive_simple_source_identity;
+				} else {
+					DBG(("%s: affine src, identity mask\n", __FUNCTION__));
+					tmp->prim_emit = emit_primitive_affine_source_identity;
+				}
+			}
+		}
+	} else {
+		if (tmp->src.is_solid) {
+			DBG(("%s: solid, no mask\n", __FUNCTION__));
+			tmp->prim_emit = emit_primitive_solid;
+			if (tmp->src.is_opaque && tmp->op == PictOpOver)
+				tmp->op = PictOpSrc;
+		} else if (tmp->src.transform == NULL) {
+			DBG(("%s: identity src, no mask\n", __FUNCTION__));
+			tmp->prim_emit = emit_primitive_identity_source;
+		} else if (tmp->src.is_affine) {
+			if (tmp->src.transform->matrix[0][1] == 0 &&
+			    tmp->src.transform->matrix[1][0] == 0) {
+				DBG(("%s: simple src, no mask\n", __FUNCTION__));
+				tmp->src.scale[0] /= tmp->src.transform->matrix[2][2];
+				tmp->src.scale[1] /= tmp->src.transform->matrix[2][2];
+				tmp->prim_emit = emit_primitive_simple_source;
+			} else {
+				DBG(("%s: affine src, no mask\n", __FUNCTION__));
+				tmp->prim_emit = emit_primitive_affine_source;
+			}
+		}
+	}
+	tmp->floats_per_rect = 3 * tmp->floats_per_vertex;
+}
+
+inline static void
+emit_spans_vertex(struct sna *sna,
+		  const struct sna_composite_spans_op *op,
+		  int16_t x, int16_t y)
+{
+	OUT_VERTEX(x, y);
+	emit_texcoord(sna, &op->base.src, x, y);
+}
+
+fastcall static void
+emit_composite_spans_primitive(struct sna *sna,
+			       const struct sna_composite_spans_op *op,
+			       const BoxRec *box,
+			       float opacity)
+{
+	emit_spans_vertex(sna, op, box->x2, box->y2);
+	OUT_VERTEX_F(opacity);
+
+	emit_spans_vertex(sna, op, box->x1, box->y2);
+	OUT_VERTEX_F(opacity);
+
+	emit_spans_vertex(sna, op, box->x1, box->y1);
+	OUT_VERTEX_F(opacity);
+}
+
+fastcall static void
+emit_spans_solid(struct sna *sna,
+		 const struct sna_composite_spans_op *op,
+		 const BoxRec *box,
+		 float opacity)
+{
+	OUT_VERTEX(box->x2, box->y2);
+	OUT_VERTEX_F(1); OUT_VERTEX_F(1);
+	OUT_VERTEX_F(opacity);
+
+	OUT_VERTEX(box->x1, box->y2);
+	OUT_VERTEX_F(0); OUT_VERTEX_F(1);
+	OUT_VERTEX_F(opacity);
+
+	OUT_VERTEX(box->x1, box->y1);
+	OUT_VERTEX_F(0); OUT_VERTEX_F(0);
+	OUT_VERTEX_F(opacity);
+}
+
+fastcall static void
+emit_spans_identity(struct sna *sna,
+		    const struct sna_composite_spans_op *op,
+		    const BoxRec *box,
+		    float opacity)
+{
+	float *v;
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+
+	float sx = op->base.src.scale[0];
+	float sy = op->base.src.scale[1];
+	int16_t tx = op->base.src.offset[0];
+	int16_t ty = op->base.src.offset[1];
+
+	assert(op->base.floats_per_rect == 12);
+	assert((sna->render.vertex_used % 4) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 3*4;
+	assert(sna->render.vertex_used <= sna->render.vertex_size);
+
+	dst.p.x = box->x2;
+	dst.p.y = box->y2;
+	v[0] = dst.f;
+	v[1] = (box->x2 + tx) * sx;
+	v[6] = v[2] = (box->y2 + ty) * sy;
+
+	dst.p.x = box->x1;
+	v[4] = dst.f;
+	v[9] = v[5] = (box->x1 + tx) * sx;
+
+	dst.p.y = box->y1;
+	v[8] = dst.f;
+	v[10] = (box->y1 + ty) * sy;
+
+	v[11] = v[7] = v[3] = opacity;
+}
+
+fastcall static void
+emit_spans_simple(struct sna *sna,
+		  const struct sna_composite_spans_op *op,
+		  const BoxRec *box,
+		  float opacity)
+{
+	float *v;
+	union {
+		struct sna_coordinate p;
+		float f;
+	} dst;
+
+	float xx = op->base.src.transform->matrix[0][0];
+	float x0 = op->base.src.transform->matrix[0][2];
+	float yy = op->base.src.transform->matrix[1][1];
+	float y0 = op->base.src.transform->matrix[1][2];
+	float sx = op->base.src.scale[0];
+	float sy = op->base.src.scale[1];
+	int16_t tx = op->base.src.offset[0];
+	int16_t ty = op->base.src.offset[1];
+
+	assert(op->base.floats_per_rect == 12);
+	assert((sna->render.vertex_used % 4) == 0);
+	v = sna->render.vertices + sna->render.vertex_used;
+	sna->render.vertex_used += 3*4;
+	assert(sna->render.vertex_used <= sna->render.vertex_size);
+
+	dst.p.x = box->x2;
+	dst.p.y = box->y2;
+	v[0] = dst.f;
+	v[1] = ((box->x2 + tx) * xx + x0) * sx;
+	v[6] = v[2] = ((box->y2 + ty) * yy + y0) * sy;
+
+	dst.p.x = box->x1;
+	v[4] = dst.f;
+	v[9] = v[5] = ((box->x1 + tx) * xx + x0) * sx;
+
+	dst.p.y = box->y1;
+	v[8] = dst.f;
+	v[10] = ((box->y1 + ty) * yy + y0) * sy;
+
+	v[11] = v[7] = v[3] = opacity;
+}
+
+fastcall static void
+emit_spans_affine(struct sna *sna,
+		  const struct sna_composite_spans_op *op,
+		  const BoxRec *box,
+		  float opacity)
+{
+	OUT_VERTEX(box->x2, box->y2);
+	emit_composite_texcoord_affine(sna, &op->base.src, box->x2, box->y2);
+	OUT_VERTEX_F(opacity);
+
+	OUT_VERTEX(box->x1, box->y2);
+	emit_composite_texcoord_affine(sna, &op->base.src, box->x1, box->y2);
+	OUT_VERTEX_F(opacity);
+
+	OUT_VERTEX(box->x1, box->y1);
+	emit_composite_texcoord_affine(sna, &op->base.src, box->x1, box->y1);
+	OUT_VERTEX_F(opacity);
+}
+
+void gen4_choose_spans_emitter(struct sna_composite_spans_op *tmp)
+{
+	tmp->prim_emit = emit_composite_spans_primitive;
+	if (tmp->base.src.is_solid) {
+		tmp->prim_emit = emit_spans_solid;
+	} else if (tmp->base.src.transform == NULL) {
+		tmp->prim_emit = emit_spans_identity;
+	} else if (tmp->base.is_affine) {
+		if (tmp->base.src.transform->matrix[0][1] == 0 &&
+		    tmp->base.src.transform->matrix[1][0] == 0) {
+			tmp->base.src.scale[0] /= tmp->base.src.transform->matrix[2][2];
+			tmp->base.src.scale[1] /= tmp->base.src.transform->matrix[2][2];
+			tmp->prim_emit = emit_spans_simple;
+		} else
+			tmp->prim_emit = emit_spans_affine;
+	}
+	tmp->base.floats_per_vertex = 4 + !tmp->base.is_affine;
+	tmp->base.floats_per_rect = 3 * tmp->base.floats_per_vertex;
+}
diff --git a/src/sna/gen4_vertex.h b/src/sna/gen4_vertex.h
new file mode 100644
index 0000000..418bdef
--- /dev/null
+++ b/src/sna/gen4_vertex.h
@@ -0,0 +1,39 @@
+#ifndef GEN4_VERTEX_H
+#define GEN4_VERTEX_H
+
+#include "compiler.h"
+
+#include "sna.h"
+#include "sna_render.h"
+
+void gen4_vertex_flush(struct sna *sna);
+int gen4_vertex_finish(struct sna *sna);
+void gen4_vertex_close(struct sna *sna);
+
+inline static uint32_t
+gen4_choose_composite_vertex_buffer(const struct sna_composite_op *op)
+{
+	int id = 2 + !op->src.is_affine;
+	if (op->mask.bo)
+		id |= (2 + !op->mask.is_affine) << 2;
+	DBG(("%s: id=%x (%d, %d)\n", __FUNCTION__, id,
+	     2 + !op->src.is_affine,
+	     op->mask.bo ?  2 + !op->mask.is_affine : 0));
+	assert(id > 0 && id < 16);
+	return id;
+}
+
+inline inline static uint32_t
+gen4_choose_spans_vertex_buffer(const struct sna_composite_op *op)
+{
+	DBG(("%s: id=%x (%d, 1)\n", __FUNCTION__,
+	     1 << 2 | (2+!op->src.is_affine),
+	     2 + !op->src.is_affine));
+	return 1 << 2 | (2+!op->src.is_affine);
+}
+
+void gen4_choose_composite_emitter(struct sna_composite_op *tmp);
+void gen4_choose_spans_emitter(struct sna_composite_spans_op *tmp);
+
+
+#endif /* GEN4_VERTEX_H */
diff --git a/src/sna/gen5_render.c b/src/sna/gen5_render.c
index 8f5ea2f..f013e09 100644
--- a/src/sna/gen5_render.c
+++ b/src/sna/gen5_render.c
@@ -42,7 +42,9 @@
 
 #include "brw/brw.h"
 #include "gen5_render.h"
+#include "gen4_vertex.h"
 
+#define NO_COMPOSITE 0
 #define NO_COMPOSITE_SPANS 0
 
 #define PREFER_BLT_FILL 1
@@ -227,154 +229,6 @@ static void gen5_magic_ca_pass(struct sna *sna,
 	state->last_primitive = sna->kgem.nbatch;
 }
 
-static void gen5_vertex_flush(struct sna *sna)
-{
-	assert(sna->render_state.gen5.vertex_offset);
-	assert(sna->render.vertex_index > sna->render.vertex_start);
-
-	DBG(("%s[%x] = %d\n", __FUNCTION__,
-	     4*sna->render_state.gen5.vertex_offset,
-	     sna->render.vertex_index - sna->render.vertex_start));
-	sna->kgem.batch[sna->render_state.gen5.vertex_offset] =
-		sna->render.vertex_index - sna->render.vertex_start;
-	sna->render_state.gen5.vertex_offset = 0;
-}
-
-static int gen5_vertex_finish(struct sna *sna)
-{
-	struct kgem_bo *bo;
-	unsigned int i;
-
-	assert(sna->render.vertex_used);
-	assert(sna->render.nvertex_reloc);
-
-	/* Note: we only need dword alignment (currently) */
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render_state.gen5.vertex_offset)
-			gen5_vertex_flush(sna);
-
-		for (i = 0; i < sna->render.nvertex_reloc; i++) {
-			DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-			     i, sna->render.vertex_reloc[i]));
-
-			sna->kgem.batch[sna->render.vertex_reloc[i]] =
-				kgem_add_reloc(&sna->kgem,
-					       sna->render.vertex_reloc[i], bo,
-					       I915_GEM_DOMAIN_VERTEX << 16,
-					       0);
-		}
-
-		sna->render.nvertex_reloc = 0;
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-		sna->render.vbo = NULL;
-		sna->render_state.gen5.vb_id = 0;
-
-		kgem_bo_destroy(&sna->kgem, bo);
-	}
-
-	sna->render.vertices = NULL;
-	sna->render.vbo = kgem_create_linear(&sna->kgem,
-					     256*1024, CREATE_GTT_MAP);
-	if (sna->render.vbo)
-		sna->render.vertices = kgem_bo_map(&sna->kgem, sna->render.vbo);
-	if (sna->render.vertices == NULL) {
-		if (sna->render.vbo)
-			kgem_bo_destroy(&sna->kgem, sna->render.vbo);
-		sna->render.vbo = NULL;
-		return 0;
-	}
-
-	if (sna->render.vertex_used) {
-		memcpy(sna->render.vertices,
-		       sna->render.vertex_data,
-		       sizeof(float)*sna->render.vertex_used);
-	}
-	sna->render.vertex_size = 64 * 1024 - 1;
-	return sna->render.vertex_size - sna->render.vertex_used;
-}
-
-static void gen5_vertex_close(struct sna *sna)
-{
-	struct kgem_bo *bo, *free_bo = NULL;
-	unsigned int i, delta = 0;
-
-	assert(sna->render_state.gen5.vertex_offset == 0);
-	if (!sna->render_state.gen5.vb_id)
-		return;
-
-	DBG(("%s: used=%d, vbo active? %d\n",
-	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo != NULL));
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render.vertex_size - sna->render.vertex_used < 64) {
-			DBG(("%s: discarding full vbo\n", __FUNCTION__));
-			sna->render.vbo = NULL;
-			sna->render.vertices = sna->render.vertex_data;
-			sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
-			free_bo = bo;
-		} else if (IS_CPU_MAP(bo->map)) {
-			DBG(("%s: converting CPU map to GTT\n", __FUNCTION__));
-			sna->render.vertices =
-				kgem_bo_map__gtt(&sna->kgem, sna->render.vbo);
-			if (sna->render.vertices == NULL) {
-				sna->render.vbo = NULL;
-				sna->render.vertices = sna->render.vertex_data;
-				sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
-				free_bo = bo;
-			}
-		}
-	} else {
-		if (sna->kgem.nbatch + sna->render.vertex_used <= sna->kgem.surface) {
-			DBG(("%s: copy to batch: %d @ %d\n", __FUNCTION__,
-			     sna->render.vertex_used, sna->kgem.nbatch));
-			memcpy(sna->kgem.batch + sna->kgem.nbatch,
-			       sna->render.vertex_data,
-			       sna->render.vertex_used * 4);
-			delta = sna->kgem.nbatch * 4;
-			bo = NULL;
-			sna->kgem.nbatch += sna->render.vertex_used;
-		} else {
-			bo = kgem_create_linear(&sna->kgem,
-						4*sna->render.vertex_used,
-						CREATE_NO_THROTTLE);
-			if (bo && !kgem_bo_write(&sna->kgem, bo,
-						 sna->render.vertex_data,
-						 4*sna->render.vertex_used)) {
-				kgem_bo_destroy(&sna->kgem, bo);
-				bo = NULL;
-			}
-			DBG(("%s: new vbo: %d\n", __FUNCTION__,
-			     sna->render.vertex_used));
-			free_bo = bo;
-		}
-	}
-
-	assert(sna->render.nvertex_reloc);
-	for (i = 0; i < sna->render.nvertex_reloc; i++) {
-		DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-		     i, sna->render.vertex_reloc[i]));
-
-		sna->kgem.batch[sna->render.vertex_reloc[i]] =
-			kgem_add_reloc(&sna->kgem,
-				       sna->render.vertex_reloc[i], bo,
-				       I915_GEM_DOMAIN_VERTEX << 16,
-				       delta);
-	}
-	sna->render.nvertex_reloc = 0;
-
-	if (sna->render.vbo == NULL) {
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-	}
-
-	if (free_bo)
-		kgem_bo_destroy(&sna->kgem, free_bo);
-}
-
 static uint32_t gen5_get_blend(int op,
 			       bool has_component_alpha,
 			       uint32_t dst_format)
@@ -671,365 +525,29 @@ gen5_bind_bo(struct sna *sna,
 	return offset * sizeof(uint32_t);
 }
 
-fastcall static void
-gen5_emit_composite_primitive_solid(struct sna *sna,
-				    const struct sna_composite_op *op,
-				    const struct sna_composite_rectangles *r)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = 1.;
-	v[2] = 1.;
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	v[4] = 0.;
-	v[5] = 1.;
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	v[7] = 0.;
-	v[8] = 0.;
-}
-
-fastcall static void
-gen5_emit_composite_primitive_identity_source(struct sna *sna,
-					      const struct sna_composite_op *op,
-					      const struct sna_composite_rectangles *r)
-{
-	const float *sf = op->src.scale;
-	float sx, sy, *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	sx = r->src.x + op->src.offset[0];
-	sy = r->src.y + op->src.offset[1];
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = (sx + r->width) * sf[0];
-	v[5] = v[2] = (sy + r->height) * sf[1];
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	v[7] = v[4] = sx * sf[0];
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	v[8] = sy * sf[1];
-}
-
-fastcall static void
-gen5_emit_composite_primitive_affine_source(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float *v;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x + r->width,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[1], &v[2]);
-	v[1] *= op->src.scale[0];
-	v[2] *= op->src.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[4], &v[5]);
-	v[4] *= op->src.scale[0];
-	v[5] *= op->src.scale[1];
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y,
-					 op->src.transform,
-					 &v[7], &v[8]);
-	v[7] *= op->src.scale[0];
-	v[8] *= op->src.scale[1];
-}
-
-fastcall static void
-gen5_emit_composite_primitive_identity_mask(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[9] = v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[13] = v[8] = msk_x * op->mask.scale[0];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[14] = msk_y * op->mask.scale[1];
-
-	v[7] = v[2] = v[1] = 1;
-	v[12] = v[11] = v[6] = 0;
-}
-
-fastcall static void
-gen5_emit_composite_primitive_identity_source_mask(struct sna *sna,
-						   const struct sna_composite_op *op,
-						   const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float src_x, src_y;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	src_x = r->src.x + op->src.offset[0];
-	src_y = r->src.y + op->src.offset[1];
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = (src_x + w) * op->src.scale[0];
-	v[2] = (src_y + h) * op->src.scale[1];
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[6] = src_x * op->src.scale[0];
-	v[7] = v[2];
-	v[8] = msk_x * op->mask.scale[0];
-	v[9] = v[4];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[11] = v[6];
-	v[12] = src_y * op->src.scale[1];
-	v[13] = v[8];
-	v[14] = msk_y * op->mask.scale[1];
-}
-
-fastcall static void
-gen5_emit_composite_primitive(struct sna *sna,
-			      const struct sna_composite_op *op,
-			      const struct sna_composite_rectangles *r)
-{
-	float src_x[3], src_y[3], src_w[3], mask_x[3], mask_y[3], mask_w[3];
-	bool is_affine = op->is_affine;
-	const float *src_sf = op->src.scale;
-	const float *mask_sf = op->mask.scale;
-
-	if (op->src.is_solid) {
-		src_x[0] = 0;
-		src_y[0] = 0;
-		src_x[1] = 0;
-		src_y[1] = 1;
-		src_x[2] = 1;
-		src_y[2] = 1;
-		src_w[0] = src_w[1] = src_w[2] = 1;
-	} else if (is_affine) {
-		sna_get_transformed_coordinates(r->src.x + op->src.offset[0],
-						r->src.y + op->src.offset[1],
-						op->src.transform,
-						&src_x[0],
-						&src_y[0]);
-
-		sna_get_transformed_coordinates(r->src.x + op->src.offset[0],
-						r->src.y + op->src.offset[1] + r->height,
-						op->src.transform,
-						&src_x[1],
-						&src_y[1]);
-
-		sna_get_transformed_coordinates(r->src.x + op->src.offset[0] + r->width,
-						r->src.y + op->src.offset[1] + r->height,
-						op->src.transform,
-						&src_x[2],
-						&src_y[2]);
-	} else {
-		sna_get_transformed_coordinates_3d(r->src.x + op->src.offset[0],
-						   r->src.y + op->src.offset[1],
-						   op->src.transform,
-						   &src_x[0],
-						   &src_y[0],
-						   &src_w[0]);
-		sna_get_transformed_coordinates_3d(r->src.x + op->src.offset[0],
-						   r->src.y + op->src.offset[1] + r->height,
-						   op->src.transform,
-						   &src_x[1],
-						   &src_y[1],
-						   &src_w[1]);
-		sna_get_transformed_coordinates_3d(r->src.x + op->src.offset[0] + r->width,
-						   r->src.y + op->src.offset[1] + r->height,
-						   op->src.transform,
-						   &src_x[2],
-						   &src_y[2],
-						   &src_w[2]);
-	}
-
-	if (op->mask.bo) {
-		if (op->mask.is_solid) {
-			mask_x[0] = 0;
-			mask_y[0] = 0;
-			mask_x[1] = 0;
-			mask_y[1] = 1;
-			mask_x[2] = 1;
-			mask_y[2] = 1;
-			mask_w[0] = mask_w[1] = mask_w[2] = 1;
-		} else if (is_affine) {
-			sna_get_transformed_coordinates(r->mask.x + op->mask.offset[0],
-							r->mask.y + op->mask.offset[1],
-							op->mask.transform,
-							&mask_x[0],
-							&mask_y[0]);
-
-			sna_get_transformed_coordinates(r->mask.x + op->mask.offset[0],
-							r->mask.y + op->mask.offset[1] + r->height,
-							op->mask.transform,
-							&mask_x[1],
-							&mask_y[1]);
-
-			sna_get_transformed_coordinates(r->mask.x + op->mask.offset[0] + r->width,
-							r->mask.y + op->mask.offset[1] + r->height,
-							op->mask.transform,
-							&mask_x[2],
-							&mask_y[2]);
-		} else {
-			sna_get_transformed_coordinates_3d(r->mask.x + op->mask.offset[0],
-							   r->mask.y + op->mask.offset[1],
-							   op->mask.transform,
-							   &mask_x[0],
-							   &mask_y[0],
-							   &mask_w[0]);
-
-			sna_get_transformed_coordinates_3d(r->mask.x + op->mask.offset[0],
-							   r->mask.y + op->mask.offset[1] + r->height,
-							   op->mask.transform,
-							   &mask_x[1],
-							   &mask_y[1],
-							   &mask_w[1]);
-			sna_get_transformed_coordinates_3d(r->mask.x + op->mask.offset[0] + r->width,
-							   r->mask.y + op->mask.offset[1] + r->height,
-							   op->mask.transform,
-							   &mask_x[2],
-							   &mask_y[2],
-							   &mask_w[2]);
-		}
-	}
-
-	OUT_VERTEX(r->dst.x + r->width, r->dst.y + r->height);
-	OUT_VERTEX_F(src_x[2] * src_sf[0]);
-	OUT_VERTEX_F(src_y[2] * src_sf[1]);
-	if (!is_affine)
-		OUT_VERTEX_F(src_w[2]);
-	if (op->mask.bo) {
-		OUT_VERTEX_F(mask_x[2] * mask_sf[0]);
-		OUT_VERTEX_F(mask_y[2] * mask_sf[1]);
-		if (!is_affine)
-			OUT_VERTEX_F(mask_w[2]);
-	}
-
-	OUT_VERTEX(r->dst.x, r->dst.y + r->height);
-	OUT_VERTEX_F(src_x[1] * src_sf[0]);
-	OUT_VERTEX_F(src_y[1] * src_sf[1]);
-	if (!is_affine)
-		OUT_VERTEX_F(src_w[1]);
-	if (op->mask.bo) {
-		OUT_VERTEX_F(mask_x[1] * mask_sf[0]);
-		OUT_VERTEX_F(mask_y[1] * mask_sf[1]);
-		if (!is_affine)
-			OUT_VERTEX_F(mask_w[1]);
-	}
-
-	OUT_VERTEX(r->dst.x, r->dst.y);
-	OUT_VERTEX_F(src_x[0] * src_sf[0]);
-	OUT_VERTEX_F(src_y[0] * src_sf[1]);
-	if (!is_affine)
-		OUT_VERTEX_F(src_w[0]);
-	if (op->mask.bo) {
-		OUT_VERTEX_F(mask_x[0] * mask_sf[0]);
-		OUT_VERTEX_F(mask_y[0] * mask_sf[1]);
-		if (!is_affine)
-			OUT_VERTEX_F(mask_w[0]);
-	}
-}
-
 static void gen5_emit_vertex_buffer(struct sna *sna,
 				    const struct sna_composite_op *op)
 {
 	int id = op->u.gen5.ve_id;
 
-	assert((unsigned)id <= 3);
+	assert((sna->render.vb_id & (1 << id)) == 0);
 
 	OUT_BATCH(GEN5_3DSTATE_VERTEX_BUFFERS | 3);
-	OUT_BATCH((id << VB0_BUFFER_INDEX_SHIFT) | VB0_VERTEXDATA |
+	OUT_BATCH(id << VB0_BUFFER_INDEX_SHIFT | VB0_VERTEXDATA |
 		  (4*op->floats_per_vertex << VB0_BUFFER_PITCH_SHIFT));
+	assert(sna->render.nvertex_reloc < ARRAY_SIZE(sna->render.vertex_reloc));
 	sna->render.vertex_reloc[sna->render.nvertex_reloc++] = sna->kgem.nbatch;
 	OUT_BATCH(0);
 	OUT_BATCH(~0); /* max address: disabled */
 	OUT_BATCH(0);
 
-	sna->render_state.gen5.vb_id |= 1 << id;
+	sna->render.vb_id |= 1 << id;
 }
 
 static void gen5_emit_primitive(struct sna *sna)
 {
 	if (sna->kgem.nbatch == sna->render_state.gen5.last_primitive) {
-		sna->render_state.gen5.vertex_offset = sna->kgem.nbatch - 5;
+		sna->render.vertex_offset = sna->kgem.nbatch - 5;
 		return;
 	}
 
@@ -1038,7 +556,7 @@ static void gen5_emit_primitive(struct sna *sna)
 		  (_3DPRIM_RECTLIST << GEN5_3DPRIMITIVE_TOPOLOGY_SHIFT) |
 		  (0 << 9) |
 		  4);
-	sna->render_state.gen5.vertex_offset = sna->kgem.nbatch;
+	sna->render.vertex_offset = sna->kgem.nbatch;
 	OUT_BATCH(0);	/* vertex count, to be filled in later */
 	OUT_BATCH(sna->render.vertex_index);
 	OUT_BATCH(1);	/* single instance */
@@ -1055,18 +573,16 @@ static bool gen5_rectangle_begin(struct sna *sna,
 	int id = op->u.gen5.ve_id;
 	int ndwords;
 
-	assert((unsigned)id <= 3);
-
 	ndwords = op->need_magic_ca_pass ? 20 : 6;
-	if ((sna->render_state.gen5.vb_id & (1 << id)) == 0)
+	if ((sna->render.vb_id & (1 << id)) == 0)
 		ndwords += 5;
 
 	if (!kgem_check_batch(&sna->kgem, ndwords))
 		return false;
 
-	if ((sna->render_state.gen5.vb_id & (1 << id)) == 0)
+	if ((sna->render.vb_id & (1 << id)) == 0)
 		gen5_emit_vertex_buffer(sna, op);
-	if (sna->render_state.gen5.vertex_offset == 0)
+	if (sna->render.vertex_offset == 0)
 		gen5_emit_primitive(sna);
 
 	return true;
@@ -1085,7 +601,7 @@ static int gen5_get_rectangles__flush(struct sna *sna,
 	if (op->need_magic_ca_pass && sna->render.vbo)
 		return 0;
 
-	return gen5_vertex_finish(sna);
+	return gen4_vertex_finish(sna);
 }
 
 inline static int gen5_get_rectangles(struct sna *sna,
@@ -1106,7 +622,7 @@ start:
 			goto flush;
 	}
 
-	if (unlikely(sna->render_state.gen5.vertex_offset == 0 &&
+	if (unlikely(sna->render.vertex_offset == 0 &&
 		     !gen5_rectangle_begin(sna, op)))
 		goto flush;
 
@@ -1117,8 +633,8 @@ start:
 	return want;
 
 flush:
-	if (sna->render_state.gen5.vertex_offset) {
-		gen5_vertex_flush(sna);
+	if (sna->render.vertex_offset) {
+		gen4_vertex_flush(sna);
 		gen5_magic_ca_pass(sna, op);
 	}
 	_kgem_submit(&sna->kgem);
@@ -1249,7 +765,7 @@ gen5_align_vertex(struct sna *sna, const struct sna_composite_op *op)
 {
 	if (op->floats_per_vertex != sna->render_state.gen5.floats_per_vertex) {
 		if (sna->render.vertex_size - sna->render.vertex_used < 2*op->floats_per_rect)
-			gen5_vertex_finish(sna);
+			gen4_vertex_finish(sna);
 
 		DBG(("aligning vertex: was %d, now %d floats per vertex, %d->%d\n",
 		     sna->render_state.gen5.floats_per_vertex,
@@ -1349,28 +865,17 @@ gen5_emit_vertex_elements(struct sna *sna,
 	 *    texture coordinate 1 if (has_mask is true): same as above
 	 */
 	struct gen5_render_state *render = &sna->render_state.gen5;
-	bool has_mask = op->mask.bo != NULL;
-	bool is_affine = op->is_affine;
-	int nelem = has_mask ? 2 : 1;
-	int selem = is_affine ? 2 : 3;
-	uint32_t w_component;
-	uint32_t src_format;
 	int id = op->u.gen5.ve_id;
+	bool has_mask = id >> 2;
+	uint32_t format, dw;
+	int offset;
 
-	assert((unsigned)id <= 3);
 	if (!DBG_NO_STATE_CACHE && render->ve_id == id)
 		return;
 
+	DBG(("%s: changing %d -> %d\n", __FUNCTION__, render->ve_id, id));
 	render->ve_id = id;
 
-	if (is_affine) {
-		src_format = GEN5_SURFACEFORMAT_R32G32_FLOAT;
-		w_component = GEN5_VFCOMPONENT_STORE_1_FLT;
-	} else {
-		src_format = GEN5_SURFACEFORMAT_R32G32B32_FLOAT;
-		w_component = GEN5_VFCOMPONENT_STORE_SRC;
-	}
-
 	/* The VUE layout
 	 *    dword 0-3: pad (0.0, 0.0, 0.0. 0.0)
 	 *    dword 4-7: position (x, y, 1.0, 1.0),
@@ -1380,43 +885,87 @@ gen5_emit_vertex_elements(struct sna *sna,
 	 * dword 4-15 are fetched from vertex buffer
 	 */
 	OUT_BATCH(GEN5_3DSTATE_VERTEX_ELEMENTS |
-		((2 * (2 + nelem)) + 1 - 2));
+		((2 * (has_mask ? 4 : 3)) + 1 - 2));
 
 	OUT_BATCH((id << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID |
 		  (GEN5_SURFACEFORMAT_R32G32B32A32_FLOAT << VE0_FORMAT_SHIFT) |
 		  (0 << VE0_OFFSET_SHIFT));
-	OUT_BATCH((GEN5_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_0_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_3_SHIFT));
+	OUT_BATCH((VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_0_SHIFT) |
+		  (VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT) |
+		  (VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT) |
+		  (VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_3_SHIFT));
 
 	/* x,y */
-	OUT_BATCH((id << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID |
-		  (GEN5_SURFACEFORMAT_R16G16_SSCALED << VE0_FORMAT_SHIFT) |
-		  (0 << VE0_OFFSET_SHIFT)); /* offsets vb in bytes */
-	OUT_BATCH((GEN5_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT));
+	OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
+		  GEN5_SURFACEFORMAT_R16G16_SSCALED << VE0_FORMAT_SHIFT |
+		  0 << VE0_OFFSET_SHIFT);
+	OUT_BATCH(VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT |
+		  VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT |
+		  VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT |
+		  VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT);
+	offset = 4;
 
 	/* u0, v0, w0 */
-	OUT_BATCH((id << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID |
-		  (src_format << VE0_FORMAT_SHIFT) |
-		  (4 << VE0_OFFSET_SHIFT));	/* offset vb in bytes */
-	OUT_BATCH((GEN5_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) |
-		  (w_component << VE1_VFCOMPONENT_2_SHIFT) |
-		  (GEN5_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT));
+	DBG(("%s: id=%d, first channel %d floats, offset=%d\n", __FUNCTION__,
+	     id, id & 3, offset));
+	dw = VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT;
+	switch (id & 3) {
+	case 1:
+		format = GEN5_SURFACEFORMAT_R32_FLOAT << VE0_FORMAT_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+		dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT;
+		dw |= VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT;
+		break;
+	default:
+		assert(0);
+	case 2:
+		format = GEN5_SURFACEFORMAT_R32G32_FLOAT << VE0_FORMAT_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+		dw |= VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT;
+		break;
+	case 3:
+		format = GEN5_SURFACEFORMAT_R32G32B32_FLOAT << VE0_FORMAT_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+		dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_2_SHIFT;
+		break;
+	}
+	OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
+		  format | offset << VE0_OFFSET_SHIFT);
+	OUT_BATCH(dw);
 
 	/* u1, v1, w1 */
 	if (has_mask) {
-		OUT_BATCH((id << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID |
-			  (src_format << VE0_FORMAT_SHIFT) |
-			  (((1 + selem) * 4) << VE0_OFFSET_SHIFT)); /* vb offset in bytes */
-		OUT_BATCH((GEN5_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) |
-			  (GEN5_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) |
-			  (w_component << VE1_VFCOMPONENT_2_SHIFT) |
-			  (GEN5_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT));
+		offset += (id & 3) * sizeof(float);
+		DBG(("%s: id=%x, second channel %d floats, offset=%d\n", __FUNCTION__,
+		     id, (id >> 2) & 3, offset));
+		dw = VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT;
+		switch ((id >> 2) & 3) {
+		case 1:
+			format = GEN5_SURFACEFORMAT_R32_FLOAT << VE0_FORMAT_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+			dw |= VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_1_SHIFT;
+			dw |= VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT;
+			break;
+		default:
+			assert(0);
+		case 2:
+			format = GEN5_SURFACEFORMAT_R32G32_FLOAT << VE0_FORMAT_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+			dw |= VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_2_SHIFT;
+			break;
+		case 3:
+			format = GEN5_SURFACEFORMAT_R32G32B32_FLOAT << VE0_FORMAT_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT;
+			dw |= VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_2_SHIFT;
+			break;
+		}
+		OUT_BATCH(id << VE0_VERTEX_BUFFER_INDEX_SHIFT | VE0_VALID |
+			  format | offset << VE0_OFFSET_SHIFT);
+		OUT_BATCH(dw);
 	}
 }
 
@@ -1698,7 +1247,7 @@ gen5_render_video(struct sna *sna,
 	tmp.mask.bo = NULL;
 	tmp.u.gen5.wm_kernel =
 		is_planar_fourcc(frame->id) ? WM_KERNEL_VIDEO_PLANAR : WM_KERNEL_VIDEO_PACKED;
-	tmp.u.gen5.ve_id = 1;
+	tmp.u.gen5.ve_id = 2;
 	tmp.is_affine = true;
 	tmp.floats_per_vertex = 3;
 	tmp.floats_per_rect = 9;
@@ -1759,7 +1308,7 @@ gen5_render_video(struct sna *sna,
 	}
 	priv->clear = false;
 
-	gen5_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	return true;
 }
 
@@ -2001,8 +1550,8 @@ static void
 gen5_render_composite_done(struct sna *sna,
 			   const struct sna_composite_op *op)
 {
-	if (sna->render_state.gen5.vertex_offset) {
-		gen5_vertex_flush(sna);
+	if (sna->render.vertex_offset) {
+		gen4_vertex_flush(sna);
 		gen5_magic_ca_pass(sna,op);
 	}
 
@@ -2388,7 +1937,6 @@ gen5_render_composite(struct sna *sna,
 	tmp->has_component_alpha = false;
 	tmp->need_magic_ca_pass = false;
 
-	tmp->prim_emit = gen5_emit_composite_primitive;
 	if (mask) {
 		if (mask->componentAlpha && PICT_FORMAT_RGB(mask->format)) {
 			tmp->has_component_alpha = true;
@@ -2432,33 +1980,15 @@ gen5_render_composite(struct sna *sna,
 		}
 
 		tmp->is_affine &= tmp->mask.is_affine;
-
-		if (tmp->src.transform == NULL && tmp->mask.transform == NULL) {
-			if (tmp->src.is_solid)
-				tmp->prim_emit = gen5_emit_composite_primitive_identity_mask;
-			else
-				tmp->prim_emit = gen5_emit_composite_primitive_identity_source_mask;
-		}
-
-		tmp->floats_per_vertex = 5 + 2 * !tmp->is_affine;
-	} else {
-		if (tmp->src.is_solid)
-			tmp->prim_emit = gen5_emit_composite_primitive_solid;
-		else if (tmp->src.transform == NULL)
-			tmp->prim_emit = gen5_emit_composite_primitive_identity_source;
-		else if (tmp->src.is_affine)
-			tmp->prim_emit = gen5_emit_composite_primitive_affine_source;
-
-		tmp->floats_per_vertex = 3 + !tmp->is_affine;
 	}
-	tmp->floats_per_rect = 3*tmp->floats_per_vertex;
+	gen4_choose_composite_emitter(tmp);
 
 	tmp->u.gen5.wm_kernel =
 		gen5_choose_composite_kernel(tmp->op,
 					     tmp->mask.bo != NULL,
 					     tmp->has_component_alpha,
 					     tmp->is_affine);
-	tmp->u.gen5.ve_id = (tmp->mask.bo != NULL) << 1 | tmp->is_affine;
+	tmp->u.gen5.ve_id = gen4_choose_composite_vertex_buffer(tmp);
 
 	tmp->blt   = gen5_render_composite_blt;
 	tmp->box   = gen5_render_composite_box;
@@ -2490,122 +2020,6 @@ cleanup_dst:
 }
 
 #if !NO_COMPOSITE_SPANS
-inline static void
-gen5_emit_composite_texcoord(struct sna *sna,
-			     const struct sna_composite_channel *channel,
-			     int16_t x, int16_t y)
-{
-	float t[3];
-
-	if (channel->is_affine) {
-		sna_get_transformed_coordinates(x + channel->offset[0],
-						y + channel->offset[1],
-						channel->transform,
-						&t[0], &t[1]);
-		OUT_VERTEX_F(t[0] * channel->scale[0]);
-		OUT_VERTEX_F(t[1] * channel->scale[1]);
-	} else {
-		t[0] = t[1] = 0; t[2] = 1;
-		sna_get_transformed_coordinates_3d(x + channel->offset[0],
-						   y + channel->offset[1],
-						   channel->transform,
-						   &t[0], &t[1], &t[2]);
-		OUT_VERTEX_F(t[0] * channel->scale[0]);
-		OUT_VERTEX_F(t[1] * channel->scale[1]);
-		OUT_VERTEX_F(t[2]);
-	}
-}
-
-inline static void
-gen5_emit_composite_texcoord_affine(struct sna *sna,
-				    const struct sna_composite_channel *channel,
-				    int16_t x, int16_t y)
-{
-	float t[2];
-
-	sna_get_transformed_coordinates(x + channel->offset[0],
-					y + channel->offset[1],
-					channel->transform,
-					&t[0], &t[1]);
-	OUT_VERTEX_F(t[0] * channel->scale[0]);
-	OUT_VERTEX_F(t[1] * channel->scale[1]);
-}
-
-inline static void
-gen5_emit_composite_spans_vertex(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 int16_t x, int16_t y)
-{
-	OUT_VERTEX(x, y);
-	gen5_emit_composite_texcoord(sna, &op->base.src, x, y);
-}
-
-fastcall static void
-gen5_emit_composite_spans_primitive(struct sna *sna,
-				    const struct sna_composite_spans_op *op,
-				    const BoxRec *box,
-				    float opacity)
-{
-	gen5_emit_composite_spans_vertex(sna, op, box->x2, box->y2);
-	OUT_VERTEX_F(opacity);
-	OUT_VERTEX_F(1);
-	if (!op->base.is_affine)
-		OUT_VERTEX_F(1);
-
-	gen5_emit_composite_spans_vertex(sna, op, box->x1, box->y2);
-	OUT_VERTEX_F(opacity);
-	OUT_VERTEX_F(1);
-	if (!op->base.is_affine)
-		OUT_VERTEX_F(1);
-
-	gen5_emit_composite_spans_vertex(sna, op, box->x1, box->y1);
-	OUT_VERTEX_F(opacity);
-	OUT_VERTEX_F(0);
-	if (!op->base.is_affine)
-		OUT_VERTEX_F(1);
-}
-
-fastcall static void
-gen5_emit_composite_spans_solid(struct sna *sna,
-				const struct sna_composite_spans_op *op,
-				const BoxRec *box,
-				float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	OUT_VERTEX_F(1); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y2);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y1);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(0);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(0);
-}
-
-fastcall static void
-gen5_emit_composite_spans_affine(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 const BoxRec *box,
-				 float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	gen5_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x2, box->y2);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y2);
-	gen5_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y2);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(1);
-
-	OUT_VERTEX(box->x1, box->y1);
-	gen5_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y1);
-	OUT_VERTEX_F(opacity); OUT_VERTEX_F(0);
-}
-
 fastcall static void
 gen5_render_composite_spans_box(struct sna *sna,
 				const struct sna_composite_spans_op *op,
@@ -2658,15 +2072,12 @@ fastcall static void
 gen5_render_composite_spans_done(struct sna *sna,
 				 const struct sna_composite_spans_op *op)
 {
-	if (sna->render_state.gen5.vertex_offset)
-		gen5_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 
 	DBG(("%s()\n", __FUNCTION__));
 
-	kgem_bo_destroy(&sna->kgem, op->base.mask.bo);
-	if (op->base.src.bo)
-		kgem_bo_destroy(&sna->kgem, op->base.src.bo);
-
+	kgem_bo_destroy(&sna->kgem, op->base.src.bo);
 	sna_render_composite_redirect_done(sna, &op->base);
 }
 
@@ -2757,24 +2168,16 @@ gen5_render_composite_spans(struct sna *sna,
 		break;
 	}
 
-	tmp->base.mask.bo = sna_render_get_solid(sna, 0);
-	if (tmp->base.mask.bo == NULL)
-		goto cleanup_src;
+	tmp->base.mask.bo = NULL;
 
 	tmp->base.is_affine = tmp->base.src.is_affine;
 	tmp->base.has_component_alpha = false;
 	tmp->base.need_magic_ca_pass = false;
 
-	tmp->prim_emit = gen5_emit_composite_spans_primitive;
-	if (tmp->base.src.is_solid)
-		tmp->prim_emit = gen5_emit_composite_spans_solid;
-	else if (tmp->base.is_affine)
-		tmp->prim_emit = gen5_emit_composite_spans_affine;
-	tmp->base.floats_per_vertex = 5 + 2*!tmp->base.is_affine;
-	tmp->base.floats_per_rect = 3 * tmp->base.floats_per_vertex;
+	gen4_choose_spans_emitter(tmp);
 
 	tmp->base.u.gen5.wm_kernel = WM_KERNEL_OPACITY | !tmp->base.is_affine;
-	tmp->base.u.gen5.ve_id = 1 << 1 | tmp->base.is_affine;
+	tmp->base.u.gen5.ve_id = gen4_choose_spans_vertex_buffer(&tmp->base);
 
 	tmp->box   = gen5_render_composite_spans_box;
 	tmp->boxes = gen5_render_composite_spans_boxes;
@@ -2952,7 +2355,7 @@ fallback_blt:
 	tmp.floats_per_vertex = 3;
 	tmp.floats_per_rect = 9;
 	tmp.u.gen5.wm_kernel = WM_KERNEL;
-	tmp.u.gen5.ve_id = 1;
+	tmp.u.gen5.ve_id = 2;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, src_bo, NULL)) {
 		kgem_submit(&sna->kgem);
@@ -2998,7 +2401,7 @@ fallback_blt:
 		} while (--n_this_time);
 	} while (n);
 
-	gen5_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	sna_render_composite_redirect_done(sna, &tmp);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 	return true;
@@ -3044,8 +2447,8 @@ static void
 gen5_render_copy_done(struct sna *sna,
 		      const struct sna_copy_op *op)
 {
-	if (sna->render_state.gen5.vertex_offset)
-		gen5_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 
 	DBG(("%s()\n", __FUNCTION__));
 }
@@ -3108,7 +2511,7 @@ fallback:
 	op->base.floats_per_vertex = 3;
 	op->base.floats_per_rect = 9;
 	op->base.u.gen5.wm_kernel = WM_KERNEL;
-	op->base.u.gen5.ve_id = 1;
+	op->base.u.gen5.ve_id = 2;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, src_bo, NULL))  {
 		kgem_submit(&sna->kgem);
@@ -3255,7 +2658,7 @@ gen5_render_fill_boxes(struct sna *sna,
 	tmp.floats_per_vertex = 3;
 	tmp.floats_per_rect = 9;
 	tmp.u.gen5.wm_kernel = WM_KERNEL;
-	tmp.u.gen5.ve_id = 1;
+	tmp.u.gen5.ve_id = 2;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) {
 		kgem_submit(&sna->kgem);
@@ -3291,7 +2694,7 @@ gen5_render_fill_boxes(struct sna *sna,
 		} while (--n_this_time);
 	} while (n);
 
-	gen5_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 	return true;
 }
@@ -3378,8 +2781,8 @@ static void
 gen5_render_fill_op_done(struct sna *sna,
 			 const struct sna_fill_op *op)
 {
-	if (sna->render_state.gen5.vertex_offset)
-		gen5_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, op->base.src.bo);
 
 	DBG(("%s()\n", __FUNCTION__));
@@ -3437,7 +2840,7 @@ gen5_render_fill(struct sna *sna, uint8_t alu,
 	op->base.floats_per_vertex = 3;
 	op->base.floats_per_rect = 9;
 	op->base.u.gen5.wm_kernel = WM_KERNEL;
-	op->base.u.gen5.ve_id = 1;
+	op->base.u.gen5.ve_id = 2;
 
 	if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) {
 		kgem_submit(&sna->kgem);
@@ -3528,7 +2931,7 @@ gen5_render_fill_one(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo,
 	tmp.need_magic_ca_pass = false;
 
 	tmp.u.gen5.wm_kernel = WM_KERNEL;
-	tmp.u.gen5.ve_id = 1;
+	tmp.u.gen5.ve_id = 2;
 
 	if (!kgem_check_bo(&sna->kgem, bo, NULL)) {
 		_kgem_submit(&sna->kgem);
@@ -3553,7 +2956,7 @@ gen5_render_fill_one(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo,
 	OUT_VERTEX_F(0);
 	OUT_VERTEX_F(0);
 
-	gen5_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 
 	return true;
@@ -3562,7 +2965,10 @@ gen5_render_fill_one(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo,
 static void
 gen5_render_flush(struct sna *sna)
 {
-	gen5_vertex_close(sna);
+	gen4_vertex_close(sna);
+
+	assert(sna->render.vb_id == 0);
+	assert(sna->render.vertex_offset == 0);
 }
 
 static void
@@ -3633,7 +3039,6 @@ gen5_render_expire(struct kgem *kgem)
 static void gen5_render_reset(struct sna *sna)
 {
 	sna->render_state.gen5.needs_invariant = true;
-	sna->render_state.gen5.vb_id = 0;
 	sna->render_state.gen5.ve_id = -1;
 	sna->render_state.gen5.last_primitive = -1;
 	sna->render_state.gen5.last_pipelined_pointers = 0;
@@ -3899,7 +3304,9 @@ bool gen5_render_init(struct sna *sna)
 	sna->kgem.retire = gen5_render_retire;
 	sna->kgem.expire = gen5_render_expire;
 
+#if !NO_COMPOSITE
 	sna->render.composite = gen5_render_composite;
+#endif
 #if !NO_COMPOSITE_SPANS
 	sna->render.check_composite_spans = gen5_check_composite_spans;
 	sna->render.composite_spans = gen5_render_composite_spans;
diff --git a/src/sna/gen5_render.h b/src/sna/gen5_render.h
index b6e5b0c..31caafc 100644
--- a/src/sna/gen5_render.h
+++ b/src/sna/gen5_render.h
@@ -749,15 +749,14 @@
 #define GEN5_VERTEXBUFFER_ACCESS_VERTEXDATA     0
 #define GEN5_VERTEXBUFFER_ACCESS_INSTANCEDATA   1
 
-#define GEN5_VFCOMPONENT_NOSTORE      0
-#define GEN5_VFCOMPONENT_STORE_SRC    1
-#define GEN5_VFCOMPONENT_STORE_0      2
-#define GEN5_VFCOMPONENT_STORE_1_FLT  3
-#define GEN5_VFCOMPONENT_STORE_1_INT  4
-#define GEN5_VFCOMPONENT_STORE_VID    5
-#define GEN5_VFCOMPONENT_STORE_IID    6
-#define GEN5_VFCOMPONENT_STORE_PID    7
-
+#define VFCOMPONENT_NOSTORE      0
+#define VFCOMPONENT_STORE_SRC    1
+#define VFCOMPONENT_STORE_0      2
+#define VFCOMPONENT_STORE_1_FLT  3
+#define VFCOMPONENT_STORE_1_INT  4
+#define VFCOMPONENT_STORE_VID    5
+#define VFCOMPONENT_STORE_IID    6
+#define VFCOMPONENT_STORE_PID    7
 
 
 /* Execution Unit (EU) defines
diff --git a/src/sna/gen6_render.c b/src/sna/gen6_render.c
index 5a60787..647ef50 100644
--- a/src/sna/gen6_render.c
+++ b/src/sna/gen6_render.c
@@ -42,6 +42,7 @@
 
 #include "brw/brw.h"
 #include "gen6_render.h"
+#include "gen4_vertex.h"
 
 #define NO_COMPOSITE 0
 #define NO_COMPOSITE_SPANS 0
@@ -947,155 +948,6 @@ static void gen6_magic_ca_pass(struct sna *sna,
 	state->last_primitive = sna->kgem.nbatch;
 }
 
-static void gen6_vertex_flush(struct sna *sna)
-{
-	assert(sna->render_state.gen6.vertex_offset);
-
-	DBG(("%s[%x] = %d\n", __FUNCTION__,
-	     4*sna->render_state.gen6.vertex_offset,
-	     sna->render.vertex_index - sna->render.vertex_start));
-	sna->kgem.batch[sna->render_state.gen6.vertex_offset] =
-		sna->render.vertex_index - sna->render.vertex_start;
-	sna->render_state.gen6.vertex_offset = 0;
-}
-
-static int gen6_vertex_finish(struct sna *sna)
-{
-	struct kgem_bo *bo;
-	unsigned int i;
-
-	DBG(("%s: used=%d / %d\n", __FUNCTION__,
-	     sna->render.vertex_used, sna->render.vertex_size));
-	assert(sna->render.vertex_used);
-	assert(sna->render.nvertex_reloc);
-
-	/* Note: we only need dword alignment (currently) */
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render_state.gen6.vertex_offset)
-			gen6_vertex_flush(sna);
-
-		for (i = 0; i < sna->render.nvertex_reloc; i++) {
-			DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-			     i, sna->render.vertex_reloc[i]));
-
-			sna->kgem.batch[sna->render.vertex_reloc[i]] =
-				kgem_add_reloc(&sna->kgem,
-					       sna->render.vertex_reloc[i], bo,
-					       I915_GEM_DOMAIN_VERTEX << 16,
-					       0);
-		}
-
-		sna->render.nvertex_reloc = 0;
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-		sna->render.vbo = NULL;
-		sna->render_state.gen6.vb_id = 0;
-
-		kgem_bo_destroy(&sna->kgem, bo);
-	}
-
-	sna->render.vertices = NULL;
-	sna->render.vbo = kgem_create_linear(&sna->kgem,
-					     256*1024, CREATE_GTT_MAP);
-	if (sna->render.vbo)
-		sna->render.vertices = kgem_bo_map(&sna->kgem, sna->render.vbo);
-	if (sna->render.vertices == NULL) {
-		if (sna->render.vbo)
-			kgem_bo_destroy(&sna->kgem, sna->render.vbo);
-		sna->render.vbo = NULL;
-		return 0;
-	}
-
-	DBG(("%s: create vbo handle=%d\n", __FUNCTION__, sna->render.vbo->handle));
-
-	kgem_bo_sync__cpu(&sna->kgem, sna->render.vbo);
-	if (sna->render.vertex_used) {
-		DBG(("%s: copying initial buffer x %d to handle=%d\n",
-		     __FUNCTION__,
-		     sna->render.vertex_used,
-		     sna->render.vbo->handle));
-		memcpy(sna->render.vertices,
-		       sna->render.vertex_data,
-		       sizeof(float)*sna->render.vertex_used);
-	}
-	sna->render.vertex_size = 64 * 1024 - 1;
-	return sna->render.vertex_size - sna->render.vertex_used;
-}
-
-static void gen6_vertex_close(struct sna *sna)
-{
-	struct kgem_bo *bo, *free_bo = NULL;
-	unsigned int i, delta = 0;
-
-	assert(sna->render_state.gen6.vertex_offset == 0);
-
-	if (!sna->render_state.gen6.vb_id)
-		return;
-
-	DBG(("%s: used=%d, vbo active? %d\n",
-	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo ? sna->render.vbo->handle : 0));
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render.vertex_size - sna->render.vertex_used < 64) {
-			DBG(("%s: discarding vbo (full), handle=%d\n", __FUNCTION__, sna->render.vbo->handle));
-			sna->render.vbo = NULL;
-			sna->render.vertices = sna->render.vertex_data;
-			sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
-			free_bo = bo;
-		}
-	} else {
-		if (sna->kgem.nbatch + sna->render.vertex_used <= sna->kgem.surface) {
-			DBG(("%s: copy to batch: %d @ %d\n", __FUNCTION__,
-			     sna->render.vertex_used, sna->kgem.nbatch));
-			memcpy(sna->kgem.batch + sna->kgem.nbatch,
-			       sna->render.vertex_data,
-			       sna->render.vertex_used * 4);
-			delta = sna->kgem.nbatch * 4;
-			bo = NULL;
-			sna->kgem.nbatch += sna->render.vertex_used;
-		} else {
-			bo = kgem_create_linear(&sna->kgem,
-						4*sna->render.vertex_used,
-						CREATE_NO_THROTTLE);
-			if (bo && !kgem_bo_write(&sna->kgem, bo,
-						 sna->render.vertex_data,
-						 4*sna->render.vertex_used)) {
-				kgem_bo_destroy(&sna->kgem, bo);
-				bo = NULL;
-			}
-			DBG(("%s: new vbo: %d\n", __FUNCTION__,
-			     sna->render.vertex_used));
-			free_bo = bo;
-		}
-	}
-
-	assert(sna->render.nvertex_reloc);
-	for (i = 0; i < sna->render.nvertex_reloc; i++) {
-		DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-		     i, sna->render.vertex_reloc[i]));
-
-		sna->kgem.batch[sna->render.vertex_reloc[i]] =
-			kgem_add_reloc(&sna->kgem,
-				       sna->render.vertex_reloc[i], bo,
-				       I915_GEM_DOMAIN_VERTEX << 16,
-				       delta);
-	}
-	sna->render.nvertex_reloc = 0;
-
-	if (sna->render.vbo == NULL) {
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
-	}
-
-	if (free_bo)
-		kgem_bo_destroy(&sna->kgem, free_bo);
-}
-
 typedef struct gen6_surface_state_padded {
 	struct gen6_surface_state state;
 	char pad[32 - sizeof(struct gen6_surface_state)];
@@ -1259,293 +1111,6 @@ gen6_bind_bo(struct sna *sna,
 	return offset * sizeof(uint32_t);
 }
 
-fastcall static void
-gen6_emit_composite_primitive_solid(struct sna *sna,
-				    const struct sna_composite_op *op,
-				    const struct sna_composite_rectangles *r)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	DBG(("%s: [%d+9] = (%d, %d)x(%d, %d)\n", __FUNCTION__,
-	     sna->render.vertex_used, r->dst.x, r->dst.y, r->width, r->height));
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-	assert(!too_large(op->dst.x + r->dst.x + r->width,
-			  op->dst.y + r->dst.y + r->height));
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-
-	v[5] = v[2] = v[1] = 1.;
-	v[8] = v[7] = v[4] = 0.;
-}
-
-fastcall static void
-gen6_emit_composite_primitive_identity_source(struct sna *sna,
-					      const struct sna_composite_op *op,
-					      const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float *v;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-
-	v[7] = v[4] = (r->src.x + op->src.offset[0]) * op->src.scale[0];
-	v[1] = v[4] + r->width * op->src.scale[0];
-
-	v[8] = (r->src.y + op->src.offset[1]) * op->src.scale[1];
-	v[5] = v[2] = v[8] + r->height * op->src.scale[1];
-}
-
-fastcall static void
-gen6_emit_composite_primitive_simple_source(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	float xx = op->src.transform->matrix[0][0];
-	float x0 = op->src.transform->matrix[0][2];
-	float yy = op->src.transform->matrix[1][1];
-	float y0 = op->src.transform->matrix[1][2];
-	float sx = op->src.scale[0];
-	float sy = op->src.scale[1];
-	int16_t tx = op->src.offset[0];
-	int16_t ty = op->src.offset[1];
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 3*3;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = ((r->src.x + r->width + tx) * xx + x0) * sx;
-	v[5] = v[2] = ((r->src.y + r->height + ty) * yy + y0) * sy;
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	v[7] = v[4] = ((r->src.x + tx) * xx + x0) * sx;
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	v[8] = ((r->src.y + ty) * yy + y0) * sy;
-}
-
-fastcall static void
-gen6_emit_composite_primitive_affine_source(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float *v;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x + r->width,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[1], &v[2]);
-	v[1] *= op->src.scale[0];
-	v[2] *= op->src.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[4], &v[5]);
-	v[4] *= op->src.scale[0];
-	v[5] *= op->src.scale[1];
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y,
-					 op->src.transform,
-					 &v[7], &v[8]);
-	v[7] *= op->src.scale[0];
-	v[8] *= op->src.scale[1];
-}
-
-fastcall static void
-gen6_emit_composite_primitive_identity_mask(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[9] = v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[13] = v[8] = msk_x * op->mask.scale[0];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[14] = msk_y * op->mask.scale[1];
-
-	v[7] = v[2] = v[1] = 1;
-	v[12] = v[11] = v[6] = 0;
-}
-
-fastcall static void
-gen6_emit_composite_primitive_identity_source_mask(struct sna *sna,
-						   const struct sna_composite_op *op,
-						   const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float src_x, src_y;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	src_x = r->src.x + op->src.offset[0];
-	src_y = r->src.y + op->src.offset[1];
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = (src_x + w) * op->src.scale[0];
-	v[2] = (src_y + h) * op->src.scale[1];
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[6] = src_x * op->src.scale[0];
-	v[7] = v[2];
-	v[8] = msk_x * op->mask.scale[0];
-	v[9] = v[4];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[11] = v[6];
-	v[12] = src_y * op->src.scale[1];
-	v[13] = v[8];
-	v[14] = msk_y * op->mask.scale[1];
-}
-
-inline static void
-gen6_emit_composite_texcoord(struct sna *sna,
-			     const struct sna_composite_channel *channel,
-			     int16_t x, int16_t y)
-{
-	x += channel->offset[0];
-	y += channel->offset[1];
-
-	if (channel->is_affine) {
-		float s, t;
-
-		sna_get_transformed_coordinates(x, y,
-						channel->transform,
-						&s, &t);
-		OUT_VERTEX_F(s * channel->scale[0]);
-		OUT_VERTEX_F(t * channel->scale[1]);
-	} else {
-		float s, t, w;
-
-		sna_get_transformed_coordinates_3d(x, y,
-						   channel->transform,
-						   &s, &t, &w);
-		OUT_VERTEX_F(s * channel->scale[0]);
-		OUT_VERTEX_F(t * channel->scale[1]);
-		OUT_VERTEX_F(w);
-	}
-}
-
-static void
-gen6_emit_composite_vertex(struct sna *sna,
-			   const struct sna_composite_op *op,
-			   int16_t srcX, int16_t srcY,
-			   int16_t mskX, int16_t mskY,
-			   int16_t dstX, int16_t dstY)
-{
-	OUT_VERTEX(dstX, dstY);
-	gen6_emit_composite_texcoord(sna, &op->src, srcX, srcY);
-	gen6_emit_composite_texcoord(sna, &op->mask, mskX, mskY);
-}
-
-fastcall static void
-gen6_emit_composite_primitive(struct sna *sna,
-			      const struct sna_composite_op *op,
-			      const struct sna_composite_rectangles *r)
-{
-	gen6_emit_composite_vertex(sna, op,
-				   r->src.x + r->width,  r->src.y + r->height,
-				   r->mask.x + r->width, r->mask.y + r->height,
-				   r->dst.x + r->width, r->dst.y + r->height);
-	gen6_emit_composite_vertex(sna, op,
-				   r->src.x,  r->src.y + r->height,
-				   r->mask.x, r->mask.y + r->height,
-				   r->dst.x,  r->dst.y + r->height);
-	gen6_emit_composite_vertex(sna, op,
-				   r->src.x,  r->src.y,
-				   r->mask.x, r->mask.y,
-				   r->dst.x,  r->dst.y);
-}
-
 static void gen6_emit_vertex_buffer(struct sna *sna,
 				    const struct sna_composite_op *op)
 {
@@ -1559,7 +1124,7 @@ static void gen6_emit_vertex_buffer(struct sna *sna,
 	OUT_BATCH(~0); /* max address: disabled */
 	OUT_BATCH(0);
 
-	sna->render_state.gen6.vb_id |= 1 << id;
+	sna->render.vb_id |= 1 << id;
 }
 
 static void gen6_emit_primitive(struct sna *sna)
@@ -1569,7 +1134,7 @@ static void gen6_emit_primitive(struct sna *sna)
 		     __FUNCTION__,
 		     sna->render.vertex_start,
 		     sna->render.vertex_index));
-		sna->render_state.gen6.vertex_offset = sna->kgem.nbatch - 5;
+		sna->render.vertex_offset = sna->kgem.nbatch - 5;
 		return;
 	}
 
@@ -1578,7 +1143,7 @@ static void gen6_emit_primitive(struct sna *sna)
 		  _3DPRIM_RECTLIST << GEN6_3DPRIMITIVE_TOPOLOGY_SHIFT |
 		  0 << 9 |
 		  4);
-	sna->render_state.gen6.vertex_offset = sna->kgem.nbatch;
+	sna->render.vertex_offset = sna->kgem.nbatch;
 	OUT_BATCH(0);	/* vertex count, to be filled in later */
 	OUT_BATCH(sna->render.vertex_index);
 	OUT_BATCH(1);	/* single instance */
@@ -1598,12 +1163,12 @@ static bool gen6_rectangle_begin(struct sna *sna,
 	int ndwords;
 
 	ndwords = op->need_magic_ca_pass ? 60 : 6;
-	if ((sna->render_state.gen6.vb_id & id) == 0)
+	if ((sna->render.vb_id & id) == 0)
 		ndwords += 5;
 	if (!kgem_check_batch(&sna->kgem, ndwords))
 		return false;
 
-	if ((sna->render_state.gen6.vb_id & id) == 0)
+	if ((sna->render.vb_id & id) == 0)
 		gen6_emit_vertex_buffer(sna, op);
 
 	gen6_emit_primitive(sna);
@@ -1615,15 +1180,15 @@ static int gen6_get_rectangles__flush(struct sna *sna,
 {
 	if (!kgem_check_batch(&sna->kgem, op->need_magic_ca_pass ? 65 : 5))
 		return 0;
-	if (!kgem_check_exec(&sna->kgem, 1))
+	if (!kgem_check_exec(&sna->kgem, 2))
 		return 0;
-	if (!kgem_check_reloc(&sna->kgem, 2))
+	if (!kgem_check_reloc(&sna->kgem, 4))
 		return 0;
 
 	if (op->need_magic_ca_pass && sna->render.vbo)
 		return 0;
 
-	return gen6_vertex_finish(sna);
+	return gen4_vertex_finish(sna);
 }
 
 inline static int gen6_get_rectangles(struct sna *sna,
@@ -1643,7 +1208,7 @@ start:
 			goto flush;
 	}
 
-	if (unlikely(sna->render_state.gen6.vertex_offset == 0 &&
+	if (unlikely(sna->render.vertex_offset == 0 &&
 		     !gen6_rectangle_begin(sna, op)))
 		goto flush;
 
@@ -1655,8 +1220,8 @@ start:
 	return want;
 
 flush:
-	if (sna->render_state.gen6.vertex_offset) {
-		gen6_vertex_flush(sna);
+	if (sna->render.vertex_offset) {
+		gen4_vertex_flush(sna);
 		gen6_magic_ca_pass(sna, op);
 	}
 	_kgem_submit(&sna->kgem);
@@ -1681,16 +1246,6 @@ inline static uint32_t *gen6_composite_get_binding_table(struct sna *sna,
 	return table;
 }
 
-static uint32_t
-gen6_choose_composite_vertex_buffer(const struct sna_composite_op *op)
-{
-	int id = 2 + !op->is_affine;
-	if (op->mask.bo)
-		id |= id << 2;
-	assert(id > 0 && id < 16);
-	return id;
-}
-
 static bool
 gen6_get_batch(struct sna *sna, const struct sna_composite_op *op)
 {
@@ -1755,11 +1310,10 @@ static void gen6_emit_composite_state(struct sna *sna,
 static void
 gen6_align_vertex(struct sna *sna, const struct sna_composite_op *op)
 {
-	assert (sna->render_state.gen6.vertex_offset == 0);
+	assert (sna->render.vertex_offset == 0);
 	if (op->floats_per_vertex != sna->render_state.gen6.floats_per_vertex) {
 		if (sna->render.vertex_size - sna->render.vertex_used < 2*op->floats_per_rect)
-			/* XXX propagate failure */
-			gen6_vertex_finish(sna);
+			gen4_vertex_finish(sna);
 
 		DBG(("aligning vertex: was %d, now %d floats per vertex, %d->%d\n",
 		     sna->render_state.gen6.floats_per_vertex,
@@ -2085,7 +1639,7 @@ gen6_render_video(struct sna *sna,
 	}
 	priv->clear = false;
 
-	gen6_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	return true;
 }
 
@@ -2335,8 +1889,8 @@ static void gen6_render_composite_done(struct sna *sna,
 {
 	DBG(("%s\n", __FUNCTION__));
 
-	if (sna->render_state.gen6.vertex_offset) {
-		gen6_vertex_flush(sna);
+	if (sna->render.vertex_offset) {
+		gen4_vertex_flush(sna);
 		gen6_magic_ca_pass(sna, op);
 	}
 
@@ -2758,7 +2312,6 @@ gen6_render_composite(struct sna *sna,
 	tmp->mask.filter = SAMPLER_FILTER_NEAREST;
 	tmp->mask.repeat = SAMPLER_EXTEND_NONE;
 
-	tmp->prim_emit = gen6_emit_composite_primitive;
 	if (mask) {
 		if (mask->componentAlpha && PICT_FORMAT_RGB(mask->format)) {
 			tmp->has_component_alpha = true;
@@ -2798,44 +2351,8 @@ gen6_render_composite(struct sna *sna,
 		}
 
 		tmp->is_affine &= tmp->mask.is_affine;
-
-		if (tmp->src.transform == NULL && tmp->mask.transform == NULL) {
-			if (tmp->src.is_solid)
-				tmp->prim_emit = gen6_emit_composite_primitive_identity_mask;
-			else
-				tmp->prim_emit = gen6_emit_composite_primitive_identity_source_mask;
-		}
-
-		tmp->floats_per_vertex = 5 + 2 * !tmp->is_affine;
-	} else {
-		if (tmp->src.is_solid) {
-			DBG(("%s: choosing gen6_emit_composite_primitive_solid\n",
-			     __FUNCTION__));
-			tmp->prim_emit = gen6_emit_composite_primitive_solid;
-			if (tmp->src.is_opaque && op == PictOpOver)
-				tmp->op = PictOpSrc;
-		} else if (tmp->src.transform == NULL) {
-			DBG(("%s: choosing gen6_emit_composite_primitive_identity_source\n",
-			     __FUNCTION__));
-			tmp->prim_emit = gen6_emit_composite_primitive_identity_source;
-		} else if (tmp->src.is_affine) {
-			if (tmp->src.transform->matrix[0][1] == 0 &&
-			    tmp->src.transform->matrix[1][0] == 0) {
-				tmp->src.scale[0] /= tmp->src.transform->matrix[2][2];
-				tmp->src.scale[1] /= tmp->src.transform->matrix[2][2];
-				DBG(("%s: choosing gen6_emit_composite_primitive_simple_source\n",
-				     __FUNCTION__));
-				tmp->prim_emit = gen6_emit_composite_primitive_simple_source;
-			} else {
-				DBG(("%s: choosing gen6_emit_composite_primitive_affine_source\n",
-				     __FUNCTION__));
-				tmp->prim_emit = gen6_emit_composite_primitive_affine_source;
-			}
-		}
-
-		tmp->floats_per_vertex = 3 + !tmp->is_affine;
 	}
-	tmp->floats_per_rect = 3 * tmp->floats_per_vertex;
+	gen4_choose_composite_emitter(tmp);
 
 	tmp->u.gen6.flags =
 		GEN6_SET_FLAGS(SAMPLER_OFFSET(tmp->src.filter,
@@ -2849,7 +2366,7 @@ gen6_render_composite(struct sna *sna,
 							    tmp->mask.bo != NULL,
 							    tmp->has_component_alpha,
 							    tmp->is_affine),
-			       gen6_choose_composite_vertex_buffer(tmp));
+			       gen4_choose_composite_vertex_buffer(tmp));
 
 	tmp->blt   = gen6_render_composite_blt;
 	tmp->box   = gen6_render_composite_box;
@@ -2885,167 +2402,6 @@ cleanup_dst:
 }
 
 #if !NO_COMPOSITE_SPANS
-inline static void
-gen6_emit_composite_texcoord_affine(struct sna *sna,
-				    const struct sna_composite_channel *channel,
-				    int16_t x, int16_t y)
-{
-	float t[2];
-
-	sna_get_transformed_coordinates(x + channel->offset[0],
-					y + channel->offset[1],
-					channel->transform,
-					&t[0], &t[1]);
-	OUT_VERTEX_F(t[0] * channel->scale[0]);
-	OUT_VERTEX_F(t[1] * channel->scale[1]);
-}
-
-inline static void
-gen6_emit_composite_spans_vertex(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 int16_t x, int16_t y)
-{
-	OUT_VERTEX(x, y);
-	gen6_emit_composite_texcoord(sna, &op->base.src, x, y);
-}
-
-fastcall static void
-gen6_emit_composite_spans_primitive(struct sna *sna,
-				    const struct sna_composite_spans_op *op,
-				    const BoxRec *box,
-				    float opacity)
-{
-	gen6_emit_composite_spans_vertex(sna, op, box->x2, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	gen6_emit_composite_spans_vertex(sna, op, box->x1, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	gen6_emit_composite_spans_vertex(sna, op, box->x1, box->y1);
-	OUT_VERTEX_F(opacity);
-}
-
-fastcall static void
-gen6_emit_composite_spans_solid(struct sna *sna,
-				const struct sna_composite_spans_op *op,
-				const BoxRec *box,
-				float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	OUT_VERTEX_F(1); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y2);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y1);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(0);
-	OUT_VERTEX_F(opacity);
-}
-
-fastcall static void
-gen6_emit_composite_spans_identity(struct sna *sna,
-				   const struct sna_composite_spans_op *op,
-				   const BoxRec *box,
-				   float opacity)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	float sx = op->base.src.scale[0];
-	float sy = op->base.src.scale[1];
-	int16_t tx = op->base.src.offset[0];
-	int16_t ty = op->base.src.offset[1];
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 3*4;
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-
-	dst.p.x = box->x2;
-	dst.p.y = box->y2;
-	v[0] = dst.f;
-	v[1] = (box->x2 + tx) * sx;
-	v[6] = v[2] = (box->y2 + ty) * sy;
-
-	dst.p.x = box->x1;
-	v[4] = dst.f;
-	v[9] = v[5] = (box->x1 + tx) * sx;
-
-	dst.p.y = box->y1;
-	v[8] = dst.f;
-	v[10] = (box->y1 + ty) * sy;
-
-	v[11] = v[7] = v[3] = opacity;
-}
-
-fastcall static void
-gen6_emit_composite_spans_simple(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 const BoxRec *box,
-				 float opacity)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	float xx = op->base.src.transform->matrix[0][0];
-	float x0 = op->base.src.transform->matrix[0][2];
-	float yy = op->base.src.transform->matrix[1][1];
-	float y0 = op->base.src.transform->matrix[1][2];
-	float sx = op->base.src.scale[0];
-	float sy = op->base.src.scale[1];
-	int16_t tx = op->base.src.offset[0];
-	int16_t ty = op->base.src.offset[1];
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 3*4;
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-
-	dst.p.x = box->x2;
-	dst.p.y = box->y2;
-	v[0] = dst.f;
-	v[1] = ((box->x2 + tx) * xx + x0) * sx;
-	v[6] = v[2] = ((box->y2 + ty) * yy + y0) * sy;
-
-	dst.p.x = box->x1;
-	v[4] = dst.f;
-	v[9] = v[5] = ((box->x1 + tx) * xx + x0) * sx;
-
-	dst.p.y = box->y1;
-	v[8] = dst.f;
-	v[10] = ((box->y1 + ty) * yy + y0) * sy;
-
-	v[11] = v[7] = v[3] = opacity;
-}
-
-fastcall static void
-gen6_emit_composite_spans_affine(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 const BoxRec *box,
-				 float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	gen6_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x2, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y2);
-	gen6_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y1);
-	gen6_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y1);
-	OUT_VERTEX_F(opacity);
-}
-
 fastcall static void
 gen6_render_composite_spans_box(struct sna *sna,
 				const struct sna_composite_spans_op *op,
@@ -3100,8 +2456,8 @@ gen6_render_composite_spans_done(struct sna *sna,
 {
 	DBG(("%s()\n", __FUNCTION__));
 
-	if (sna->render_state.gen6.vertex_offset)
-		gen6_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 
 	if (op->base.src.bo)
 		kgem_bo_destroy(&sna->kgem, op->base.src.bo);
@@ -3200,22 +2556,7 @@ gen6_render_composite_spans(struct sna *sna,
 	tmp->base.is_affine = tmp->base.src.is_affine;
 	tmp->base.need_magic_ca_pass = false;
 
-	tmp->prim_emit = gen6_emit_composite_spans_primitive;
-	if (tmp->base.src.is_solid) {
-		tmp->prim_emit = gen6_emit_composite_spans_solid;
-	} else if (tmp->base.src.transform == NULL) {
-		tmp->prim_emit = gen6_emit_composite_spans_identity;
-	} else if (tmp->base.is_affine) {
-		if (tmp->base.src.transform->matrix[0][1] == 0 &&
-		    tmp->base.src.transform->matrix[1][0] == 0) {
-			tmp->base.src.scale[0] /= tmp->base.src.transform->matrix[2][2];
-			tmp->base.src.scale[1] /= tmp->base.src.transform->matrix[2][2];
-			tmp->prim_emit = gen6_emit_composite_spans_simple;
-		} else
-			tmp->prim_emit = gen6_emit_composite_spans_affine;
-	}
-	tmp->base.floats_per_vertex = 4 + !tmp->base.is_affine;
-	tmp->base.floats_per_rect = 3 * tmp->base.floats_per_vertex;
+	gen4_choose_spans_emitter(tmp);
 
 	tmp->base.u.gen6.flags =
 		GEN6_SET_FLAGS(SAMPLER_OFFSET(tmp->base.src.filter,
@@ -3224,7 +2565,7 @@ gen6_render_composite_spans(struct sna *sna,
 					      SAMPLER_EXTEND_PAD),
 			       gen6_get_blend(tmp->base.op, false, tmp->base.dst.format),
 			       GEN6_WM_KERNEL_OPACITY | !tmp->base.is_affine,
-			       1 << 2 | (2+!tmp->base.is_affine));
+			       gen4_choose_spans_vertex_buffer(&tmp->base));
 
 	tmp->box   = gen6_render_composite_spans_box;
 	tmp->boxes = gen6_render_composite_spans_boxes;
@@ -3541,7 +2882,7 @@ fallback_blt:
 		} while (--n_this_time);
 	} while (n);
 
-	gen6_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	sna_render_composite_redirect_done(sna, &tmp);
 	if (tmp.src.bo != src_bo)
 		kgem_bo_destroy(&sna->kgem, tmp.src.bo);
@@ -3588,8 +2929,8 @@ gen6_render_copy_done(struct sna *sna, const struct sna_copy_op *op)
 {
 	DBG(("%s()\n", __FUNCTION__));
 
-	if (sna->render_state.gen6.vertex_offset)
-		gen6_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 }
 
 static bool
@@ -3844,7 +3185,7 @@ gen6_render_fill_boxes(struct sna *sna,
 		} while (--n_this_time);
 	} while (n);
 
-	gen6_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 	sna_render_composite_redirect_done(sna, &tmp);
 	return true;
@@ -3937,8 +3278,8 @@ gen6_render_op_fill_done(struct sna *sna, const struct sna_fill_op *op)
 {
 	DBG(("%s()\n", __FUNCTION__));
 
-	if (sna->render_state.gen6.vertex_offset)
-		gen6_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, op->base.src.bo);
 }
 
@@ -4092,7 +3433,7 @@ gen6_render_fill_one(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo,
 	v[7] = v[2]  = v[3]  = 1;
 	v[6] = v[10] = v[11] = 0;
 
-	gen6_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 
 	return true;
@@ -4174,7 +3515,7 @@ gen6_render_clear(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo)
 	v[7] = v[2]  = v[3]  = 1;
 	v[6] = v[10] = v[11] = 0;
 
-	gen6_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 
 	return true;
@@ -4182,7 +3523,10 @@ gen6_render_clear(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo)
 
 static void gen6_render_flush(struct sna *sna)
 {
-	gen6_vertex_close(sna);
+	gen4_vertex_close(sna);
+
+	assert(sna->render.vb_id == 0);
+	assert(sna->render.vertex_offset == 0);
 }
 
 static void
@@ -4234,7 +3578,6 @@ static void gen6_render_reset(struct sna *sna)
 {
 	sna->render_state.gen6.needs_invariant = true;
 	sna->render_state.gen6.first_state_packet = true;
-	sna->render_state.gen6.vb_id = 0;
 	sna->render_state.gen6.ve_id = 3 << 2;
 	sna->render_state.gen6.last_primitive = -1;
 
diff --git a/src/sna/gen7_render.c b/src/sna/gen7_render.c
index cc3199d..04ffdc5 100644
--- a/src/sna/gen7_render.c
+++ b/src/sna/gen7_render.c
@@ -42,6 +42,7 @@
 
 #include "brw/brw.h"
 #include "gen7_render.h"
+#include "gen4_vertex.h"
 
 #define NO_COMPOSITE 0
 #define NO_COMPOSITE_SPANS 0
@@ -1092,147 +1093,6 @@ static void gen7_magic_ca_pass(struct sna *sna,
 	state->last_primitive = sna->kgem.nbatch;
 }
 
-static void gen7_vertex_flush(struct sna *sna)
-{
-	assert(sna->render_state.gen7.vertex_offset);
-
-	DBG(("%s[%x] = %d\n", __FUNCTION__,
-	     4*sna->render_state.gen7.vertex_offset,
-	     sna->render.vertex_index - sna->render.vertex_start));
-	sna->kgem.batch[sna->render_state.gen7.vertex_offset] =
-		sna->render.vertex_index - sna->render.vertex_start;
-	sna->render_state.gen7.vertex_offset = 0;
-}
-
-static int gen7_vertex_finish(struct sna *sna)
-{
-	struct kgem_bo *bo;
-	unsigned int i;
-
-	assert(sna->render.vertex_used);
-	assert(sna->render.nvertex_reloc);
-
-	/* Note: we only need dword alignment (currently) */
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render_state.gen7.vertex_offset)
-			gen7_vertex_flush(sna);
-
-		for (i = 0; i < sna->render.nvertex_reloc; i++) {
-			DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-			     i, sna->render.vertex_reloc[i]));
-
-			sna->kgem.batch[sna->render.vertex_reloc[i]] =
-				kgem_add_reloc(&sna->kgem,
-					       sna->render.vertex_reloc[i], bo,
-					       I915_GEM_DOMAIN_VERTEX << 16,
-					       0);
-		}
-
-		sna->render.nvertex_reloc = 0;
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-		sna->render.vbo = NULL;
-		sna->render_state.gen7.vb_id = 0;
-
-		kgem_bo_destroy(&sna->kgem, bo);
-	}
-
-	sna->render.vertices = NULL;
-	sna->render.vbo = kgem_create_linear(&sna->kgem,
-					     256*1024, CREATE_GTT_MAP);
-	if (sna->render.vbo)
-		sna->render.vertices = kgem_bo_map(&sna->kgem, sna->render.vbo);
-	if (sna->render.vertices == NULL) {
-		if (sna->render.vbo)
-			kgem_bo_destroy(&sna->kgem, sna->render.vbo);
-		sna->render.vbo = NULL;
-		return 0;
-	}
-
-	kgem_bo_sync__cpu(&sna->kgem, sna->render.vbo);
-	if (sna->render.vertex_used) {
-		memcpy(sna->render.vertices,
-		       sna->render.vertex_data,
-		       sizeof(float)*sna->render.vertex_used);
-	}
-	sna->render.vertex_size = 64 * 1024 - 1;
-	return sna->render.vertex_size - sna->render.vertex_used;
-}
-
-static void gen7_vertex_close(struct sna *sna)
-{
-	struct kgem_bo *bo, *free_bo = NULL;
-	unsigned int i, delta = 0;
-
-	assert(sna->render_state.gen7.vertex_offset == 0);
-
-	if (!sna->render_state.gen7.vb_id)
-		return;
-
-	DBG(("%s: used=%d, vbo active? %d\n",
-	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo ? sna->render.vbo->handle : 0));
-
-	bo = sna->render.vbo;
-	if (bo) {
-		if (sna->render.vertex_size - sna->render.vertex_used < 64) {
-			DBG(("%s: discarding vbo (full), handle=%d\n", __FUNCTION__, sna->render.vbo->handle));
-			sna->render.vbo = NULL;
-			sna->render.vertices = sna->render.vertex_data;
-			sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data);
-			free_bo = bo;
-		}
-	} else {
-		if (sna->kgem.nbatch + sna->render.vertex_used <= sna->kgem.surface) {
-			DBG(("%s: copy to batch: %d @ %d\n", __FUNCTION__,
-			     sna->render.vertex_used, sna->kgem.nbatch));
-			memcpy(sna->kgem.batch + sna->kgem.nbatch,
-			       sna->render.vertex_data,
-			       sna->render.vertex_used * 4);
-			delta = sna->kgem.nbatch * 4;
-			bo = NULL;
-			sna->kgem.nbatch += sna->render.vertex_used;
-		} else {
-			bo = kgem_create_linear(&sna->kgem,
-						4*sna->render.vertex_used,
-						CREATE_NO_THROTTLE);
-			if (bo && !kgem_bo_write(&sna->kgem, bo,
-						 sna->render.vertex_data,
-						 4*sna->render.vertex_used)) {
-				kgem_bo_destroy(&sna->kgem, bo);
-				bo = NULL;
-			}
-			DBG(("%s: new vbo: %d\n", __FUNCTION__,
-			     sna->render.vertex_used));
-			free_bo = bo;
-		}
-	}
-
-	assert(sna->render.nvertex_reloc);
-	for (i = 0; i < sna->render.nvertex_reloc; i++) {
-		DBG(("%s: reloc[%d] = %d\n", __FUNCTION__,
-		     i, sna->render.vertex_reloc[i]));
-
-		sna->kgem.batch[sna->render.vertex_reloc[i]] =
-			kgem_add_reloc(&sna->kgem,
-				       sna->render.vertex_reloc[i], bo,
-				       I915_GEM_DOMAIN_VERTEX << 16,
-				       delta);
-	}
-	sna->render.nvertex_reloc = 0;
-
-	if (sna->render.vbo == NULL) {
-		sna->render.vertex_used = 0;
-		sna->render.vertex_index = 0;
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
-	}
-
-	if (free_bo)
-		kgem_bo_destroy(&sna->kgem, free_bo);
-}
-
 static void null_create(struct sna_static_stream *stream)
 {
 	/* A bunch of zeros useful for legacy border color and depth-stencil */
@@ -1384,290 +1244,6 @@ gen7_bind_bo(struct sna *sna,
 	return offset * sizeof(uint32_t);
 }
 
-fastcall static void
-gen7_emit_composite_primitive_solid(struct sna *sna,
-				    const struct sna_composite_op *op,
-				    const struct sna_composite_rectangles *r)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-	assert(!too_large(op->dst.x + r->dst.x + r->width,
-			  op->dst.y + r->dst.y + r->height));
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-
-	v[5] = v[2] = v[1] = 1.;
-	v[8] = v[7] = v[4] = 0.;
-}
-
-fastcall static void
-gen7_emit_composite_primitive_identity_source(struct sna *sna,
-					      const struct sna_composite_op *op,
-					      const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float *v;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-
-	v[7] = v[4] = (r->src.x + op->src.offset[0]) * op->src.scale[0];
-	v[1] = v[4] + r->width * op->src.scale[0];
-
-	v[8] = (r->src.y + op->src.offset[1]) * op->src.scale[1];
-	v[5] = v[2] = v[8] + r->height * op->src.scale[1];
-}
-
-fastcall static void
-gen7_emit_composite_primitive_simple_source(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	float xx = op->src.transform->matrix[0][0];
-	float x0 = op->src.transform->matrix[0][2];
-	float yy = op->src.transform->matrix[1][1];
-	float y0 = op->src.transform->matrix[1][2];
-	float sx = op->src.scale[0];
-	float sy = op->src.scale[1];
-	int16_t tx = op->src.offset[0];
-	int16_t ty = op->src.offset[1];
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 3*3;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = ((r->src.x + r->width + tx) * xx + x0) * sx;
-	v[5] = v[2] = ((r->src.y + r->height + ty) * yy + y0) * sy;
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	v[7] = v[4] = ((r->src.x + tx) * xx + x0) * sx;
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	v[8] = ((r->src.y + ty) * yy + y0) * sy;
-}
-
-fastcall static void
-gen7_emit_composite_primitive_affine_source(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float *v;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 9;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x + r->width,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[1], &v[2]);
-	v[1] *= op->src.scale[0];
-	v[2] *= op->src.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[3] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y + r->height,
-					 op->src.transform,
-					 &v[4], &v[5]);
-	v[4] *= op->src.scale[0];
-	v[5] *= op->src.scale[1];
-
-	dst.p.y = r->dst.y;
-	v[6] = dst.f;
-	_sna_get_transformed_coordinates(op->src.offset[0] + r->src.x,
-					 op->src.offset[1] + r->src.y,
-					 op->src.transform,
-					 &v[7], &v[8]);
-	v[7] *= op->src.scale[0];
-	v[8] *= op->src.scale[1];
-}
-
-fastcall static void
-gen7_emit_composite_primitive_identity_mask(struct sna *sna,
-					    const struct sna_composite_op *op,
-					    const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[9] = v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[13] = v[8] = msk_x * op->mask.scale[0];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[14] = msk_y * op->mask.scale[1];
-
-	v[7] = v[2] = v[1] = 1;
-	v[12] = v[11] = v[6] = 0;
-}
-
-fastcall static void
-gen7_emit_composite_primitive_identity_source_mask(struct sna *sna,
-						   const struct sna_composite_op *op,
-						   const struct sna_composite_rectangles *r)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-	float src_x, src_y;
-	float msk_x, msk_y;
-	float w, h;
-	float *v;
-
-	src_x = r->src.x + op->src.offset[0];
-	src_y = r->src.y + op->src.offset[1];
-	msk_x = r->mask.x + op->mask.offset[0];
-	msk_y = r->mask.y + op->mask.offset[1];
-	w = r->width;
-	h = r->height;
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 15;
-
-	dst.p.x = r->dst.x + r->width;
-	dst.p.y = r->dst.y + r->height;
-	v[0] = dst.f;
-	v[1] = (src_x + w) * op->src.scale[0];
-	v[2] = (src_y + h) * op->src.scale[1];
-	v[3] = (msk_x + w) * op->mask.scale[0];
-	v[4] = (msk_y + h) * op->mask.scale[1];
-
-	dst.p.x = r->dst.x;
-	v[5] = dst.f;
-	v[6] = src_x * op->src.scale[0];
-	v[7] = v[2];
-	v[8] = msk_x * op->mask.scale[0];
-	v[9] = v[4];
-
-	dst.p.y = r->dst.y;
-	v[10] = dst.f;
-	v[11] = v[6];
-	v[12] = src_y * op->src.scale[1];
-	v[13] = v[8];
-	v[14] = msk_y * op->mask.scale[1];
-}
-
-inline static void
-gen7_emit_composite_texcoord(struct sna *sna,
-			     const struct sna_composite_channel *channel,
-			     int16_t x, int16_t y)
-{
-	x += channel->offset[0];
-	y += channel->offset[1];
-
-	if (channel->is_affine) {
-		float s, t;
-
-		sna_get_transformed_coordinates(x, y,
-						channel->transform,
-						&s, &t);
-		OUT_VERTEX_F(s * channel->scale[0]);
-		OUT_VERTEX_F(t * channel->scale[1]);
-	} else {
-		float s, t, w;
-
-		sna_get_transformed_coordinates_3d(x, y,
-						   channel->transform,
-						   &s, &t, &w);
-		OUT_VERTEX_F(s * channel->scale[0]);
-		OUT_VERTEX_F(t * channel->scale[1]);
-		OUT_VERTEX_F(w);
-	}
-}
-
-static void
-gen7_emit_composite_vertex(struct sna *sna,
-			   const struct sna_composite_op *op,
-			   int16_t srcX, int16_t srcY,
-			   int16_t mskX, int16_t mskY,
-			   int16_t dstX, int16_t dstY)
-{
-	OUT_VERTEX(dstX, dstY);
-	gen7_emit_composite_texcoord(sna, &op->src, srcX, srcY);
-	gen7_emit_composite_texcoord(sna, &op->mask, mskX, mskY);
-}
-
-fastcall static void
-gen7_emit_composite_primitive(struct sna *sna,
-			      const struct sna_composite_op *op,
-			      const struct sna_composite_rectangles *r)
-{
-	gen7_emit_composite_vertex(sna, op,
-				   r->src.x + r->width,  r->src.y + r->height,
-				   r->mask.x + r->width, r->mask.y + r->height,
-				   r->dst.x + r->width, r->dst.y + r->height);
-	gen7_emit_composite_vertex(sna, op,
-				   r->src.x,  r->src.y + r->height,
-				   r->mask.x, r->mask.y + r->height,
-				   r->dst.x,  r->dst.y + r->height);
-	gen7_emit_composite_vertex(sna, op,
-				   r->src.x,  r->src.y,
-				   r->mask.x, r->mask.y,
-				   r->dst.x,  r->dst.y);
-}
-
 static void gen7_emit_vertex_buffer(struct sna *sna,
 				    const struct sna_composite_op *op)
 {
@@ -1683,19 +1259,19 @@ static void gen7_emit_vertex_buffer(struct sna *sna,
 	OUT_BATCH(~0); /* max address: disabled */
 	OUT_BATCH(0);
 
-	sna->render_state.gen7.vb_id |= 1 << id;
+	sna->render.vb_id |= 1 << id;
 }
 
 static void gen7_emit_primitive(struct sna *sna)
 {
 	if (sna->kgem.nbatch == sna->render_state.gen7.last_primitive) {
-		sna->render_state.gen7.vertex_offset = sna->kgem.nbatch - 5;
+		sna->render.vertex_offset = sna->kgem.nbatch - 5;
 		return;
 	}
 
 	OUT_BATCH(GEN7_3DPRIMITIVE | (7- 2));
 	OUT_BATCH(GEN7_3DPRIMITIVE_VERTEX_SEQUENTIAL | _3DPRIM_RECTLIST);
-	sna->render_state.gen7.vertex_offset = sna->kgem.nbatch;
+	sna->render.vertex_offset = sna->kgem.nbatch;
 	OUT_BATCH(0);	/* vertex count, to be filled in later */
 	OUT_BATCH(sna->render.vertex_index);
 	OUT_BATCH(1);	/* single instance */
@@ -1713,12 +1289,12 @@ static bool gen7_rectangle_begin(struct sna *sna,
 	int ndwords;
 
 	ndwords = op->need_magic_ca_pass ? 60 : 6;
-	if ((sna->render_state.gen7.vb_id & id) == 0)
+	if ((sna->render.vb_id & id) == 0)
 		ndwords += 5;
 	if (!kgem_check_batch(&sna->kgem, ndwords))
 		return false;
 
-	if ((sna->render_state.gen7.vb_id & id) == 0)
+	if ((sna->render.vb_id & id) == 0)
 		gen7_emit_vertex_buffer(sna, op);
 
 	gen7_emit_primitive(sna);
@@ -1738,7 +1314,7 @@ static int gen7_get_rectangles__flush(struct sna *sna,
 	if (op->need_magic_ca_pass && sna->render.vbo)
 		return 0;
 
-	return gen7_vertex_finish(sna);
+	return gen4_vertex_finish(sna);
 }
 
 inline static int gen7_get_rectangles(struct sna *sna,
@@ -1758,7 +1334,7 @@ start:
 			goto flush;
 	}
 
-	if (unlikely(sna->render_state.gen7.vertex_offset == 0 &&
+	if (unlikely(sna->render.vertex_offset == 0 &&
 		     !gen7_rectangle_begin(sna, op)))
 		goto flush;
 
@@ -1770,8 +1346,8 @@ start:
 	return want;
 
 flush:
-	if (sna->render_state.gen7.vertex_offset) {
-		gen7_vertex_flush(sna);
+	if (sna->render.vertex_offset) {
+		gen4_vertex_flush(sna);
 		gen7_magic_ca_pass(sna, op);
 	}
 	_kgem_submit(&sna->kgem);
@@ -1796,16 +1372,6 @@ inline static uint32_t *gen7_composite_get_binding_table(struct sna *sna,
 	return table;
 }
 
-static uint32_t
-gen7_choose_composite_vertex_buffer(const struct sna_composite_op *op)
-{
-	int id = 2 + !op->is_affine;
-	if (op->mask.bo)
-		id |= id << 2;
-	assert(id > 0 && id < 16);
-	return id;
-}
-
 static void
 gen7_get_batch(struct sna *sna, const struct sna_composite_op *op)
 {
@@ -1872,7 +1438,7 @@ gen7_align_vertex(struct sna *sna, const struct sna_composite_op *op)
 {
 	if (op->floats_per_vertex != sna->render_state.gen7.floats_per_vertex) {
 		if (sna->render.vertex_size - sna->render.vertex_used < 2*op->floats_per_rect)
-			gen7_vertex_finish(sna);
+			gen4_vertex_finish(sna);
 
 		DBG(("aligning vertex: was %d, now %d floats per vertex, %d->%d\n",
 		     sna->render_state.gen7.floats_per_vertex,
@@ -2197,7 +1763,7 @@ gen7_render_video(struct sna *sna,
 	}
 	priv->clear = false;
 
-	gen7_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	return true;
 }
 
@@ -2445,8 +2011,8 @@ static void gen7_composite_channel_convert(struct sna_composite_channel *channel
 static void gen7_render_composite_done(struct sna *sna,
 				       const struct sna_composite_op *op)
 {
-	if (sna->render_state.gen7.vertex_offset) {
-		gen7_vertex_flush(sna);
+	if (sna->render.vertex_offset) {
+		gen4_vertex_flush(sna);
 		gen7_magic_ca_pass(sna, op);
 	}
 
@@ -2875,7 +2441,6 @@ gen7_render_composite(struct sna *sna,
 	tmp->mask.filter = SAMPLER_FILTER_NEAREST;
 	tmp->mask.repeat = SAMPLER_EXTEND_NONE;
 
-	tmp->prim_emit = gen7_emit_composite_primitive;
 	if (mask) {
 		if (mask->componentAlpha && PICT_FORMAT_RGB(mask->format)) {
 			tmp->has_component_alpha = true;
@@ -2915,35 +2480,9 @@ gen7_render_composite(struct sna *sna,
 		}
 
 		tmp->is_affine &= tmp->mask.is_affine;
-
-		if (tmp->src.transform == NULL && tmp->mask.transform == NULL) {
-			if (tmp->src.is_solid)
-				tmp->prim_emit = gen7_emit_composite_primitive_identity_mask;
-			else
-				tmp->prim_emit = gen7_emit_composite_primitive_identity_source_mask;
-		}
-
-		tmp->floats_per_vertex = 5 + 2 * !tmp->is_affine;
-	} else {
-		if (tmp->src.is_solid) {
-			tmp->prim_emit = gen7_emit_composite_primitive_solid;
-			if (tmp->src.is_opaque && op == PictOpOver)
-				tmp->op = PictOpSrc;
-		} else if (tmp->src.transform == NULL)
-			tmp->prim_emit = gen7_emit_composite_primitive_identity_source;
-		else if (tmp->src.is_affine) {
-			if (tmp->src.transform->matrix[0][1] == 0 &&
-			    tmp->src.transform->matrix[1][0] == 0) {
-				tmp->src.scale[0] /= tmp->src.transform->matrix[2][2];
-				tmp->src.scale[1] /= tmp->src.transform->matrix[2][2];
-				tmp->prim_emit = gen7_emit_composite_primitive_simple_source;
-			} else
-				tmp->prim_emit = gen7_emit_composite_primitive_affine_source;
-		}
-
-		tmp->floats_per_vertex = 3 + !tmp->is_affine;
 	}
-	tmp->floats_per_rect = 3 * tmp->floats_per_vertex;
+
+	gen4_choose_composite_emitter(tmp);
 
 	tmp->u.gen7.flags =
 		GEN7_SET_FLAGS(SAMPLER_OFFSET(tmp->src.filter,
@@ -2957,7 +2496,7 @@ gen7_render_composite(struct sna *sna,
 							    tmp->mask.bo != NULL,
 							    tmp->has_component_alpha,
 							    tmp->is_affine),
-			       gen7_choose_composite_vertex_buffer(tmp));
+			       gen4_choose_composite_vertex_buffer(tmp));
 
 	tmp->blt   = gen7_render_composite_blt;
 	tmp->box   = gen7_render_composite_box;
@@ -2993,167 +2532,6 @@ cleanup_dst:
 }
 
 #if !NO_COMPOSITE_SPANS
-inline static void
-gen7_emit_composite_texcoord_affine(struct sna *sna,
-				    const struct sna_composite_channel *channel,
-				    int16_t x, int16_t y)
-{
-	float t[2];
-
-	sna_get_transformed_coordinates(x + channel->offset[0],
-					y + channel->offset[1],
-					channel->transform,
-					&t[0], &t[1]);
-	OUT_VERTEX_F(t[0] * channel->scale[0]);
-	OUT_VERTEX_F(t[1] * channel->scale[1]);
-}
-
-inline static void
-gen7_emit_composite_spans_vertex(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 int16_t x, int16_t y)
-{
-	OUT_VERTEX(x, y);
-	gen7_emit_composite_texcoord(sna, &op->base.src, x, y);
-}
-
-fastcall static void
-gen7_emit_composite_spans_primitive(struct sna *sna,
-				    const struct sna_composite_spans_op *op,
-				    const BoxRec *box,
-				    float opacity)
-{
-	gen7_emit_composite_spans_vertex(sna, op, box->x2, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	gen7_emit_composite_spans_vertex(sna, op, box->x1, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	gen7_emit_composite_spans_vertex(sna, op, box->x1, box->y1);
-	OUT_VERTEX_F(opacity);
-}
-
-fastcall static void
-gen7_emit_composite_spans_solid(struct sna *sna,
-				const struct sna_composite_spans_op *op,
-				const BoxRec *box,
-				float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	OUT_VERTEX_F(1); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y2);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(1);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y1);
-	OUT_VERTEX_F(0); OUT_VERTEX_F(0);
-	OUT_VERTEX_F(opacity);
-}
-
-fastcall static void
-gen7_emit_composite_spans_identity(struct sna *sna,
-				   const struct sna_composite_spans_op *op,
-				   const BoxRec *box,
-				   float opacity)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	float sx = op->base.src.scale[0];
-	float sy = op->base.src.scale[1];
-	int16_t tx = op->base.src.offset[0];
-	int16_t ty = op->base.src.offset[1];
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 3*4;
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-
-	dst.p.x = box->x2;
-	dst.p.y = box->y2;
-	v[0] = dst.f;
-	v[1] = (box->x2 + tx) * sx;
-	v[6] = v[2] = (box->y2 + ty) * sy;
-
-	dst.p.x = box->x1;
-	v[4] = dst.f;
-	v[9] = v[5] = (box->x1 + tx) * sx;
-
-	dst.p.y = box->y1;
-	v[8] = dst.f;
-	v[10] = (box->y1 + ty) * sy;
-
-	v[11] = v[7] = v[3] = opacity;
-}
-
-fastcall static void
-gen7_emit_composite_spans_simple(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 const BoxRec *box,
-				 float opacity)
-{
-	float *v;
-	union {
-		struct sna_coordinate p;
-		float f;
-	} dst;
-
-	float xx = op->base.src.transform->matrix[0][0];
-	float x0 = op->base.src.transform->matrix[0][2];
-	float yy = op->base.src.transform->matrix[1][1];
-	float y0 = op->base.src.transform->matrix[1][2];
-	float sx = op->base.src.scale[0];
-	float sy = op->base.src.scale[1];
-	int16_t tx = op->base.src.offset[0];
-	int16_t ty = op->base.src.offset[1];
-
-	v = sna->render.vertices + sna->render.vertex_used;
-	sna->render.vertex_used += 3*4;
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-
-	dst.p.x = box->x2;
-	dst.p.y = box->y2;
-	v[0] = dst.f;
-	v[1] = ((box->x2 + tx) * xx + x0) * sx;
-	v[6] = v[2] = ((box->y2 + ty) * yy + y0) * sy;
-
-	dst.p.x = box->x1;
-	v[4] = dst.f;
-	v[9] = v[5] = ((box->x1 + tx) * xx + x0) * sx;
-
-	dst.p.y = box->y1;
-	v[8] = dst.f;
-	v[10] = ((box->y1 + ty) * yy + y0) * sy;
-
-	v[11] = v[7] = v[3] = opacity;
-}
-
-fastcall static void
-gen7_emit_composite_spans_affine(struct sna *sna,
-				 const struct sna_composite_spans_op *op,
-				 const BoxRec *box,
-				 float opacity)
-{
-	OUT_VERTEX(box->x2, box->y2);
-	gen7_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x2, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y2);
-	gen7_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y2);
-	OUT_VERTEX_F(opacity);
-
-	OUT_VERTEX(box->x1, box->y1);
-	gen7_emit_composite_texcoord_affine(sna, &op->base.src,
-					    box->x1, box->y1);
-	OUT_VERTEX_F(opacity);
-}
-
 fastcall static void
 gen7_render_composite_spans_box(struct sna *sna,
 				const struct sna_composite_spans_op *op,
@@ -3206,8 +2584,8 @@ fastcall static void
 gen7_render_composite_spans_done(struct sna *sna,
 				 const struct sna_composite_spans_op *op)
 {
-	if (sna->render_state.gen7.vertex_offset)
-		gen7_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 
 	DBG(("%s()\n", __FUNCTION__));
 
@@ -3288,22 +2666,7 @@ gen7_render_composite_spans(struct sna *sna,
 	tmp->base.is_affine = tmp->base.src.is_affine;
 	tmp->base.need_magic_ca_pass = false;
 
-	tmp->prim_emit = gen7_emit_composite_spans_primitive;
-	if (tmp->base.src.is_solid) {
-		tmp->prim_emit = gen7_emit_composite_spans_solid;
-	} else if (tmp->base.src.transform == NULL) {
-		tmp->prim_emit = gen7_emit_composite_spans_identity;
-	} else if (tmp->base.is_affine) {
-		if (tmp->base.src.transform->matrix[0][1] == 0 &&
-		    tmp->base.src.transform->matrix[1][0] == 0) {
-			tmp->base.src.scale[0] /= tmp->base.src.transform->matrix[2][2];
-			tmp->base.src.scale[1] /= tmp->base.src.transform->matrix[2][2];
-			tmp->prim_emit = gen7_emit_composite_spans_simple;
-		} else
-			tmp->prim_emit = gen7_emit_composite_spans_affine;
-	}
-	tmp->base.floats_per_vertex = 4 + !tmp->base.is_affine;
-	tmp->base.floats_per_rect = 3 * tmp->base.floats_per_vertex;
+	gen4_choose_spans_emitter(tmp);
 
 	tmp->base.u.gen7.flags =
 		GEN7_SET_FLAGS(SAMPLER_OFFSET(tmp->base.src.filter,
@@ -3312,7 +2675,7 @@ gen7_render_composite_spans(struct sna *sna,
 					      SAMPLER_EXTEND_PAD),
 			       gen7_get_blend(tmp->base.op, false, tmp->base.dst.format),
 			       GEN7_WM_KERNEL_OPACITY | !tmp->base.is_affine,
-			       1 << 2 | (2+!tmp->base.is_affine));
+			       gen4_choose_spans_vertex_buffer(&tmp->base));
 
 	tmp->box   = gen7_render_composite_spans_box;
 	tmp->boxes = gen7_render_composite_spans_boxes;
@@ -3618,7 +2981,7 @@ fallback_blt:
 		} while (--n_this_time);
 	} while (n);
 
-	gen7_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	sna_render_composite_redirect_done(sna, &tmp);
 	if (tmp.src.bo != src_bo)
 		kgem_bo_destroy(&sna->kgem, tmp.src.bo);
@@ -3663,8 +3026,8 @@ gen7_render_copy_blt(struct sna *sna,
 static void
 gen7_render_copy_done(struct sna *sna, const struct sna_copy_op *op)
 {
-	if (sna->render_state.gen7.vertex_offset)
-		gen7_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 }
 
 static bool
@@ -3916,7 +3279,7 @@ gen7_render_fill_boxes(struct sna *sna,
 		} while (--n_this_time);
 	} while (n);
 
-	gen7_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 	sna_render_composite_redirect_done(sna, &tmp);
 	return true;
@@ -4007,8 +3370,8 @@ gen7_render_fill_op_boxes(struct sna *sna,
 static void
 gen7_render_fill_op_done(struct sna *sna, const struct sna_fill_op *op)
 {
-	if (sna->render_state.gen7.vertex_offset)
-		gen7_vertex_flush(sna);
+	if (sna->render.vertex_offset)
+		gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, op->base.src.bo);
 }
 
@@ -4158,7 +3521,7 @@ gen7_render_fill_one(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo,
 	v[7] = v[2]  = v[3]  = 1;
 	v[6] = v[10] = v[11] = 0;
 
-	gen7_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 
 	return true;
@@ -4238,7 +3601,7 @@ gen7_render_clear(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo)
 	v[7] = v[2]  = v[3]  = 1;
 	v[6] = v[10] = v[11] = 0;
 
-	gen7_vertex_flush(sna);
+	gen4_vertex_flush(sna);
 	kgem_bo_destroy(&sna->kgem, tmp.src.bo);
 
 	return true;
@@ -4246,7 +3609,10 @@ gen7_render_clear(struct sna *sna, PixmapPtr dst, struct kgem_bo *bo)
 
 static void gen7_render_flush(struct sna *sna)
 {
-	gen7_vertex_close(sna);
+	gen4_vertex_close(sna);
+
+	assert(sna->render.vb_id == 0);
+	assert(sna->render.vertex_offset == 0);
 }
 
 static void
@@ -4299,7 +3665,6 @@ static void gen7_render_reset(struct sna *sna)
 {
 	sna->render_state.gen7.emit_flush = false;
 	sna->render_state.gen7.needs_invariant = true;
-	sna->render_state.gen7.vb_id = 0;
 	sna->render_state.gen7.ve_id = 3 << 2;
 	sna->render_state.gen7.last_primitive = -1;
 
diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 974201e..adeead1 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -533,6 +533,7 @@ static struct kgem_bo *__kgem_bo_init(struct kgem_bo *bo,
 
 	bo->refcnt = 1;
 	bo->handle = handle;
+	bo->target_handle = -1;
 	num_pages(bo) = num_pages;
 	bucket(bo) = cache_bucket(num_pages);
 	bo->reusable = true;
@@ -2044,6 +2045,7 @@ static void kgem_commit(struct kgem *kgem)
 
 		bo->presumed_offset = bo->exec->offset;
 		bo->exec = NULL;
+		bo->target_handle = -1;
 
 		if (!bo->refcnt && !bo->reusable) {
 			assert(!bo->snoop);
@@ -2312,6 +2314,7 @@ void kgem_reset(struct kgem *kgem)
 
 			bo->binding.offset = 0;
 			bo->exec = NULL;
+			bo->target_handle = -1;
 			bo->dirty = false;
 			bo->rq = NULL;
 			bo->domain = DOMAIN_NONE;
@@ -2469,7 +2472,7 @@ void _kgem_submit(struct kgem *kgem)
 
 	kgem_finish_buffers(kgem);
 
-#if HAS_DEBUG_FULL && SHOW_BATCH
+#if SHOW_BATCH
 	__kgem_batch_debug(kgem, batch_end);
 #endif
 
@@ -3137,10 +3140,14 @@ struct kgem_bo *kgem_create_linear(struct kgem *kgem, int size, unsigned flags)
 	size = (size + PAGE_SIZE - 1) / PAGE_SIZE;
 	bo = search_linear_cache(kgem, size, CREATE_INACTIVE | flags);
 	if (bo) {
+		assert(!kgem_busy(kgem, bo->handle));
 		bo->refcnt = 1;
 		return bo;
 	}
 
+	if (flags & CREATE_CACHED)
+		return NULL;
+
 	handle = gem_create(kgem->fd, size);
 	if (handle == 0)
 		return NULL;
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index c5199c2..1b14c20 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -260,8 +260,9 @@ enum {
 	CREATE_SCANOUT = 0x10,
 	CREATE_PRIME = 0x20,
 	CREATE_TEMPORARY = 0x40,
-	CREATE_NO_RETIRE = 0x80,
-	CREATE_NO_THROTTLE = 0x100,
+	CREATE_CACHED = 0x80,
+	CREATE_NO_RETIRE = 0x100,
+	CREATE_NO_THROTTLE = 0x200,
 };
 struct kgem_bo *kgem_create_2d(struct kgem *kgem,
 			       int width,
@@ -631,7 +632,7 @@ bool kgem_expire_cache(struct kgem *kgem);
 void kgem_purge_cache(struct kgem *kgem);
 void kgem_cleanup_cache(struct kgem *kgem);
 
-#if HAS_EXTRA_DEBUG
+#if HAS_DEBUG_FULL
 void __kgem_batch_debug(struct kgem *kgem, uint32_t nbatch);
 #else
 static inline void __kgem_batch_debug(struct kgem *kgem, uint32_t nbatch)
diff --git a/src/sna/kgem_debug.c b/src/sna/kgem_debug.c
index 595c20f..48c7588 100644
--- a/src/sna/kgem_debug.c
+++ b/src/sna/kgem_debug.c
@@ -62,7 +62,7 @@ kgem_debug_get_bo_for_reloc_entry(struct kgem *kgem,
 		return NULL;
 
 	list_for_each_entry(bo, &kgem->next_request->buffers, request)
-		if (bo->handle == reloc->target_handle && bo->proxy == NULL)
+		if (bo->target_handle == reloc->target_handle && bo->proxy == NULL)
 			break;
 
 	assert(&bo->request != &kgem->next_request->buffers);
@@ -74,6 +74,9 @@ static int kgem_debug_handle_is_fenced(struct kgem *kgem, uint32_t handle)
 {
 	int i;
 
+	if (kgem->has_handle_lut)
+		return kgem->exec[handle].flags & EXEC_OBJECT_NEEDS_FENCE;
+
 	for (i = 0; i < kgem->nexec; i++)
 		if (kgem->exec[i].handle == handle)
 			return kgem->exec[i].flags & EXEC_OBJECT_NEEDS_FENCE;
@@ -86,7 +89,7 @@ static int kgem_debug_handle_tiling(struct kgem *kgem, uint32_t handle)
 	struct kgem_bo *bo;
 
 	list_for_each_entry(bo, &kgem->next_request->buffers, request)
-		if (bo->handle == handle)
+		if (bo->target_handle == handle)
 			return bo->tiling;
 
 	return 0;
@@ -95,7 +98,7 @@ static int kgem_debug_handle_tiling(struct kgem *kgem, uint32_t handle)
 void
 kgem_debug_print(const uint32_t *data,
 		 uint32_t offset, unsigned int index,
-		 char *fmt, ...)
+		 const char *fmt, ...)
 {
 	va_list va;
 	char buf[240];
diff --git a/src/sna/kgem_debug.h b/src/sna/kgem_debug.h
index 82d6f66..a0c9fc1 100644
--- a/src/sna/kgem_debug.h
+++ b/src/sna/kgem_debug.h
@@ -4,7 +4,7 @@
 void
 kgem_debug_print(const uint32_t *data,
 		 uint32_t offset, unsigned int index,
-		 char *fmt, ...);
+		 const char *fmt, ...);
 
 struct drm_i915_gem_relocation_entry *
 kgem_debug_get_reloc_entry(struct kgem *kgem, uint32_t offset);
diff --git a/src/sna/kgem_debug_gen5.c b/src/sna/kgem_debug_gen5.c
index e23ceb1..8b55dd9 100644
--- a/src/sna/kgem_debug_gen5.c
+++ b/src/sna/kgem_debug_gen5.c
@@ -73,7 +73,7 @@ static void gen5_update_vertex_buffer(struct kgem *kgem, const uint32_t *data)
 	int i, size;
 
 	reloc = kgem_debug_get_reloc_entry(kgem, &data[1] - kgem->batch);
-	if (reloc->target_handle == 0) {
+	if (reloc->target_handle == -1) {
 		base = kgem->batch;
 		size = kgem->nbatch * sizeof(uint32_t);
 	} else {
@@ -529,20 +529,19 @@ int kgem_gen5_decode_3d(struct kgem *kgem, uint32_t offset)
 		for (i = 1; i < len;) {
 			gen5_update_vertex_elements(kgem, (i - 1)/2, data + i);
 
-			kgem_debug_print(data, offset, i, "buffer %d: %svalid, type 0x%04x, "
-				  "src offset 0x%04x bytes\n",
-				  data[i] >> 27,
-				  data[i] & (1 << 26) ? "" : "in",
-				  (data[i] >> 16) & 0x1ff,
-				  data[i] & 0x07ff);
+			kgem_debug_print(data, offset, i,
+					 "buffer %d: %svalid, type 0x%04x, "
+					 "src offset 0x%04x bytes\n",
+					 data[i] >> 27,
+					 data[i] & (1 << 26) ? "" : "in",
+					 (data[i] >> 16) & 0x1ff,
+					 data[i] & 0x07ff);
 			i++;
-			kgem_debug_print(data, offset, i, "(%s, %s, %s, %s), "
-				  "dst offset 0x%02x bytes\n",
+			kgem_debug_print(data, offset, i, "(%s, %s, %s, %s)\n",
 				  get_965_element_component(data[i], 0),
 				  get_965_element_component(data[i], 1),
 				  get_965_element_component(data[i], 2),
-				  get_965_element_component(data[i], 3),
-				  (data[i] & 0xff) * 4);
+				  get_965_element_component(data[i], 3));
 			i++;
 		}
 		state.num_ve = (len - 1) / 2; /* XXX? */
diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c
index e92c453..90e6e23 100644
--- a/src/sna/sna_render.c
+++ b/src/sna/sna_render.c
@@ -1704,7 +1704,7 @@ sna_render_picture_convert(struct sna *sna,
 						   PICT_FORMAT_B(picture->format));
 
 		DBG(("%s: converting to %08x from %08x using composite alpha-fixup\n",
-		     __FUNCTION__, picture->format));
+		     __FUNCTION__, (unsigned)picture->format));
 
 		tmp = screen->CreatePixmap(screen, w, h, pixmap->drawable.bitsPerPixel, 0);
 		if (tmp == NULL)
@@ -1993,7 +1993,7 @@ sna_render_composite_redirect_done(struct sna *sna,
 		}
 		if (t->damage) {
 			DBG(("%s: combining damage (all? %d), offset=(%d, %d)\n",
-			     __FUNCTION__, DAMAGE_IS_ALL(t->damage),
+			     __FUNCTION__, (int)DAMAGE_IS_ALL(t->damage),
 			     t->box.x1, t->box.y1));
 			sna_damage_combine(t->real_damage,
 					   DAMAGE_PTR(t->damage),
diff --git a/src/sna/sna_render.h b/src/sna/sna_render.h
index ea1d781..51f155a 100644
--- a/src/sna/sna_render.h
+++ b/src/sna/sna_render.h
@@ -5,6 +5,9 @@
 
 #include <picturestr.h>
 
+#include <stdbool.h>
+#include <stdint.h>
+
 #define GRADIENT_CACHE_SIZE 16
 
 #define GXinvalid 0xff
@@ -284,6 +287,8 @@ struct sna_render {
 	pixman_glyph_cache_t *glyph_cache;
 #endif
 
+	uint16_t vb_id;
+	uint16_t vertex_offset;
 	uint16_t vertex_start;
 	uint16_t vertex_index;
 	uint16_t vertex_used;
@@ -341,9 +346,7 @@ struct gen4_render_state {
 	int ve_id;
 	uint32_t drawrect_offset;
 	uint32_t drawrect_limit;
-	uint32_t vb_id;
 	uint32_t last_pipelined_pointers;
-	uint16_t vertex_offset;
 	uint16_t last_primitive;
 	int16_t floats_per_vertex;
 	uint16_t surface_table;
@@ -363,8 +366,6 @@ struct gen5_render_state {
 	int ve_id;
 	uint32_t drawrect_offset;
 	uint32_t drawrect_limit;
-	uint32_t vb_id;
-	uint16_t vertex_offset;
 	uint16_t last_primitive;
 	int16_t floats_per_vertex;
 	uint16_t surface_table;
@@ -414,9 +415,7 @@ struct gen6_render_state {
 	uint32_t kernel;
 
 	uint16_t num_sf_outputs;
-	uint16_t vb_id;
 	uint16_t ve_id;
-	uint16_t vertex_offset;
 	uint16_t last_primitive;
 	int16_t floats_per_vertex;
 	uint16_t surface_table;
@@ -466,9 +465,7 @@ struct gen7_render_state {
 	uint32_t kernel;
 
 	uint16_t num_sf_outputs;
-	uint16_t vb_id;
 	uint16_t ve_id;
-	uint16_t vertex_offset;
 	uint16_t last_primitive;
 	int16_t floats_per_vertex;
 	uint16_t surface_table;
diff --git a/src/sna/sna_render_inline.h b/src/sna/sna_render_inline.h
index 51c78bc..7314889 100644
--- a/src/sna/sna_render_inline.h
+++ b/src/sna/sna_render_inline.h
@@ -17,6 +17,17 @@ static inline bool need_redirect(struct sna *sna, PixmapPtr dst)
 		dst->drawable.height > sna->render.max_3d_size);
 }
 
+static inline float pack_2s(int16_t x, int16_t y)
+{
+	union {
+		struct sna_coordinate p;
+		float f;
+	} u;
+	u.p.x = x;
+	u.p.y = y;
+	return u.f;
+}
+
 static inline int vertex_space(struct sna *sna)
 {
 	return sna->render.vertex_size - sna->render.vertex_used;
@@ -28,21 +39,7 @@ static inline void vertex_emit(struct sna *sna, float v)
 }
 static inline void vertex_emit_2s(struct sna *sna, int16_t x, int16_t y)
 {
-	int16_t *v = (int16_t *)&sna->render.vertices[sna->render.vertex_used++];
-	assert(sna->render.vertex_used <= sna->render.vertex_size);
-	v[0] = x;
-	v[1] = y;
-}
-
-static inline float pack_2s(int16_t x, int16_t y)
-{
-	union {
-		struct sna_coordinate p;
-		float f;
-	} u;
-	u.p.x = x;
-	u.p.y = y;
-	return u.f;
+	vertex_emit(sna, pack_2s(x, y));
 }
 
 static inline int batch_space(struct sna *sna)
diff --git a/src/sna/sna_trapezoids.c b/src/sna/sna_trapezoids.c
index 81ae9c4..8cd987e 100644
--- a/src/sna/sna_trapezoids.c
+++ b/src/sna/sna_trapezoids.c
@@ -5552,7 +5552,7 @@ sna_composite_trapezoids(CARD8 op,
 		     dst->pDrawable->width,
 		     dst->pDrawable->height,
 		     too_small(priv),
-		     DAMAGE_IS_ALL(priv->cpu_damage),
+		     (int)DAMAGE_IS_ALL(priv->cpu_damage),
 		     !picture_is_gpu(src)));
 		force_fallback = true;
 	}


More information about the xorg-commit mailing list