cell: Added support for untwiddling textures during glReadPixels. This allows glRea...
authorJonathan White <jwhite@tungstengraphics.com>
Mon, 27 Oct 2008 22:29:20 +0000 (16:29 -0600)
committerJonathan White <jwhite@tungstengraphics.com>
Mon, 27 Oct 2008 22:31:22 +0000 (16:31 -0600)
src/gallium/drivers/cell/ppu/cell_texture.c
src/gallium/drivers/cell/ppu/cell_texture.h

index 9ac2f3bbb9689c4328674c13b093ab35612c0e76..8ae4439f6cce6c4d32dcd1ad8128210ceae2a901 100644 (file)
@@ -41,7 +41,6 @@
 #include "cell_state.h"
 #include "cell_texture.h"
 
-
 /* Simple, maximally packed layout.
  */
 
@@ -210,6 +209,87 @@ twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
    }
 }
 
+/**
+ * For Cell.  Basically, rearrange the pixels/quads from this layout:
+ *  +--+--+--+--+
+ *  |p0|p1|p2|p3|....
+ *  +--+--+--+--+
+ *
+ * to this layout:
+ *  +--+--+
+ *  |p0|p1|....
+ *  +--+--+
+ *  |p2|p3|
+ *  +--+--+
+ */
+static void
+twiddle_tile(const uint *tileIn, uint *tileOut)
+{
+   int y, x;
+
+   for (y = 0; y < TILE_SIZE; y+=2) {
+      for (x = 0; x < TILE_SIZE; x+=2) {
+         int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
+         tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
+         tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
+         tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
+         tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
+      }
+   }
+}
+
+/**
+ * Convert image from tiled layout to linear layout.  4-byte pixels.
+ */
+static void
+untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
+                     uint src_stride, const uint *src)
+{
+   const uint tile_size2 = tile_size * tile_size;
+   const uint h_t = (h + tile_size - 1) / tile_size;
+   const uint w_t = (w + tile_size - 1) / tile_size;
+   uint *tile_buf;
+
+   uint it, jt;  /* tile counters */
+   uint i, j;    /* intra-tile counters */
+
+   src_stride /= 4; /* convert from bytes to pixels */
+
+   tile_buf = align_malloc(tile_size * tile_size * 4, 16);
+   
+   /* loop over src tiles */
+   for (it = 0; it < h_t; it++) {
+      for (jt = 0; jt < w_t; jt++) {
+         /* start of src tile: */
+         const uint *tsrc = src + (it * w_t + jt) * tile_size2;
+         
+         twiddle_tile(tsrc, tile_buf);
+         tsrc = tile_buf;
+
+         /* compute size of this tile (may be smaller than tile_size) */
+         /* XXX note: a compiler bug was found here. That's why the code
+          * looks as it does.
+          */
+         uint tile_width = w - jt * tile_size;
+         tile_width = MIN2(tile_width, tile_size);
+         uint tile_height = h - it * tile_size;
+         tile_height = MIN2(tile_height, tile_size);
+
+         /* loop over texels in the tile */
+         for (i = 0; i < tile_height; i++) {
+            for (j = 0; j < tile_width; j++) {
+               uint dsti = it * tile_size + i;
+               uint dstj = jt * tile_size + j;
+               ASSERT(dsti < h);
+               ASSERT(dstj < w);
+               dst[dsti * src_stride + dstj] = tsrc[i * tile_size + j];
+            }
+         }
+      }
+   }
+
+   align_free(tile_buf);
+}
 
 /**
  * Convert linear texture image data to tiled format for SPU usage.
@@ -260,6 +340,47 @@ cell_twiddle_texture(struct pipe_screen *screen,
    pipe_buffer_unmap(screen, surface->buffer);
 }
 
+/**
+ * Convert SPU tiled texture image data to linear format for app usage.
+ */
+static void
+cell_untwiddle_texture(struct pipe_screen *screen,
+                     struct pipe_surface *surface)
+{
+   struct cell_texture *ct = cell_texture(surface->texture);
+   const uint level = surface->level;
+   const uint texWidth = ct->base.width[level];
+   const uint texHeight = ct->base.height[level];
+   const void *map = pipe_buffer_map(screen, surface->buffer,
+                                     PIPE_BUFFER_USAGE_CPU_READ);
+   const uint *src = (const uint *) ((const ubyte *) map + surface->offset);
+
+   switch (ct->base.format) {
+   case PIPE_FORMAT_A8R8G8B8_UNORM:
+      {
+         int numFaces = ct->base.target == PIPE_TEXTURE_CUBE ? 6 : 1;
+         int offset = surface->stride * texHeight * 4 * surface->face;
+         uint *dst;
+
+         if (!ct->untiled_data[level]) {
+            ct->untiled_data[level] =
+               align_malloc(surface->stride * texHeight * 4 * numFaces, 16);
+         }
+
+         dst = (uint *) ((ubyte *) ct->untiled_data[level] + offset);
+
+         untwiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst,
+                              surface->stride, src);
+      }
+      break;
+   default:
+      printf("Cell: untwiddle unsupported texture format\n");
+      ;
+   }
+
+   pipe_buffer_unmap(screen, surface->buffer);
+}
+
 
 static struct pipe_surface *
 cell_get_tex_surface(struct pipe_screen *screen,
@@ -294,13 +415,18 @@ cell_get_tex_surface(struct pipe_screen *screen,
       ps->zslice = zslice;
 
       if (pt->target == PIPE_TEXTURE_CUBE || pt->target == PIPE_TEXTURE_3D) {
-        ps->offset += ((pt->target == PIPE_TEXTURE_CUBE) ? face : zslice) *
-                      ps->nblocksy *
-                      ps->stride;
+                ps->offset += ((pt->target == PIPE_TEXTURE_CUBE) ? face : zslice) *
+                     ps->nblocksy *
+                     ps->stride;
       }
       else {
-        assert(face == 0);
-        assert(zslice == 0);
+         assert(face == 0);
+         assert(zslice == 0);
+      }
+
+      if (ps->usage & PIPE_BUFFER_USAGE_CPU_READ) {
+         /* convert from tiled to linear layout */
+         cell_untwiddle_texture(screen, ps);
       }
    }
    return ps;
@@ -311,6 +437,13 @@ static void
 cell_tex_surface_release(struct pipe_screen *screen, 
                          struct pipe_surface **s)
 {
+   struct cell_texture *ct = cell_texture((*s)->texture);
+   const uint level = (*s)->level;
+
+   if ((*s)->usage & PIPE_BUFFER_USAGE_CPU_READ) {
+      align_free(ct->untiled_data[level]);
+   }
+
    /* XXX if done rendering to teximage, re-tile */
 
    pipe_texture_reference(&(*s)->texture, NULL); 
@@ -325,6 +458,10 @@ cell_surface_map(struct pipe_screen *screen,
                  unsigned flags)
 {
    ubyte *map;
+   struct cell_texture *ct = cell_texture(surface->texture);
+   const uint level = surface->level;
+
+   assert(ct);
 
    if (flags & ~surface->usage) {
       assert(0);
@@ -335,7 +472,14 @@ cell_surface_map(struct pipe_screen *screen,
    if (map == NULL)
       return NULL;
    else
-      return (void *) (map + surface->offset);
+   {
+      if (surface->usage & PIPE_BUFFER_USAGE_CPU_READ) {
+         return (void *) ((ubyte *) ct->untiled_data[level] + surface->offset);
+      }
+      else {
+         return (void *) (map + surface->offset);
+      }
+   }
 }
 
 
index 2f5fe0dd1b75104ed4f606e7d248670ebc78aa2f..7018b0c9bf75862d5c3bad78837e85c45fd82b56 100644 (file)
@@ -52,6 +52,7 @@ struct cell_texture
    struct pipe_buffer *tiled_buffer[CELL_MAX_TEXTURE_LEVELS];
    /** Mapped, tiled texture data */
    void *tiled_mapped[CELL_MAX_TEXTURE_LEVELS];
+   void *untiled_data[CELL_MAX_TEXTURE_LEVELS];
 };