nouveau: Fix nv20-40 swizzled miptree RTs
[mesa.git] / src / gallium / drivers / nv20 / nv20_miptree.c
1 #include "pipe/p_state.h"
2 #include "pipe/p_defines.h"
3 #include "pipe/p_inlines.h"
4 #include "util/u_format.h"
5 #include "util/u_math.h"
6
7 #include "nv20_context.h"
8 #include "nv20_screen.h"
9 #include "../nv04/nv04_surface_2d.h"
10
11 static void
12 nv20_miptree_layout(struct nv20_miptree *nv20mt)
13 {
14 struct pipe_texture *pt = &nv20mt->base;
15 uint width = pt->width0;
16 uint offset = 0;
17 int nr_faces, l, f;
18 uint wide_pitch = pt->tex_usage & (PIPE_TEXTURE_USAGE_SAMPLER |
19 PIPE_TEXTURE_USAGE_DEPTH_STENCIL |
20 PIPE_TEXTURE_USAGE_RENDER_TARGET |
21 PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
22 PIPE_TEXTURE_USAGE_PRIMARY);
23
24 if (pt->target == PIPE_TEXTURE_CUBE) {
25 nr_faces = 6;
26 } else {
27 nr_faces = 1;
28 }
29
30 for (l = 0; l <= pt->last_level; l++) {
31 if (wide_pitch && (pt->tex_usage & NOUVEAU_TEXTURE_USAGE_LINEAR))
32 nv20mt->level[l].pitch = align(util_format_get_stride(pt->format, pt->width0), 64);
33 else
34 nv20mt->level[l].pitch = util_format_get_stride(pt->format, width);
35
36 nv20mt->level[l].image_offset =
37 CALLOC(nr_faces, sizeof(unsigned));
38
39 width = u_minify(width, 1);
40 }
41
42 for (f = 0; f < nr_faces; f++) {
43 for (l = 0; l < pt->last_level; l++) {
44 nv20mt->level[l].image_offset[f] = offset;
45
46 if (!(pt->tex_usage & NOUVEAU_TEXTURE_USAGE_LINEAR) &&
47 u_minify(pt->width0, l + 1) > 1 && u_minify(pt->height0, l + 1) > 1)
48 offset += align(nv20mt->level[l].pitch * u_minify(pt->height0, l), 64);
49 else
50 offset += nv20mt->level[l].pitch * u_minify(pt->height0, l);
51 }
52
53 nv20mt->level[l].image_offset[f] = offset;
54 offset += nv20mt->level[l].pitch * u_minify(pt->height0, l);
55 }
56
57 nv20mt->total_size = offset;
58 }
59
60 static struct pipe_texture *
61 nv20_miptree_blanket(struct pipe_screen *pscreen, const struct pipe_texture *pt,
62 const unsigned *stride, struct pipe_buffer *pb)
63 {
64 struct nv20_miptree *mt;
65
66 /* Only supports 2D, non-mipmapped textures for the moment */
67 if (pt->target != PIPE_TEXTURE_2D || pt->last_level != 0 ||
68 pt->depth0 != 1)
69 return NULL;
70
71 mt = CALLOC_STRUCT(nv20_miptree);
72 if (!mt)
73 return NULL;
74
75 mt->base = *pt;
76 pipe_reference_init(&mt->base.reference, 1);
77 mt->base.screen = pscreen;
78 mt->level[0].pitch = stride[0];
79 mt->level[0].image_offset = CALLOC(1, sizeof(unsigned));
80
81 pipe_buffer_reference(&mt->buffer, pb);
82 mt->bo = nouveau_bo(mt->buffer);
83 return &mt->base;
84 }
85
86 static struct pipe_texture *
87 nv20_miptree_create(struct pipe_screen *screen, const struct pipe_texture *pt)
88 {
89 struct nv20_miptree *mt;
90 unsigned buf_usage = PIPE_BUFFER_USAGE_PIXEL |
91 NOUVEAU_BUFFER_USAGE_TEXTURE;
92
93 mt = MALLOC(sizeof(struct nv20_miptree));
94 if (!mt)
95 return NULL;
96 mt->base = *pt;
97 pipe_reference_init(&mt->base.reference, 1);
98 mt->base.screen = screen;
99
100 /* Swizzled textures must be POT */
101 if (pt->width0 & (pt->width0 - 1) ||
102 pt->height0 & (pt->height0 - 1))
103 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
104 else
105 if (pt->tex_usage & (PIPE_TEXTURE_USAGE_PRIMARY |
106 PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
107 PIPE_TEXTURE_USAGE_DEPTH_STENCIL))
108 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
109 else
110 if (pt->tex_usage & PIPE_TEXTURE_USAGE_DYNAMIC)
111 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
112 else {
113 switch (pt->format) {
114 /* TODO: Figure out which formats can be swizzled */
115 case PIPE_FORMAT_A8R8G8B8_UNORM:
116 case PIPE_FORMAT_X8R8G8B8_UNORM:
117 case PIPE_FORMAT_R16_SNORM:
118 {
119 if (debug_get_bool_option("NOUVEAU_NO_SWIZZLE", FALSE))
120 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
121 break;
122 }
123 default:
124 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
125 }
126 }
127
128 if (pt->tex_usage & PIPE_TEXTURE_USAGE_DYNAMIC)
129 buf_usage |= PIPE_BUFFER_USAGE_CPU_READ_WRITE;
130
131 /* apparently we can't render to swizzled surfaces smaller than 64 bytes, so make them linear.
132 * If the user did not ask for a render target, they can still render to it, but it will cost them an extra copy.
133 * This also happens for small mipmaps of large textures. */
134 if (pt->tex_usage & PIPE_TEXTURE_USAGE_RENDER_TARGET && util_format_get_stride(pt->format, pt->width0) < 64)
135 mt->base.tex_usage |= NOUVEAU_TEXTURE_USAGE_LINEAR;
136
137 nv20_miptree_layout(mt);
138
139 mt->buffer = screen->buffer_create(screen, 256, buf_usage, mt->total_size);
140 if (!mt->buffer) {
141 FREE(mt);
142 return NULL;
143 }
144 mt->bo = nouveau_bo(mt->buffer);
145
146 return &mt->base;
147 }
148
149 static void
150 nv20_miptree_destroy(struct pipe_texture *pt)
151 {
152 struct nv20_miptree *nv20mt = (struct nv20_miptree *)pt;
153 int l;
154
155 pipe_buffer_reference(&nv20mt->buffer, NULL);
156 for (l = 0; l <= pt->last_level; l++) {
157 if (nv20mt->level[l].image_offset)
158 FREE(nv20mt->level[l].image_offset);
159 }
160 }
161
162 static struct pipe_surface *
163 nv20_miptree_surface_get(struct pipe_screen *screen, struct pipe_texture *pt,
164 unsigned face, unsigned level, unsigned zslice,
165 unsigned flags)
166 {
167 struct nv20_miptree *nv20mt = (struct nv20_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 = nv20mt->level[level].pitch;
183
184 if (pt->target == PIPE_TEXTURE_CUBE) {
185 ns->base.offset = nv20mt->level[level].image_offset[face];
186 } else
187 if (pt->target == PIPE_TEXTURE_3D) {
188 ns->base.offset = nv20mt->level[level].image_offset[zslice];
189 } else {
190 ns->base.offset = nv20mt->level[level].image_offset[0];
191 }
192
193 /* create a linear temporary that we can render into if necessary.
194 * Note that ns->pitch is always a multiple of 64 for linear surfaces and swizzled surfaces are POT, so
195 * ns->pitch & 63 is equivalent to (ns->pitch < 64 && swizzled)*/
196 if((ns->pitch & 63) && (ns->base.usage & (PIPE_BUFFER_USAGE_GPU_WRITE | NOUVEAU_BUFFER_USAGE_NO_RENDER)) == PIPE_BUFFER_USAGE_GPU_WRITE)
197 return &nv04_surface_wrap_for_render(screen, ((struct nv20_screen*)screen)->eng2d, ns)->base;
198
199 return &ns->base;
200 }
201
202 static void
203 nv20_miptree_surface_destroy(struct pipe_surface *ps)
204 {
205 struct nv04_surface* ns = (struct nv04_surface*)ps;
206 if(ns->backing)
207 {
208 struct nv20_screen* screen = (struct nv20_screen*)ps->texture->screen;
209 if(ns->backing->base.usage & PIPE_BUFFER_USAGE_GPU_WRITE)
210 screen->eng2d->copy(screen->eng2d, &ns->backing->base, 0, 0, ps, 0, 0, ns->base.width, ns->base.height);
211 nv20_miptree_surface_destroy(&ns->backing->base);
212 }
213
214 pipe_texture_reference(&ps->texture, NULL);
215 FREE(ps);
216 }
217
218 void nv20_screen_init_miptree_functions(struct pipe_screen *pscreen)
219 {
220 pscreen->texture_create = nv20_miptree_create;
221 pscreen->texture_blanket = nv20_miptree_blanket;
222 pscreen->texture_destroy = nv20_miptree_destroy;
223 pscreen->get_tex_surface = nv20_miptree_surface_get;
224 pscreen->tex_surface_destroy = nv20_miptree_surface_destroy;
225 }
226