Tizen 2.0 Release
[profile/ivi/osmesa.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 #include "util/u_transfer.h"
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_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_screen *scrn, struct pipe_resource *pt)
159 {
160    struct cell_screen *screen = cell_screen(scrn);
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_create_surface(struct pipe_context *ctx,
308                     struct pipe_resource *pt,
309                     const struct pipe_surface *surf_tmpl)
310 {
311    struct cell_resource *ct = cell_resource(pt);
312    struct pipe_surface *ps;
313
314    assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer);
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 = surf_tmpl->format;
320       ps->context = ctx;
321       ps->width = u_minify(pt->width0, surf_tmpl->u.tex.level);
322       ps->height = u_minify(pt->height0, surf_tmpl->u.tex.level);
323       /* XXX may need to override usage flags (see sp_texture.c) */
324       ps->usage = surf_tmpl->usage;
325       ps->u.tex.level = surf_tmpl->u.tex.level;
326       ps->u.tex.first_layer = surf_tmpl->u.tex.first_layer;
327       ps->u.tex.last_layer = surf_tmpl->u.tex.last_layer;
328    }
329    return ps;
330 }
331
332
333 static void 
334 cell_surface_destroy(struct pipe_context *ctx, struct pipe_surface *surf)
335 {
336    pipe_resource_reference(&surf->texture, NULL);
337    FREE(surf);
338 }
339
340
341 /**
342  * Create new pipe_transfer object.
343  * This is used by the user to put tex data into a texture (and get it
344  * back out for glGetTexImage).
345  */
346 static struct pipe_transfer *
347 cell_get_transfer(struct pipe_context *ctx,
348                   struct pipe_resource *resource,
349                   unsigned level,
350                   unsigned usage,
351                   const struct pipe_box *box)
352 {
353    struct cell_resource *ct = cell_resource(resource);
354    struct cell_transfer *ctrans;
355    enum pipe_format format = resource->format;
356
357    assert(resource);
358    assert(level <= resource->last_level);
359
360    /* make sure the requested region is in the image bounds */
361    assert(box->x + box->width <= u_minify(resource->width0, level));
362    assert(box->y + box->height <= u_minify(resource->height0, level));
363    assert(box->z + box->depth <= (u_minify(resource->depth0, level) + resource->array_size - 1));
364
365    ctrans = CALLOC_STRUCT(cell_transfer);
366    if (ctrans) {
367       struct pipe_transfer *pt = &ctrans->base;
368       pipe_resource_reference(&pt->resource, resource);
369       pt->level = level;
370       pt->usage = usage;
371       pt->box = *box;
372       pt->stride = ct->stride[level];
373
374       ctrans->offset = ct->level_offset[level];
375
376       if (resource->target == PIPE_TEXTURE_CUBE || resource->target == PIPE_TEXTURE_3D) {
377          unsigned h_tile = align(u_minify(resource->height0, level), TILE_SIZE);
378          ctrans->offset += box->z * util_format_get_nblocksy(format, h_tile) * pt->stride;
379       }
380       else {
381          assert(box->z == 0);
382       }
383
384       return pt;
385    }
386    return NULL;
387 }
388
389
390 static void 
391 cell_transfer_destroy(struct pipe_context *ctx, struct pipe_transfer *t)
392 {
393    struct cell_transfer *transfer = cell_transfer(t);
394    /* Effectively do the texture_update work here - if texture images
395     * needed post-processing to put them into hardware layout, this is
396     * where it would happen.  For cell, nothing to do.
397     */
398    assert (transfer->base.resource);
399    pipe_resource_reference(&transfer->base.resource, NULL);
400    FREE(transfer);
401 }
402
403
404 /**
405  * Return pointer to texture image data in linear layout.
406  */
407 static void *
408 cell_transfer_map(struct pipe_context *ctx, struct pipe_transfer *transfer)
409 {
410    struct cell_transfer *ctrans = cell_transfer(transfer);
411    struct pipe_resource *pt = transfer->resource;
412    struct cell_resource *ct = cell_resource(pt);
413
414    assert(transfer->resource);
415
416    if (ct->mapped == NULL) {
417       ct->mapped = ct->data;
418    }
419
420
421    /* Better test would be resource->is_linear
422     */
423    if (transfer->resource->target != PIPE_BUFFER) {
424       const uint level = ctrans->base.level;
425       const uint texWidth = u_minify(pt->width0, level);
426       const uint texHeight = u_minify(pt->height0, level);
427       unsigned size;
428
429
430       /*
431        * Create a buffer of ordinary memory for the linear texture.
432        * This is the memory that the user will read/write.
433        */
434       size = (util_format_get_stride(pt->format, align(texWidth, TILE_SIZE)) *
435               util_format_get_nblocksy(pt->format, align(texHeight, TILE_SIZE)));
436
437       ctrans->map = align_malloc(size, 16);
438       if (!ctrans->map)
439          return NULL; /* out of memory */
440
441       if (transfer->usage & PIPE_TRANSFER_READ) {
442          /* Textures always stored twiddled, need to untwiddle the
443           * texture to make a linear version.
444           */
445          const uint bpp = util_format_get_blocksize(ct->base.format);
446          if (bpp == 4) {
447             const uint *src = (uint *) (ct->mapped + ctrans->offset);
448             uint *dst = ctrans->map;
449             untwiddle_image_uint(texWidth, texHeight, TILE_SIZE,
450                                  dst, transfer->stride, src);
451          }
452          else {
453             // xxx fix
454          }
455       }
456    }
457    else {
458       unsigned stride = transfer->stride;
459       enum pipe_format format = pt->format;
460       unsigned blocksize = util_format_get_blocksize(format);
461
462       ctrans->map = (ct->mapped + 
463                      ctrans->offset +
464                      ctrans->base.box.y / util_format_get_blockheight(format) * stride +
465                      ctrans->base.box.x / util_format_get_blockwidth(format) * blocksize);
466    }
467
468
469    return ctrans->map;
470 }
471
472
473 /**
474  * Called when user is done reading/writing texture data.
475  * If new data was written, this is where we convert the linear data
476  * to tiled data.
477  */
478 static void
479 cell_transfer_unmap(struct pipe_context *ctx,
480                     struct pipe_transfer *transfer)
481 {
482    struct cell_transfer *ctrans = cell_transfer(transfer);
483    struct pipe_resource *pt = transfer->resource;
484    struct cell_resource *ct = cell_resource(pt);
485    const uint level = ctrans->base.level;
486    const uint texWidth = u_minify(pt->width0, level);
487    const uint texHeight = u_minify(pt->height0, level);
488    const uint stride = ct->stride[level];
489
490    if (!ct->mapped) {
491       assert(0);
492       return;
493    }
494
495    if (pt->target != PIPE_BUFFER) {
496       if (transfer->usage & PIPE_TRANSFER_WRITE) {
497          /* The user wrote new texture data into the mapped buffer.
498           * We need to convert the new linear data into the twiddled/tiled format.
499           */
500          const uint bpp = util_format_get_blocksize(ct->base.format);
501          if (bpp == 4) {
502             const uint *src = ctrans->map;
503             uint *dst = (uint *) (ct->mapped + ctrans->offset);
504             twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src);
505          }
506          else {
507             // xxx fix
508          }
509       }
510       
511       align_free(ctrans->map);
512    }
513    else {
514       /* nothing to do */
515    }
516
517    ctrans->map = NULL;
518 }
519
520
521
522 /* This used to be overriden by the co-state tracker, but really needs
523  * to be active with sw_winsys.
524  *
525  * Contrasting with llvmpipe and softpipe, this is the only place
526  * where we use the ct->dt display target in any real sense.
527  *
528  * Basically just untwiddle our local data into the linear
529  * displaytarget.
530  */
531 static void
532 cell_flush_frontbuffer(struct pipe_screen *_screen,
533                        struct pipe_resource *resource,
534                        unsigned level, unsigned layer,
535                        void *context_private)
536 {
537    struct cell_screen *screen = cell_screen(_screen);
538    struct sw_winsys *winsys = screen->winsys;
539    struct cell_resource *ct = cell_resource(resource);
540
541    if (!ct->dt)
542       return;
543
544    /* Need to untwiddle from our internal representation here:
545     */
546    {
547       unsigned *map = winsys->displaytarget_map(winsys, ct->dt,
548                                                 (PIPE_TRANSFER_READ |
549                                                  PIPE_TRANSFER_WRITE));
550       unsigned *src = (unsigned *)(ct->data + ct->level_offset[level]);
551
552       untwiddle_image_uint(u_minify(resource->width0, level),
553                            u_minify(resource->height0, level),
554                            TILE_SIZE,
555                            map,
556                            ct->dt_stride,
557                            src);
558
559       winsys->displaytarget_unmap(winsys, ct->dt);
560    }
561
562    winsys->displaytarget_display(winsys, ct->dt, context_private);
563 }
564
565
566
567 /**
568  * Create buffer which wraps user-space data.
569  */
570 static struct pipe_resource *
571 cell_user_buffer_create(struct pipe_screen *screen,
572                             void *ptr,
573                             unsigned bytes,
574                             unsigned bind_flags)
575 {
576    struct cell_resource *buffer;
577
578    buffer = CALLOC_STRUCT(cell_resource);
579    if(!buffer)
580       return NULL;
581
582    pipe_reference_init(&buffer->base.reference, 1);
583    buffer->base.screen = screen;
584    buffer->base.format = PIPE_FORMAT_R8_UNORM; /* ?? */
585    buffer->base.bind = PIPE_BIND_TRANSFER_READ | bind_flags;
586    buffer->base.usage = PIPE_USAGE_IMMUTABLE;
587    buffer->base.flags = 0;
588    buffer->base.width0 = bytes;
589    buffer->base.height0 = 1;
590    buffer->base.depth0 = 1;
591    buffer->base.array_size = 1;
592    buffer->userBuffer = TRUE;
593    buffer->data = ptr;
594
595    return &buffer->base;
596 }
597
598
599 static struct pipe_resource *
600 cell_resource_from_handle(struct pipe_screen *screen,
601                           const struct pipe_resource *templat,
602                           struct winsys_handle *handle)
603 {
604    /* XXX todo */
605    return NULL;
606 }
607
608
609 static boolean 
610 cell_resource_get_handle(struct pipe_screen *scree,
611                          struct pipe_resource *tex,
612                          struct winsys_handle *handle)
613 {
614    /* XXX todo */
615    return FALSE;
616 }
617
618
619 void
620 cell_init_screen_texture_funcs(struct pipe_screen *screen)
621 {
622    screen->resource_create = cell_resource_create;
623    screen->resource_destroy = cell_resource_destroy;
624    screen->resource_from_handle = cell_resource_from_handle;
625    screen->resource_get_handle = cell_resource_get_handle;
626    screen->user_buffer_create = cell_user_buffer_create;
627
628    screen->flush_frontbuffer = cell_flush_frontbuffer;
629 }
630
631 void
632 cell_init_texture_transfer_funcs(struct cell_context *cell)
633 {
634    cell->pipe.get_transfer = cell_get_transfer;
635    cell->pipe.transfer_destroy = cell_transfer_destroy;
636    cell->pipe.transfer_map = cell_transfer_map;
637    cell->pipe.transfer_unmap = cell_transfer_unmap;
638
639    cell->pipe.transfer_flush_region = u_default_transfer_flush_region;
640    cell->pipe.transfer_inline_write = u_default_transfer_inline_write;
641
642    cell->pipe.create_surface = cell_create_surface;
643    cell->pipe.surface_destroy = cell_surface_destroy;
644 }