Merge branch 'gallium-noconstbuf'
[mesa.git] / src / gallium / drivers / nv50 / nv50_miptree.c
index 229a59cb743781a23513375a567fa6ab5f7ffcfa..cecb1efc9008fcf7a06c87bd0cbdc129bab64c55 100644 (file)
 #include "pipe/p_state.h"
 #include "pipe/p_defines.h"
 #include "pipe/p_inlines.h"
+#include "util/u_format.h"
 
 #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 INLINE unsigned
+get_zslice_offset(unsigned tile_mode, unsigned z, unsigned pitch, unsigned nb_h)
+{
+       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 == size (in bytes) of a volume-tile */
+       unsigned pitch_2d = tile_h * 64;
+       unsigned pitch_3d = tile_d * align(nb_h, tile_h) * pitch;
+
+       return (z % tile_d) * pitch_2d + (z / tile_d) * pitch_3d;
 }
 
 static struct pipe_texture *
@@ -42,8 +76,8 @@ nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
        struct nouveau_device *dev = nouveau_screen(pscreen)->device;
        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 width = tmp->width0, height = tmp->height0;
+       unsigned depth = tmp->depth0, image_alignment;
        uint32_t tile_flags;
        int ret, i, l;
 
@@ -67,55 +101,52 @@ 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];
-
-               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);
+               unsigned nblocksy = util_format_get_nblocksy(pt->format, height);
 
                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->pitch = align(util_format_get_stride(pt->format, width), 64);
+               lvl->tile_mode = get_tile_mode(nblocksy, depth);
 
-               width = MAX2(1, width >> 1);
-               height = MAX2(1, height >> 1);
-               depth = MAX2(1, depth >> 1);
+               width = u_minify(width, 1);
+               height = u_minify(height, 1);
+               depth = u_minify(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(util_format_get_nblocksy(pt->format, u_minify(pt->height0, l)), tile_h);
+                       size *= align(u_minify(pt->depth0, 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,
                                  mt->level[0].tile_mode, tile_flags,
                                  &mt->base.bo);
        if (ret) {
+               for (l = 0; l <= pt->last_level; ++l)
+                       FREE(mt->level[l].image_offset);
                FREE(mt);
                return NULL;
        }
@@ -132,7 +163,7 @@ nv50_miptree_blanket(struct pipe_screen *pscreen, const struct pipe_texture *pt,
 
        /* Only supports 2D, non-mipmapped textures for the moment */
        if (pt->target != PIPE_TEXTURE_2D || pt->last_level != 0 ||
-           pt->depth[0] != 1)
+           pt->depth0 != 1)
                return NULL;
 
        mt = CALLOC_STRUCT(nv50_miptree);
@@ -155,6 +186,10 @@ static void
 nv50_miptree_destroy(struct pipe_texture *pt)
 {
        struct nv50_miptree *mt = nv50_miptree(pt);
+       unsigned l;
+
+       for (l = 0; l <= pt->last_level; ++l)
+               FREE(mt->level[l].image_offset);
 
        nouveau_bo_ref(NULL, &mt->base.bo);
        FREE(mt);
@@ -168,23 +203,18 @@ nv50_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
        struct nv50_miptree *mt = nv50_miptree(pt);
        struct nv50_miptree_level *lvl = &mt->level[level];
        struct pipe_surface *ps;
-       int img;
+       unsigned img = 0;
 
        if (pt->target == PIPE_TEXTURE_CUBE)
                img = face;
-       else
-       if (pt->target == PIPE_TEXTURE_3D)
-               img = zslice;
-       else
-               img = 0;
 
        ps = CALLOC_STRUCT(pipe_surface);
        if (!ps)
                return NULL;
        pipe_texture_reference(&ps->texture, pt);
        ps->format = pt->format;
-       ps->width = pt->width[level];
-       ps->height = pt->height[level];
+       ps->width = u_minify(pt->width0, level);
+       ps->height = u_minify(pt->height0, level);
        ps->usage = flags;
        pipe_reference_init(&ps->reference, 1);
        ps->face = face;
@@ -192,6 +222,12 @@ nv50_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
        ps->zslice = zslice;
        ps->offset = lvl->image_offset[img];
 
+       if (pt->target == PIPE_TEXTURE_3D) {
+               unsigned nb_h = util_format_get_nblocksy(pt->format, ps->height);
+               ps->offset += get_zslice_offset(lvl->tile_mode, zslice,
+                                               lvl->pitch, nb_h);
+       }
+
        return ps;
 }