nv50_miptree.c revision 77b1d2e09cfe968a01978a9042ad4edcaad3ada2
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);
35}
36
37static uint32_t
38nv50_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
112void
113nv50_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
125boolean
126nv50_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
144const 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
156static INLINE boolean
157nv50_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
185boolean
186nv50_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
206static void
207nv50_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
253struct pipe_resource *
254nv50_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
304struct pipe_resource *
305nv50_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. */
346INLINE unsigned
347nv50_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
369struct nv50_surface *
370nv50_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
403struct pipe_surface *
404nv50_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