nouveau: Work around nv04-nv40 miptrees not matching nouveau_miptree.
[mesa.git] / src / gallium / drivers / nv40 / nv40_miptree.c
1 #include "pipe/p_state.h"
2 #include "pipe/p_defines.h"
3 #include "pipe/p_inlines.h"
4 #include "util/u_math.h"
5
6 #include "nv40_context.h"
7
8
9
10 static void
11 nv40_miptree_layout(struct nv40_miptree *mt)
12 {
13 struct pipe_texture *pt = &mt->base;
14 uint width = pt->width0;
15 uint offset = 0;
16 int nr_faces, l, f;
17 uint wide_pitch = pt->tex_usage & (PIPE_TEXTURE_USAGE_SAMPLER |
18 PIPE_TEXTURE_USAGE_DEPTH_STENCIL |
19 PIPE_TEXTURE_USAGE_RENDER_TARGET |
20 PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
21 PIPE_TEXTURE_USAGE_PRIMARY);
22
23 if (pt->target == PIPE_TEXTURE_CUBE) {
24 nr_faces = 6;
25 } else
26 if (pt->target == PIPE_TEXTURE_3D) {
27 nr_faces = pt->depth0;
28 } else {
29 nr_faces = 1;
30 }
31
32 for (l = 0; l <= pt->last_level; l++) {
33 if (wide_pitch && (pt->tex_usage & NOUVEAU_TEXTURE_USAGE_LINEAR))
34 mt->level[l].pitch = align(pf_get_stride(pt->format, pt->width0), 64);
35 else
36 mt->level[l].pitch = pf_get_stride(pt->format, width);
37
38 mt->level[l].image_offset =
39 CALLOC(nr_faces, sizeof(unsigned));
40
41 width = u_minify(width, 1);
42 }
43
44 for (f = 0; f < nr_faces; f++) {
45 for (l = 0; l < pt->last_level; l++) {
46 mt->level[l].image_offset[f] = offset;
47
48 if (!(pt->tex_usage & NOUVEAU_TEXTURE_USAGE_LINEAR) &&
49 u_minify(pt->width0, l + 1) > 1 && u_minify(pt->height0, l + 1) > 1)
50 offset += align(mt->level[l].pitch * u_minify(pt->height0, l), 64);
51 else
52 offset += mt->level[l].pitch * u_minify(pt->height0, l);
53 }
54
55 mt->level[l].image_offset[f] = offset;
56 offset += mt->level[l].pitch * u_minify(pt->height0, l);
57 }
58
59 mt->total_size = offset;
60 }
61
62 static struct pipe_texture *
63 nv40_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *pt)
64 {
65 struct nv40_miptree *mt;
66 unsigned buf_usage = PIPE_BUFFER_USAGE_PIXEL |
67 NOUVEAU_BUFFER_USAGE_TEXTURE;
68
69 mt = MALLOC(sizeof(struct nv40_miptree));
70 if (!mt)
71 return NULL;
72 mt->base = *pt;
73 pipe_reference_init(&mt->base.reference, 1);
74 mt->base.screen = pscreen;
75
76 /* Swizzled textures must be POT */
77 if (pt->width0 & (pt->width0 - 1) ||
78 pt->height0 & (pt->height0 - 1))
79 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
80 else
81 if (pt->tex_usage & (PIPE_TEXTURE_USAGE_PRIMARY |
82 PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
83 PIPE_TEXTURE_USAGE_DEPTH_STENCIL))
84 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
85 else
86 if (pt->tex_usage & PIPE_TEXTURE_USAGE_DYNAMIC)
87 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
88 else {
89 switch (pt->format) {
90 /* TODO: Figure out which formats can be swizzled */
91 case PIPE_FORMAT_A8R8G8B8_UNORM:
92 case PIPE_FORMAT_X8R8G8B8_UNORM:
93 case PIPE_FORMAT_R16_SNORM:
94 {
95 if (debug_get_bool_option("NOUVEAU_NO_SWIZZLE", FALSE))
96 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
97 break;
98 }
99 default:
100 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
101 }
102 }
103
104 if (pt->tex_usage & PIPE_TEXTURE_USAGE_DYNAMIC)
105 buf_usage |= PIPE_BUFFER_USAGE_CPU_READ_WRITE;
106
107 nv40_miptree_layout(mt);
108
109 mt->buffer = pscreen->buffer_create(pscreen, 256, buf_usage, mt->total_size);
110 if (!mt->buffer) {
111 FREE(mt);
112 return NULL;
113 }
114 mt->bo = nouveau_bo(mt->buffer);
115 return &mt->base;
116 }
117
118 static struct pipe_texture *
119 nv40_miptree_blanket(struct pipe_screen *pscreen, const struct pipe_texture *pt,
120 const unsigned *stride, struct pipe_buffer *pb)
121 {
122 struct nv40_miptree *mt;
123
124 /* Only supports 2D, non-mipmapped textures for the moment */
125 if (pt->target != PIPE_TEXTURE_2D || pt->last_level != 0 ||
126 pt->depth0 != 1)
127 return NULL;
128
129 mt = CALLOC_STRUCT(nv40_miptree);
130 if (!mt)
131 return NULL;
132
133 mt->base = *pt;
134 pipe_reference_init(&mt->base.reference, 1);
135 mt->base.screen = pscreen;
136 mt->level[0].pitch = stride[0];
137 mt->level[0].image_offset = CALLOC(1, sizeof(unsigned));
138
139 /* Assume whoever created this buffer expects it to be linear for now */
140 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
141
142 pipe_buffer_reference(&mt->buffer, pb);
143 mt->bo = nouveau_bo(mt->buffer);
144 return &mt->base;
145 }
146
147 static void
148 nv40_miptree_destroy(struct pipe_texture *pt)
149 {
150 struct nv40_miptree *mt = (struct nv40_miptree *)pt;
151 int l;
152
153 pipe_buffer_reference(&mt->buffer, NULL);
154 for (l = 0; l <= pt->last_level; l++) {
155 if (mt->level[l].image_offset)
156 FREE(mt->level[l].image_offset);
157 }
158
159 FREE(mt);
160 }
161
162 static struct pipe_surface *
163 nv40_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
164 unsigned face, unsigned level, unsigned zslice,
165 unsigned flags)
166 {
167 struct nv40_miptree *mt = (struct nv40_miptree *)pt;
168 struct nv04_surface *ns;
169
170 ns = CALLOC_STRUCT(nv04_surface);
171 if (!ns)
172 return NULL;
173 pipe_texture_reference(&ns->base.texture, pt);
174 ns->base.format = pt->format;
175 ns->base.width = u_minify(pt->width0, level);
176 ns->base.height = u_minify(pt->height0, level);
177 ns->base.usage = flags;
178 pipe_reference_init(&ns->base.reference, 1);
179 ns->base.face = face;
180 ns->base.level = level;
181 ns->base.zslice = zslice;
182 ns->pitch = mt->level[level].pitch;
183
184 if (pt->target == PIPE_TEXTURE_CUBE) {
185 ns->base.offset = mt->level[level].image_offset[face];
186 } else
187 if (pt->target == PIPE_TEXTURE_3D) {
188 ns->base.offset = mt->level[level].image_offset[zslice];
189 } else {
190 ns->base.offset = mt->level[level].image_offset[0];
191 }
192
193 return &ns->base;
194 }
195
196 static void
197 nv40_miptree_surface_del(struct pipe_surface *ps)
198 {
199 pipe_texture_reference(&ps->texture, NULL);
200 FREE(ps);
201 }
202
203 void
204 nv40_screen_init_miptree_functions(struct pipe_screen *pscreen)
205 {
206 pscreen->texture_create = nv40_miptree_create;
207 pscreen->texture_blanket = nv40_miptree_blanket;
208 pscreen->texture_destroy = nv40_miptree_destroy;
209 pscreen->get_tex_surface = nv40_miptree_surface_new;
210 pscreen->tex_surface_destroy = nv40_miptree_surface_del;
211 }
212