nv50_miptree.c revision 32cd1a0345eaf1f4da8a60a4ac2145ff51383d59
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
29static struct pipe_texture *
30nv50_miptree_create(struct pipe_screen *pscreen, const struct pipe_texture *tmp)
31{
32	struct pipe_winsys *ws = pscreen->winsys;
33	struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
34	struct pipe_texture *pt = &mt->base;
35	unsigned usage, width = tmp->width[0], height = tmp->height[0];
36	unsigned depth = tmp->depth[0];
37	int i, l;
38
39	mt->base = *tmp;
40	mt->base.refcount = 1;
41	mt->base.screen = pscreen;
42
43	usage = PIPE_BUFFER_USAGE_PIXEL;
44	switch (pt->format) {
45	case PIPE_FORMAT_Z24S8_UNORM:
46	case PIPE_FORMAT_Z16_UNORM:
47		usage |= NOUVEAU_BUFFER_USAGE_ZETA;
48		break;
49	default:
50		break;
51	}
52
53	switch (pt->target) {
54	case PIPE_TEXTURE_3D:
55		mt->image_nr = pt->depth[0];
56		break;
57	case PIPE_TEXTURE_CUBE:
58		mt->image_nr = 6;
59		break;
60	default:
61		mt->image_nr = 1;
62		break;
63	}
64
65	for (l = 0; l <= pt->last_level; l++) {
66		struct nv50_miptree_level *lvl = &mt->level[l];
67
68		pt->width[l] = width;
69		pt->height[l] = height;
70		pt->depth[l] = depth;
71		pt->nblocksx[l] = pf_get_nblocksx(&pt->block, width);
72		pt->nblocksy[l] = pf_get_nblocksy(&pt->block, height);
73
74		lvl->image_offset = CALLOC(mt->image_nr, sizeof(int));
75		lvl->image = CALLOC(mt->image_nr, sizeof(struct pipe_buffer *));
76
77		width = MAX2(1, width >> 1);
78		height = MAX2(1, height >> 1);
79		depth = MAX2(1, depth >> 1);
80	}
81
82	for (i = 0; i < mt->image_nr; i++) {
83		for (l = 0; l <= pt->last_level; l++) {
84			struct nv50_miptree_level *lvl = &mt->level[l];
85			int size;
86
87			size  = align(pt->width[l], 8) * pt->block.size;
88			size  = align(size, 64);
89			size *= align(pt->height[l], 8) * pt->block.size;
90
91			lvl->image[i] = ws->buffer_create(ws, 256, 0, size);
92			lvl->image_offset[i] = mt->total_size;
93
94			mt->total_size += size;
95		}
96	}
97
98	mt->buffer = ws->buffer_create(ws, 256, usage, mt->total_size);
99	if (!mt->buffer) {
100		FREE(mt);
101		return NULL;
102	}
103
104	return &mt->base;
105}
106
107static struct pipe_texture *
108nv50_miptree_blanket(struct pipe_screen *pscreen, const struct pipe_texture *pt,
109		     const unsigned *stride, struct pipe_buffer *pb)
110{
111	struct nv50_miptree *mt;
112
113	/* Only supports 2D, non-mipmapped textures for the moment */
114	if (pt->target != PIPE_TEXTURE_2D || pt->last_level != 0 ||
115	    pt->depth[0] != 1)
116		return NULL;
117
118	mt = CALLOC_STRUCT(nv50_miptree);
119	if (!mt)
120		return NULL;
121
122	mt->base = *pt;
123	mt->base.refcount = 1;
124	mt->base.screen = pscreen;
125	mt->image_nr = 1;
126	mt->level[0].image_offset = CALLOC(1, sizeof(unsigned));
127
128	pipe_buffer_reference(pscreen, &mt->buffer, pb);
129	return &mt->base;
130}
131
132static INLINE void
133mark_dirty(uint32_t *flags, unsigned image)
134{
135	flags[image / 32] |= (1 << (image % 32));
136}
137
138static INLINE void
139mark_clean(uint32_t *flags, unsigned image)
140{
141	flags[image / 32] &= ~(1 << (image % 32));
142}
143
144static INLINE int
145is_dirty(uint32_t *flags, unsigned image)
146{
147	return !!(flags[image / 32] & (1 << (image % 32)));
148}
149
150static void
151nv50_miptree_release(struct pipe_screen *pscreen, struct pipe_texture **ppt)
152{
153	struct pipe_texture *pt = *ppt;
154
155	*ppt = NULL;
156
157	if (--pt->refcount <= 0) {
158		struct nv50_miptree *mt = nv50_miptree(pt);
159
160		pipe_buffer_reference(pscreen, &mt->buffer, NULL);
161		FREE(mt);
162	}
163}
164
165void
166nv50_miptree_sync(struct pipe_screen *pscreen, struct nv50_miptree *mt,
167		  unsigned level, unsigned image)
168{
169	struct nv50_screen *nvscreen = nv50_screen(pscreen);
170	struct nv50_miptree_level *lvl = &mt->level[level];
171	struct pipe_surface *dst, *src;
172	unsigned face = 0, zslice = 0;
173
174	if (!is_dirty(lvl->image_dirty_cpu, image))
175		return;
176
177	if (mt->base.target == PIPE_TEXTURE_CUBE)
178		face = image;
179	else
180	if (mt->base.target == PIPE_TEXTURE_3D)
181		zslice = image;
182
183	/* Mark as clean already - so we don't continually call this function
184	 * trying to get a GPU_WRITE pipe_surface!
185	 */
186	mark_clean(lvl->image_dirty_cpu, image);
187
188	/* Pretend we're doing CPU access so we get the backing pipe_surface
189	 * and not a view into the larger miptree.
190	 */
191	src = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
192				       PIPE_BUFFER_USAGE_CPU_READ);
193
194	/* Pretend we're only reading with the GPU so surface doesn't get marked
195	 * as dirtied by the GPU.
196	 */
197	dst = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
198				       PIPE_BUFFER_USAGE_GPU_READ);
199
200	nv50_surface_do_copy(nvscreen, dst, 0, 0, src, 0, 0, dst->width, dst->height);
201
202	pscreen->tex_surface_release(pscreen, &dst);
203	pscreen->tex_surface_release(pscreen, &src);
204}
205
206/* The reverse of the above */
207static void
208nv50_miptree_sync_cpu(struct pipe_screen *pscreen, struct nv50_miptree *mt,
209		      unsigned level, unsigned image)
210{
211	struct nv50_screen *nvscreen = nv50_screen(pscreen);
212	struct nv50_miptree_level *lvl = &mt->level[level];
213	struct pipe_surface *dst, *src;
214	unsigned face = 0, zslice = 0;
215
216	if (!is_dirty(lvl->image_dirty_gpu, image))
217		return;
218
219	if (mt->base.target == PIPE_TEXTURE_CUBE)
220		face = image;
221	else
222	if (mt->base.target == PIPE_TEXTURE_3D)
223		zslice = image;
224
225	mark_clean(lvl->image_dirty_gpu, image);
226
227	src = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
228				       PIPE_BUFFER_USAGE_GPU_READ);
229	dst = pscreen->get_tex_surface(pscreen, &mt->base, face, level, zslice,
230				       PIPE_BUFFER_USAGE_CPU_READ);
231
232	nv50_surface_do_copy(nvscreen, dst, 0, 0, src, 0, 0, dst->width, dst->height);
233
234	pscreen->tex_surface_release(pscreen, &dst);
235	pscreen->tex_surface_release(pscreen, &src);
236}
237
238static struct pipe_surface *
239nv50_miptree_surface_new(struct pipe_screen *pscreen, struct pipe_texture *pt,
240			 unsigned face, unsigned level, unsigned zslice,
241			 unsigned flags)
242{
243	struct nv50_miptree *mt = nv50_miptree(pt);
244	struct nv50_miptree_level *lvl = &mt->level[level];
245	struct pipe_surface *ps;
246	int img;
247
248	if (pt->target == PIPE_TEXTURE_CUBE)
249		img = face;
250	else
251	if (pt->target == PIPE_TEXTURE_3D)
252		img = zslice;
253	else
254		img = 0;
255
256	ps = CALLOC_STRUCT(pipe_surface);
257	if (!ps)
258		return NULL;
259	pipe_texture_reference(&ps->texture, pt);
260	ps->format = pt->format;
261	ps->width = pt->width[level];
262	ps->height = pt->height[level];
263	ps->block = pt->block;
264	ps->nblocksx = pt->nblocksx[level];
265	ps->nblocksy = pt->nblocksy[level];
266	ps->stride = ps->width * ps->block.size;
267	ps->usage = flags;
268	ps->status = PIPE_SURFACE_STATUS_DEFINED;
269	ps->refcount = 1;
270	ps->face = face;
271	ps->level = level;
272	ps->zslice = zslice;
273
274	if (flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE) {
275		assert(!(flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE));
276		nv50_miptree_sync_cpu(pscreen, mt, level, img);
277
278		ps->offset = 0;
279		pipe_texture_reference(&ps->texture, pt);
280
281		if (flags & PIPE_BUFFER_USAGE_CPU_WRITE)
282			mark_dirty(lvl->image_dirty_cpu, img);
283	} else {
284		nv50_miptree_sync(pscreen, mt, level, img);
285
286		ps->offset = lvl->image_offset[img];
287		pipe_texture_reference(&ps->texture, pt);
288
289		if (flags & PIPE_BUFFER_USAGE_GPU_WRITE)
290			mark_dirty(lvl->image_dirty_gpu, img);
291	}
292
293	return ps;
294}
295
296static void
297nv50_miptree_surface_del(struct pipe_screen *pscreen,
298			 struct pipe_surface **psurface)
299{
300	struct pipe_surface *ps = *psurface;
301	struct nv50_surface *s = nv50_surface(ps);
302
303	*psurface = NULL;
304
305	if (--ps->refcount <= 0) {
306		pipe_texture_reference(&ps->texture, NULL);
307		FREE(s);
308	}
309}
310
311void
312nv50_screen_init_miptree_functions(struct pipe_screen *pscreen)
313{
314	pscreen->texture_create = nv50_miptree_create;
315	pscreen->texture_blanket = nv50_miptree_blanket;
316	pscreen->texture_release = nv50_miptree_release;
317	pscreen->get_tex_surface = nv50_miptree_surface_new;
318	pscreen->tex_surface_release = nv50_miptree_surface_del;
319}
320
321