Home | History | Annotate | Download | only in nv50
      1 /*
      2  * Copyright 2008 Ben Skeggs
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice shall be included in
     12  * all copies or substantial portions of the Software.
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     18  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
     19  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     20  * SOFTWARE.
     21  */
     22 
     23 #include "pipe/p_state.h"
     24 #include "pipe/p_defines.h"
     25 #include "util/u_inlines.h"
     26 #include "util/u_format.h"
     27 
     28 #include "nv50_context.h"
     29 #include "nv50_resource.h"
     30 
     31 static INLINE uint32_t
     32 nv50_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz)
     33 {
     34    return nvc0_tex_choose_tile_dims(nx, ny * 2, nz);
     35 }
     36 
     37 static uint32_t
     38 nv50_mt_choose_storage_type(struct nv50_miptree *mt, boolean compressed)
     39 {
     40    const unsigned ms = mt->ms_x + mt->ms_y;
     41 
     42    uint32_t tile_flags;
     43 
     44    if (unlikely(mt->base.base.flags & NOUVEAU_RESOURCE_FLAG_LINEAR))
     45       return 0;
     46    if (unlikely(mt->base.base.bind & PIPE_BIND_CURSOR))
     47       return 0;
     48 
     49    switch (mt->base.base.format) {
     50    case PIPE_FORMAT_Z16_UNORM:
     51       tile_flags = 0x6c + ms;
     52       break;
     53    case PIPE_FORMAT_S8_UINT_Z24_UNORM:
     54       tile_flags = 0x18 + ms;
     55       break;
     56    case PIPE_FORMAT_Z24X8_UNORM:
     57    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
     58       tile_flags = 0x128 + ms;
     59       break;
     60    case PIPE_FORMAT_Z32_FLOAT:
     61       tile_flags = 0x40 + ms;
     62       break;
     63    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
     64       tile_flags = 0x60 + ms;
     65       break;
     66    default:
     67       switch (util_format_get_blocksizebits(mt->base.base.format)) {
     68       case 128:
     69          assert(ms < 3);
     70          tile_flags = 0x74;
     71          break;
     72       case 64:
     73          switch (ms) {
     74          case 2: tile_flags = 0xfc; break;
     75          case 3: tile_flags = 0xfd; break;
     76          default:
     77             tile_flags = 0x70;
     78             break;
     79          }
     80          break;
     81       case 32:
     82          if (mt->base.base.bind & PIPE_BIND_SCANOUT) {
     83             assert(ms == 0);
     84             tile_flags = 0x7a;
     85          } else {
     86             switch (ms) {
     87             case 2: tile_flags = 0xf8; break;
     88             case 3: tile_flags = 0xf9; break;
     89             default:
     90                tile_flags = 0x70;
     91                break;
     92             }
     93          }
     94          break;
     95       case 16:
     96       case 8:
     97          tile_flags = 0x70;
     98          break;
     99       default:
    100          return 0;
    101       }
    102       if (mt->base.base.bind & PIPE_BIND_CURSOR)
    103          tile_flags = 0;
    104    }
    105 
    106    if (!compressed)
    107       tile_flags &= ~0x180;
    108 
    109    return tile_flags;
    110 }
    111 
    112 void
    113 nv50_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt)
    114 {
    115    struct nv50_miptree *mt = nv50_miptree(pt);
    116 
    117    nouveau_bo_ref(NULL, &mt->base.bo);
    118 
    119    nouveau_fence_ref(NULL, &mt->base.fence);
    120    nouveau_fence_ref(NULL, &mt->base.fence_wr);
    121 
    122    FREE(mt);
    123 }
    124 
    125 boolean
    126 nv50_miptree_get_handle(struct pipe_screen *pscreen,
    127                         struct pipe_resource *pt,
    128                         struct winsys_handle *whandle)
    129 {
    130    struct nv50_miptree *mt = nv50_miptree(pt);
    131    unsigned stride;
    132 
    133    if (!mt || !mt->base.bo)
    134       return FALSE;
    135 
    136    stride = mt->level[0].pitch;
    137 
    138    return nouveau_screen_bo_get_handle(pscreen,
    139                                        mt->base.bo,
    140                                        stride,
    141                                        whandle);
    142 }
    143 
    144 const struct u_resource_vtbl nv50_miptree_vtbl =
    145 {
    146    nv50_miptree_get_handle,         /* get_handle */
    147    nv50_miptree_destroy,            /* resource_destroy */
    148    nv50_miptree_transfer_new,       /* get_transfer */
    149    nv50_miptree_transfer_del,       /* transfer_destroy */
    150    nv50_miptree_transfer_map,       /* transfer_map */
    151    u_default_transfer_flush_region, /* transfer_flush_region */
    152    nv50_miptree_transfer_unmap,     /* transfer_unmap */
    153    u_default_transfer_inline_write  /* transfer_inline_write */
    154 };
    155 
    156 static INLINE boolean
    157 nv50_miptree_init_ms_mode(struct nv50_miptree *mt)
    158 {
    159    switch (mt->base.base.nr_samples) {
    160    case 8:
    161       mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS8;
    162       mt->ms_x = 2;
    163       mt->ms_y = 1;
    164       break;
    165    case 4:
    166       mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS4;
    167       mt->ms_x = 1;
    168       mt->ms_y = 1;
    169       break;
    170    case 2:
    171       mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS2;
    172       mt->ms_x = 1;
    173       break;
    174    case 1:
    175    case 0:
    176       mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS1;
    177       break;
    178    default:
    179       NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples);
    180       return FALSE;
    181    }
    182    return TRUE;
    183 }
    184 
    185 boolean
    186 nv50_miptree_init_layout_linear(struct nv50_miptree *mt)
    187 {
    188    struct pipe_resource *pt = &mt->base.base;
    189    const unsigned blocksize = util_format_get_blocksize(pt->format);
    190 
    191    if (util_format_is_depth_or_stencil(pt->format))
    192       return FALSE;
    193 
    194    if ((pt->last_level > 0) || (pt->depth0 > 1) || (pt->array_size > 1))
    195       return FALSE;
    196    if (mt->ms_x | mt->ms_y)
    197       return FALSE;
    198 
    199    mt->level[0].pitch = align(pt->width0 * blocksize, 64);
    200 
    201    mt->total_size = mt->level[0].pitch * pt->height0;
    202 
    203    return TRUE;
    204 }
    205 
    206 static void
    207 nv50_miptree_init_layout_tiled(struct nv50_miptree *mt)
    208 {
    209    struct pipe_resource *pt = &mt->base.base;
    210    unsigned w, h, d, l;
    211    const unsigned blocksize = util_format_get_blocksize(pt->format);
    212 
    213    mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
    214 
    215    w = pt->width0 << mt->ms_x;
    216    h = pt->height0 << mt->ms_y;
    217 
    218    /* For 3D textures, a mipmap is spanned by all the layers, for array
    219     * textures and cube maps, each layer contains its own mipmaps.
    220     */
    221    d = mt->layout_3d ? pt->depth0 : 1;
    222 
    223    for (l = 0; l <= pt->last_level; ++l) {
    224       struct nv50_miptree_level *lvl = &mt->level[l];
    225       unsigned tsx, tsy, tsz;
    226       unsigned nbx = util_format_get_nblocksx(pt->format, w);
    227       unsigned nby = util_format_get_nblocksy(pt->format, h);
    228 
    229       lvl->offset = mt->total_size;
    230 
    231       lvl->tile_mode = nv50_tex_choose_tile_dims(nbx, nby, d);
    232 
    233       tsx = NV50_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */
    234       tsy = NV50_TILE_SIZE_Y(lvl->tile_mode);
    235       tsz = NV50_TILE_SIZE_Z(lvl->tile_mode);
    236 
    237       lvl->pitch = align(nbx * blocksize, tsx);
    238 
    239       mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz);
    240 
    241       w = u_minify(w, 1);
    242       h = u_minify(h, 1);
    243       d = u_minify(d, 1);
    244    }
    245 
    246    if (pt->array_size > 1) {
    247       mt->layer_stride = align(mt->total_size,
    248                                NV50_TILE_SIZE(mt->level[0].tile_mode));
    249       mt->total_size = mt->layer_stride * pt->array_size;
    250    }
    251 }
    252 
    253 struct pipe_resource *
    254 nv50_miptree_create(struct pipe_screen *pscreen,
    255                     const struct pipe_resource *templ)
    256 {
    257    struct nouveau_device *dev = nouveau_screen(pscreen)->device;
    258    struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
    259    struct pipe_resource *pt = &mt->base.base;
    260    int ret;
    261    union nouveau_bo_config bo_config;
    262    uint32_t bo_flags;
    263 
    264    if (!mt)
    265       return NULL;
    266 
    267    mt->base.vtbl = &nv50_miptree_vtbl;
    268    *pt = *templ;
    269    pipe_reference_init(&pt->reference, 1);
    270    pt->screen = pscreen;
    271 
    272    bo_config.nv50.memtype = nv50_mt_choose_storage_type(mt, TRUE);
    273 
    274    if (!nv50_miptree_init_ms_mode(mt)) {
    275       FREE(mt);
    276       return NULL;
    277    }
    278 
    279    if (bo_config.nv50.memtype != 0) {
    280       nv50_miptree_init_layout_tiled(mt);
    281    } else
    282    if (!nv50_miptree_init_layout_linear(mt)) {
    283       FREE(mt);
    284       return NULL;
    285    }
    286    bo_config.nv50.tile_mode = mt->level[0].tile_mode;
    287 
    288    bo_flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_NOSNOOP;
    289    if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET))
    290       bo_flags |= NOUVEAU_BO_CONTIG;
    291 
    292    ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config,
    293                         &mt->base.bo);
    294    if (ret) {
    295       FREE(mt);
    296       return NULL;
    297    }
    298    mt->base.domain = NOUVEAU_BO_VRAM;
    299    mt->base.address = mt->base.bo->offset;
    300 
    301    return pt;
    302 }
    303 
    304 struct pipe_resource *
    305 nv50_miptree_from_handle(struct pipe_screen *pscreen,
    306                          const struct pipe_resource *templ,
    307                          struct winsys_handle *whandle)
    308 {
    309    struct nv50_miptree *mt;
    310    unsigned stride;
    311 
    312    /* only supports 2D, non-mipmapped textures for the moment */
    313    if ((templ->target != PIPE_TEXTURE_2D &&
    314         templ->target != PIPE_TEXTURE_RECT) ||
    315        templ->last_level != 0 ||
    316        templ->depth0 != 1 ||
    317        templ->array_size > 1)
    318       return NULL;
    319 
    320    mt = CALLOC_STRUCT(nv50_miptree);
    321    if (!mt)
    322       return NULL;
    323 
    324    mt->base.bo = nouveau_screen_bo_from_handle(pscreen, whandle, &stride);
    325    if (mt->base.bo == NULL) {
    326       FREE(mt);
    327       return NULL;
    328    }
    329    mt->base.domain = NOUVEAU_BO_VRAM;
    330    mt->base.address = mt->base.bo->offset;
    331 
    332    mt->base.base = *templ;
    333    mt->base.vtbl = &nv50_miptree_vtbl;
    334    pipe_reference_init(&mt->base.base.reference, 1);
    335    mt->base.base.screen = pscreen;
    336    mt->level[0].pitch = stride;
    337    mt->level[0].offset = 0;
    338    mt->level[0].tile_mode = mt->base.bo->config.nv50.tile_mode;
    339 
    340    /* no need to adjust bo reference count */
    341    return &mt->base.base;
    342 }
    343 
    344 
    345 /* Offset of zslice @z from start of level @l. */
    346 INLINE unsigned
    347 nv50_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
    348 {
    349    const struct pipe_resource *pt = &mt->base.base;
    350 
    351    unsigned tds = NV50_TILE_SHIFT_Z(mt->level[l].tile_mode);
    352    unsigned ths = NV50_TILE_SHIFT_Y(mt->level[l].tile_mode);
    353 
    354    unsigned nby = util_format_get_nblocksy(pt->format,
    355                                            u_minify(pt->height0, l));
    356 
    357    /* to next 2D tile slice within a 3D tile */
    358    unsigned stride_2d = NV50_TILE_SIZE_2D(mt->level[l].tile_mode);
    359 
    360    /* to slice in the next (in z direction) 3D tile */
    361    unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
    362 
    363    return (z & ((1 << tds) - 1)) * stride_2d + (z >> tds) * stride_3d;
    364 }
    365 
    366 /* Surface functions.
    367  */
    368 
    369 struct nv50_surface *
    370 nv50_surface_from_miptree(struct nv50_miptree *mt,
    371                           const struct pipe_surface *templ)
    372 {
    373    struct pipe_surface *ps;
    374    struct nv50_surface *ns = CALLOC_STRUCT(nv50_surface);
    375    if (!ns)
    376       return NULL;
    377    ps = &ns->base;
    378 
    379    pipe_reference_init(&ps->reference, 1);
    380    pipe_resource_reference(&ps->texture, &mt->base.base);
    381 
    382    ps->format = templ->format;
    383    ps->usage = templ->usage;
    384    ps->u.tex.level = templ->u.tex.level;
    385    ps->u.tex.first_layer = templ->u.tex.first_layer;
    386    ps->u.tex.last_layer = templ->u.tex.last_layer;
    387 
    388    ns->width = u_minify(mt->base.base.width0, ps->u.tex.level);
    389    ns->height = u_minify(mt->base.base.height0, ps->u.tex.level);
    390    ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1;
    391    ns->offset = mt->level[templ->u.tex.level].offset;
    392 
    393    /* comment says there are going to be removed, but they're used by the st */
    394    ps->width = ns->width;
    395    ps->height = ns->height;
    396 
    397    ns->width <<= mt->ms_x;
    398    ns->height <<= mt->ms_y;
    399 
    400    return ns;
    401 }
    402 
    403 struct pipe_surface *
    404 nv50_miptree_surface_new(struct pipe_context *pipe,
    405                          struct pipe_resource *pt,
    406                          const struct pipe_surface *templ)
    407 {
    408    struct nv50_miptree *mt = nv50_miptree(pt);
    409    struct nv50_surface *ns = nv50_surface_from_miptree(mt, templ);
    410    if (!ns)
    411       return NULL;
    412    ns->base.context = pipe;
    413 
    414    if (ns->base.u.tex.first_layer) {
    415       const unsigned l = ns->base.u.tex.level;
    416       const unsigned z = ns->base.u.tex.first_layer;
    417 
    418       if (mt->layout_3d) {
    419          ns->offset += nv50_mt_zslice_offset(mt, l, z);
    420 
    421          /* TODO: switch to depth 1 tiles; but actually this shouldn't happen */
    422          if (ns->depth > 1 &&
    423              (z & (NV50_TILE_SIZE_Z(mt->level[l].tile_mode) - 1)))
    424             NOUVEAU_ERR("Creating unsupported 3D surface !\n");
    425       } else {
    426          ns->offset += mt->layer_stride * z;
    427       }
    428    }
    429 
    430    return &ns->base;
    431 }
    432