Merge branch 'mesa_7_6_branch' into mesa_7_7_branch
[mesa.git] / src / gallium / drivers / nv50 / nv50_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 "pipe/p_inlines.h"
26
27 #include "nv50_context.h"
28
29 /* The restrictions in tile mode selection probably aren't necessary. */
30 static INLINE uint32_t
31 get_tile_mode(unsigned ny, unsigned d)
32 {
33 uint32_t tile_mode = 0x00;
34
35 if (ny > 32) tile_mode = 0x04; /* height 64 tiles */
36 else
37 if (ny > 16) tile_mode = 0x03; /* height 32 tiles */
38 else
39 if (ny > 8) tile_mode = 0x02; /* height 16 tiles */
40 else
41 if (ny > 4) tile_mode = 0x01; /* height 8 tiles */
42
43 if (d == 1)
44 return tile_mode;
45 else
46 if (tile_mode > 0x02)
47 tile_mode = 0x02;
48
49 if (d > 16 && tile_mode < 0x02)
50 return tile_mode | 0x50; /* depth 32 tiles */
51 if (d > 8) return tile_mode | 0x40; /* depth 16 tiles */
52 if (d > 4) return tile_mode | 0x30; /* depth 8 tiles */
53 if (d > 2) return tile_mode | 0x20; /* depth 4 tiles */
54
55 return tile_mode | 0x10;
56 }
57
58 static struct pipe_texture *
59 nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
60 {
61 struct nouveau_device *dev = nouveau_screen(pscreen)->device;
62 struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
63 struct pipe_texture *pt = &mt->base.base;
64 unsigned width = tmp->width[0], height = tmp->height[0];
65 unsigned depth = tmp->depth[0], image_alignment;
66 uint32_t tile_flags;
67 int ret, i, l;
68
69 *pt = *tmp;
70 pipe_reference_init(&pt->reference, 1);
71 pt->screen = pscreen;
72
73 switch (pt->format) {
74 case PIPE_FORMAT_Z32_FLOAT:
75 tile_flags = 0x4800;
76 break;
77 case PIPE_FORMAT_Z24S8_UNORM:
78 tile_flags = 0x1800;
79 break;
80 case PIPE_FORMAT_X8Z24_UNORM:
81 case PIPE_FORMAT_S8Z24_UNORM:
82 tile_flags = 0x2800;
83 break;
84 default:
85 tile_flags = 0x7000;
86 break;
87 }
88
89 /* XXX: texture arrays */
90 mt->image_nr = (pt->target == PIPE_TEXTURE_CUBE) ? 6 : 1;
91
92 for (l = 0; l <= pt->last_level; l++) {
93 struct nv50_miptree_level *lvl = &mt->level[l];
94
95 pt->width[l] = width;
96 pt->height[l] = height;
97 pt->depth[l] = depth;
98 pt->nblocksx[l] = pf_get_nblocksx(&pt->block, width);
99 pt->nblocksy[l] = pf_get_nblocksy(&pt->block, height);
100
101 lvl->image_offset = CALLOC(mt->image_nr, sizeof(int));
102 lvl->pitch = align(pt->nblocksx[l] * pt->block.size, 64);
103 lvl->tile_mode = get_tile_mode(pt->nblocksy[l], depth);
104
105 width = MAX2(1, width >> 1);
106 height = MAX2(1, height >> 1);
107 depth = MAX2(1, depth >> 1);
108 }
109
110 image_alignment = get_tile_height(mt->level[0].tile_mode) * 64;
111 image_alignment *= get_tile_depth(mt->level[0].tile_mode);
112
113 /* NOTE the distinction between arrays of mip-mapped 2D textures and
114 * mip-mapped 3D textures. We can't use image_nr == depth for 3D mip.
115 */
116 for (i = 0; i < mt->image_nr; i++) {
117 for (l = 0; l <= pt->last_level; l++) {
118 struct nv50_miptree_level *lvl = &mt->level[l];
119 int size;
120 unsigned tile_h = get_tile_height(lvl->tile_mode);
121 unsigned tile_d = get_tile_depth(lvl->tile_mode);
122
123 size = lvl->pitch;
124 size *= align(pt->nblocksy[l], tile_h);
125 size *= align(pt->depth[l], tile_d);
126
127 lvl->image_offset[i] = mt->total_size;
128
129 mt->total_size += size;
130 }
131 mt->total_size = align(mt->total_size, image_alignment);
132 }
133
134 ret = nouveau_bo_new_tile(dev, NOUVEAU_BO_VRAM, 256, mt->total_size,
135 mt->level[0].tile_mode, tile_flags,
136 &mt->base.bo);
137 if (ret) {
138 FREE(mt);
139 return NULL;
140 }
141
142 return pt;
143 }
144
145 static struct pipe_texture *
146 nv50_miptree_blanket(struct pipe_screen *pscreen, const struct pipe_texture *pt,
147 const unsigned *stride, struct pipe_buffer *pb)
148 {
149 struct nouveau_bo *bo = nouveau_bo(pb);
150 struct nv50_miptree *mt;
151
152 /* Only supports 2D, non-mipmapped textures for the moment */
153 if (pt->target != PIPE_TEXTURE_2D || pt->last_level != 0 ||
154 pt->depth[0] != 1)
155 return NULL;
156
157 mt = CALLOC_STRUCT(nv50_miptree);
158 if (!mt)
159 return NULL;
160
161 mt->base.base = *pt;
162 pipe_reference_init(&mt->base.base.reference, 1);
163 mt->base.base.screen = pscreen;
164 mt->image_nr = 1;
165 mt->level[0].pitch = *stride;
166 mt->level[0].image_offset = CALLOC(1, sizeof(unsigned));
167 mt->level[0].tile_mode = bo->tile_mode;
168
169 nouveau_bo_ref(bo, &mt->base.bo);
170 return &mt->base.base;
171 }
172
173 static void
174 nv50_miptree_destroy(struct pipe_texture *pt)
175 {
176 struct nv50_miptree *mt = nv50_miptree(pt);
177
178 nouveau_bo_ref(NULL, &mt->base.bo);
179 FREE(mt);
180 }
181
182 static struct pipe_surface *
183 nv50_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
184 unsigned face, unsigned level, unsigned zslice,
185 unsigned flags)
186 {
187 struct nv50_miptree *mt = nv50_miptree(pt);
188 struct nv50_miptree_level *lvl = &mt->level[level];
189 struct pipe_surface *ps;
190 int img;
191
192 if (pt->target == PIPE_TEXTURE_CUBE)
193 img = face;
194 else
195 if (pt->target == PIPE_TEXTURE_3D)
196 img = zslice;
197 else
198 img = 0;
199
200 ps = CALLOC_STRUCT(pipe_surface);
201 if (!ps)
202 return NULL;
203 pipe_texture_reference(&ps->texture, pt);
204 ps->format = pt->format;
205 ps->width = pt->width[level];
206 ps->height = pt->height[level];
207 ps->usage = flags;
208 pipe_reference_init(&ps->reference, 1);
209 ps->face = face;
210 ps->level = level;
211 ps->zslice = zslice;
212 ps->offset = lvl->image_offset[img];
213
214 return ps;
215 }
216
217 static void
218 nv50_miptree_surface_del(struct pipe_surface *ps)
219 {
220 struct nv50_surface *s = nv50_surface(ps);
221
222 pipe_texture_reference(&ps->texture, NULL);
223 FREE(s);
224 }
225
226 void
227 nv50_screen_init_miptree_functions(struct pipe_screen *pscreen)
228 {
229 pscreen->texture_create = nv50_miptree_create;
230 pscreen->texture_blanket = nv50_miptree_blanket;
231 pscreen->texture_destroy = nv50_miptree_destroy;
232 pscreen->get_tex_surface = nv50_miptree_surface_new;
233 pscreen->tex_surface_destroy = nv50_miptree_surface_del;
234 }
235