Merge remote branch 'origin/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_resource.h"
46
47 #include "state_tracker/sw_winsys.h"
48
49
50
51 static boolean
52 cell_resource_layout(struct pipe_screen *screen,
53 struct cell_resource *ct)
54 {
55 struct pipe_resource *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_resource * 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.bind,
109 ct->base.format,
110 ct->base.width0,
111 ct->base.height0,
112 16,
113 &ct->dt_stride );
114
115 return ct->dt != NULL;
116 }
117
118 static struct pipe_resource *
119 cell_resource_create(struct pipe_screen *screen,
120 const struct pipe_resource *templat)
121 {
122 struct cell_resource *ct = CALLOC_STRUCT(cell_resource);
123 if (!ct)
124 return NULL;
125
126 ct->base = *templat;
127 pipe_reference_init(&ct->base.reference, 1);
128 ct->base.screen = screen;
129
130 /* Create both a displaytarget (linear) and regular texture
131 * (twiddled). Convert twiddled->linear at flush_frontbuffer time.
132 */
133 if (ct->base.bind & (PIPE_BIND_DISPLAY_TARGET |
134 PIPE_BIND_SCANOUT |
135 PIPE_BIND_SHARED)) {
136 if (!cell_displaytarget_layout(screen, ct))
137 goto fail;
138 }
139
140 if (!cell_resource_layout(screen, ct))
141 goto fail;
142
143 return &ct->base;
144
145 fail:
146 if (ct->dt) {
147 struct sw_winsys *winsys = cell_screen(screen)->winsys;
148 winsys->displaytarget_destroy(winsys, ct->dt);
149 }
150
151 FREE(ct);
152
153 return NULL;
154 }
155
156
157 static void
158 cell_resource_destroy(struct pipe_resource *pt)
159 {
160 struct cell_screen *screen = cell_screen(pt->screen);
161 struct sw_winsys *winsys = screen->winsys;
162 struct cell_resource *ct = cell_resource(pt);
163
164 if (ct->dt) {
165 /* display target */
166 winsys->displaytarget_destroy(winsys, ct->dt);
167 }
168 else if (!ct->userBuffer) {
169 align_free(ct->data);
170 }
171
172 FREE(ct);
173 }
174
175
176
177 /**
178 * Convert image from linear layout to tiled layout. 4-byte pixels.
179 */
180 static void
181 twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
182 uint src_stride, const uint *src)
183 {
184 const uint tile_size2 = tile_size * tile_size;
185 const uint h_t = (h + tile_size - 1) / tile_size;
186 const uint w_t = (w + tile_size - 1) / tile_size;
187
188 uint it, jt; /* tile counters */
189 uint i, j; /* intra-tile counters */
190
191 src_stride /= 4; /* convert from bytes to pixels */
192
193 /* loop over dest tiles */
194 for (it = 0; it < h_t; it++) {
195 for (jt = 0; jt < w_t; jt++) {
196 /* start of dest tile: */
197 uint *tdst = dst + (it * w_t + jt) * tile_size2;
198
199 /* compute size of this tile (may be smaller than tile_size) */
200 /* XXX note: a compiler bug was found here. That's why the code
201 * looks as it does.
202 */
203 uint tile_width = w - jt * tile_size;
204 tile_width = MIN2(tile_width, tile_size);
205 uint tile_height = h - it * tile_size;
206 tile_height = MIN2(tile_height, tile_size);
207
208 /* loop over texels in the tile */
209 for (i = 0; i < tile_height; i++) {
210 for (j = 0; j < tile_width; j++) {
211 const uint srci = it * tile_size + i;
212 const uint srcj = jt * tile_size + j;
213 ASSERT(srci < h);
214 ASSERT(srcj < w);
215 tdst[i * tile_size + j] = src[srci * src_stride + srcj];
216 }
217 }
218 }
219 }
220 }
221
222
223 /**
224 * For Cell. Basically, rearrange the pixels/quads from this layout:
225 * +--+--+--+--+
226 * |p0|p1|p2|p3|....
227 * +--+--+--+--+
228 *
229 * to this layout:
230 * +--+--+
231 * |p0|p1|....
232 * +--+--+
233 * |p2|p3|
234 * +--+--+
235 */
236 static void
237 twiddle_tile(const uint *tileIn, uint *tileOut)
238 {
239 int y, x;
240
241 for (y = 0; y < TILE_SIZE; y+=2) {
242 for (x = 0; x < TILE_SIZE; x+=2) {
243 int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
244 tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
245 tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
246 tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
247 tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
248 }
249 }
250 }
251
252
253 /**
254 * Convert image from tiled layout to linear layout. 4-byte pixels.
255 */
256 static void
257 untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
258 uint dst_stride, const uint *src)
259 {
260 const uint tile_size2 = tile_size * tile_size;
261 const uint h_t = (h + tile_size - 1) / tile_size;
262 const uint w_t = (w + tile_size - 1) / tile_size;
263 uint *tile_buf;
264 uint it, jt; /* tile counters */
265 uint i, j; /* intra-tile counters */
266
267 dst_stride /= 4; /* convert from bytes to pixels */
268
269 tile_buf = align_malloc(tile_size * tile_size * 4, 16);
270
271 /* loop over src tiles */
272 for (it = 0; it < h_t; it++) {
273 for (jt = 0; jt < w_t; jt++) {
274 /* start of src tile: */
275 const uint *tsrc = src + (it * w_t + jt) * tile_size2;
276
277 twiddle_tile(tsrc, tile_buf);
278 tsrc = tile_buf;
279
280 /* compute size of this tile (may be smaller than tile_size) */
281 /* XXX note: a compiler bug was found here. That's why the code
282 * looks as it does.
283 */
284 uint tile_width = w - jt * tile_size;
285 tile_width = MIN2(tile_width, tile_size);
286 uint tile_height = h - it * tile_size;
287 tile_height = MIN2(tile_height, tile_size);
288
289 /* loop over texels in the tile */
290 for (i = 0; i < tile_height; i++) {
291 for (j = 0; j < tile_width; j++) {
292 uint dsti = it * tile_size + i;
293 uint dstj = jt * tile_size + j;
294 ASSERT(dsti < h);
295 ASSERT(dstj < w);
296 dst[dsti * dst_stride + dstj] = tsrc[i * tile_size + j];
297 }
298 }
299 }
300 }
301
302 align_free(tile_buf);
303 }
304
305
306 static struct pipe_surface *
307 cell_get_tex_surface(struct pipe_screen *screen,
308 struct pipe_resource *pt,
309 unsigned face, unsigned level, unsigned zslice,
310 unsigned usage)
311 {
312 struct cell_resource *ct = cell_resource(pt);
313 struct pipe_surface *ps;
314
315 ps = CALLOC_STRUCT(pipe_surface);
316 if (ps) {
317 pipe_reference_init(&ps->reference, 1);
318 pipe_resource_reference(&ps->texture, pt);
319 ps->format = pt->format;
320 ps->width = u_minify(pt->width0, level);
321 ps->height = u_minify(pt->height0, level);
322 ps->offset = ct->level_offset[level];
323 /* XXX may need to override usage flags (see sp_texture.c) */
324 ps->usage = usage;
325 ps->face = face;
326 ps->level = level;
327 ps->zslice = zslice;
328
329 if (pt->target == PIPE_TEXTURE_CUBE) {
330 unsigned h_tile = align(ps->height, TILE_SIZE);
331 ps->offset += face * util_format_get_nblocksy(ps->format, h_tile) * ct->stride[level];
332 }
333 else if (pt->target == PIPE_TEXTURE_3D) {
334 unsigned h_tile = align(ps->height, TILE_SIZE);
335 ps->offset += zslice * util_format_get_nblocksy(ps->format, h_tile) * ct->stride[level];
336 }
337 else {
338 assert(face == 0);
339 assert(zslice == 0);
340 }
341 }
342 return ps;
343 }
344
345
346 static void
347 cell_tex_surface_destroy(struct pipe_surface *surf)
348 {
349 pipe_resource_reference(&surf->texture, NULL);
350 FREE(surf);
351 }
352
353
354 /**
355 * Create new pipe_transfer object.
356 * This is used by the user to put tex data into a texture (and get it
357 * back out for glGetTexImage).
358 */
359 static struct pipe_transfer *
360 cell_get_transfer(struct pipe_context *ctx,
361 struct pipe_resource *resource,
362 struct pipe_subresource sr,
363 unsigned usage,
364 const struct pipe_box *box)
365 {
366 struct cell_resource *ct = cell_resource(resource);
367 struct cell_transfer *ctrans;
368 enum pipe_format *format = resource->format;
369
370 assert(resource);
371 assert(level <= resource->last_level);
372
373 /* make sure the requested region is in the image bounds */
374 assert(box->x + box->width <= u_minify(resource->width0, sr.level));
375 assert(box->y + box->height <= u_minify(resource->height0, sr.level));
376 assert(box->z + box->depth <= u_minify(resource->depth0, sr.level));
377
378 ctrans = CALLOC_STRUCT(cell_transfer);
379 if (ctrans) {
380 struct pipe_transfer *pt = &ctrans->base;
381 pipe_resource_reference(&pt->resource, resource);
382 pt->sr = sr;
383 pt->usage = usage;
384 pt->box = *box;
385 pt->stride = ct->stride[sr.level];
386
387 ctrans->offset = ct->level_offset[sr.level];
388
389 if (resource->target == PIPE_TEXTURE_CUBE) {
390 unsigned h_tile = align(u_minify(resource->height0, sr.level), TILE_SIZE);
391 ctrans->offset += sr.face * util_format_get_nblocksy(format, h_tile) * pt->stride;
392 }
393 else if (resource->target == PIPE_TEXTURE_3D) {
394 unsigned h_tile = align(u_minify(resource->height0, sr.level), TILE_SIZE);
395 ctrans->offset += box->z * util_format_get_nblocksy(format, h_tile) * pt->stride;
396 }
397 else {
398 assert(sr.face == 0);
399 assert(box->z == 0);
400 }
401
402 return pt;
403 }
404 return NULL;
405 }
406
407
408 static void
409 cell_transfer_destroy(struct pipe_context *ctx, struct pipe_transfer *t)
410 {
411 struct cell_transfer *transfer = cell_transfer(t);
412 /* Effectively do the texture_update work here - if texture images
413 * needed post-processing to put them into hardware layout, this is
414 * where it would happen. For cell, nothing to do.
415 */
416 assert (transfer->base.resource);
417 pipe_resource_reference(&transfer->base.resource, NULL);
418 FREE(transfer);
419 }
420
421
422 /**
423 * Return pointer to texture image data in linear layout.
424 */
425 static void *
426 cell_transfer_map(struct pipe_context *ctx, struct pipe_transfer *transfer)
427 {
428 struct cell_transfer *ctrans = cell_transfer(transfer);
429 struct pipe_resource *pt = transfer->resource;
430 struct cell_resource *ct = cell_resource(pt);
431
432 assert(transfer->resource);
433
434 if (ct->mapped == NULL) {
435 ct->mapped = ct->data;
436 }
437
438
439 /* Better test would be resource->is_linear
440 */
441 if (transfer->resource->target != PIPE_BUFFER) {
442 const uint level = ctrans->base.sr.level;
443 const uint texWidth = u_minify(pt->width0, level);
444 const uint texHeight = u_minify(pt->height0, level);
445 unsigned size;
446
447
448 /*
449 * Create a buffer of ordinary memory for the linear texture.
450 * This is the memory that the user will read/write.
451 */
452 size = (util_format_get_stride(pt->format, align(texWidth, TILE_SIZE)) *
453 util_format_get_nblocksy(pt->format, align(texHeight, TILE_SIZE)));
454
455 ctrans->map = align_malloc(size, 16);
456 if (!ctrans->map)
457 return NULL; /* out of memory */
458
459 if (transfer->usage & PIPE_TRANSFER_READ) {
460 /* Textures always stored twiddled, need to untwiddle the
461 * texture to make a linear version.
462 */
463 const uint bpp = util_format_get_blocksize(ct->base.format);
464 if (bpp == 4) {
465 const uint *src = (uint *) (ct->mapped + ctrans->offset);
466 uint *dst = ctrans->map;
467 untwiddle_image_uint(texWidth, texHeight, TILE_SIZE,
468 dst, transfer->stride, src);
469 }
470 else {
471 // xxx fix
472 }
473 }
474 }
475 else {
476 unsigned stride = transfer->stride;
477 enum pipe_format format = pt->format;
478 unsigned blocksize = util_format_get_blocksize(format);
479
480 ctrans->map = (ct->mapped +
481 ctrans->offset +
482 ctrans->base.box.y / util_format_get_blockheight(format) * stride +
483 ctrans->base.box.x / util_format_get_blockwidth(format) * blocksize);
484 }
485
486
487 return ctrans->map;
488 }
489
490
491 /**
492 * Called when user is done reading/writing texture data.
493 * If new data was written, this is where we convert the linear data
494 * to tiled data.
495 */
496 static void
497 cell_transfer_unmap(struct pipe_context *ctx,
498 struct pipe_transfer *transfer)
499 {
500 struct cell_transfer *ctrans = cell_transfer(transfer);
501 struct pipe_resource *pt = transfer->resource;
502 struct cell_resource *ct = cell_resource(pt);
503 const uint level = ctrans->base.sr.level;
504 const uint texWidth = u_minify(pt->width0, level);
505 const uint texHeight = u_minify(pt->height0, level);
506 const uint stride = ct->stride[level];
507
508 if (!ct->mapped) {
509 assert(0);
510 return;
511 }
512
513 if (pt->target != PIPE_BUFFER) {
514 if (transfer->usage & PIPE_TRANSFER_WRITE) {
515 /* The user wrote new texture data into the mapped buffer.
516 * We need to convert the new linear data into the twiddled/tiled format.
517 */
518 const uint bpp = util_format_get_blocksize(ct->base.format);
519 if (bpp == 4) {
520 const uint *src = ctrans->map;
521 uint *dst = (uint *) (ct->mapped + ctrans->offset);
522 twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src);
523 }
524 else {
525 // xxx fix
526 }
527 }
528
529 align_free(ctrans->map);
530 }
531 else {
532 /* nothing to do */
533 }
534
535 ctrans->map = NULL;
536 }
537
538
539
540 /* This used to be overriden by the co-state tracker, but really needs
541 * to be active with sw_winsys.
542 *
543 * Contrasting with llvmpipe and softpipe, this is the only place
544 * where we use the ct->dt display target in any real sense.
545 *
546 * Basically just untwiddle our local data into the linear
547 * displaytarget.
548 */
549 static void
550 cell_flush_frontbuffer(struct pipe_screen *_screen,
551 struct pipe_surface *surface,
552 void *context_private)
553 {
554 struct cell_screen *screen = cell_screen(_screen);
555 struct sw_winsys *winsys = screen->winsys;
556 struct cell_resource *ct = cell_resource(surface->texture);
557
558 if (!ct->dt)
559 return;
560
561 /* Need to untwiddle from our internal representation here:
562 */
563 {
564 unsigned *map = winsys->displaytarget_map(winsys, ct->dt,
565 (PIPE_TRANSFER_READ |
566 PIPE_TRANSFER_WRITE));
567 unsigned *src = (unsigned *)(ct->data + ct->level_offset[surface->level]);
568
569 untwiddle_image_uint(surface->width,
570 surface->height,
571 TILE_SIZE,
572 map,
573 ct->dt_stride,
574 src);
575
576 winsys->displaytarget_unmap(winsys, ct->dt);
577 }
578
579 winsys->displaytarget_display(winsys, ct->dt, context_private);
580 }
581
582
583
584 /**
585 * Create buffer which wraps user-space data.
586 */
587 static struct pipe_resource *
588 cell_user_buffer_create(struct pipe_screen *screen,
589 void *ptr,
590 unsigned bytes,
591 unsigned bind_flags)
592 {
593 struct cell_resource *buffer;
594
595 buffer = CALLOC_STRUCT(cell_resource);
596 if(!buffer)
597 return NULL;
598
599 pipe_reference_init(&buffer->base.reference, 1);
600 buffer->base.screen = screen;
601 buffer->base.format = PIPE_FORMAT_R8_UNORM; /* ?? */
602 buffer->base.bind = PIPE_BIND_TRANSFER_READ | bind_flags;
603 buffer->base.usage = PIPE_USAGE_IMMUTABLE;
604 buffer->base.flags = 0;
605 buffer->base.width0 = bytes;
606 buffer->base.height0 = 1;
607 buffer->base.depth0 = 1;
608 buffer->userBuffer = TRUE;
609 buffer->data = ptr;
610
611 return &buffer->base;
612 }
613
614
615
616
617 void
618 cell_init_screen_texture_funcs(struct pipe_screen *screen)
619 {
620 screen->resource_create = cell_resource_create;
621 screen->resource_destroy = cell_resource_destroy;
622 screen->resource_from_handle = cell_resource_from_handle;
623 screen->resource_get_handle = cell_resource_get_handle;
624 screen->user_buffer_create = cell_user_buffer_create;
625
626 screen->get_tex_surface = cell_get_tex_surface;
627 screen->tex_surface_destroy = cell_tex_surface_destroy;
628
629 screen->flush_frontbuffer = cell_flush_frontbuffer;
630 }
631
632 void
633 cell_init_transfer_funcs(struct cell_context *cell)
634 {
635 cell->pipe.get_transfer = cell_get_transfer;
636 cell->pipe.transfer_destroy = cell_transfer_destroy;
637 cell->pipe.transfer_map = cell_transfer_map;
638 cell->pipe.transfer_unmap = cell_transfer_unmap;
639
640 cell->pipe.transfer_flush_region = u_default_transfer_flush_region;
641 cell->pipe.transfer_inline_write = u_default_transfer_inline_write;
642 }