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