Merge branch '7.8'
[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 "util/u_inlines.h"
37
38 #include "util/u_format.h"
39 #include "util/u_math.h"
40 #include "util/u_memory.h"
41
42 #include "cell_context.h"
43 #include "cell_screen.h"
44 #include "cell_state.h"
45 #include "cell_texture.h"
46
47 #include "state_tracker/sw_winsys.h"
48
49
50
51 static boolean
52 cell_texture_layout(struct pipe_screen *screen,
53 struct cell_texture *ct)
54 {
55 struct pipe_texture *pt = &ct->base;
56 unsigned level;
57 unsigned width = pt->width0;
58 unsigned height = pt->height0;
59 unsigned depth = pt->depth0;
60
61 ct->buffer_size = 0;
62
63 for (level = 0; level <= pt->last_level; level++) {
64 unsigned size;
65 unsigned w_tile, h_tile;
66
67 assert(level < CELL_MAX_TEXTURE_LEVELS);
68
69 /* width, height, rounded up to tile size */
70 w_tile = align(width, TILE_SIZE);
71 h_tile = align(height, TILE_SIZE);
72
73 ct->stride[level] = util_format_get_stride(pt->format, w_tile);
74
75 ct->level_offset[level] = ct->buffer_size;
76
77 size = ct->stride[level] * util_format_get_nblocksy(pt->format, h_tile);
78 if (pt->target == PIPE_TEXTURE_CUBE)
79 size *= 6;
80 else
81 size *= depth;
82
83 ct->buffer_size += size;
84
85 width = u_minify(width, 1);
86 height = u_minify(height, 1);
87 depth = u_minify(depth, 1);
88 }
89
90 ct->data = align_malloc(ct->buffer_size, 16);
91
92 return ct->data != NULL;
93 }
94
95
96 /**
97 * Texture layout for simple color buffers.
98 */
99 static boolean
100 cell_displaytarget_layout(struct pipe_screen *screen,
101 struct cell_texture * ct)
102 {
103 struct sw_winsys *winsys = cell_screen(screen)->winsys;
104
105 /* Round up the surface size to a multiple of the tile size?
106 */
107 ct->dt = winsys->displaytarget_create(winsys,
108 ct->base.format,
109 ct->base.width0,
110 ct->base.height0,
111 16,
112 &ct->dt_stride );
113
114 return ct->dt != NULL;
115 }
116
117 static struct pipe_texture *
118 cell_texture_create(struct pipe_screen *screen,
119 const struct pipe_texture *templat)
120 {
121 struct cell_texture *ct = CALLOC_STRUCT(cell_texture);
122 if (!ct)
123 return NULL;
124
125 ct->base = *templat;
126 pipe_reference_init(&ct->base.reference, 1);
127 ct->base.screen = screen;
128
129 /* Create both a displaytarget (linear) and regular texture
130 * (twiddled). Convert twiddled->linear at flush_frontbuffer time.
131 */
132 if (ct->base.tex_usage & (PIPE_TEXTURE_USAGE_DISPLAY_TARGET |
133 PIPE_TEXTURE_USAGE_SCANOUT |
134 PIPE_TEXTURE_USAGE_SHARED)) {
135 if (!cell_displaytarget_layout(screen, ct))
136 goto fail;
137 }
138
139 if (!cell_texture_layout(screen, ct))
140 goto fail;
141
142 return &ct->base;
143
144 fail:
145 if (ct->dt) {
146 struct sw_winsys *winsys = cell_screen(screen)->winsys;
147 winsys->displaytarget_destroy(winsys, ct->dt);
148 }
149
150 FREE(ct);
151
152 return NULL;
153 }
154
155
156 static void
157 cell_texture_destroy(struct pipe_texture *pt)
158 {
159 struct cell_screen *screen = cell_screen(pt->screen);
160 struct sw_winsys *winsys = screen->winsys;
161 struct cell_texture *ct = cell_texture(pt);
162
163 if (ct->dt) {
164 /* display target */
165 winsys->displaytarget_destroy(winsys, ct->dt);
166 }
167
168 align_free(ct->data);
169
170 FREE(ct);
171 }
172
173
174
175 /**
176 * Convert image from linear layout to tiled layout. 4-byte pixels.
177 */
178 static void
179 twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
180 uint src_stride, const uint *src)
181 {
182 const uint tile_size2 = tile_size * tile_size;
183 const uint h_t = (h + tile_size - 1) / tile_size;
184 const uint w_t = (w + tile_size - 1) / tile_size;
185
186 uint it, jt; /* tile counters */
187 uint i, j; /* intra-tile counters */
188
189 src_stride /= 4; /* convert from bytes to pixels */
190
191 /* loop over dest tiles */
192 for (it = 0; it < h_t; it++) {
193 for (jt = 0; jt < w_t; jt++) {
194 /* start of dest tile: */
195 uint *tdst = dst + (it * w_t + jt) * tile_size2;
196
197 /* compute size of this tile (may be smaller than tile_size) */
198 /* XXX note: a compiler bug was found here. That's why the code
199 * looks as it does.
200 */
201 uint tile_width = w - jt * tile_size;
202 tile_width = MIN2(tile_width, tile_size);
203 uint tile_height = h - it * tile_size;
204 tile_height = MIN2(tile_height, tile_size);
205
206 /* loop over texels in the tile */
207 for (i = 0; i < tile_height; i++) {
208 for (j = 0; j < tile_width; j++) {
209 const uint srci = it * tile_size + i;
210 const uint srcj = jt * tile_size + j;
211 ASSERT(srci < h);
212 ASSERT(srcj < w);
213 tdst[i * tile_size + j] = src[srci * src_stride + srcj];
214 }
215 }
216 }
217 }
218 }
219
220
221 /**
222 * For Cell. Basically, rearrange the pixels/quads from this layout:
223 * +--+--+--+--+
224 * |p0|p1|p2|p3|....
225 * +--+--+--+--+
226 *
227 * to this layout:
228 * +--+--+
229 * |p0|p1|....
230 * +--+--+
231 * |p2|p3|
232 * +--+--+
233 */
234 static void
235 twiddle_tile(const uint *tileIn, uint *tileOut)
236 {
237 int y, x;
238
239 for (y = 0; y < TILE_SIZE; y+=2) {
240 for (x = 0; x < TILE_SIZE; x+=2) {
241 int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
242 tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
243 tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
244 tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
245 tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
246 }
247 }
248 }
249
250
251 /**
252 * Convert image from tiled layout to linear layout. 4-byte pixels.
253 */
254 static void
255 untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
256 uint dst_stride, const uint *src)
257 {
258 const uint tile_size2 = tile_size * tile_size;
259 const uint h_t = (h + tile_size - 1) / tile_size;
260 const uint w_t = (w + tile_size - 1) / tile_size;
261 uint *tile_buf;
262 uint it, jt; /* tile counters */
263 uint i, j; /* intra-tile counters */
264
265 dst_stride /= 4; /* convert from bytes to pixels */
266
267 tile_buf = align_malloc(tile_size * tile_size * 4, 16);
268
269 /* loop over src tiles */
270 for (it = 0; it < h_t; it++) {
271 for (jt = 0; jt < w_t; jt++) {
272 /* start of src tile: */
273 const uint *tsrc = src + (it * w_t + jt) * tile_size2;
274
275 twiddle_tile(tsrc, tile_buf);
276 tsrc = tile_buf;
277
278 /* compute size of this tile (may be smaller than tile_size) */
279 /* XXX note: a compiler bug was found here. That's why the code
280 * looks as it does.
281 */
282 uint tile_width = w - jt * tile_size;
283 tile_width = MIN2(tile_width, tile_size);
284 uint tile_height = h - it * tile_size;
285 tile_height = MIN2(tile_height, tile_size);
286
287 /* loop over texels in the tile */
288 for (i = 0; i < tile_height; i++) {
289 for (j = 0; j < tile_width; j++) {
290 uint dsti = it * tile_size + i;
291 uint dstj = jt * tile_size + j;
292 ASSERT(dsti < h);
293 ASSERT(dstj < w);
294 dst[dsti * dst_stride + dstj] = tsrc[i * tile_size + j];
295 }
296 }
297 }
298 }
299
300 align_free(tile_buf);
301 }
302
303
304 static struct pipe_surface *
305 cell_get_tex_surface(struct pipe_screen *screen,
306 struct pipe_texture *pt,
307 unsigned face, unsigned level, unsigned zslice,
308 unsigned usage)
309 {
310 struct cell_texture *ct = cell_texture(pt);
311 struct pipe_surface *ps;
312
313 ps = CALLOC_STRUCT(pipe_surface);
314 if (ps) {
315 pipe_reference_init(&ps->reference, 1);
316 pipe_texture_reference(&ps->texture, pt);
317 ps->format = pt->format;
318 ps->width = u_minify(pt->width0, level);
319 ps->height = u_minify(pt->height0, level);
320 ps->offset = ct->level_offset[level];
321 /* XXX may need to override usage flags (see sp_texture.c) */
322 ps->usage = usage;
323 ps->face = face;
324 ps->level = level;
325 ps->zslice = zslice;
326
327 if (pt->target == PIPE_TEXTURE_CUBE) {
328 unsigned h_tile = align(ps->height, TILE_SIZE);
329 ps->offset += face * util_format_get_nblocksy(ps->format, h_tile) * ct->stride[level];
330 }
331 else if (pt->target == PIPE_TEXTURE_3D) {
332 unsigned h_tile = align(ps->height, TILE_SIZE);
333 ps->offset += zslice * util_format_get_nblocksy(ps->format, h_tile) * ct->stride[level];
334 }
335 else {
336 assert(face == 0);
337 assert(zslice == 0);
338 }
339 }
340 return ps;
341 }
342
343
344 static void
345 cell_tex_surface_destroy(struct pipe_surface *surf)
346 {
347 pipe_texture_reference(&surf->texture, NULL);
348 FREE(surf);
349 }
350
351
352 /**
353 * Create new pipe_transfer object.
354 * This is used by the user to put tex data into a texture (and get it
355 * back out for glGetTexImage).
356 */
357 static struct pipe_transfer *
358 cell_get_tex_transfer(struct pipe_screen *screen,
359 struct pipe_texture *texture,
360 unsigned face, unsigned level, unsigned zslice,
361 enum pipe_transfer_usage usage,
362 unsigned x, unsigned y, unsigned w, unsigned h)
363 {
364 struct cell_texture *ct = cell_texture(texture);
365 struct cell_transfer *ctrans;
366
367 assert(texture);
368 assert(level <= texture->last_level);
369
370 ctrans = CALLOC_STRUCT(cell_transfer);
371 if (ctrans) {
372 struct pipe_transfer *pt = &ctrans->base;
373 pipe_texture_reference(&pt->texture, texture);
374 pt->x = x;
375 pt->y = y;
376 pt->width = w;
377 pt->height = h;
378 pt->stride = ct->stride[level];
379 pt->usage = usage;
380 pt->face = face;
381 pt->level = level;
382 pt->zslice = zslice;
383
384 ctrans->offset = ct->level_offset[level];
385
386 if (texture->target == PIPE_TEXTURE_CUBE) {
387 unsigned h_tile = align(u_minify(texture->height0, level), TILE_SIZE);
388 ctrans->offset += face * util_format_get_nblocksy(texture->format, h_tile) * pt->stride;
389 }
390 else if (texture->target == PIPE_TEXTURE_3D) {
391 unsigned h_tile = align(u_minify(texture->height0, level), TILE_SIZE);
392 ctrans->offset += zslice * util_format_get_nblocksy(texture->format, h_tile) * pt->stride;
393 }
394 else {
395 assert(face == 0);
396 assert(zslice == 0);
397 }
398 return pt;
399 }
400 return NULL;
401 }
402
403
404 static void
405 cell_tex_transfer_destroy(struct pipe_transfer *t)
406 {
407 struct cell_transfer *transfer = cell_transfer(t);
408 /* Effectively do the texture_update work here - if texture images
409 * needed post-processing to put them into hardware layout, this is
410 * where it would happen. For cell, nothing to do.
411 */
412 assert (transfer->base.texture);
413 pipe_texture_reference(&transfer->base.texture, NULL);
414 FREE(transfer);
415 }
416
417
418 /**
419 * Return pointer to texture image data in linear layout.
420 */
421 static void *
422 cell_transfer_map(struct pipe_screen *screen, struct pipe_transfer *transfer)
423 {
424 struct cell_transfer *ctrans = cell_transfer(transfer);
425 struct pipe_texture *pt = transfer->texture;
426 struct cell_texture *ct = cell_texture(pt);
427 const uint level = ctrans->base.level;
428 const uint texWidth = u_minify(pt->width0, level);
429 const uint texHeight = u_minify(pt->height0, level);
430 const uint stride = ct->stride[level];
431 unsigned size;
432
433 assert(transfer->texture);
434
435 if (ct->mapped == NULL) {
436 ct->mapped = ct->data;
437 }
438
439 /*
440 * Create a buffer of ordinary memory for the linear texture.
441 * This is the memory that the user will read/write.
442 */
443 size = util_format_get_stride(pt->format, align(texWidth, TILE_SIZE)) *
444 util_format_get_nblocksy(pt->format, align(texHeight, TILE_SIZE));
445
446 ctrans->map = align_malloc(size, 16);
447 if (!ctrans->map)
448 return NULL; /* out of memory */
449
450 if (transfer->usage & PIPE_TRANSFER_READ) {
451 /* need to untwiddle the texture to make a linear version */
452 const uint bpp = util_format_get_blocksize(ct->base.format);
453 if (bpp == 4) {
454 const uint *src = (uint *) (ct->mapped + ctrans->offset);
455 uint *dst = ctrans->map;
456 untwiddle_image_uint(texWidth, texHeight, TILE_SIZE,
457 dst, stride, src);
458 }
459 else {
460 // xxx fix
461 }
462 }
463
464 return ctrans->map;
465 }
466
467
468 /**
469 * Called when user is done reading/writing texture data.
470 * If new data was written, this is where we convert the linear data
471 * to tiled data.
472 */
473 static void
474 cell_transfer_unmap(struct pipe_screen *screen,
475 struct pipe_transfer *transfer)
476 {
477 struct cell_transfer *ctrans = cell_transfer(transfer);
478 struct pipe_texture *pt = transfer->texture;
479 struct cell_texture *ct = cell_texture(pt);
480 const uint level = ctrans->base.level;
481 const uint texWidth = u_minify(pt->width0, level);
482 const uint texHeight = u_minify(pt->height0, level);
483 const uint stride = ct->stride[level];
484
485 if (!ct->mapped) {
486 assert(0);
487 return;
488 }
489
490 if (transfer->usage & PIPE_TRANSFER_WRITE) {
491 /* The user wrote new texture data into the mapped buffer.
492 * We need to convert the new linear data into the twiddled/tiled format.
493 */
494 const uint bpp = util_format_get_blocksize(ct->base.format);
495 if (bpp == 4) {
496 const uint *src = ctrans->map;
497 uint *dst = (uint *) (ct->mapped + ctrans->offset);
498 twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src);
499 }
500 else {
501 // xxx fix
502 }
503 }
504
505 align_free(ctrans->map);
506 ctrans->map = NULL;
507 }
508
509
510
511 /* This used to be overriden by the co-state tracker, but really needs
512 * to be active with sw_winsys.
513 *
514 * Contrasting with llvmpipe and softpipe, this is the only place
515 * where we use the ct->dt display target in any real sense.
516 *
517 * Basically just untwiddle our local data into the linear
518 * displaytarget.
519 */
520 static void
521 cell_flush_frontbuffer(struct pipe_screen *_screen,
522 struct pipe_surface *surface,
523 void *context_private)
524 {
525 struct cell_screen *screen = cell_screen(_screen);
526 struct sw_winsys *winsys = screen->winsys;
527 struct cell_texture *ct = cell_texture(surface->texture);
528
529 if (!ct->dt)
530 return;
531
532 /* Need to untwiddle from our internal representation here:
533 */
534 {
535 unsigned *map = winsys->displaytarget_map(winsys, ct->dt,
536 (PIPE_BUFFER_USAGE_CPU_READ |
537 PIPE_BUFFER_USAGE_CPU_WRITE));
538 unsigned *src = (unsigned *)(ct->data + ct->level_offset[surface->level]);
539
540 untwiddle_image_uint(surface->width,
541 surface->height,
542 TILE_SIZE,
543 map,
544 ct->dt_stride,
545 src);
546
547 winsys->displaytarget_unmap(winsys, ct->dt);
548 }
549
550 winsys->displaytarget_display(winsys, ct->dt, context_private);
551 }
552
553
554 void
555 cell_init_screen_texture_funcs(struct pipe_screen *screen)
556 {
557 screen->texture_create = cell_texture_create;
558 screen->texture_destroy = cell_texture_destroy;
559
560 screen->get_tex_surface = cell_get_tex_surface;
561 screen->tex_surface_destroy = cell_tex_surface_destroy;
562
563 screen->get_tex_transfer = cell_get_tex_transfer;
564 screen->tex_transfer_destroy = cell_tex_transfer_destroy;
565
566 screen->transfer_map = cell_transfer_map;
567 screen->transfer_unmap = cell_transfer_unmap;
568
569 screen->flush_frontbuffer = cell_flush_frontbuffer;
570 }