nv50: add 3d texture tiling and mip-mapping
authorChristoph Bumiller <e0425955@student.tuwien.ac.at>
Tue, 3 Nov 2009 22:19:56 +0000 (23:19 +0100)
committerChristoph Bumiller <e0425955@student.tuwien.ac.at>
Tue, 3 Nov 2009 23:53:51 +0000 (00:53 +0100)
Mip-mapped 3D textures are not arrays of 2D layers
with a mip-map layout like 2D textures, therefore we
cannot use image_nr == depth for them.

Making use of "volume tiling" modes now, the allowed
modes are 0xZY where Z <= 5 and y <= 5.

src/gallium/drivers/nv50/nv50_context.h
src/gallium/drivers/nv50/nv50_miptree.c
src/gallium/drivers/nv50/nv50_tex.c
src/gallium/drivers/nv50/nv50_transfer.c

index 890defb..4b0f062 100644 (file)
@@ -69,6 +69,18 @@ struct nv50_sampler_stateobj {
        unsigned tsc[8];
 };
 
+static INLINE unsigned
+get_tile_height(uint32_t tile_mode)
+{
+        return 1 << ((tile_mode & 0xf) + 2);
+}
+
+static INLINE unsigned
+get_tile_depth(uint32_t tile_mode)
+{
+        return 1 << (tile_mode >> 4);
+}
+
 struct nv50_miptree_level {
        int *image_offset;
        unsigned pitch;
index 229a59c..9c20c5c 100644 (file)
 
 #include "nv50_context.h"
 
+/* The restrictions in tile mode selection probably aren't necessary. */
 static INLINE uint32_t
-get_tile_mode(unsigned ny)
+get_tile_mode(unsigned ny, unsigned d)
 {
-       if (ny > 32) return 4;
-       if (ny > 16) return 3;
-       if (ny >  8) return 2;
-       if (ny >  4) return 1;
-       return 0;
+       uint32_t tile_mode = 0x00;
+
+       if (ny > 32) tile_mode = 0x04; /* height 64 tiles */
+       else
+       if (ny > 16) tile_mode = 0x03; /* height 32 tiles */
+       else
+       if (ny >  8) tile_mode = 0x02; /* height 16 tiles */
+       else
+       if (ny >  4) tile_mode = 0x01; /* height 8 tiles */
+
+       if (d == 1)
+               return tile_mode;
+       else
+       if (tile_mode > 0x02)
+               tile_mode = 0x02;
+
+       if (d > 16 && tile_mode < 0x02)
+               return tile_mode | 0x50; /* depth 32 tiles */
+       if (d >  8) return tile_mode | 0x40; /* depth 16 tiles */
+       if (d >  4) return tile_mode | 0x30; /* depth 8 tiles */
+       if (d >  2) return tile_mode | 0x20; /* depth 4 tiles */
+
+       return tile_mode | 0x10;
 }
 
 static struct pipe_texture *
@@ -43,7 +62,7 @@ nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
        struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
        struct pipe_texture *pt = &mt->base.base;
        unsigned width = tmp->width[0], height = tmp->height[0];
-       unsigned depth = tmp->depth[0];
+       unsigned depth = tmp->depth[0], image_alignment;
        uint32_t tile_flags;
        int ret, i, l;
 
@@ -67,17 +86,8 @@ nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
                break;
        }
 
-       switch (pt->target) {
-       case PIPE_TEXTURE_3D:
-               mt->image_nr = pt->depth[0];
-               break;
-       case PIPE_TEXTURE_CUBE:
-               mt->image_nr = 6;
-               break;
-       default:
-               mt->image_nr = 1;
-               break;
-       }
+       /* XXX: texture arrays */
+       mt->image_nr = (pt->target == PIPE_TEXTURE_CUBE) ? 6 : 1;
 
        for (l = 0; l <= pt->last_level; l++) {
                struct nv50_miptree_level *lvl = &mt->level[l];
@@ -90,26 +100,35 @@ nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
 
                lvl->image_offset = CALLOC(mt->image_nr, sizeof(int));
                lvl->pitch = align(pt->nblocksx[l] * pt->block.size, 64);
-               lvl->tile_mode = get_tile_mode(pt->nblocksy[l]);
+               lvl->tile_mode = get_tile_mode(pt->nblocksy[l], depth);
 
                width = MAX2(1, width >> 1);
                height = MAX2(1, height >> 1);
                depth = MAX2(1, depth >> 1);
        }
 
+       image_alignment  = get_tile_height(mt->level[0].tile_mode) * 64;
+       image_alignment *= get_tile_depth(mt->level[0].tile_mode);
+
+       /* NOTE the distinction between arrays of mip-mapped 2D textures and
+        * mip-mapped 3D textures. We can't use image_nr == depth for 3D mip.
+        */
        for (i = 0; i < mt->image_nr; i++) {
                for (l = 0; l <= pt->last_level; l++) {
                        struct nv50_miptree_level *lvl = &mt->level[l];
                        int size;
-                       unsigned tile_ny = 1 << (lvl->tile_mode + 2);
+                       unsigned tile_h = get_tile_height(lvl->tile_mode);
+                       unsigned tile_d = get_tile_depth(lvl->tile_mode);
 
-                       size  = align(pt->nblocksx[l] * pt->block.size, 64);
-                       size *= align(pt->nblocksy[l], tile_ny);
+                       size  = lvl->pitch;
+                       size *= align(pt->nblocksy[l], tile_h);
+                       size *= align(pt->depth[l], tile_d);
 
                        lvl->image_offset[i] = mt->total_size;
 
                        mt->total_size += size;
                }
+               mt->total_size = align(mt->total_size, image_alignment);
        }
 
        ret = nouveau_bo_new_tile(dev, NOUVEAU_BO_VRAM, 256, mt->total_size,
index 52ccdaa..2813f54 100644 (file)
@@ -96,19 +96,44 @@ nv50_tex_construct(struct nv50_context *nv50, struct nouveau_stateobj *so,
        if (i == NV50_TEX_FORMAT_LIST_SIZE)
                 return 1;
 
-       mode = (nv50->sampler[unit]->normalized ? 0xd0005000 : 0x5001d000) |
-              (mt->base.bo->tile_mode << 22);
+       if (nv50->sampler[unit]->normalized)
+               mode = 0x50001000 | (1 << 31);
+       else {
+               mode = 0x50001000 | (7 << 14);
+               assert(mt->base.base.target == PIPE_TEXTURE_2D);
+       }
+
+       mode |= ((mt->base.bo->tile_mode & 0x0f) << 22) |
+               ((mt->base.bo->tile_mode & 0xf0) << 21);
+
        if (pf_type(mt->base.base.format) == PIPE_FORMAT_TYPE_SRGB)
                mode |= 0x0400;
 
+       switch (mt->base.base.target) {
+       case PIPE_TEXTURE_1D:
+               break;
+       case PIPE_TEXTURE_2D:
+               mode |= (1 << 14);
+               break;
+       case PIPE_TEXTURE_3D:
+               mode |= (2 << 14);
+               break;
+       case PIPE_TEXTURE_CUBE:
+               mode |= (3 << 14);
+               break;
+       default:
+               assert(!"unsupported texture target");
+               break;
+       }
+
        so_data (so, nv50_tex_format_list[i].hw);
        so_reloc(so, mt->base.bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_LOW |
-                    NOUVEAU_BO_RD, 0, 0);
+                NOUVEAU_BO_RD, 0, 0);
        so_data (so, mode);
        so_data (so, 0x00300000);
-       so_data (so, mt->base.base.width[0]);
+       so_data (so, mt->base.base.width[0] | (1 << 31));
        so_data (so, (mt->base.base.last_level << 28) |
-                    (mt->base.base.depth[0] << 16) | mt->base.base.height[0]);
+                (mt->base.base.depth[0] << 16) | mt->base.base.height[0]);
        so_data (so, 0x03000000);
        so_data (so, mt->base.base.last_level << 4);
 
@@ -124,7 +149,7 @@ nv50_tex_validate(struct nv50_context *nv50)
        unsigned i, unit, push;
 
        push = MAX2(nv50->miptree_nr, nv50->state.miptree_nr) * 2 + 23 + 6;
-       so = so_new(nv50->miptree_nr * 9 + push, nv50->miptree_nr + 2);
+       so = so_new(nv50->miptree_nr * 9 + push, nv50->miptree_nr * 2 + 2);
 
        nv50_so_init_sifc(nv50, so, nv50->screen->tic, NOUVEAU_BO_VRAM,
                          nv50->miptree_nr * 8 * 4);
index 9c00809..ea61357 100644 (file)
@@ -12,6 +12,7 @@ struct nv50_transfer {
        int level_pitch;
        int level_width;
        int level_height;
+       int level_depth;
        int level_x;
        int level_y;
 };
@@ -20,10 +21,10 @@ static void
 nv50_transfer_rect_m2mf(struct pipe_screen *pscreen,
                        struct nouveau_bo *src_bo, unsigned src_offset,
                        int src_pitch, unsigned src_tile_mode,
-                       int sx, int sy, int sw, int sh,
+                       int sx, int sy, int sw, int sh, int sd,
                        struct nouveau_bo *dst_bo, unsigned dst_offset,
                        int dst_pitch, unsigned dst_tile_mode,
-                       int dx, int dy, int dw, int dh,
+                       int dx, int dy, int dw, int dh, int dd,
                        int cpp, int width, int height,
                        unsigned src_reloc, unsigned dst_reloc)
 {
@@ -51,7 +52,7 @@ nv50_transfer_rect_m2mf(struct pipe_screen *pscreen,
                OUT_RING  (chan, src_tile_mode << 4);
                OUT_RING  (chan, sw * cpp);
                OUT_RING  (chan, sh);
-               OUT_RING  (chan, 1);
+               OUT_RING  (chan, sd);
                OUT_RING  (chan, 0);
        }
 
@@ -70,7 +71,7 @@ nv50_transfer_rect_m2mf(struct pipe_screen *pscreen,
                OUT_RING  (chan, dst_tile_mode << 4);
                OUT_RING  (chan, dw * cpp);
                OUT_RING  (chan, dh);
-               OUT_RING  (chan, 1);
+               OUT_RING  (chan, dd);
                OUT_RING  (chan, 0);
        }
 
@@ -114,6 +115,20 @@ nv50_transfer_rect_m2mf(struct pipe_screen *pscreen,
        }
 }
 
+static INLINE unsigned
+get_zslice_offset(unsigned tile_mode, unsigned z, unsigned pitch, unsigned ny)
+{
+       unsigned tile_h = get_tile_height(tile_mode);
+       unsigned tile_d = get_tile_depth(tile_mode);
+
+       /* pitch_2d == to next slice within this volume-tile */
+       /* pitch_3d == to next slice in next 2D array of blocks */
+       unsigned pitch_2d = tile_h * 64;
+       unsigned pitch_3d = tile_d * align(ny, tile_h) * pitch;
+
+       return (z % tile_d) * pitch_2d + (z / tile_d) * pitch_3d;
+}
+
 static struct pipe_transfer *
 nv50_transfer_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
                  unsigned face, unsigned level, unsigned zslice,
@@ -129,9 +144,6 @@ nv50_transfer_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
 
        if (pt->target == PIPE_TEXTURE_CUBE)
                image = face;
-       else
-       if (pt->target == PIPE_TEXTURE_3D)
-               image = zslice;
 
        tx = CALLOC_STRUCT(nv50_transfer);
        if (!tx)
@@ -157,6 +169,7 @@ nv50_transfer_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
        tx->level_pitch = lvl->pitch;
        tx->level_width = mt->base.base.width[level];
        tx->level_height = mt->base.base.height[level];
+       tx->level_depth = mt->base.base.depth[level];
        tx->level_offset = lvl->image_offset[image];
        tx->level_tiling = lvl->tile_mode;
        tx->level_x = pf_get_nblocksx(&tx->base.block, x);
@@ -168,6 +181,11 @@ nv50_transfer_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
                return NULL;
        }
 
+       if (pt->target == PIPE_TEXTURE_3D)
+               tx->level_offset += get_zslice_offset(lvl->tile_mode, zslice,
+                                                     lvl->pitch,
+                                                     tx->base.nblocksy);
+
        if (usage & PIPE_TRANSFER_READ) {
                nx = pf_get_nblocksx(&tx->base.block, tx->base.width);
                ny = pf_get_nblocksy(&tx->base.block, tx->base.height);
@@ -176,10 +194,11 @@ nv50_transfer_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
                                        tx->level_pitch, tx->level_tiling,
                                        x, y,
                                        tx->base.nblocksx, tx->base.nblocksy,
+                                       tx->level_depth,
                                        tx->bo, 0,
                                        tx->base.stride, tx->bo->tile_mode,
                                        0, 0,
-                                       tx->base.nblocksx, tx->base.nblocksy,
+                                       tx->base.nblocksx, tx->base.nblocksy, 1,
                                        tx->base.block.size, nx, ny,
                                        NOUVEAU_BO_VRAM | NOUVEAU_BO_GART,
                                        NOUVEAU_BO_GART);
@@ -199,14 +218,16 @@ nv50_transfer_del(struct pipe_transfer *ptx)
 
        if (ptx->usage & PIPE_TRANSFER_WRITE) {
                struct pipe_screen *pscreen = ptx->texture->screen;
+
                nv50_transfer_rect_m2mf(pscreen, tx->bo, 0,
                                        tx->base.stride, tx->bo->tile_mode,
                                        0, 0,
-                                       tx->base.nblocksx, tx->base.nblocksy,
+                                       tx->base.nblocksx, tx->base.nblocksy, 1,
                                        mt->base.bo, tx->level_offset,
                                        tx->level_pitch, tx->level_tiling,
                                        tx->level_x, tx->level_y,
                                        tx->base.nblocksx, tx->base.nblocksy,
+                                       tx->level_depth,
                                        tx->base.block.size, nx, ny,
                                        NOUVEAU_BO_GART, NOUVEAU_BO_VRAM |
                                        NOUVEAU_BO_GART);