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 "nvc0_context.h"
29 #include "nvc0_resource.h"
30 
31 uint32_t
nvc0_tex_choose_tile_dims(unsigned nx,unsigned ny,unsigned nz)32 nvc0_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz)
33 {
34    uint32_t tile_mode = 0x000;
35 
36    if (ny > 64) tile_mode = 0x040; /* height 128 tiles */
37    else
38    if (ny > 32) tile_mode = 0x030; /* height 64 tiles */
39    else
40    if (ny > 16) tile_mode = 0x020; /* height 32 tiles */
41    else
42    if (ny >  8) tile_mode = 0x010; /* height 16 tiles */
43 
44    if (nz == 1)
45       return tile_mode;
46    else
47    if (tile_mode > 0x020)
48       tile_mode = 0x020;
49 
50    if (nz > 16 && tile_mode < 0x020)
51       return tile_mode | 0x500; /* depth 32 tiles */
52    if (nz > 8) return tile_mode | 0x400; /* depth 16 tiles */
53    if (nz > 4) return tile_mode | 0x300; /* depth 8 tiles */
54    if (nz > 2) return tile_mode | 0x200; /* depth 4 tiles */
55 
56    return tile_mode | 0x100;
57 }
58 
59 static uint32_t
nvc0_mt_choose_storage_type(struct nv50_miptree * mt,boolean compressed)60 nvc0_mt_choose_storage_type(struct nv50_miptree *mt, boolean compressed)
61 {
62    const unsigned ms = util_logbase2(mt->base.base.nr_samples);
63 
64    uint32_t tile_flags;
65 
66    compressed = FALSE; /* not yet supported */
67 
68    if (unlikely(mt->base.base.bind & PIPE_BIND_CURSOR))
69       return 0;
70    if (unlikely(mt->base.base.flags & NOUVEAU_RESOURCE_FLAG_LINEAR))
71       return 0;
72 
73    switch (mt->base.base.format) {
74    case PIPE_FORMAT_Z16_UNORM:
75       if (compressed)
76          tile_flags = 0x02 + ms;
77       else
78          tile_flags = 0x01;
79       break;
80    case PIPE_FORMAT_S8_UINT_Z24_UNORM:
81       if (compressed)
82          tile_flags = 0x51 + ms;
83       else
84          tile_flags = 0x46;
85       break;
86    case PIPE_FORMAT_Z24X8_UNORM:
87    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
88       if (compressed)
89          tile_flags = 0x17 + ms;
90       else
91          tile_flags = 0x11;
92       break;
93    case PIPE_FORMAT_Z32_FLOAT:
94       if (compressed)
95          tile_flags = 0x86 + ms;
96       else
97          tile_flags = 0x7b;
98       break;
99    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
100       if (compressed)
101          tile_flags = 0xce + ms;
102       else
103          tile_flags = 0xc3;
104       break;
105    default:
106       switch (util_format_get_blocksizebits(mt->base.base.format)) {
107       case 128:
108          if (compressed)
109             tile_flags = 0xf4 + ms;
110          else
111             tile_flags = 0xfe;
112          break;
113       case 64:
114          if (compressed) {
115             switch (ms) {
116             case 0: tile_flags = 0xe6; break;
117             case 1: tile_flags = 0xeb; break;
118             case 2: tile_flags = 0xed; break;
119             case 3: tile_flags = 0xf2; break;
120             default:
121                return 0;
122             }
123          } else {
124             tile_flags = 0xfe;
125          }
126          break;
127       case 32:
128          if (compressed) {
129             switch (ms) {
130             case 0: tile_flags = 0xdb; break;
131             case 1: tile_flags = 0xdd; break;
132             case 2: tile_flags = 0xdf; break;
133             case 3: tile_flags = 0xe4; break;
134             default:
135                return 0;
136             }
137          } else {
138             tile_flags = 0xfe;
139          }
140          break;
141       case 16:
142       case 8:
143          tile_flags = 0xfe;
144          break;
145       default:
146          return 0;
147       }
148       break;
149    }
150 
151    return tile_flags;
152 }
153 
154 static INLINE boolean
nvc0_miptree_init_ms_mode(struct nv50_miptree * mt)155 nvc0_miptree_init_ms_mode(struct nv50_miptree *mt)
156 {
157    switch (mt->base.base.nr_samples) {
158    case 8:
159       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS8;
160       mt->ms_x = 2;
161       mt->ms_y = 1;
162       break;
163    case 4:
164       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS4;
165       mt->ms_x = 1;
166       mt->ms_y = 1;
167       break;
168    case 2:
169       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS2;
170       mt->ms_x = 1;
171       break;
172    case 1:
173    case 0:
174       mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS1;
175       break;
176    default:
177       NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples);
178       return FALSE;
179    }
180    return TRUE;
181 }
182 
183 boolean
184 nv50_miptree_init_layout_linear(struct nv50_miptree *);
185 
186 static void
nvc0_miptree_init_layout_video(struct nv50_miptree * mt)187 nvc0_miptree_init_layout_video(struct nv50_miptree *mt)
188 {
189    const struct pipe_resource *pt = &mt->base.base;
190    const unsigned blocksize = util_format_get_blocksize(pt->format);
191 
192    unsigned nbx = util_format_get_nblocksx(pt->format, pt->width0);
193    unsigned nby = util_format_get_nblocksy(pt->format, pt->height0);
194 
195    assert(pt->last_level == 0);
196    assert(mt->ms_x == 0 &&
197           mt->ms_y == 0);
198    assert(!util_format_is_compressed(pt->format));
199 
200    assert(nby > 8);
201    mt->level[0].tile_mode = 0x10;
202    mt->level[0].pitch = align(nbx * blocksize, 64);
203    mt->total_size = align(nby, 16) * mt->level[0].pitch;
204 
205    if (pt->array_size > 1) {
206       mt->layer_stride = align(mt->total_size, NVC0_TILE_SIZE(0x10));
207       mt->total_size = mt->layer_stride * pt->array_size;
208    }
209 }
210 
211 static void
nvc0_miptree_init_layout_tiled(struct nv50_miptree * mt)212 nvc0_miptree_init_layout_tiled(struct nv50_miptree *mt)
213 {
214    struct pipe_resource *pt = &mt->base.base;
215    unsigned w, h, d, l;
216    const unsigned blocksize = util_format_get_blocksize(pt->format);
217 
218    mt->layout_3d = pt->target == PIPE_TEXTURE_3D;
219 
220    w = pt->width0 << mt->ms_x;
221    h = pt->height0 << mt->ms_y;
222 
223    /* For 3D textures, a mipmap is spanned by all the layers, for array
224     * textures and cube maps, each layer contains its own mipmaps.
225     */
226    d = mt->layout_3d ? pt->depth0 : 1;
227 
228    for (l = 0; l <= pt->last_level; ++l) {
229       struct nv50_miptree_level *lvl = &mt->level[l];
230       unsigned tsx, tsy, tsz;
231       unsigned nbx = util_format_get_nblocksx(pt->format, w);
232       unsigned nby = util_format_get_nblocksy(pt->format, h);
233 
234       lvl->offset = mt->total_size;
235 
236       lvl->tile_mode = nvc0_tex_choose_tile_dims(nbx, nby, d);
237 
238       tsx = NVC0_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */
239       tsy = NVC0_TILE_SIZE_Y(lvl->tile_mode);
240       tsz = NVC0_TILE_SIZE_Z(lvl->tile_mode);
241 
242       lvl->pitch = align(nbx * blocksize, tsx);
243 
244       mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz);
245 
246       w = u_minify(w, 1);
247       h = u_minify(h, 1);
248       d = u_minify(d, 1);
249    }
250 
251    if (pt->array_size > 1) {
252       mt->layer_stride = align(mt->total_size,
253                                NVC0_TILE_SIZE(mt->level[0].tile_mode));
254       mt->total_size = mt->layer_stride * pt->array_size;
255    }
256 }
257 
258 const struct u_resource_vtbl nvc0_miptree_vtbl =
259 {
260    nv50_miptree_get_handle,         /* get_handle */
261    nv50_miptree_destroy,            /* resource_destroy */
262    nvc0_miptree_transfer_new,       /* get_transfer */
263    nvc0_miptree_transfer_del,       /* transfer_destroy */
264    nvc0_miptree_transfer_map,       /* transfer_map */
265    u_default_transfer_flush_region, /* transfer_flush_region */
266    nvc0_miptree_transfer_unmap,     /* transfer_unmap */
267    u_default_transfer_inline_write  /* transfer_inline_write */
268 };
269 
270 struct pipe_resource *
nvc0_miptree_create(struct pipe_screen * pscreen,const struct pipe_resource * templ)271 nvc0_miptree_create(struct pipe_screen *pscreen,
272                     const struct pipe_resource *templ)
273 {
274    struct nouveau_device *dev = nouveau_screen(pscreen)->device;
275    struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
276    struct pipe_resource *pt = &mt->base.base;
277    int ret;
278    union nouveau_bo_config bo_config;
279    uint32_t bo_flags;
280 
281    if (!mt)
282       return NULL;
283 
284    mt->base.vtbl = &nvc0_miptree_vtbl;
285    *pt = *templ;
286    pipe_reference_init(&pt->reference, 1);
287    pt->screen = pscreen;
288 
289    bo_config.nvc0.memtype = nvc0_mt_choose_storage_type(mt, TRUE);
290 
291    if (!nvc0_miptree_init_ms_mode(mt)) {
292       FREE(mt);
293       return NULL;
294    }
295 
296    if (unlikely(pt->flags & NVC0_RESOURCE_FLAG_VIDEO)) {
297       nvc0_miptree_init_layout_video(mt);
298    } else
299    if (likely(bo_config.nvc0.memtype)) {
300       nvc0_miptree_init_layout_tiled(mt);
301    } else
302    if (!nv50_miptree_init_layout_linear(mt)) {
303       FREE(mt);
304       return NULL;
305    }
306    bo_config.nvc0.tile_mode = mt->level[0].tile_mode;
307 
308    mt->base.domain = NOUVEAU_BO_VRAM;
309 
310    bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP;
311 
312    if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET))
313       bo_flags |= NOUVEAU_BO_CONTIG;
314 
315    ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config,
316                         &mt->base.bo);
317    if (ret) {
318       FREE(mt);
319       return NULL;
320    }
321    mt->base.address = mt->base.bo->offset;
322 
323    return pt;
324 }
325 
326 /* Offset of zslice @z from start of level @l. */
327 INLINE unsigned
nvc0_mt_zslice_offset(const struct nv50_miptree * mt,unsigned l,unsigned z)328 nvc0_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
329 {
330    const struct pipe_resource *pt = &mt->base.base;
331 
332    unsigned tds = NVC0_TILE_SHIFT_Z(mt->level[l].tile_mode);
333    unsigned ths = NVC0_TILE_SHIFT_Y(mt->level[l].tile_mode);
334 
335    unsigned nby = util_format_get_nblocksy(pt->format,
336                                            u_minify(pt->height0, l));
337 
338    /* to next 2D tile slice within a 3D tile */
339    unsigned stride_2d = NVC0_TILE_SIZE_2D(mt->level[l].tile_mode);
340 
341    /* to slice in the next (in z direction) 3D tile */
342    unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
343 
344    return (z & (1 << (tds - 1))) * stride_2d + (z >> tds) * stride_3d;
345 }
346 
347 /* Surface functions.
348  */
349 
350 struct pipe_surface *
nvc0_miptree_surface_new(struct pipe_context * pipe,struct pipe_resource * pt,const struct pipe_surface * templ)351 nvc0_miptree_surface_new(struct pipe_context *pipe,
352                          struct pipe_resource *pt,
353                          const struct pipe_surface *templ)
354 {
355    struct nv50_surface *ns = nv50_surface_from_miptree(nv50_miptree(pt), templ);
356    if (!ns)
357       return NULL;
358    ns->base.context = pipe;
359    return &ns->base;
360 }
361