Mesa (nvfx-next-6): nvfx: rewrite render temporaries code, also affecting 2D and resource code

Luca Barbieri lb at kemper.freedesktop.org
Sun Apr 18 14:25:33 UTC 2010


Module: Mesa
Branch: nvfx-next-6
Commit: f6d8e5880be83ce38787ac5f38c69869bc4e2540
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=f6d8e5880be83ce38787ac5f38c69869bc4e2540

Author: Luca Barbieri <luca at luca-barbieri.com>
Date:   Sun Apr 18 15:22:43 2010 +0200

nvfx: rewrite render temporaries code, also affecting 2D and resource code

This commit rewrites the render temporaries code to make it actually robust
and try to make it perform decently.

It also significantly affects how miptrees and the 2D code work.

---

 src/gallium/drivers/nvfx/nvfx_context.c    |    6 +-
 src/gallium/drivers/nvfx/nvfx_context.h    |    3 +-
 src/gallium/drivers/nvfx/nvfx_fragtex.c    |    4 +
 src/gallium/drivers/nvfx/nvfx_miptree.c    |  120 ++++++++++-----
 src/gallium/drivers/nvfx/nvfx_resource.h   |   71 ++++-----
 src/gallium/drivers/nvfx/nvfx_state.c      |    4 +-
 src/gallium/drivers/nvfx/nvfx_state_emit.c |   28 +++-
 src/gallium/drivers/nvfx/nvfx_state_fb.c   |  192 ++++++++++++++++--------
 src/gallium/drivers/nvfx/nvfx_surface.c    |  223 +++++++++++++---------------
 src/gallium/drivers/nvfx/nvfx_transfer.c   |    6 +-
 10 files changed, 376 insertions(+), 281 deletions(-)

diff --git a/src/gallium/drivers/nvfx/nvfx_context.c b/src/gallium/drivers/nvfx/nvfx_context.c
index aee7587..ff30994 100644
--- a/src/gallium/drivers/nvfx/nvfx_context.c
+++ b/src/gallium/drivers/nvfx/nvfx_context.c
@@ -14,9 +14,7 @@ nvfx_flush(struct pipe_context *pipe, unsigned flags,
 	struct nouveau_channel *chan = screen->base.channel;
 	struct nouveau_grobj *eng3d = screen->eng3d;
 
-	if (flags & PIPE_FLUSH_RENDER_CACHE)
-		nvfx_surface_flush_render_cache(&nvfx->render_cache);
-
+	/* XXX: we need to actually be intelligent here */
 	if (flags & PIPE_FLUSH_TEXTURE_CACHE) {
 		BEGIN_RING(chan, eng3d, 0x1fd8, 1);
 		OUT_RING  (chan, 2);
@@ -68,7 +66,7 @@ nvfx_create(struct pipe_screen *pscreen, void *priv)
 	nvfx->is_nv4x = screen->is_nv4x;
 
 	nvfx_init_query_functions(nvfx);
-	nvfx_init_surface_functions(&nvfx->pipe);
+	nvfx_init_surface_functions(nvfx);
 	nvfx_init_state_functions(nvfx);
 	nvfx_init_resource_functions(&nvfx->pipe);
 
diff --git a/src/gallium/drivers/nvfx/nvfx_context.h b/src/gallium/drivers/nvfx/nvfx_context.h
index d379e6a..30c4dec 100644
--- a/src/gallium/drivers/nvfx/nvfx_context.h
+++ b/src/gallium/drivers/nvfx/nvfx_context.h
@@ -187,7 +187,8 @@ extern void nvfx_draw_elements_swtnl(struct pipe_context *pipe,
 extern void nvfx_vtxfmt_validate(struct nvfx_context *nvfx);
 
 /* nvfx_fb.c */
-extern void nvfx_state_framebuffer_validate(struct nvfx_context *nvfx);
+extern int nvfx_framebuffer_prepare(struct nvfx_context *nvfx);
+extern void nvfx_framebuffer_validate(struct nvfx_context *nvfx, unsigned prepare_result);
 void
 nvfx_framebuffer_relocate(struct nvfx_context *nvfx);
 
diff --git a/src/gallium/drivers/nvfx/nvfx_fragtex.c b/src/gallium/drivers/nvfx/nvfx_fragtex.c
index f5f6b0c..6cb3210 100644
--- a/src/gallium/drivers/nvfx/nvfx_fragtex.c
+++ b/src/gallium/drivers/nvfx/nvfx_fragtex.c
@@ -16,6 +16,10 @@ nvfx_fragtex_validate(struct nvfx_context *nvfx)
 		samplers &= ~(1 << unit);
 
 		if(nvfx->fragment_sampler_views[unit] && nvfx->tex_sampler[unit]) {
+			util_dirty_surfaces_use_for_sampling(&nvfx->pipe,
+					&((struct nvfx_miptree*)nvfx->fragment_sampler_views[unit]->texture)->dirty_surfaces,
+					nvfx_surface_flush);
+
 			if(!nvfx->is_nv4x)
 				nv30_fragtex_set(nvfx, unit);
 			else
diff --git a/src/gallium/drivers/nvfx/nvfx_miptree.c b/src/gallium/drivers/nvfx/nvfx_miptree.c
index 59133b3..549d4b0 100644
--- a/src/gallium/drivers/nvfx/nvfx_miptree.c
+++ b/src/gallium/drivers/nvfx/nvfx_miptree.c
@@ -10,6 +10,7 @@
 #include "nvfx_resource.h"
 #include "nvfx_transfer.h"
 #include "state_tracker/drm_api.h"
+#include "nv04_2d.h"
 
 static void
 nvfx_miptree_choose_format(struct nvfx_miptree *mt)
@@ -107,16 +108,23 @@ nvfx_miptree_get_handle(struct pipe_screen *pscreen,
 
 
 static void
+nvfx_miptree_surface_final_destroy(struct pipe_surface* ps)
+{
+	struct nvfx_surface* ns = (struct nvfx_surface*)ps;
+	pipe_resource_reference(&ps->texture, 0);
+	nouveau_bo_ref(0, &ns->temp);
+	FREE(ps);
+}
+
+static void
 nvfx_miptree_destroy(struct pipe_screen *screen, struct pipe_resource *pt)
 {
 	struct nvfx_miptree *mt = (struct nvfx_miptree *)pt;
+	util_surfaces_destroy(&mt->surfaces, pt, nvfx_miptree_surface_final_destroy);
 	nouveau_screen_bo_release(screen, mt->base.bo);
 	FREE(mt);
 }
 
-
-
-
 struct u_resource_vtbl nvfx_miptree_vtbl = 
 {
    nvfx_miptree_get_handle,	      /* get_handle */
@@ -131,6 +139,21 @@ struct u_resource_vtbl nvfx_miptree_vtbl =
 };
 
 static struct nvfx_miptree*
+nvfx_miptree_alloc()
+{
+        struct nvfx_miptree *mt;
+
+        mt = CALLOC_STRUCT(nvfx_miptree);
+        if (!mt)
+                return NULL;
+
+        mt->base.vtbl = &nvfx_miptree_vtbl;
+        util_dirty_surfaces_init(&mt->dirty_surfaces);
+
+        return mt;
+}
+
+static struct nvfx_miptree*
 nvfx_miptree_create_skeleton(struct pipe_screen *pscreen, const struct pipe_resource *pt)
 {
         struct nvfx_miptree *mt;
@@ -138,12 +161,11 @@ nvfx_miptree_create_skeleton(struct pipe_screen *pscreen, const struct pipe_reso
         if(pt->width0 > 4096 || pt->height0 > 4096)
                 return NULL;
 
-        mt = CALLOC_STRUCT(nvfx_miptree);
-        if (!mt)
-                return NULL;
+        mt = nvfx_miptree_alloc();
+        if(!mt)
+        	return NULL;
 
         mt->base.base = *pt;
-        mt->base.vtbl = &nvfx_miptree_vtbl;
         pipe_reference_init(&mt->base.base.reference, 1);
         mt->base.base.screen = pscreen;
 
@@ -205,6 +227,39 @@ nvfx_miptree_from_handle(struct pipe_screen *pscreen, const struct pipe_resource
         return &mt->base.base;
 }
 
+/* hack to use the 3D engine on arbitrary regions
+ * TODO: we may want stop using util_blitter and have ad-hoc 3D code to avoid this horribly inefficient setup
+ */
+struct pipe_resource*
+nvfx_miptree_from_region(struct pipe_screen* pscreen, struct nv04_region* rgn, enum pipe_format format, unsigned w, unsigned h)
+{
+	struct nvfx_miptree* mt = nvfx_miptree_alloc();
+	struct pipe_resource* pt;
+	if (!mt)
+		return NULL;
+
+	pt = &mt->base.base;
+        pipe_reference_init(&pt->reference, 1);
+	pt->screen = pscreen;
+	pt->target = PIPE_TEXTURE_2D;
+	pt->format = format;
+	pt->width0 = w;
+	pt->height0 = h;
+	pt->depth0 = rgn->pitch ? 1 : rgn->d;
+	pt->bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
+	if(util_format_is_depth_or_stencil(format))
+		pt->bind |= PIPE_BIND_DEPTH_STENCIL;
+	pt->_usage = PIPE_USAGE_DEFAULT;
+	mt->linear_pitch = rgn->pitch;
+	if(rgn->pitch)
+		pt->flags = NVFX_RESOURCE_FLAG_LINEAR;
+
+	nouveau_bo_ref(rgn->bo, &mt->base.bo);
+
+	nvfx_miptree_layout(mt);
+	return &mt->base.base;
+}
+
 /* Surface helpers, not strictly required to implement the resource vtbl:
  */
 struct pipe_surface *
@@ -215,32 +270,23 @@ nvfx_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_resource *pt,
 	struct nvfx_miptree *mt = (struct nvfx_miptree *)pt;
 	struct nvfx_surface *ns;
 
-	ns = CALLOC_STRUCT(nvfx_surface);
-	if (!ns)
-		return NULL;
-	pipe_resource_reference(&ns->base.texture, pt);
-	ns->base.format = pt->format;
-	ns->base.width = u_minify(pt->width0, level);
-	ns->base.height = u_minify(pt->height0, level);
-	ns->base.usage = flags;
-	pipe_reference_init(&ns->base.reference, 1);
-	ns->base.face = face;
-	ns->base.level = level;
-	ns->base.zslice = zslice;
-
-	if(mt->linear_pitch)
-		ns->pitch = mt->linear_pitch;
-	else
-		ns->pitch = util_format_get_stride(ns->base.format, ns->base.width);
-
-	ns->base.offset = mt->level_offset[level];
-	if (pt->target == PIPE_TEXTURE_CUBE)
-		ns->base.offset += mt->face_size * face;
-	else if (pt->target == PIPE_TEXTURE_3D && mt->linear_pitch)
-		ns->base.offset += zslice
-		* util_format_get_2d_size(ns->base.format, (mt->linear_pitch ? mt->linear_pitch : util_format_get_stride(ns->base.format, ns->base.width)),  ns->base.height);
+	ns = (struct nvfx_surface*)util_surfaces_get(&mt->surfaces, sizeof(struct nvfx_surface), pscreen, pt, face, level, zslice, flags);
+	if(ns->base.base.offset == ~0) {
+		util_dirty_surface_init(&ns->base);
+		if(mt->linear_pitch)
+			ns->pitch = mt->linear_pitch;
+		else
+			ns->pitch = util_format_get_stride(ns->base.base.format, ns->base.base.width);
+
+		ns->base.base.offset = mt->level_offset[level];
+		if (pt->target == PIPE_TEXTURE_CUBE)
+			ns->base.base.offset += mt->face_size * face;
+		else if (pt->target == PIPE_TEXTURE_3D && mt->linear_pitch)
+			ns->base.base.offset += zslice
+			* util_format_get_2d_size(ns->base.base.format, (mt->linear_pitch ? mt->linear_pitch : util_format_get_stride(ns->base.base.format, ns->base.base.width)),  ns->base.base.height);
+	}
 
-	return &ns->base;
+	return &ns->base.base;
 }
 
 void
@@ -248,12 +294,10 @@ nvfx_miptree_surface_del(struct pipe_surface *ps)
 {
 	struct nvfx_surface* ns = (struct nvfx_surface*)ps;
 
-	if(ns->render)
+	if(!ns->temp)
 	{
-		nvfx_surface_flush(ps);
-		nouveau_bo_ref(0, &ns->render);
+		util_surfaces_detach(&((struct nvfx_miptree*)ps->texture)->surfaces, ps);
+		pipe_resource_reference(&ps->texture, 0);
+		FREE(ps);
 	}
-
-	pipe_resource_reference(&ps->texture, NULL);
-	FREE(ps);
 }
diff --git a/src/gallium/drivers/nvfx/nvfx_resource.h b/src/gallium/drivers/nvfx/nvfx_resource.h
index 149f502..806ecfd 100644
--- a/src/gallium/drivers/nvfx/nvfx_resource.h
+++ b/src/gallium/drivers/nvfx/nvfx_resource.h
@@ -3,10 +3,12 @@
 
 #include "util/u_transfer.h"
 #include "util/u_double_list.h"
+#include "util/u_surfaces.h"
+#include "util/u_dirty_surfaces.h"
 
 struct pipe_resource;
 struct nouveau_bo;
-
+struct nv04_region;
 
 /* This gets further specialized into either buffer or texture
  * structures.  In the future we'll want to remove much of that
@@ -24,21 +26,34 @@ struct nvfx_resource {
 
 #define NVFX_MAX_TEXTURE_LEVELS  16
 
+/* We have the following invariants for render temporaries
+ *
+ * 1. Render temporaries are always linear
+ * 2. Render temporaries are always up to date
+ * 3. Currently, render temporaries are destroy when the resource is used for sampling, but kept for any other use
+ *
+ * Also, we do NOT flush temporaries on any pipe->flush().
+ * This is fine, as long as scanout targets and shared resources never need temps
+ *
+ * TODO: we may want to also support swizzled temporaries to improve performance in some cases.
+ */
+
 struct nvfx_miptree {
         struct nvfx_resource base;
 
         unsigned linear_pitch; /* for linear textures, 0 for swizzled and compressed textures with level-dependent minimal pitch */
         unsigned face_size; /* 128-byte aligned face/total size */
         unsigned level_offset[NVFX_MAX_TEXTURE_LEVELS];
+
+        struct util_surfaces surfaces;
+        struct util_dirty_surfaces dirty_surfaces;
 };
 
 struct nvfx_surface {
-	struct pipe_surface base;
+	struct util_dirty_surface base;
 	unsigned pitch;
 
-	struct nouveau_bo* render;
-        struct list_head render_list;
-        struct list_head* render_cache;
+	struct nouveau_bo* temp;
 };
 
 static INLINE 
@@ -55,6 +70,12 @@ nvfx_surface_buffer(struct pipe_surface *surf)
 	return mt->bo;
 }
 
+static INLINE struct util_dirty_surfaces*
+nvfx_surface_get_dirty_surfaces(struct pipe_surface* surf)
+{
+	struct nvfx_miptree *mt = (struct nvfx_miptree *)surf->texture;
+	return &mt->dirty_surfaces;
+}
 
 void
 nvfx_init_resource_functions(struct pipe_context *pipe);
@@ -74,6 +95,9 @@ nvfx_miptree_from_handle(struct pipe_screen *pscreen,
 			 const struct pipe_resource *template,
 			 struct winsys_handle *whandle);
 
+struct pipe_resource*
+nvfx_miptree_from_region(struct pipe_screen* pscreen, struct nv04_region* rgn, enum pipe_format format, unsigned w, unsigned h);
+
 struct pipe_resource *
 nvfx_buffer_create(struct pipe_screen *pscreen,
 		   const struct pipe_resource *template);
@@ -94,43 +118,10 @@ nvfx_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_resource *pt,
 			 unsigned face, unsigned level, unsigned zslice,
 			 unsigned flags);
 
-
-void
-nvfx_surface_copy_render_temp(struct pipe_surface* surf, int dir);
-
-void
-nvfx_surface_do_use_render_temp(struct pipe_surface* surf, struct list_head* render_cache);
-
-static inline void
-nvfx_surface_use_render_temp(struct pipe_surface* surf, struct list_head* render_cache)
-{
-	if(((struct nvfx_surface*)surf)->render_cache != render_cache)
-		nvfx_surface_do_use_render_temp(surf, render_cache);
-}
-
-void
-nvfx_surface_do_flush(struct pipe_surface* surf);
-
-static inline void
-nvfx_surface_flush(struct pipe_surface* surf)
-{
-	if(((struct nvfx_surface*)surf)->render_cache)
-		nvfx_surface_do_flush(surf);
-}
-
 void
-nvfx_surface_do_flush_render_cache(struct list_head* list);
-
-static inline void
-nvfx_surface_flush_render_cache(struct list_head* list)
-{
-	if(!LIST_IS_EMPTY(list))
-		nvfx_surface_do_flush_render_cache(list);
-}
-
-struct nvfx_render_target;
+nvfx_surface_create_temp(struct pipe_context* pipe, struct pipe_surface* surf);
 
 void
-nvfx_surface_get_render_target(struct pipe_surface* surf, int all_swizzled, struct nvfx_render_target* target);
+nvfx_surface_flush(struct pipe_context* pipe, struct pipe_surface* surf);
 
 #endif
diff --git a/src/gallium/drivers/nvfx/nvfx_state.c b/src/gallium/drivers/nvfx/nvfx_state.c
index 315de49..ddf12ba 100644
--- a/src/gallium/drivers/nvfx/nvfx_state.c
+++ b/src/gallium/drivers/nvfx/nvfx_state.c
@@ -92,10 +92,10 @@ nvfx_sampler_state_create(struct pipe_context *pipe,
 	/* on nv30, we use this as an internal flag */
 	ps->fmt = cso->normalized_coords ? 0 : NV40TCL_TEX_FORMAT_RECT;
 	ps->en = 0;
-	ps->filt = nvfx_tex_filter(cso);
+	ps->filt = nvfx_tex_filter(cso) | 0x1fd6;
 	ps->wrap = (nvfx_tex_wrap_mode(cso->wrap_s) << NV34TCL_TX_WRAP_S_SHIFT) |
 		    (nvfx_tex_wrap_mode(cso->wrap_t) << NV34TCL_TX_WRAP_T_SHIFT) |
-		    (nvfx_tex_wrap_mode(cso->wrap_r) << NV34TCL_TX_WRAP_R_SHIFT) |
+		    (3 /*nvfx_tex_wrap_mode(cso->wrap_r) */ << NV34TCL_TX_WRAP_R_SHIFT) |
 		    nvfx_tex_wrap_compare_mode(cso);
 	ps->bcol = nvfx_tex_border_color(cso->border_color);
 
diff --git a/src/gallium/drivers/nvfx/nvfx_state_emit.c b/src/gallium/drivers/nvfx/nvfx_state_emit.c
index 8489b3e..5f71e4c 100644
--- a/src/gallium/drivers/nvfx/nvfx_state_emit.c
+++ b/src/gallium/drivers/nvfx/nvfx_state_emit.c
@@ -7,11 +7,22 @@ static boolean
 nvfx_state_validate_common(struct nvfx_context *nvfx)
 {
 	struct nouveau_channel* chan = nvfx->screen->base.channel;
-	unsigned dirty = nvfx->dirty;
+	unsigned dirty;
+	unsigned prepare_result;
+
+	dirty = nvfx->dirty;
 
 	if(nvfx != nvfx->screen->cur_ctx)
 		dirty = ~0;
 
+	/* this can use the 3D engine to unswizzle render targets to temporaries
+	 * of course, this dirties all 3D state, so we must restart from scratch afterwards
+	 */
+	if(nvfx->dirty & NVFX_NEW_FB)
+		prepare_result = nvfx_framebuffer_prepare(nvfx);
+
+	dirty = nvfx->dirty;
+
 	if(nvfx->render_mode == HW)
 	{
 		if(dirty & (NVFX_NEW_VERTPROG | NVFX_NEW_VERTCONST | NVFX_NEW_UCP))
@@ -36,9 +47,6 @@ nvfx_state_validate_common(struct nvfx_context *nvfx)
 			nvfx_vtxfmt_validate(nvfx);
 	}
 
-	if(dirty & NVFX_NEW_FB)
-		nvfx_state_framebuffer_validate(nvfx);
-
 	if(dirty & NVFX_NEW_RAST)
 		sb_emit(chan, nvfx->rasterizer->sb, nvfx->rasterizer->sb_len);
 
@@ -54,6 +62,10 @@ nvfx_state_validate_common(struct nvfx_context *nvfx)
 	if(dirty & NVFX_NEW_SAMPLER)
 		nvfx_fragtex_validate(nvfx);
 
+	/* must be after fragtex, so we don't flush dirtied temporaries if they are mapped both as sampler and render target */
+	if(dirty & NVFX_NEW_FB)
+		nvfx_framebuffer_validate(nvfx, prepare_result);
+
 	if(dirty & NVFX_NEW_BLEND)
 		sb_emit(chan, nvfx->blend->sb, nvfx->blend->sb_len);
 
@@ -107,13 +119,13 @@ nvfx_state_emit(struct nvfx_context *nvfx)
 		for(int i = 0; i < nvfx->framebuffer.nr_cbufs; ++i)
 		{
 			if(render_temps & (1 << i))
-				nvfx_surface_use_render_temp(nvfx->framebuffer.cbufs[i], &nvfx->render_cache);
+				util_dirty_surface_set_dirty(nvfx_surface_get_dirty_surfaces(nvfx->framebuffer.cbufs[i]),
+						(struct util_dirty_surface*)nvfx->framebuffer.cbufs[i]);
 		}
 
 		if(render_temps & 0x80)
-		{
-			nvfx_surface_use_render_temp(nvfx->framebuffer.zsbuf, &nvfx->render_cache);
-		}
+			util_dirty_surface_set_dirty(nvfx_surface_get_dirty_surfaces(nvfx->framebuffer.zsbuf),
+					(struct util_dirty_surface*)nvfx->framebuffer.zsbuf);
 	}
 }
 
diff --git a/src/gallium/drivers/nvfx/nvfx_state_fb.c b/src/gallium/drivers/nvfx/nvfx_state_fb.c
index d13cf9f..ba89eee 100644
--- a/src/gallium/drivers/nvfx/nvfx_state_fb.c
+++ b/src/gallium/drivers/nvfx/nvfx_state_fb.c
@@ -3,18 +3,53 @@
 #include "nouveau/nouveau_util.h"
 #include "util/u_format.h"
 
-void
-nvfx_state_framebuffer_validate(struct nvfx_context *nvfx)
+static inline boolean
+nvfx_surface_linear_renderable(struct pipe_surface* surf)
+{
+	return (surf->texture->flags & NVFX_RESOURCE_FLAG_LINEAR)
+		&& !(surf->offset & 63)
+		&& !(((struct nvfx_surface*)surf)->pitch & 63);
+}
+
+static inline boolean
+nvfx_surface_swizzled_renderable(struct pipe_framebuffer_state* fb, struct pipe_surface* surf)
+{
+	/* TODO: return FALSE if we have a format not supporting swizzled rendering (e.g. r8); currently those are not supported at all */
+	return !((struct nvfx_miptree*)surf->texture)->linear_pitch
+		&& (surf->texture->target != PIPE_TEXTURE_3D || u_minify(surf->texture->depth0, surf->level) <= 1)
+		&& !(surf->offset & 127)
+		&& (surf->width == fb->width)
+		&& (surf->height == fb->height)
+		&& !((struct nvfx_surface*)surf)->temp;
+}
+
+static boolean
+nvfx_surface_get_render_target(struct pipe_surface* surf, int all_swizzled, struct nvfx_render_target* target)
+{
+	struct nvfx_surface* ns = (struct nvfx_surface*)surf;
+	if(!ns->temp)
+	{
+		target->bo = ((struct nvfx_miptree*)surf->texture)->base.bo;
+		target->offset = surf->offset;
+		target->pitch = align(ns->pitch, 64);
+		return FALSE;
+	}
+	else
+	{
+		target->offset = 0;
+		target->pitch = align(ns->pitch, 64);
+
+		target->bo = ns->temp;
+		return TRUE;
+	}
+}
+
+int
+nvfx_framebuffer_prepare(struct nvfx_context *nvfx)
 {
 	struct pipe_framebuffer_state *fb = &nvfx->framebuffer;
-	struct nouveau_channel *chan = nvfx->screen->base.channel;
-	uint32_t rt_enable = 0, rt_format = 0;
-	int i, colour_format = 0, zeta_format = 0;
-	unsigned rt_flags = NOUVEAU_BO_RDWR | NOUVEAU_BO_VRAM;
-	unsigned w = fb->width;
-	unsigned h = fb->height;
+	int i, color_format = 0, zeta_format = 0;
 	int all_swizzled = 1;
-	int render_temps = 0;
 
 	if(!nvfx->is_nv4x)
 		assert(fb->nr_cbufs <= 2);
@@ -22,51 +57,70 @@ nvfx_state_framebuffer_validate(struct nvfx_context *nvfx)
 		assert(fb->nr_cbufs <= 4);
 
 	for (i = 0; i < fb->nr_cbufs; i++) {
-		if (colour_format)
-			assert(colour_format == fb->cbufs[i]->format);
-		else
-			colour_format = fb->cbufs[i]->format;
-
-		rt_enable |= (NV34TCL_RT_ENABLE_COLOR0 << i);
-
-		if(((struct nvfx_miptree*)fb->cbufs[i]->texture)->linear_pitch
-				|| (fb->cbufs[i]->texture->target == PIPE_TEXTURE_3D && u_minify(fb->cbufs[i]->texture->depth0, fb->cbufs[0]->level) > 1)
-				|| (fb->cbufs[i]->offset & 63)
-				|| (fb->cbufs[i]->width != fb->width)
-				|| (fb->cbufs[i]->height != fb->height)
-				)
+		if (color_format) {
+			if(color_format != fb->cbufs[i]->format)
+				return -1;
+		} else
+			color_format = fb->cbufs[i]->format;
+
+		if(!nvfx_surface_swizzled_renderable(fb, fb->cbufs[i]))
 			all_swizzled = 0;
 	}
 
-	for (i = 0; i < fb->nr_cbufs; i++)
-	{
-		nvfx_surface_get_render_target(fb->cbufs[i], all_swizzled, &nvfx->hw_rt[i]);
-		if(nvfx->hw_rt[i].bo != ((struct nvfx_miptree*)fb->cbufs[i]->texture)->base.bo)
-			render_temps |= (1 << i);
-		assert(util_format_get_stride(fb->cbufs[i]->format, fb->width) <= nvfx->hw_rt[i].pitch);
-		assert(all_swizzled || nvfx->hw_rt[i].offset + nvfx->hw_rt[i].pitch * fb->height <= nvfx->hw_rt[i].bo->size);
+	if (fb->zsbuf) {
+		/* TODO: return FALSE if we have a format not supporting a depth buffer (e.g. r8); currently those are not supported at all */
+		if(!nvfx_surface_swizzled_renderable(fb, fb->zsbuf))
+			all_swizzled = 0;
+
+		if(all_swizzled && util_format_get_blocksize(color_format) != util_format_get_blocksize(zeta_format))
+			all_swizzled = 0;
 	}
-	for(; i < 4; ++i)
-		nvfx->hw_rt[i].bo = 0;
 
+	for (i = 0; i < fb->nr_cbufs; i++) {
+		if(!((struct nvfx_surface*)fb->cbufs[i])->temp && !all_swizzled && !nvfx_surface_linear_renderable(fb->cbufs[i]))
+			nvfx_surface_create_temp(&nvfx->pipe, fb->cbufs[i]);
+	}
+
+	if(fb->zsbuf) {
+		if(!((struct nvfx_surface*)fb->zsbuf)->temp && !all_swizzled && !nvfx_surface_linear_renderable(fb->zsbuf))
+			nvfx_surface_create_temp(&nvfx->pipe, fb->zsbuf);
+	}
+
+	return all_swizzled;
+}
+
+void
+nvfx_framebuffer_validate(struct nvfx_context *nvfx, unsigned prepare_result)
+{
+	struct pipe_framebuffer_state *fb = &nvfx->framebuffer;
+	struct nouveau_channel *chan = nvfx->screen->base.channel;
+	uint32_t rt_enable, rt_format;
+	int i;
+	unsigned rt_flags = NOUVEAU_BO_RDWR | NOUVEAU_BO_VRAM;
+	unsigned w = fb->width;
+	unsigned h = fb->height;
+
+	rt_enable = (NV34TCL_RT_ENABLE_COLOR0 << fb->nr_cbufs) - 1;
 	if (rt_enable & (NV34TCL_RT_ENABLE_COLOR1 |
 			 NV40TCL_RT_ENABLE_COLOR2 | NV40TCL_RT_ENABLE_COLOR3))
 		rt_enable |= NV34TCL_RT_ENABLE_MRT;
 
-	if (fb->zsbuf) {
-		zeta_format = fb->zsbuf->format;
-		nvfx_surface_get_render_target(fb->zsbuf, 0, &nvfx->hw_zeta);
+	nvfx->state.render_temps = 0;
 
-		if(nvfx->hw_zeta.bo != ((struct nvfx_miptree*)fb->zsbuf->texture)->base.bo)
-			render_temps |= 0x80;
+	for (i = 0; i < fb->nr_cbufs; i++)
+		nvfx->state.render_temps |= nvfx_surface_get_render_target(fb->cbufs[i], prepare_result, &nvfx->hw_rt[i]) << i;
+
+	for(; i < 4; ++i)
+		nvfx->hw_rt[i].bo = 0;
+
+	if (fb->zsbuf) {
+		nvfx->state.render_temps |= nvfx_surface_get_render_target(fb->zsbuf, prepare_result, &nvfx->hw_zeta) << 7;
 
 		assert(util_format_get_stride(fb->zsbuf->format, fb->width) <= nvfx->hw_zeta.pitch);
 		assert(nvfx->hw_zeta.offset + nvfx->hw_zeta.pitch * fb->height <= nvfx->hw_zeta.bo->size);
 	}
 
-	nvfx->state.render_temps = render_temps;
-
-	if (fb->nr_cbufs && all_swizzled) {
+	if (prepare_result) {
 		assert(!(fb->width & (fb->width - 1)) && !(fb->height & (fb->height - 1)));
 
 		rt_format = NV34TCL_RT_FORMAT_TYPE_SWIZZLED |
@@ -75,33 +129,43 @@ nvfx_state_framebuffer_validate(struct nvfx_context *nvfx)
 	} else
 		rt_format = NV34TCL_RT_FORMAT_TYPE_LINEAR;
 
-	switch (colour_format) {
-	case PIPE_FORMAT_B8G8R8X8_UNORM:
-		rt_format |= NV34TCL_RT_FORMAT_COLOR_X8R8G8B8;
-		break;
-	case PIPE_FORMAT_B8G8R8A8_UNORM:
-	case 0:
-		rt_format |= NV34TCL_RT_FORMAT_COLOR_A8R8G8B8;
-		break;
-	case PIPE_FORMAT_B5G6R5_UNORM:
+	if(fb->nr_cbufs > 0) {
+		switch (fb->cbufs[0]->format) {
+		case PIPE_FORMAT_B8G8R8X8_UNORM:
+			rt_format |= NV34TCL_RT_FORMAT_COLOR_X8R8G8B8;
+			break;
+		case PIPE_FORMAT_B8G8R8A8_UNORM:
+		case 0:
+			rt_format |= NV34TCL_RT_FORMAT_COLOR_A8R8G8B8;
+			break;
+		case PIPE_FORMAT_B5G6R5_UNORM:
+			rt_format |= NV34TCL_RT_FORMAT_COLOR_R5G6B5;
+			break;
+		default:
+			assert(0);
+		}
+	} else if(fb->zsbuf && util_format_get_blocksize(fb->zsbuf->format) == 2)
 		rt_format |= NV34TCL_RT_FORMAT_COLOR_R5G6B5;
-		break;
-	default:
-		assert(0);
-	}
+	else
+		rt_format |= NV34TCL_RT_FORMAT_COLOR_A8R8G8B8;
 
-	switch (zeta_format) {
-	case PIPE_FORMAT_Z16_UNORM:
+	if(fb->zsbuf) {
+		switch (fb->zsbuf->format) {
+		case PIPE_FORMAT_Z16_UNORM:
+			rt_format |= NV34TCL_RT_FORMAT_ZETA_Z16;
+			break;
+		case PIPE_FORMAT_S8_USCALED_Z24_UNORM:
+		case PIPE_FORMAT_X8Z24_UNORM:
+		case 0:
+			rt_format |= NV34TCL_RT_FORMAT_ZETA_Z24S8;
+			break;
+		default:
+			assert(0);
+		}
+	} else if(fb->nr_cbufs && util_format_get_blocksize(fb->cbufs[0]->format) == 2)
 		rt_format |= NV34TCL_RT_FORMAT_ZETA_Z16;
-		break;
-	case PIPE_FORMAT_S8_USCALED_Z24_UNORM:
-	case PIPE_FORMAT_X8Z24_UNORM:
-	case 0:
+	else
 		rt_format |= NV34TCL_RT_FORMAT_ZETA_Z24S8;
-		break;
-	default:
-		assert(0);
-	}
 
 	if ((rt_enable & NV34TCL_RT_ENABLE_COLOR0) || fb->zsbuf) {
 		struct nvfx_render_target *rt0 = &nvfx->hw_rt[0];
@@ -118,6 +182,8 @@ nvfx_state_framebuffer_validate(struct nvfx_context *nvfx)
 				pitch |= (pitch << 16);
 		}
 
+		//printf("rendering to bo %p [%i] at offset %i with pitch %i\n", rt0->bo, rt0->bo->handle, rt0->offset, pitch);
+
 		OUT_RING(chan, RING_3D(NV34TCL_DMA_COLOR0, 1));
 		OUT_RELOC(chan, rt0->bo, 0,
 			      rt_flags | NOUVEAU_BO_OR,
@@ -170,7 +236,7 @@ nvfx_state_framebuffer_validate(struct nvfx_context *nvfx)
 		}
 	}
 
-	if (zeta_format) {
+	if (fb->zsbuf) {
 		OUT_RING(chan, RING_3D(NV34TCL_DMA_ZETA, 1));
 		OUT_RELOC(chan, nvfx->hw_zeta.bo, 0,
 			      rt_flags | NOUVEAU_BO_OR,
diff --git a/src/gallium/drivers/nvfx/nvfx_surface.c b/src/gallium/drivers/nvfx/nvfx_surface.c
index 44df192..e7e68ed 100644
--- a/src/gallium/drivers/nvfx/nvfx_surface.c
+++ b/src/gallium/drivers/nvfx/nvfx_surface.c
@@ -16,15 +16,14 @@
 #include "nv04_2d.h"
 
 static INLINE void
-nvfx_region_init(struct nv04_region* rgn, struct nvfx_surface* surf, unsigned x, unsigned y)
+nvfx_surface_get_region(struct nv04_region* rgn, struct nvfx_surface* ns, unsigned x, unsigned y, boolean for_write)
 {
-	rgn->bo = ((struct nvfx_miptree*)surf->base.texture)->base.bo;
-	rgn->offset = surf->base.offset;
+	struct pipe_surface* surf = &ns->base.base;
 	rgn->x = x;
 	rgn->y = y;
 	rgn->z = 0;
 
-	unsigned bits = util_format_get_blocksizebits(surf->base.texture->format);
+	unsigned bits = util_format_get_blocksizebits(surf->texture->format);
 	switch(bits)
 	{
 	case 8:
@@ -42,38 +41,50 @@ nvfx_region_init(struct nv04_region* rgn, struct nvfx_surface* surf, unsigned x,
 		assert(shift >= 2);
 		rgn->bpps = 2;
 		shift -= 2;
-		assert(surf->base.texture->flags & NVFX_RESOURCE_FLAG_LINEAR);
+		assert(surf->texture->flags & NVFX_RESOURCE_FLAG_LINEAR);
 
-		rgn->x = util_format_get_nblocksx(surf->base.format, x) << shift;
-		rgn->y = util_format_get_nblocksy(surf->base.format, y);
+		rgn->x = util_format_get_nblocksx(surf->format, x) << shift;
+		rgn->y = util_format_get_nblocksy(surf->format, y);
 	}
 
-        if(!(surf->base.texture->flags & NVFX_RESOURCE_FLAG_LINEAR))
-        {
-		unsigned depth = u_minify(surf->base.texture->depth0, surf->base.level);
+	if(ns->temp) {
+		rgn->bo = ns->temp;
+		rgn->offset = 0;
+		rgn->pitch = ns->pitch;
 
-		// TODO: move this code to surface creation?
-		if((depth <= 1) && (surf->base.height <= 1 || surf->base.width <= 2))
-			rgn->pitch = surf->base.width << rgn->bpps;
-		else if(depth > 1 && surf->base.height <= 2 && surf->base.width <= 2)
+		if(for_write)
+			util_dirty_surface_set_dirty(nvfx_surface_get_dirty_surfaces(surf), &ns->base);
+	} else {
+		rgn->bo = ((struct nvfx_miptree*)surf->texture)->base.bo;
+		rgn->offset = surf->offset;
+
+		if(!(surf->texture->flags & NVFX_RESOURCE_FLAG_LINEAR))
 		{
-			rgn->pitch = surf->base.width << rgn->bpps;
-			rgn->offset += (surf->base.zslice * surf->base.width * surf->base.height) << rgn->bpps;
+			unsigned depth = u_minify(surf->texture->depth0, surf->level);
+
+			// TODO: move this code to surface creation?
+			if((depth <= 1) && (surf->height <= 1 || surf->width <= 2))
+				rgn->pitch = surf->width << rgn->bpps;
+			else if(depth > 1 && surf->height <= 2 && surf->width <= 2)
+			{
+				rgn->pitch = surf->width << rgn->bpps;
+				rgn->offset += (surf->zslice * surf->width * surf->height) << rgn->bpps;
+			}
+			else
+			{
+				rgn->pitch = 0;
+				rgn->z = surf->zslice;
+				rgn->w = surf->width;
+				rgn->h = surf->height;
+				rgn->d = depth;
+			}
 		}
 		else
 		{
-			rgn->pitch = 0;
-			rgn->z = surf->base.zslice;
-			rgn->w = surf->base.width;
-			rgn->h = surf->base.height;
-			rgn->d = depth;
+			rgn->pitch = ns->pitch;
+			//rgn->w = rgn->h = rgn->d = rgn->z = 0; // undefined for non-swizzled
 		}
 	}
-	else
-	{
-		rgn->pitch = surf->pitch;
-		//rgn->w = rgn->h = rgn->d = rgn->z = 0; // undefined for non-swizzled
-	}
 }
 
 // TODO: actually test this for all formats, it's probably wrong for some...
@@ -156,9 +167,6 @@ nvfx_surface_copy(struct pipe_context* pipe, struct pipe_surface *dsts,
 	struct nv04_2d_context *ctx = nvfx_screen(pipe->screen)->eng2d;
 	struct nv04_region dst, src;
 
-	nvfx_surface_flush(dsts);
-	nvfx_surface_flush(srcs);
-
 	if(!w || !h)
 		return;
 
@@ -175,8 +183,8 @@ nvfx_surface_copy(struct pipe_context* pipe, struct pipe_surface *dsts,
 
 	//printf("%i %ix%i\n", dsts->format, w, h);
 
-	nvfx_region_init(&dst, (struct nvfx_surface*)dsts, dx, dy);
-	nvfx_region_init(&src, (struct nvfx_surface*)srcs, sx, sy);
+	nvfx_surface_get_region(&dst, (struct nvfx_surface*)dsts, dx, dy, TRUE);
+	nvfx_surface_get_region(&src, (struct nvfx_surface*)srcs, sx, sy, FALSE);
 	w = util_format_get_stride(dsts->format, w) >> dst.bpps;
 	h = util_format_get_nblocksy(dsts->format, h);
 
@@ -209,12 +217,10 @@ nvfx_surface_fill(struct pipe_context* pipe, struct pipe_surface *dsts,
 	struct nv04_2d_context *ctx = nvfx_screen(pipe->screen)->eng2d;
 	struct nv04_region dst;
 	/* Always try to use the GPU right now, if possible
-	 * If the user wanted the surface data on the CPU, he would have cleared with memset */
-
-	nvfx_surface_flush(dsts);
+	 * If the user wanted the surface data on the CPU, he would have cleared with memset (hopefully) */
 
 	// we don't care about interior pixel order since we set all them to the same value
-	nvfx_region_init(&dst, (struct nvfx_surface*)dsts, dx, dy);
+	nvfx_surface_get_region(&dst, (struct nvfx_surface*)dsts, dx, dy, TRUE);
 	w = util_format_get_stride(dsts->format, w) >> dst.bpps;
 	h = util_format_get_nblocksy(dsts->format, h);
 
@@ -254,109 +260,82 @@ nvfx_init_surface_functions(struct nvfx_context* nvfx)
 	nvfx->pipe.surface_fill = nvfx_surface_fill;
 }
 
-void
-nvfx_surface_copy_render_temp(struct pipe_surface* surf, int dir)
+static void
+nvfx_surface_copy_temp(struct pipe_context* pipe, struct pipe_surface* surf, int to_temp)
 {
 	struct nv04_2d_context* ctx = nvfx_screen(surf->texture->screen)->eng2d;
 	struct nvfx_surface* ns = (struct nvfx_surface*)surf;
-	unsigned w = ns->base.width;
-	unsigned h = ns->base.height;
+	unsigned w = surf->width;
+	unsigned h = surf->height;
 	struct nv04_region surfrgn;
-	struct nv04_region render;
-	nvfx_region_init(&surfrgn, ns, 0, 0);
-	render.bo = ns->render;
-	render.offset = 0;
-	render.pitch = align(util_format_get_stride(ns->base.format, ns->base.width), 64);
-	render.bpps = surfrgn.bpps;
-	render.x = render.y = render.z = 0;
-	struct nv04_region* dst = dir ? &render : &surfrgn;
-	struct nv04_region* src = dir ? &surfrgn : &render;
-	if(nv04_region_copy_2d(ctx, dst, src, w, h,
-			nvfx_surface_format(ns->base.format), nv04_scaled_image_format(ns->base.format),
-			1, 1))
-		nv04_region_copy_cpu(dst, src, w, h);
-}
+	struct nv04_region temp;
+	temp.bo = ns->temp;
+	/* zero it out temporarily, so we can get the original region */
+	ns->temp = 0;
+	nvfx_surface_get_region(&surfrgn, ns, 0, 0, !to_temp);
+	ns->temp = temp.bo;
+
+	temp.offset = 0;
+	temp.pitch = align(util_format_get_stride(surf->format, surf->width), 64);
+	temp.bpps = surfrgn.bpps;
+	temp.x = temp.y = temp.z = 0;
+
+	struct nv04_region* dst = to_temp ? &temp : &surfrgn;
+	struct nv04_region* src = to_temp ? &surfrgn : &temp;
+	int ret = nv04_region_copy_2d(ctx, dst, src, w, h,
+			nvfx_surface_format(surf->format), nv04_scaled_image_format(surf->format),
+			1, 1);
+	if(!ret)
+	{}
+	else if(ret > 0) {
+		// TODO: use ad-hoc 3D code here, this is horribly inefficient
+		struct pipe_resource* tempt = nvfx_miptree_from_region(surf->texture->screen, &temp, surf->texture->format, w, h);
+		struct pipe_surface* temps = nvfx_miptree_surface_new(tempt->screen, tempt, 0, 0, 0, 0);
+		struct pipe_surface* dsts = to_temp ? temps : surf;
+		struct pipe_surface* srcs = to_temp ? surf : temps;
+
+		/* TODO: check this earlier! */
+		if(dsts->texture->bind & PIPE_BIND_RENDER_TARGET
+			&& srcs->texture->bind & PIPE_BIND_SAMPLER_VIEW)
+		{
+			struct blitter_context* blitter = nvfx_get_blitter(pipe, 1);
+			struct nvfx_context* nvfx = (struct nvfx_context*)pipe;
+			void* ib = nvfx->idxbuf;
+			unsigned fmt = nvfx->idxbuf_format;
+			util_blitter_copy(blitter, dsts, dst->x, dst->y, srcs, src->x, src->y, w, h, TRUE);
+			nvfx->idxbuf = ib;
+			nvfx->idxbuf_format = fmt;
+			// velem restore already sets NVFX_NEW_ARRAYS
+		}
+		else
+			nv04_region_copy_cpu(dst, src, w, h);
 
-void
-nvfx_surface_do_use_render_temp(struct pipe_surface* surf, struct list_head* render_cache)
-{
-	struct nvfx_surface* ns = (struct nvfx_surface*)surf;
-	if(!ns->render_cache)
-		nvfx_surface_copy_render_temp(surf, 1);
-	else
-	{
-		/* We can keep the surface only on one cache, and the user expects that flushing the old cache
-		 * flushes this surface. Thus, we have no choice except flushing now.
-		 */
-		nvfx_surface_copy_render_temp(surf, 0);
-		LIST_DEL(&ns->render_list);
+		pipe_surface_reference(&temps, 0);
+		pipe_resource_reference(&tempt, 0);
 	}
-
-	ns->render_cache = render_cache;
-	LIST_ADDTAIL(&ns->render_list, render_cache);
+	else
+		nv04_region_copy_cpu(dst, src, w, h);
 }
 
 void
-nvfx_surface_do_flush(struct pipe_surface* surf)
+nvfx_surface_create_temp(struct pipe_context* pipe, struct pipe_surface* surf)
 {
 	struct nvfx_surface* ns = (struct nvfx_surface*)surf;
-	//printf("flushing bo %i %ix%i\n", ns->render->handle, ns->base.width, ns->base.height);
-
-	nvfx_surface_copy_render_temp(surf, 0);
-
-	LIST_DEL(&ns->render_list);
-	ns->render_cache = 0;
+	unsigned size = util_format_get_2d_size(surf->format, align(ns->pitch, 64), surf->height);
+	// TODO: 256 alignment is probably too much, 128 or 64 should be correct
+	// TODO: should we put _GART here? seems we shouldn't
+	nouveau_bo_new(nouveau_screen(surf->texture->screen)->device, NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM, 256, size, &ns->temp);
+	nvfx_surface_copy_temp(pipe, surf, 1);
 }
 
-static inline
-int nvfx_surface_renderable(struct nvfx_surface* ns, int all_swizzled)
-{
-	if(all_swizzled)
-		assert(!(ns->base.texture->flags & NVFX_RESOURCE_FLAG_LINEAR));
-
-	if(ns->base.texture->flags & NVFX_RESOURCE_FLAG_LINEAR)
-		return !(ns->base.offset & 63) && !(ns->pitch & 63);
-	else
-		return all_swizzled;
-}
-
-void nvfx_surface_get_render_target(struct pipe_surface* surf, int all_swizzled, struct nvfx_render_target* target)
+void
+nvfx_surface_flush(struct pipe_context* pipe, struct pipe_surface* surf)
 {
 	struct nvfx_surface* ns = (struct nvfx_surface*)surf;
-	if(nvfx_surface_renderable(ns, all_swizzled))
-	{
-		nvfx_surface_flush(surf);
 
-		target->bo = ((struct nvfx_miptree*)ns->base.texture)->base.bo;
-		target->offset = ns->base.offset;
-		target->pitch = align(ns->pitch, 64);
-	}
-	else
-	{
-		if(!ns->render)
-		{
-			unsigned size = util_format_get_2d_size(ns->base.format, align(ns->pitch, 64), ns->base.height);
-			// TODO: 256 alignment is probably too much, 128 or 64 should be correct
-			// TODO: should we put _GART here? seems we shouldn't
-			nouveau_bo_new(nouveau_screen(surf->texture->screen)->device, NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM, 256, size, &ns->render);
-//			printf("render temp!\n");
-		}
+	nvfx_surface_copy_temp(pipe, surf, 0);
+	nouveau_bo_ref(0, &ns->temp);
 
-		target->bo = ns->render;
-		target->offset = 0;
-		target->pitch = align(ns->pitch, 64);
-	}
+	util_dirty_surface_set_clean(nvfx_surface_get_dirty_surfaces(surf), &ns->base);
 }
 
-void
-nvfx_surface_do_flush_render_cache(struct list_head* list)
-{
-	struct list_head* cur = list->next;
-	struct list_head* next;
-	for(cur = list->next; cur != list; cur = next)
-	{
-		struct nvfx_surface* ns = LIST_ENTRY(struct nvfx_surface, cur, render_list);
-		next = cur->next;
-		nvfx_surface_flush(&ns->base);
-	}
-}
diff --git a/src/gallium/drivers/nvfx/nvfx_transfer.c b/src/gallium/drivers/nvfx/nvfx_transfer.c
index d076cb7..b224d62 100644
--- a/src/gallium/drivers/nvfx/nvfx_transfer.c
+++ b/src/gallium/drivers/nvfx/nvfx_transfer.c
@@ -179,10 +179,10 @@ nvfx_miptree_transfer_map(struct pipe_context *pipe, struct pipe_transfer *ptx)
 					     nouveau_screen_transfer_flags(ptx->usage));
 
 	if(!tx->direct)
-		return map + ns->base.offset;
+		return map + ns->base.base.offset;
 	else
-		return map + ns->base.offset
-		+ util_format_get_2d_size(ns->base.format, ns->pitch, ptx->box.y)
+		return map + ns->base.base.offset
+		+ util_format_get_2d_size(ns->base.base.format, ns->pitch, ptx->box.y)
 		+ util_format_get_stride(ptx->resource->format, ptx->box.x);
 }
 




More information about the mesa-commit mailing list