summaryrefslogtreecommitdiff
path: root/src/gallium/drivers/nv50/nv50_miptree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers/nv50/nv50_miptree.c')
-rw-r--r--src/gallium/drivers/nv50/nv50_miptree.c160
1 files changed, 146 insertions, 14 deletions
diff --git a/src/gallium/drivers/nv50/nv50_miptree.c b/src/gallium/drivers/nv50/nv50_miptree.c
index 2497371232..63a23d06b8 100644
--- a/src/gallium/drivers/nv50/nv50_miptree.c
+++ b/src/gallium/drivers/nv50/nv50_miptree.c
@@ -27,14 +27,16 @@
#include "nv50_context.h"
static struct pipe_texture *
-nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *pt)
+nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
{
struct pipe_winsys *ws = pscreen->winsys;
struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
- unsigned usage, width = pt->width[0], height = pt->height[0];
- int i;
+ struct pipe_texture *pt = &mt->base;
+ unsigned usage, width = tmp->width[0], height = tmp->height[0];
+ unsigned depth = tmp->depth[0];
+ int i, l;
- mt->base = *pt;
+ mt->base = *tmp;
mt->base.refcount = 1;
mt->base.screen = pscreen;
@@ -59,17 +61,38 @@ nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *pt)
mt->image_nr = 1;
break;
}
- mt->image_offset = CALLOC(mt->image_nr, sizeof(int));
+
+ for (l = 0; l <= pt->last_level; l++) {
+ struct nv50_miptree_level *lvl = &mt->level[l];
+
+ pt->width[l] = width;
+ pt->height[l] = height;
+ pt->depth[l] = depth;
+ pt->nblocksx[l] = pf_get_nblocksx(&pt->block, width);
+ pt->nblocksy[l] = pf_get_nblocksy(&pt->block, height);
+
+ lvl->image_offset = CALLOC(mt->image_nr, sizeof(int));
+ lvl->image = CALLOC(mt->image_nr, sizeof(struct pipe_buffer *));
+
+ width = MAX2(1, width >> 1);
+ height = MAX2(1, height >> 1);
+ depth = MAX2(1, depth >> 1);
+ }
for (i = 0; i < mt->image_nr; i++) {
- int image_size;
+ for (l = 0; l <= pt->last_level; l++) {
+ struct nv50_miptree_level *lvl = &mt->level[l];
+ int size;
+
+ size = align(pt->width[l], 8) * pt->block.size;
+ size = align(size, 64);
+ size *= align(pt->height[l], 8) * pt->block.size;
- image_size = align(width, 8) * pt->block.size;
- image_size = align(image_size, 64);
- image_size *= align(height, 8) * pt->block.size;
+ lvl->image[i] = ws->buffer_create(ws, 256, 0, size);
+ lvl->image_offset[i] = mt->total_size;
- mt->image_offset[i] = mt->total_size;
- mt->total_size += image_size;
+ mt->total_size += size;
+ }
}
mt->buffer = ws->buffer_create(ws, 256, usage, mt->total_size);
@@ -81,6 +104,24 @@ nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *pt)
return &mt->base;
}
+static INLINE void
+mark_dirty(uint32_t *flags, unsigned image)
+{
+ flags[image / 32] |= (1 << (image % 32));
+}
+
+static INLINE void
+mark_clean(uint32_t *flags, unsigned image)
+{
+ flags[image / 32] &= ~(1 << (image % 32));
+}
+
+static INLINE int
+is_dirty(uint32_t *flags, unsigned image)
+{
+ return !!(flags[image / 32] & (1 << (image % 32)));
+}
+
static void
nv50_miptree_release(struct pipe_screen *pscreen, struct pipe_texture **ppt)
{
@@ -96,12 +137,86 @@ nv50_miptree_release(struct pipe_screen *pscreen, struct pipe_texture **ppt)
}
}
+void
+nv50_miptree_sync(struct pipe_screen *pscreen, struct nv50_miptree *mt,
+ unsigned level, unsigned image)
+{
+ struct nouveau_winsys *nvws = nv50_screen(pscreen)->nvws;
+ struct nv50_miptree_level *lvl = &mt->level[level];
+ struct pipe_surface *dst, *src;
+ unsigned face = 0, zslice = 0;
+
+ if (!is_dirty(lvl->image_dirty_cpu, image))
+ return;
+
+ if (mt->base.target == PIPE_TEXTURE_CUBE)
+ face = image;
+ else
+ if (mt->base.target == PIPE_TEXTURE_3D)
+ zslice = image;
+
+ /* Mark as clean already - so we don't continually call this function
+ * trying to get a GPU_WRITE pipe_surface!
+ */
+ mark_clean(lvl->image_dirty_cpu, image);
+
+ /* Pretend we're doing CPU access so we get the backing pipe_surface
+ * and not a view into the larger miptree.
+ */
+ src = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
+ PIPE_BUFFER_USAGE_CPU_READ);
+
+ /* Pretend we're only reading with the GPU so surface doesn't get marked
+ * as dirtied by the GPU.
+ */
+ dst = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
+ PIPE_BUFFER_USAGE_GPU_READ);
+
+ nvws->surface_copy(nvws, dst, 0, 0, src, 0, 0, dst->width, dst->height);
+
+ pscreen->tex_surface_release(pscreen, &dst);
+ pscreen->tex_surface_release(pscreen, &src);
+}
+
+/* The reverse of the above */
+void
+nv50_miptree_sync_cpu(struct pipe_screen *pscreen, struct nv50_miptree *mt,
+ unsigned level, unsigned image)
+{
+ struct nouveau_winsys *nvws = nv50_screen(pscreen)->nvws;
+ struct nv50_miptree_level *lvl = &mt->level[level];
+ struct pipe_surface *dst, *src;
+ unsigned face = 0, zslice = 0;
+
+ if (!is_dirty(lvl->image_dirty_gpu, image))
+ return;
+
+ if (mt->base.target == PIPE_TEXTURE_CUBE)
+ face = image;
+ else
+ if (mt->base.target == PIPE_TEXTURE_3D)
+ zslice = image;
+
+ mark_clean(lvl->image_dirty_gpu, image);
+
+ src = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
+ PIPE_BUFFER_USAGE_GPU_READ);
+ dst = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
+ PIPE_BUFFER_USAGE_CPU_READ);
+
+ nvws->surface_copy(nvws, dst, 0, 0, src, 0, 0, dst->width, dst->height);
+
+ pscreen->tex_surface_release(pscreen, &dst);
+ pscreen->tex_surface_release(pscreen, &src);
+}
+
static struct pipe_surface *
nv50_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
unsigned face, unsigned level, unsigned zslice,
unsigned flags)
{
struct nv50_miptree *mt = nv50_miptree(pt);
+ struct nv50_miptree_level *lvl = &mt->level[level];
struct nv50_surface *s;
struct pipe_surface *ps;
int img;
@@ -128,12 +243,29 @@ nv50_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
ps->nblocksx = pt->nblocksx[level];
ps->nblocksy = pt->nblocksy[level];
ps->stride = ps->width * ps->block.size;
- ps->offset = mt->image_offset[img];
ps->usage = flags;
ps->status = PIPE_SURFACE_STATUS_DEFINED;
- pipe_texture_reference(&ps->texture, pt);
- pipe_buffer_reference(pscreen, &ps->buffer, mt->buffer);
+ if (flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE) {
+ assert(!(flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE));
+ nv50_miptree_sync_cpu(pscreen, mt, level, img);
+
+ ps->offset = 0;
+ pipe_texture_reference(&ps->texture, pt);
+ pipe_buffer_reference(pscreen, &ps->buffer, lvl->image[img]);
+
+ if (flags & PIPE_BUFFER_USAGE_CPU_WRITE)
+ mark_dirty(lvl->image_dirty_cpu, img);
+ } else {
+ nv50_miptree_sync(pscreen, mt, level, img);
+
+ ps->offset = lvl->image_offset[img];
+ pipe_texture_reference(&ps->texture, pt);
+ pipe_buffer_reference(pscreen, &ps->buffer, mt->buffer);
+
+ if (flags & PIPE_BUFFER_USAGE_GPU_WRITE)
+ mark_dirty(lvl->image_dirty_gpu, img);
+ }
return ps;
}