nv50_miptree.c revision cad17554c4b121c03e188dd0281718a52d603a15
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#include "nv50_transfer.h"
31
32static INLINE uint32_t
33nv50_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz)
34{
35   return nvc0_tex_choose_tile_dims(nx, ny * 2, nz) >> 4;
36}
37
38static uint32_t
39nv50_mt_choose_storage_type(struct nv50_miptree *mt, boolean compressed)
40{
41   const unsigned ms = util_logbase2(mt->base.base.nr_samples);
42
43   uint32_t tile_flags;
44
45   if (mt->base.base.bind & PIPE_BIND_CURSOR)
46      return NOUVEAU_BO_TILE_SCANOUT;
47
48   switch (mt->base.base.format) {
49   case PIPE_FORMAT_Z16_UNORM:
50      tile_flags = 0x6c00 + (ms << 8);
51      break;
52   case PIPE_FORMAT_S8_USCALED_Z24_UNORM:
53      tile_flags = 0x1800 + (ms << 8);
54      break;
55   case PIPE_FORMAT_Z24X8_UNORM:
56   case PIPE_FORMAT_Z24_UNORM_S8_USCALED:
57      tile_flags = 0x22800 + (ms << 8);
58      break;
59   case PIPE_FORMAT_Z32_FLOAT_S8X24_USCALED:
60      tile_flags = 0x6000 + (ms << 8);
61      break;
62   default:
63      switch (util_format_get_blocksizebits(mt->base.base.format)) {
64      case 128:
65         assert(ms < 3);
66         tile_flags = 0x7400;
67         break;
68      case 64:
69         switch (ms) {
70         case 2: tile_flags = 0x17c00; break;
71         case 3: tile_flags = 0x17d00; break;
72         default:
73            tile_flags = 0x7000;
74            break;
75         }
76         break;
77      case 32:
78         if (mt->base.base.bind & PIPE_BIND_SCANOUT) {
79            assert(ms == 0);
80            tile_flags = 0x7a00;
81         } else {
82            switch (ms) {
83            case 2: tile_flags = 0x17800; break;
84            case 3: tile_flags = 0x17900; break;
85            default:
86               tile_flags = 0x7000;
87               break;
88            }
89         }
90         break;
91      case 16:
92      case 8:
93         tile_flags = 0x7000;
94         break;
95      default:
96         return 0;
97      }
98      if (mt->base.base.bind & PIPE_BIND_CURSOR)
99         tile_flags = 0;
100   }
101
102   if (mt->base.base.bind & (PIPE_BIND_SCANOUT | PIPE_BIND_CURSOR))
103      tile_flags |= NOUVEAU_BO_TILE_SCANOUT;
104
105   if (!compressed)
106      tile_flags &= ~0x30000;
107
108   return tile_flags;
109}
110
111void
112nv50_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt)
113{
114   struct nv50_miptree *mt = nv50_miptree(pt);
115
116   nouveau_screen_bo_release(pscreen, mt->base.bo);
117
118   FREE(mt);
119}
120
121boolean
122nv50_miptree_get_handle(struct pipe_screen *pscreen,
123                        struct pipe_resource *pt,
124                        struct winsys_handle *whandle)
125{
126   struct nv50_miptree *mt = nv50_miptree(pt);
127   unsigned stride;
128
129   if (!mt || !mt->base.bo)
130      return FALSE;
131
132   stride = util_format_get_stride(mt->base.base.format,
133                                   mt->base.base.width0);
134
135   return nouveau_screen_bo_get_handle(pscreen,
136                                       mt->base.bo,
137                                       stride,
138                                       whandle);
139}
140
141const struct u_resource_vtbl nv50_miptree_vtbl =
142{
143   nv50_miptree_get_handle,         /* get_handle */
144   nv50_miptree_destroy,            /* resource_destroy */
145   nv50_miptree_transfer_new,       /* get_transfer */
146   nv50_miptree_transfer_del,       /* transfer_destroy */
147   nv50_miptree_transfer_map,       /* transfer_map */
148   u_default_transfer_flush_region, /* transfer_flush_region */
149   nv50_miptree_transfer_unmap,     /* transfer_unmap */
150   u_default_transfer_inline_write  /* transfer_inline_write */
151};
152
153static INLINE boolean
154nv50_miptree_init_ms_mode(struct nv50_miptree *mt)
155{
156   switch (mt->base.base.nr_samples) {
157   case 8:
158      mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS8;
159      mt->ms_x = 2;
160      mt->ms_y = 1;
161      break;
162   case 4:
163      mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS4;
164      mt->ms_x = 1;
165      mt->ms_y = 1;
166      break;
167   case 2:
168      mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS2;
169      mt->ms_x = 1;
170      break;
171   case 1:
172   case 0:
173      mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS1;
174      break;
175   default:
176      NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples);
177      return FALSE;
178   }
179   return TRUE;
180}
181
182boolean
183nv50_miptree_init_layout_linear(struct nv50_miptree *mt)
184{
185   struct pipe_resource *pt = &mt->base.base;
186
187   if (util_format_is_depth_or_stencil(pt->format))
188      return FALSE;
189
190   if ((pt->last_level > 0) || (pt->depth0 > 1) || (pt->array_size > 1))
191      return FALSE;
192   if (mt->ms_x | mt->ms_y)
193      return FALSE;
194
195   mt->level[0].pitch = align(pt->width0, 64);
196
197   mt->total_size = mt->level[0].pitch * pt->height0;
198
199   return TRUE;
200}
201
202static void
203nv50_miptree_init_layout_tiled(struct nv50_miptree *mt)
204{
205   struct pipe_resource *pt = &mt->base.base;
206   unsigned w, h, d, l;
207   const unsigned blocksize = util_format_get_blocksize(pt->format);
208
209   mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
210
211   w = pt->width0 << mt->ms_x;
212   h = pt->height0 << mt->ms_y;
213
214   /* For 3D textures, a mipmap is spanned by all the layers, for array
215    * textures and cube maps, each layer contains its own mipmaps.
216    */
217   d = mt->layout_3d ? pt->depth0 : 1;
218
219   for (l = 0; l <= pt->last_level; ++l) {
220      struct nv50_miptree_level *lvl = &mt->level[l];
221      unsigned tsx, tsy, tsz;
222      unsigned nbx = util_format_get_nblocksx(pt->format, w);
223      unsigned nby = util_format_get_nblocksy(pt->format, h);
224
225      lvl->offset = mt->total_size;
226
227      lvl->tile_mode = nv50_tex_choose_tile_dims(nbx, nby, d);
228
229      tsx = NV50_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */
230      tsy = NV50_TILE_SIZE_Y(lvl->tile_mode);
231      tsz = NV50_TILE_SIZE_Z(lvl->tile_mode);
232
233      lvl->pitch = align(nbx * blocksize, tsx);
234
235      mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz);
236
237      w = u_minify(w, 1);
238      h = u_minify(h, 1);
239      d = u_minify(d, 1);
240   }
241
242   if (pt->array_size > 1) {
243      mt->layer_stride = align(mt->total_size,
244                               NV50_TILE_SIZE(mt->level[0].tile_mode));
245      mt->total_size = mt->layer_stride * pt->array_size;
246   }
247}
248
249struct pipe_resource *
250nv50_miptree_create(struct pipe_screen *pscreen,
251                    const struct pipe_resource *templ)
252{
253   struct nouveau_device *dev = nouveau_screen(pscreen)->device;
254   struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
255   struct pipe_resource *pt = &mt->base.base;
256   int ret;
257   uint32_t tile_flags;
258
259   if (!mt)
260      return NULL;
261
262   mt->base.vtbl = &nv50_miptree_vtbl;
263   *pt = *templ;
264   pipe_reference_init(&pt->reference, 1);
265   pt->screen = pscreen;
266
267   tile_flags = nv50_mt_choose_storage_type(mt, TRUE);
268
269   if (!nv50_miptree_init_ms_mode(mt)) {
270      FREE(mt);
271      return NULL;
272   }
273
274   if (tile_flags & NOUVEAU_BO_TILE_LAYOUT_MASK) {
275      nv50_miptree_init_layout_tiled(mt);
276   } else
277   if (!nv50_miptree_init_layout_linear(mt)) {
278      FREE(mt);
279      return NULL;
280   }
281
282   ret = nouveau_bo_new_tile(dev, NOUVEAU_BO_VRAM, 4096,
283                             mt->total_size,
284                             mt->level[0].tile_mode, tile_flags,
285                             &mt->base.bo);
286   if (ret) {
287      FREE(mt);
288      return NULL;
289   }
290   mt->base.domain = NOUVEAU_BO_VRAM;
291
292   return pt;
293}
294
295struct pipe_resource *
296nv50_miptree_from_handle(struct pipe_screen *pscreen,
297                         const struct pipe_resource *templ,
298                         struct winsys_handle *whandle)
299{
300   struct nv50_miptree *mt;
301   unsigned stride;
302
303   /* only supports 2D, non-mipmapped textures for the moment */
304   if ((templ->target != PIPE_TEXTURE_2D &&
305        templ->target != PIPE_TEXTURE_RECT) ||
306       templ->last_level != 0 ||
307       templ->depth0 != 1 ||
308       templ->array_size > 1)
309      return NULL;
310
311   mt = CALLOC_STRUCT(nv50_miptree);
312   if (!mt)
313      return NULL;
314
315   mt->base.bo = nouveau_screen_bo_from_handle(pscreen, whandle, &stride);
316   if (mt->base.bo == NULL) {
317      FREE(mt);
318      return NULL;
319   }
320
321   mt->base.base = *templ;
322   mt->base.vtbl = &nv50_miptree_vtbl;
323   pipe_reference_init(&mt->base.base.reference, 1);
324   mt->base.base.screen = pscreen;
325   mt->level[0].pitch = stride;
326   mt->level[0].offset = 0;
327   mt->level[0].tile_mode = mt->base.bo->tile_mode;
328
329   /* no need to adjust bo reference count */
330   return &mt->base.base;
331}
332
333
334/* Offset of zslice @z from start of level @l. */
335INLINE unsigned
336nv50_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
337{
338   const struct pipe_resource *pt = &mt->base.base;
339
340   unsigned tds = NV50_TILE_SHIFT_Z(mt->level[l].tile_mode);
341   unsigned ths = NV50_TILE_SHIFT_Y(mt->level[l].tile_mode);
342
343   unsigned nby = util_format_get_nblocksy(pt->format,
344                                           u_minify(pt->height0, l));
345
346   /* to next 2D tile slice within a 3D tile */
347   unsigned stride_2d = NV50_TILE_SIZE_2D(mt->level[l].tile_mode);
348
349   /* to slice in the next (in z direction) 3D tile */
350   unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
351
352   return (z & ((1 << tds) - 1)) * stride_2d + (z >> tds) * stride_3d;
353}
354
355/* Surface functions.
356 */
357
358struct nv50_surface *
359nv50_surface_from_miptree(struct nv50_miptree *mt,
360                          const struct pipe_surface *templ)
361{
362   struct pipe_surface *ps;
363   struct nv50_surface *ns = CALLOC_STRUCT(nv50_surface);
364   if (!ns)
365      return NULL;
366   ps = &ns->base;
367
368   pipe_reference_init(&ps->reference, 1);
369   pipe_resource_reference(&ps->texture, &mt->base.base);
370
371   ps->format = templ->format;
372   ps->usage = templ->usage;
373   ps->u.tex.level = templ->u.tex.level;
374   ps->u.tex.first_layer = templ->u.tex.first_layer;
375   ps->u.tex.last_layer = templ->u.tex.last_layer;
376
377   ns->width = u_minify(mt->base.base.width0, ps->u.tex.level);
378   ns->height = u_minify(mt->base.base.height0, ps->u.tex.level);
379   ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1;
380   ns->offset = mt->level[templ->u.tex.level].offset;
381
382   /* comment says there are going to be removed, but they're used by the st */
383   ps->width = ns->width;
384   ps->height = ns->height;
385
386   ns->width <<= mt->ms_x;
387   ns->height <<= mt->ms_y;
388
389   return ns;
390}
391
392struct pipe_surface *
393nv50_miptree_surface_new(struct pipe_context *pipe,
394                         struct pipe_resource *pt,
395                         const struct pipe_surface *templ)
396{
397   struct nv50_miptree *mt = nv50_miptree(pt);
398   struct nv50_surface *ns = nv50_surface_from_miptree(mt, templ);
399   if (!ns)
400      return NULL;
401   ns->base.context = pipe;
402
403   if (ns->base.u.tex.first_layer) {
404      const unsigned l = ns->base.u.tex.level;
405      const unsigned z = ns->base.u.tex.first_layer;
406
407      if (mt->layout_3d) {
408         ns->offset += nv50_mt_zslice_offset(mt, l, z);
409
410         if (z & (NV50_TILE_SIZE_Z(mt->level[l].tile_mode) - 1))
411            NOUVEAU_ERR("Creating unsupported 3D surface !\n");
412      } else {
413         ns->offset += mt->layer_stride * z;
414      }
415   }
416
417   return &ns->base;
418}
419
420void
421nv50_miptree_surface_del(struct pipe_context *pipe, struct pipe_surface *ps)
422{
423   struct nv50_surface *s = nv50_surface(ps);
424
425   pipe_resource_reference(&ps->texture, NULL);
426
427   FREE(s);
428}
429