nvc0: use tile flags in a way compatible with nouveau
[mesa.git] / src / gallium / drivers / nvc0 / nvc0_miptree.c
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 #include "nvc0_transfer.h"
31
32 static INLINE uint32_t
33 get_tile_dims(unsigned nx, unsigned ny, unsigned nz)
34 {
35 uint32_t tile_mode = 0x000;
36
37 if (ny > 64) tile_mode = 0x040; /* height 128 tiles */
38 else
39 if (ny > 32) tile_mode = 0x030; /* height 64 tiles */
40 else
41 if (ny > 16) tile_mode = 0x020; /* height 32 tiles */
42 else
43 if (ny > 8) tile_mode = 0x010; /* height 16 tiles */
44
45 if (nz == 1)
46 return tile_mode;
47 else
48 if (tile_mode > 0x020)
49 tile_mode = 0x020;
50
51 if (nz > 16 && tile_mode < 0x020)
52 return tile_mode | 0x500; /* depth 32 tiles */
53 if (nz > 8) return tile_mode | 0x400; /* depth 16 tiles */
54 if (nz > 4) return tile_mode | 0x300; /* depth 8 tiles */
55 if (nz > 2) return tile_mode | 0x200; /* depth 4 tiles */
56
57 return tile_mode | 0x100;
58 }
59
60 static INLINE unsigned
61 get_zslice_offset(uint32_t tile_mode, unsigned z, unsigned pitch, unsigned nbh)
62 {
63 unsigned tile_h = NVC0_TILE_H(tile_mode);
64 unsigned tile_d = NVC0_TILE_D(tile_mode);
65
66 /* pitch_2d == to next slice within this volume tile */
67 /* pitch_3d == size (in bytes) of a volume tile */
68 unsigned pitch_2d = tile_h * 64;
69 unsigned pitch_3d = tile_d * align(nbh, tile_h) * pitch;
70
71 return (z % tile_d) * pitch_2d + (z / tile_d) * pitch_3d;
72 }
73
74 static void
75 nvc0_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt)
76 {
77 struct nvc0_miptree *mt = nvc0_miptree(pt);
78 unsigned l;
79
80 for (l = 0; l <= pt->last_level; ++l)
81 FREE(mt->level[l].image_offset);
82
83 nouveau_screen_bo_release(pscreen, mt->base.bo);
84
85 FREE(mt);
86 }
87
88 static boolean
89 nvc0_miptree_get_handle(struct pipe_screen *pscreen,
90 struct pipe_resource *pt,
91 struct winsys_handle *whandle)
92 {
93 struct nvc0_miptree *mt = nvc0_miptree(pt);
94 unsigned stride;
95
96 if (!mt || !mt->base.bo)
97 return FALSE;
98
99 stride = util_format_get_stride(mt->base.base.format,
100 mt->base.base.width0);
101
102 return nouveau_screen_bo_get_handle(pscreen,
103 mt->base.bo,
104 stride,
105 whandle);
106 }
107
108 const struct u_resource_vtbl nvc0_miptree_vtbl =
109 {
110 nvc0_miptree_get_handle, /* get_handle */
111 nvc0_miptree_destroy, /* resource_destroy */
112 NULL, /* is_resource_referenced */
113 nvc0_miptree_transfer_new, /* get_transfer */
114 nvc0_miptree_transfer_del, /* transfer_destroy */
115 nvc0_miptree_transfer_map, /* transfer_map */
116 u_default_transfer_flush_region, /* transfer_flush_region */
117 nvc0_miptree_transfer_unmap, /* transfer_unmap */
118 u_default_transfer_inline_write /* transfer_inline_write */
119 };
120
121 struct pipe_resource *
122 nvc0_miptree_create(struct pipe_screen *pscreen,
123 const struct pipe_resource *templ)
124 {
125 struct nouveau_device *dev = nouveau_screen(pscreen)->device;
126 struct nvc0_miptree *mt = CALLOC_STRUCT(nvc0_miptree);
127 struct pipe_resource *pt = &mt->base.base;
128 int ret, i;
129 unsigned w, h, d, l, image_alignment, alloc_size;
130 uint32_t tile_flags;
131
132 if (!mt)
133 return NULL;
134
135 mt->base.vtbl = &nvc0_miptree_vtbl;
136 *pt = *templ;
137 pipe_reference_init(&pt->reference, 1);
138 pt->screen = pscreen;
139
140 w = pt->width0;
141 h = pt->height0;
142 d = pt->depth0;
143
144 switch (pt->format) {
145 case PIPE_FORMAT_Z16_UNORM:
146 tile_flags = 0x0700; /* COMPRESSED */
147 tile_flags = 0x0200; /* NORMAL ? */
148 tile_flags = 0x0100; /* NORMAL ? */
149 break;
150 case PIPE_FORMAT_S8_USCALED_Z24_UNORM:
151 tile_flags = 0x5300; /* MSAA 4, COMPRESSED */
152 tile_flags = 0x4600; /* NORMAL */
153 break;
154 case PIPE_FORMAT_Z24X8_UNORM:
155 case PIPE_FORMAT_Z24_UNORM_S8_USCALED:
156 tile_flags = 0x1100; /* NORMAL */
157 if (w * h >= 128 * 128 && 0)
158 tile_flags = 0x1700; /* COMPRESSED, requires magic */
159 break;
160 case PIPE_FORMAT_R32G32B32A32_FLOAT:
161 tile_flags = 0xf500; /* COMPRESSED */
162 tile_flags = 0xf700; /* MSAA 2 */
163 tile_flags = 0xf900; /* MSAA 4 */
164 tile_flags = 0xfe00; /* NORMAL */
165 break;
166 case PIPE_FORMAT_Z32_FLOAT_S8X24_USCALED:
167 tile_flags = 0xce00; /* COMPRESSED */
168 tile_flags = 0xcf00; /* MSAA 2, COMPRESSED */
169 tile_flags = 0xd000; /* MSAA 4, COMPRESSED */
170 tile_flags = 0xc300; /* NORMAL */
171 break;
172 case PIPE_FORMAT_R16G16B16A16_UNORM:
173 tile_flags = 0xe900; /* COMPRESSED */
174 break;
175 default:
176 tile_flags = 0xe000; /* MSAA 4, COMPRESSED 32 BIT */
177 tile_flags = 0xfe00; /* NORMAL 32 BIT */
178 if (w * h >= 128 * 128 && 0)
179 tile_flags = 0xdb00; /* COMPRESSED 32 BIT, requires magic */
180 break;
181 }
182
183 /* XXX: texture arrays */
184 mt->image_nr = (pt->target == PIPE_TEXTURE_CUBE) ? 6 : 1;
185
186 for (l = 0; l <= pt->last_level; l++) {
187 struct nvc0_miptree_level *lvl = &mt->level[l];
188 unsigned nby = util_format_get_nblocksy(pt->format, h);
189
190 lvl->image_offset = CALLOC(mt->image_nr, sizeof(int));
191 lvl->pitch = align(util_format_get_stride(pt->format, w), 64);
192 lvl->tile_mode = get_tile_dims(w, nby, d);
193
194 w = u_minify(w, 1);
195 h = u_minify(h, 1);
196 d = u_minify(d, 1);
197 }
198
199 image_alignment = NVC0_TILE_H(mt->level[0].tile_mode) * 64;
200 image_alignment *= NVC0_TILE_D(mt->level[0].tile_mode);
201
202 /* NOTE the distinction between arrays of mip-mapped 2D textures and
203 * mip-mapped 3D textures. We can't use image_nr == depth for 3D mip.
204 */
205 for (i = 0; i < mt->image_nr; i++) {
206 for (l = 0; l <= pt->last_level; l++) {
207 struct nvc0_miptree_level *lvl = &mt->level[l];
208 int size;
209 unsigned tile_h = NVC0_TILE_H(lvl->tile_mode);
210 unsigned tile_d = NVC0_TILE_D(lvl->tile_mode);
211
212 h = u_minify(pt->height0, l);
213 d = u_minify(pt->depth0, l);
214
215 size = lvl->pitch;
216 size *= align(util_format_get_nblocksy(pt->format, h), tile_h);
217 size *= align(d, tile_d);
218
219 lvl->image_offset[i] = mt->total_size;
220
221 mt->total_size += size;
222 }
223 mt->total_size = align(mt->total_size, image_alignment);
224 }
225
226 alloc_size = mt->total_size;
227 if (tile_flags == 0x1700)
228 alloc_size *= 3; /* HiZ, XXX: correct size */
229
230 ret = nouveau_bo_new_tile(dev, NOUVEAU_BO_VRAM, 256, alloc_size,
231 mt->level[0].tile_mode, tile_flags,
232 &mt->base.bo);
233 if (ret) {
234 for (l = 0; l <= pt->last_level; ++l)
235 FREE(mt->level[l].image_offset);
236 FREE(mt);
237 return NULL;
238 }
239
240 return pt;
241 }
242
243 struct pipe_resource *
244 nvc0_miptree_from_handle(struct pipe_screen *pscreen,
245 const struct pipe_resource *templ,
246 struct winsys_handle *whandle)
247 {
248 struct nvc0_miptree *mt;
249 unsigned stride;
250
251 /* only supports 2D, non-mip mapped textures for the moment */
252 if ((templ->target != PIPE_TEXTURE_2D &&
253 templ->target != PIPE_TEXTURE_RECT) ||
254 templ->last_level != 0 ||
255 templ->depth0 != 1)
256 return NULL;
257
258 mt = CALLOC_STRUCT(nvc0_miptree);
259 if (!mt)
260 return NULL;
261
262 mt->base.bo = nouveau_screen_bo_from_handle(pscreen, whandle, &stride);
263 if (mt->base.bo == NULL) {
264 FREE(mt);
265 return NULL;
266 }
267
268 mt->base.base = *templ;
269 mt->base.vtbl = &nvc0_miptree_vtbl;
270 pipe_reference_init(&mt->base.base.reference, 1);
271 mt->base.base.screen = pscreen;
272 mt->image_nr = 1;
273 mt->level[0].pitch = stride;
274 mt->level[0].image_offset = CALLOC(1, sizeof(unsigned));
275 mt->level[0].tile_mode = mt->base.bo->tile_mode;
276
277 /* no need to adjust bo reference count */
278 return &mt->base.base;
279 }
280
281
282 /* Surface functions.
283 */
284
285 struct pipe_surface *
286 nvc0_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_resource *pt,
287 unsigned face, unsigned level, unsigned zslice,
288 unsigned flags)
289 {
290 struct nvc0_miptree *mt = nvc0_miptree(pt);
291 struct nvc0_miptree_level *lvl = &mt->level[level];
292 struct pipe_surface *ps;
293 unsigned img = 0;
294
295 if (pt->target == PIPE_TEXTURE_CUBE)
296 img = face;
297
298 ps = CALLOC_STRUCT(pipe_surface);
299 if (!ps)
300 return NULL;
301 pipe_resource_reference(&ps->texture, pt);
302 ps->format = pt->format;
303 ps->width = u_minify(pt->width0, level);
304 ps->height = u_minify(pt->height0, level);
305 ps->usage = flags;
306 pipe_reference_init(&ps->reference, 1);
307 ps->face = face;
308 ps->level = level;
309 ps->zslice = zslice;
310 ps->offset = lvl->image_offset[img];
311
312 if (pt->target == PIPE_TEXTURE_3D)
313 ps->offset += get_zslice_offset(lvl->tile_mode, zslice, lvl->pitch,
314 util_format_get_nblocksy(pt->format,
315 ps->height));
316 return ps;
317 }
318
319 void
320 nvc0_miptree_surface_del(struct pipe_surface *ps)
321 {
322 struct nvc0_surface *s = nvc0_surface(ps);
323
324 pipe_resource_reference(&ps->texture, NULL);
325
326 FREE(s);
327 }