cell: update cell driver after gallium reference count changes
[mesa.git] / src / gallium / drivers / cell / ppu / cell_texture.c
1 /**************************************************************************
2 *
3 * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27 /*
28 * Authors:
29 * Keith Whitwell <keith@tungstengraphics.com>
30 * Michel Dänzer <michel@tungstengraphics.com>
31 * Brian Paul
32 */
33
34 #include "pipe/p_context.h"
35 #include "pipe/p_defines.h"
36 #include "pipe/p_inlines.h"
37 #include "pipe/internal/p_winsys_screen.h"
38 #include "util/u_math.h"
39 #include "util/u_memory.h"
40
41 #include "cell_context.h"
42 #include "cell_state.h"
43 #include "cell_texture.h"
44
45
46
47 static unsigned
48 minify(unsigned d)
49 {
50 return MAX2(1, d>>1);
51 }
52
53
54 static void
55 cell_texture_layout(struct cell_texture *ct)
56 {
57 struct pipe_texture *pt = &ct->base;
58 unsigned level;
59 unsigned width = pt->width[0];
60 unsigned height = pt->height[0];
61 unsigned depth = pt->depth[0];
62
63 ct->buffer_size = 0;
64
65 for (level = 0; level <= pt->last_level; level++) {
66 unsigned size;
67 unsigned w_tile, h_tile;
68
69 assert(level < CELL_MAX_TEXTURE_LEVELS);
70
71 /* width, height, rounded up to tile size */
72 w_tile = align(width, TILE_SIZE);
73 h_tile = align(height, TILE_SIZE);
74
75 pt->width[level] = width;
76 pt->height[level] = height;
77 pt->depth[level] = depth;
78 pt->nblocksx[level] = pf_get_nblocksx(&pt->block, w_tile);
79 pt->nblocksy[level] = pf_get_nblocksy(&pt->block, h_tile);
80
81 ct->stride[level] = pt->nblocksx[level] * pt->block.size;
82
83 ct->level_offset[level] = ct->buffer_size;
84
85 size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size;
86 if (pt->target == PIPE_TEXTURE_CUBE)
87 size *= 6;
88 else
89 size *= depth;
90
91 ct->buffer_size += size;
92
93 width = minify(width);
94 height = minify(height);
95 depth = minify(depth);
96 }
97 }
98
99
100 static struct pipe_texture *
101 cell_texture_create(struct pipe_screen *screen,
102 const struct pipe_texture *templat)
103 {
104 struct cell_texture *ct = CALLOC_STRUCT(cell_texture);
105 if (!ct)
106 return NULL;
107
108 ct->base = *templat;
109 pipe_reference_init(&ct->base.reference, 1);
110 ct->base.screen = screen;
111
112 cell_texture_layout(ct);
113
114 ct->buffer = screen->buffer_create(screen, 32, PIPE_BUFFER_USAGE_PIXEL,
115 ct->buffer_size);
116
117 if (!ct->buffer) {
118 FREE(ct);
119 return NULL;
120 }
121
122 return &ct->base;
123 }
124
125
126 static void
127 cell_texture_destroy(struct pipe_texture *pt)
128 {
129 struct cell_texture *ct = cell_texture(pt);
130
131 if (ct->mapped) {
132 pipe_buffer_unmap(ct->buffer->screen, ct->buffer);
133 ct->mapped = NULL;
134 }
135
136 pipe_buffer_reference(&ct->buffer, NULL);
137
138 FREE(ct);
139 }
140
141
142
143 /**
144 * Convert image from linear layout to tiled layout. 4-byte pixels.
145 */
146 static void
147 twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
148 uint src_stride, const uint *src)
149 {
150 const uint tile_size2 = tile_size * tile_size;
151 const uint h_t = (h + tile_size - 1) / tile_size;
152 const uint w_t = (w + tile_size - 1) / tile_size;
153
154 uint it, jt; /* tile counters */
155 uint i, j; /* intra-tile counters */
156
157 src_stride /= 4; /* convert from bytes to pixels */
158
159 /* loop over dest tiles */
160 for (it = 0; it < h_t; it++) {
161 for (jt = 0; jt < w_t; jt++) {
162 /* start of dest tile: */
163 uint *tdst = dst + (it * w_t + jt) * tile_size2;
164
165 /* compute size of this tile (may be smaller than tile_size) */
166 /* XXX note: a compiler bug was found here. That's why the code
167 * looks as it does.
168 */
169 uint tile_width = w - jt * tile_size;
170 tile_width = MIN2(tile_width, tile_size);
171 uint tile_height = h - it * tile_size;
172 tile_height = MIN2(tile_height, tile_size);
173
174 /* loop over texels in the tile */
175 for (i = 0; i < tile_height; i++) {
176 for (j = 0; j < tile_width; j++) {
177 const uint srci = it * tile_size + i;
178 const uint srcj = jt * tile_size + j;
179 ASSERT(srci < h);
180 ASSERT(srcj < w);
181 tdst[i * tile_size + j] = src[srci * src_stride + srcj];
182 }
183 }
184 }
185 }
186 }
187
188
189 /**
190 * For Cell. Basically, rearrange the pixels/quads from this layout:
191 * +--+--+--+--+
192 * |p0|p1|p2|p3|....
193 * +--+--+--+--+
194 *
195 * to this layout:
196 * +--+--+
197 * |p0|p1|....
198 * +--+--+
199 * |p2|p3|
200 * +--+--+
201 */
202 static void
203 twiddle_tile(const uint *tileIn, uint *tileOut)
204 {
205 int y, x;
206
207 for (y = 0; y < TILE_SIZE; y+=2) {
208 for (x = 0; x < TILE_SIZE; x+=2) {
209 int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
210 tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
211 tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
212 tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
213 tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
214 }
215 }
216 }
217
218
219 /**
220 * Convert image from tiled layout to linear layout. 4-byte pixels.
221 */
222 static void
223 untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
224 uint dst_stride, const uint *src)
225 {
226 const uint tile_size2 = tile_size * tile_size;
227 const uint h_t = (h + tile_size - 1) / tile_size;
228 const uint w_t = (w + tile_size - 1) / tile_size;
229 uint *tile_buf;
230 uint it, jt; /* tile counters */
231 uint i, j; /* intra-tile counters */
232
233 dst_stride /= 4; /* convert from bytes to pixels */
234
235 tile_buf = align_malloc(tile_size * tile_size * 4, 16);
236
237 /* loop over src tiles */
238 for (it = 0; it < h_t; it++) {
239 for (jt = 0; jt < w_t; jt++) {
240 /* start of src tile: */
241 const uint *tsrc = src + (it * w_t + jt) * tile_size2;
242
243 twiddle_tile(tsrc, tile_buf);
244 tsrc = tile_buf;
245
246 /* compute size of this tile (may be smaller than tile_size) */
247 /* XXX note: a compiler bug was found here. That's why the code
248 * looks as it does.
249 */
250 uint tile_width = w - jt * tile_size;
251 tile_width = MIN2(tile_width, tile_size);
252 uint tile_height = h - it * tile_size;
253 tile_height = MIN2(tile_height, tile_size);
254
255 /* loop over texels in the tile */
256 for (i = 0; i < tile_height; i++) {
257 for (j = 0; j < tile_width; j++) {
258 uint dsti = it * tile_size + i;
259 uint dstj = jt * tile_size + j;
260 ASSERT(dsti < h);
261 ASSERT(dstj < w);
262 dst[dsti * dst_stride + dstj] = tsrc[i * tile_size + j];
263 }
264 }
265 }
266 }
267
268 align_free(tile_buf);
269 }
270
271
272 static struct pipe_surface *
273 cell_get_tex_surface(struct pipe_screen *screen,
274 struct pipe_texture *pt,
275 unsigned face, unsigned level, unsigned zslice,
276 unsigned usage)
277 {
278 struct cell_texture *ct = cell_texture(pt);
279 struct pipe_surface *ps;
280
281 ps = CALLOC_STRUCT(pipe_surface);
282 if (ps) {
283 pipe_reference_init(&ps->reference, 1);
284 pipe_texture_reference(&ps->texture, pt);
285 ps->format = pt->format;
286 ps->width = pt->width[level];
287 ps->height = pt->height[level];
288 ps->offset = ct->level_offset[level];
289 /* XXX may need to override usage flags (see sp_texture.c) */
290 ps->usage = usage;
291 ps->face = face;
292 ps->level = level;
293 ps->zslice = zslice;
294
295 if (pt->target == PIPE_TEXTURE_CUBE) {
296 ps->offset += face * pt->nblocksy[level] * ct->stride[level];
297 }
298 else if (pt->target == PIPE_TEXTURE_3D) {
299 ps->offset += zslice * pt->nblocksy[level] * ct->stride[level];
300 }
301 else {
302 assert(face == 0);
303 assert(zslice == 0);
304 }
305 }
306 return ps;
307 }
308
309
310 static void
311 cell_tex_surface_destroy(struct pipe_surface *surf)
312 {
313 pipe_texture_reference(&surf->texture, NULL);
314 FREE(surf);
315 }
316
317
318 /**
319 * Create new pipe_transfer object.
320 * This is used by the user to put tex data into a texture (and get it
321 * back out for glGetTexImage).
322 */
323 static struct pipe_transfer *
324 cell_get_tex_transfer(struct pipe_screen *screen,
325 struct pipe_texture *texture,
326 unsigned face, unsigned level, unsigned zslice,
327 enum pipe_transfer_usage usage,
328 unsigned x, unsigned y, unsigned w, unsigned h)
329 {
330 struct cell_texture *ct = cell_texture(texture);
331 struct cell_transfer *ctrans;
332
333 assert(texture);
334 assert(level <= texture->last_level);
335
336 ctrans = CALLOC_STRUCT(cell_transfer);
337 if (ctrans) {
338 struct pipe_transfer *pt = &ctrans->base;
339 pipe_texture_reference(&pt->texture, texture);
340 pt->format = texture->format;
341 pt->block = texture->block;
342 pt->x = x;
343 pt->y = y;
344 pt->width = w;
345 pt->height = h;
346 pt->nblocksx = texture->nblocksx[level];
347 pt->nblocksy = texture->nblocksy[level];
348 pt->stride = ct->stride[level];
349 pt->usage = usage;
350 pt->face = face;
351 pt->level = level;
352 pt->zslice = zslice;
353
354 ctrans->offset = ct->level_offset[level];
355
356 if (texture->target == PIPE_TEXTURE_CUBE) {
357 ctrans->offset += face * pt->nblocksy * pt->stride;
358 }
359 else if (texture->target == PIPE_TEXTURE_3D) {
360 ctrans->offset += zslice * pt->nblocksy * pt->stride;
361 }
362 else {
363 assert(face == 0);
364 assert(zslice == 0);
365 }
366 return pt;
367 }
368 return NULL;
369 }
370
371
372 static void
373 cell_tex_transfer_destroy(struct pipe_transfer *t)
374 {
375 struct cell_transfer *transfer = cell_transfer(t);
376 /* Effectively do the texture_update work here - if texture images
377 * needed post-processing to put them into hardware layout, this is
378 * where it would happen. For cell, nothing to do.
379 */
380 assert (transfer->base.texture);
381 pipe_texture_reference(&transfer->base.texture, NULL);
382 FREE(transfer);
383 }
384
385
386 /**
387 * Return pointer to texture image data in linear layout.
388 */
389 static void *
390 cell_transfer_map(struct pipe_screen *screen, struct pipe_transfer *transfer)
391 {
392 struct cell_transfer *ctrans = cell_transfer(transfer);
393 struct pipe_texture *pt = transfer->texture;
394 struct cell_texture *ct = cell_texture(pt);
395 const uint level = ctrans->base.level;
396 const uint texWidth = pt->width[level];
397 const uint texHeight = pt->height[level];
398 const uint stride = ct->stride[level];
399 unsigned flags = 0x0;
400 unsigned size;
401
402 assert(transfer->texture);
403
404 if (transfer->usage != PIPE_TRANSFER_READ) {
405 flags |= PIPE_BUFFER_USAGE_CPU_WRITE;
406 }
407
408 if (transfer->usage != PIPE_TRANSFER_WRITE) {
409 flags |= PIPE_BUFFER_USAGE_CPU_READ;
410 }
411
412 if (!ct->mapped) {
413 /* map now */
414 ct->mapped = pipe_buffer_map(screen, ct->buffer, flags);
415 }
416
417 /*
418 * Create a buffer of ordinary memory for the linear texture.
419 * This is the memory that the user will read/write.
420 */
421 size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size;
422
423 ctrans->map = align_malloc(size, 16);
424 if (!ctrans->map)
425 return NULL; /* out of memory */
426
427 if (transfer->usage & PIPE_TRANSFER_READ) {
428 /* need to untwiddle the texture to make a linear version */
429 const uint bpp = pf_get_size(ct->base.format);
430 if (bpp == 4) {
431 const uint *src = (uint *) (ct->mapped + ctrans->offset);
432 uint *dst = ctrans->map;
433 untwiddle_image_uint(texWidth, texHeight, TILE_SIZE,
434 dst, stride, src);
435 }
436 else {
437 // xxx fix
438 }
439 }
440
441 return ctrans->map;
442 }
443
444
445 /**
446 * Called when user is done reading/writing texture data.
447 * If new data was written, this is where we convert the linear data
448 * to tiled data.
449 */
450 static void
451 cell_transfer_unmap(struct pipe_screen *screen,
452 struct pipe_transfer *transfer)
453 {
454 struct cell_transfer *ctrans = cell_transfer(transfer);
455 struct pipe_texture *pt = transfer->texture;
456 struct cell_texture *ct = cell_texture(pt);
457 const uint level = ctrans->base.level;
458 const uint texWidth = pt->width[level];
459 const uint texHeight = pt->height[level];
460 const uint stride = ct->stride[level];
461
462 if (!ct->mapped) {
463 /* map now */
464 ct->mapped = pipe_buffer_map(screen, ct->buffer,
465 PIPE_BUFFER_USAGE_CPU_READ);
466 }
467
468 if (transfer->usage & PIPE_TRANSFER_WRITE) {
469 /* The user wrote new texture data into the mapped buffer.
470 * We need to convert the new linear data into the twiddled/tiled format.
471 */
472 const uint bpp = pf_get_size(ct->base.format);
473 if (bpp == 4) {
474 const uint *src = ctrans->map;
475 uint *dst = (uint *) (ct->mapped + ctrans->offset);
476 twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src);
477 }
478 else {
479 // xxx fix
480 }
481 }
482
483 align_free(ctrans->map);
484 ctrans->map = NULL;
485 }
486
487
488 void
489 cell_init_screen_texture_funcs(struct pipe_screen *screen)
490 {
491 screen->texture_create = cell_texture_create;
492 screen->texture_destroy = cell_texture_destroy;
493
494 screen->get_tex_surface = cell_get_tex_surface;
495 screen->tex_surface_destroy = cell_tex_surface_destroy;
496
497 screen->get_tex_transfer = cell_get_tex_transfer;
498 screen->tex_transfer_destroy = cell_tex_transfer_destroy;
499
500 screen->transfer_map = cell_transfer_map;
501 screen->transfer_unmap = cell_transfer_unmap;
502 }