Merge branch 'mesa_7_5_branch' into mesa_7_6_branch
[mesa.git] / src / gallium / drivers / llvmpipe / lp_tile_cache.c
index 08c48e4d1c10b6106b88ded1020fbf73c556ef7f..143afec3d35313da6335383dc518980854ebea50 100644 (file)
 
 #include "pipe/p_inlines.h"
 #include "util/u_memory.h"
+#include "util/u_math.h"
 #include "util/u_tile.h"
+#include "util/u_rect.h"
 #include "lp_context.h"
 #include "lp_surface.h"
 #include "lp_texture.h"
+#include "lp_tile_soa.h"
 #include "lp_tile_cache.h"
 
-#define NUM_ENTRIES 50
-
-
-/** XXX move these */
-#define MAX_WIDTH 2048
-#define MAX_HEIGHT 2048
-
-
-struct llvmpipe_tile_cache
-{
-   struct pipe_screen *screen;
-   struct pipe_surface *surface;  /**< the surface we're caching */
-   struct pipe_transfer *transfer;
-   void *transfer_map;
-   struct pipe_texture *texture;  /**< if caching a texture */
-   struct llvmpipe_cached_tile entries[NUM_ENTRIES];
-   uint clear_flags[(MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32];
-   float clear_color[4];  /**< for color bufs */
-   uint clear_val;        /**< for z+stencil, or packed color clear value */
-   boolean depth_stencil; /**< Is the surface a depth/stencil format? */
-
-   struct pipe_transfer *tex_trans;
-   void *tex_trans_map;
-   int tex_face, tex_level, tex_z;
-
-   struct llvmpipe_cached_tile tile;  /**< scratch tile for clears */
-};
-
-
-/**
- * Return the position in the cache for the tile that contains win pos (x,y).
- * We currently use a direct mapped cache so this is like a hack key.
- * At some point we should investige something more sophisticated, like
- * a LRU replacement policy.
- */
-#define CACHE_POS(x, y) \
-   (((x) / TILE_SIZE + ((y) / TILE_SIZE) * 5) % NUM_ENTRIES)
-
-
-
-/**
- * Is the tile at (x,y) in cleared state?
- */
-static INLINE uint
-is_clear_flag_set(const uint *bitvec, int x, int y)
-{
-   int pos, bit;
-   x /= TILE_SIZE;
-   y /= TILE_SIZE;
-   pos = y * (MAX_WIDTH / TILE_SIZE) + x;
-   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
-   bit = bitvec[pos / 32] & (1 << (pos & 31));
-   return bit;
-}
-   
-
-/**
- * Mark the tile at (x,y) as not cleared.
- */
-static INLINE void
-clear_clear_flag(uint *bitvec, int x, int y)
-{
-   int pos;
-   x /= TILE_SIZE;
-   y /= TILE_SIZE;
-   pos = y * (MAX_WIDTH / TILE_SIZE) + x;
-   assert(pos / 32 < (MAX_WIDTH / TILE_SIZE) * (MAX_HEIGHT / TILE_SIZE) / 32);
-   bitvec[pos / 32] &= ~(1 << (pos & 31));
-}
-   
 
 struct llvmpipe_tile_cache *
 lp_create_tile_cache( struct pipe_screen *screen )
 {
    struct llvmpipe_tile_cache *tc;
-   uint pos;
 
    tc = CALLOC_STRUCT( llvmpipe_tile_cache );
-   if (tc) {
-      tc->screen = screen;
-      for (pos = 0; pos < NUM_ENTRIES; pos++) {
-         tc->entries[pos].x =
-         tc->entries[pos].y = -1;
-      }
-   }
+   if(!tc)
+      return NULL;
+
+   tc->screen = screen;
+
    return tc;
 }
 
@@ -133,19 +63,21 @@ void
 lp_destroy_tile_cache(struct llvmpipe_tile_cache *tc)
 {
    struct pipe_screen *screen;
-   uint pos;
+   unsigned x, y;
+
+   for (y = 0; y < MAX_HEIGHT; y += TILE_SIZE) {
+      for (x = 0; x < MAX_WIDTH; x += TILE_SIZE) {
+         struct llvmpipe_cached_tile *tile = &tc->entries[y/TILE_SIZE][x/TILE_SIZE];
 
-   for (pos = 0; pos < NUM_ENTRIES; pos++) {
-      /*assert(tc->entries[pos].x < 0);*/
+         if(tile->color)
+            align_free(tile->color);
+      }
    }
+
    if (tc->transfer) {
       screen = tc->transfer->texture->screen;
       screen->tex_transfer_destroy(tc->transfer);
    }
-   if (tc->tex_trans) {
-      screen = tc->tex_trans->texture->screen;
-      screen->tex_transfer_destroy(tc->tex_trans);
-   }
 
    FREE( tc );
 }
@@ -158,8 +90,6 @@ void
 lp_tile_cache_set_surface(struct llvmpipe_tile_cache *tc,
                           struct pipe_surface *ps)
 {
-   assert(!tc->texture);
-
    if (tc->transfer) {
       struct pipe_screen *screen = tc->transfer->texture->screen;
 
@@ -179,19 +109,23 @@ lp_tile_cache_set_surface(struct llvmpipe_tile_cache *tc,
 
    if (ps) {
       struct pipe_screen *screen = ps->texture->screen;
+      unsigned x, y;
 
       tc->transfer = screen->get_tex_transfer(screen, ps->texture, ps->face,
                                               ps->level, ps->zslice,
                                               PIPE_TRANSFER_READ_WRITE,
                                               0, 0, ps->width, ps->height);
 
-      tc->depth_stencil = (ps->format == PIPE_FORMAT_S8Z24_UNORM ||
-                           ps->format == PIPE_FORMAT_X8Z24_UNORM ||
-                           ps->format == PIPE_FORMAT_Z24S8_UNORM ||
-                           ps->format == PIPE_FORMAT_Z24X8_UNORM ||
-                           ps->format == PIPE_FORMAT_Z16_UNORM ||
-                           ps->format == PIPE_FORMAT_Z32_UNORM ||
-                           ps->format == PIPE_FORMAT_S8_UNORM);
+      for (y = 0; y < ps->height; y += TILE_SIZE) {
+         for (x = 0; x < ps->width; x += TILE_SIZE) {
+            struct llvmpipe_cached_tile *tile = &tc->entries[y/TILE_SIZE][x/TILE_SIZE];
+
+            tile->status = LP_TILE_STATUS_UNDEFINED;
+
+            if(!tile->color)
+               tile->color = align_malloc( TILE_SIZE*TILE_SIZE*NUM_CHANNELS, 16 );
+         }
+      }
    }
 }
 
@@ -211,9 +145,6 @@ lp_tile_cache_map_transfers(struct llvmpipe_tile_cache *tc)
 {
    if (tc->transfer && !tc->transfer_map)
       tc->transfer_map = tc->screen->transfer_map(tc->screen, tc->transfer);
-
-   if (tc->tex_trans && !tc->tex_trans_map)
-      tc->tex_trans_map = tc->screen->transfer_map(tc->screen, tc->tex_trans);
 }
 
 
@@ -224,154 +155,28 @@ lp_tile_cache_unmap_transfers(struct llvmpipe_tile_cache *tc)
       tc->screen->transfer_unmap(tc->screen, tc->transfer);
       tc->transfer_map = NULL;
    }
-
-   if (tc->tex_trans_map) {
-      tc->screen->transfer_unmap(tc->screen, tc->tex_trans);
-      tc->tex_trans_map = NULL;
-   }
-}
-
-
-/**
- * Specify the texture to cache.
- */
-void
-lp_tile_cache_set_texture(struct llvmpipe_tile_cache *tc,
-                          struct pipe_texture *texture)
-{
-   uint i;
-
-   assert(!tc->transfer);
-
-   pipe_texture_reference(&tc->texture, texture);
-
-   if (tc->tex_trans) {
-      struct pipe_screen *screen = tc->tex_trans->texture->screen;
-
-      if (tc->tex_trans_map) {
-         screen->transfer_unmap(screen, tc->tex_trans);
-         tc->tex_trans_map = NULL;
-      }
-
-      screen->tex_transfer_destroy(tc->tex_trans);
-      tc->tex_trans = NULL;
-   }
-
-   /* mark as entries as invalid/empty */
-   /* XXX we should try to avoid this when the teximage hasn't changed */
-   for (i = 0; i < NUM_ENTRIES; i++) {
-      tc->entries[i].x = -1;
-   }
-
-   tc->tex_face = -1; /* any invalid value here */
 }
 
 
 /**
- * Set pixels in a tile to the given clear color/value, float.
- */
-static void
-clear_tile_rgba(struct llvmpipe_cached_tile *tile,
-                enum pipe_format format,
-                const float clear_value[4])
-{
-   if (clear_value[0] == 0.0 &&
-       clear_value[1] == 0.0 &&
-       clear_value[2] == 0.0 &&
-       clear_value[3] == 0.0) {
-      memset(tile->data.color, 0, sizeof(tile->data.color));
-   }
-   else {
-      uint i, j;
-      for (i = 0; i < TILE_SIZE; i++) {
-         for (j = 0; j < TILE_SIZE; j++) {
-            tile->data.color[i][j][0] = clear_value[0];
-            tile->data.color[i][j][1] = clear_value[1];
-            tile->data.color[i][j][2] = clear_value[2];
-            tile->data.color[i][j][3] = clear_value[3];
-         }
-      }
-   }
-}
-
-
-/**
- * Set a tile to a solid value/color.
+ * Set a tile to a solid color.
  */
 static void
 clear_tile(struct llvmpipe_cached_tile *tile,
-           enum pipe_format format,
-           uint clear_value)
+           uint8_t clear_color[4])
 {
-   uint i, j;
-
-   switch (pf_get_size(format)) {
-   case 1:
-      memset(tile->data.any, 0, TILE_SIZE * TILE_SIZE);
-      break;
-   case 2:
-      if (clear_value == 0) {
-         memset(tile->data.any, 0, 2 * TILE_SIZE * TILE_SIZE);
-      }
-      else {
-         for (i = 0; i < TILE_SIZE; i++) {
-            for (j = 0; j < TILE_SIZE; j++) {
-               tile->data.depth16[i][j] = (ushort) clear_value;
-            }
-         }
-      }
-      break;
-   case 4:
-      if (clear_value == 0) {
-         memset(tile->data.any, 0, 4 * TILE_SIZE * TILE_SIZE);
-      }
-      else {
-         for (i = 0; i < TILE_SIZE; i++) {
-            for (j = 0; j < TILE_SIZE; j++) {
-               tile->data.color32[i][j] = clear_value;
-            }
-         }
-      }
-      break;
-   default:
-      assert(0);
+   if (clear_color[0] == clear_color[1] &&
+       clear_color[1] == clear_color[2] &&
+       clear_color[2] == clear_color[3]) {
+      memset(tile->color, clear_color[0], TILE_SIZE * TILE_SIZE * 4);
    }
-}
-
-
-/**
- * Actually clear the tiles which were flagged as being in a clear state.
- */
-static void
-lp_tile_cache_flush_clear(struct llvmpipe_tile_cache *tc)
-{
-   struct pipe_transfer *pt = tc->transfer;
-   const uint w = tc->transfer->width;
-   const uint h = tc->transfer->height;
-   uint x, y;
-   uint numCleared = 0;
-
-   /* clear the scratch tile to the clear value */
-   clear_tile(&tc->tile, pt->format, tc->clear_val);
-
-   /* push the tile to all positions marked as clear */
-   for (y = 0; y < h; y += TILE_SIZE) {
-      for (x = 0; x < w; x += TILE_SIZE) {
-         if (is_clear_flag_set(tc->clear_flags, x, y)) {
-            pipe_put_tile_raw(pt,
-                              x, y, TILE_SIZE, TILE_SIZE,
-                              tc->tile.data.color32, 0/*STRIDE*/);
-
-            /* do this? */
-            clear_clear_flag(tc->clear_flags, x, y);
-
-            numCleared++;
-         }
-      }
+   else {
+      uint x, y, chan;
+      for (y = 0; y < TILE_SIZE; y++)
+         for (x = 0; x < TILE_SIZE; x++)
+            for (chan = 0; chan < 4; ++chan)
+               TILE_PIXEL(tile->color, x, y, chan) = clear_color[chan];
    }
-#if 0
-   debug_printf("num cleared: %u\n", numCleared);
-#endif
 }
 
 
@@ -383,209 +188,85 @@ void
 lp_flush_tile_cache(struct llvmpipe_tile_cache *tc)
 {
    struct pipe_transfer *pt = tc->transfer;
-   int inuse = 0, pos;
-
-   if (pt) {
-      /* caching a drawing transfer */
-      for (pos = 0; pos < NUM_ENTRIES; pos++) {
-         struct llvmpipe_cached_tile *tile = tc->entries + pos;
-         if (tile->x >= 0) {
-            if (tc->depth_stencil) {
-               pipe_put_tile_raw(pt,
-                                 tile->x, tile->y, TILE_SIZE, TILE_SIZE,
-                                 tile->data.depth32, 0/*STRIDE*/);
-            }
-            else {
-               pipe_put_tile_rgba(pt,
-                                  tile->x, tile->y, TILE_SIZE, TILE_SIZE,
-                                  (float *) tile->data.color);
-            }
-            tile->x = tile->y = -1;  /* mark as empty */
-            inuse++;
-         }
-      }
-
-#if TILE_CLEAR_OPTIMIZATION
-      lp_tile_cache_flush_clear(tc);
-#endif
-   }
-   else if (tc->texture) {
-      /* caching a texture, mark all entries as empty */
-      for (pos = 0; pos < NUM_ENTRIES; pos++) {
-         tc->entries[pos].x = -1;
-      }
-      tc->tex_face = -1;
-   }
+   unsigned x, y;
 
-#if 0
-   debug_printf("flushed tiles in use: %d\n", inuse);
-#endif
-}
-
-
-/**
- * Get a tile from the cache.
- * \param x, y  position of tile, in pixels
- */
-struct llvmpipe_cached_tile *
-lp_get_cached_tile(struct llvmpipe_tile_cache *tc, int x, int y)
-{
-   struct pipe_transfer *pt = tc->transfer;
+   if(!pt)
+      return;
 
-   /* tile pos in framebuffer: */
-   const int tile_x = x & ~(TILE_SIZE - 1);
-   const int tile_y = y & ~(TILE_SIZE - 1);
-
-   /* cache pos/entry: */
-   const int pos = CACHE_POS(x, y);
-   struct llvmpipe_cached_tile *tile = tc->entries + pos;
-
-   if (tile_x != tile->x ||
-       tile_y != tile->y) {
-
-      if (tile->x != -1) {
-         /* put dirty tile back in framebuffer */
-         if (tc->depth_stencil) {
-            pipe_put_tile_raw(pt,
-                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
-                              tile->data.depth32, 0/*STRIDE*/);
-         }
-         else {
-            pipe_put_tile_rgba(pt,
-                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
-                               (float *) tile->data.color);
+   /* push the tile to all positions marked as clear */
+   for (y = 0; y < pt->height; y += TILE_SIZE) {
+      for (x = 0; x < pt->width; x += TILE_SIZE) {
+         struct llvmpipe_cached_tile *tile = &tc->entries[y/TILE_SIZE][x/TILE_SIZE];
+
+         switch(tile->status) {
+         case LP_TILE_STATUS_UNDEFINED:
+            break;
+
+         case LP_TILE_STATUS_CLEAR: {
+            /**
+             * Actually clear the tiles which were flagged as being in a clear state.
+             */
+
+            struct pipe_screen *screen = pt->texture->screen;
+            unsigned tw = TILE_SIZE;
+            unsigned th = TILE_SIZE;
+            void *dst;
+
+            if (pipe_clip_tile(x, y, &tw, &th, pt))
+               continue;
+
+            dst = screen->transfer_map(screen, pt);
+            assert(dst);
+            if(!dst)
+               continue;
+
+            util_fill_rect(dst, &pt->block, pt->stride,
+                           x, y, tw,  th,
+                           tc->clear_val);
+
+            screen->transfer_unmap(screen, pt);
+            break;
          }
-      }
-
-      tile->x = tile_x;
-      tile->y = tile_y;
 
-      if (is_clear_flag_set(tc->clear_flags, x, y)) {
-         /* don't get tile from framebuffer, just clear it */
-         if (tc->depth_stencil) {
-            clear_tile(tile, pt->format, tc->clear_val);
-         }
-         else {
-            clear_tile_rgba(tile, pt->format, tc->clear_color);
-         }
-         clear_clear_flag(tc->clear_flags, x, y);
-      }
-      else {
-         /* get new tile data from transfer */
-         if (tc->depth_stencil) {
-            pipe_get_tile_raw(pt,
-                              tile->x, tile->y, TILE_SIZE, TILE_SIZE,
-                              tile->data.depth32, 0/*STRIDE*/);
-         }
-         else {
-            pipe_get_tile_rgba(pt,
-                               tile->x, tile->y, TILE_SIZE, TILE_SIZE,
-                               (float *) tile->data.color);
+         case LP_TILE_STATUS_DEFINED:
+            lp_put_tile_rgba_soa(pt, x, y, tile->color);
+            break;
          }
       }
    }
-
-   return tile;
 }
 
 
 /**
- * Given the texture face, level, zslice, x and y values, compute
- * the cache entry position/index where we'd hope to find the
- * cached texture tile.
- * This is basically a direct-map cache.
- * XXX There's probably lots of ways in which we can improve this.
- */
-static INLINE uint
-tex_cache_pos(int x, int y, int z, int face, int level)
-{
-   uint entry = x + y * 9 + z * 3 + face + level * 7;
-   return entry % NUM_ENTRIES;
-}
-
-
-/**
- * Similar to lp_get_cached_tile() but for textures.
- * Tiles are read-only and indexed with more params.
+ * Get a tile from the cache.
+ * \param x, y  position of tile, in pixels
  */
-const struct llvmpipe_cached_tile *
-lp_get_cached_tile_tex(struct llvmpipe_tile_cache *tc, 
-                       int x, int y, int z,
-                       int face, int level)
+void *
+lp_get_cached_tile(struct llvmpipe_tile_cache *tc,
+                   unsigned x, unsigned y )
 {
-   struct pipe_screen *screen = tc->screen;
-   /* tile pos in framebuffer: */
-   const int tile_x = x & ~(TILE_SIZE - 1);
-   const int tile_y = y & ~(TILE_SIZE - 1);
-   /* cache pos/entry: */
-   const uint pos = tex_cache_pos(x / TILE_SIZE, y / TILE_SIZE, z,
-                                  face, level);
-   struct llvmpipe_cached_tile *tile = tc->entries + pos;
-
-   if (tc->texture) {
-      struct llvmpipe_texture *lpt = llvmpipe_texture(tc->texture);
-      if (lpt->modified) {
-         /* texture was modified, invalidate all cached tiles */
-         uint p;
-         for (p = 0; p < NUM_ENTRIES; p++) {
-            tile = tc->entries + p;
-            tile->x = -1;
-         }
-         lpt->modified = FALSE;
-      }
-   }
-
-   if (tile_x != tile->x ||
-       tile_y != tile->y ||
-       z != tile->z ||
-       face != tile->face ||
-       level != tile->level) {
-      /* cache miss */
-
-#if 0
-      printf("miss at %u  x=%d y=%d z=%d face=%d level=%d\n", pos,
-             x/TILE_SIZE, y/TILE_SIZE, z, face, level);
-#endif
-      /* check if we need to get a new transfer */
-      if (!tc->tex_trans ||
-          tc->tex_face != face ||
-          tc->tex_level != level ||
-          tc->tex_z != z) {
-         /* get new transfer (view into texture) */
-
-         if (tc->tex_trans) {
-            if (tc->tex_trans_map) {
-               tc->screen->transfer_unmap(tc->screen, tc->tex_trans);
-               tc->tex_trans_map = NULL;
-            }
-
-            screen->tex_transfer_destroy(tc->tex_trans);
-            tc->tex_trans = NULL;
-         }
-
-         tc->tex_trans = screen->get_tex_transfer(screen, tc->texture, face, level, z, 
-                                                  PIPE_TRANSFER_READ, 0, 0,
-                                                  tc->texture->width[level],
-                                                  tc->texture->height[level]);
-         tc->tex_trans_map = screen->transfer_map(screen, tc->tex_trans);
+   struct llvmpipe_cached_tile *tile = &tc->entries[y/TILE_SIZE][x/TILE_SIZE];
+   struct pipe_transfer *pt = tc->transfer;
+   
+   switch(tile->status) {
+   case LP_TILE_STATUS_CLEAR:
+      /* don't get tile from framebuffer, just clear it */
+      clear_tile(tile, tc->clear_color);
+      tile->status = LP_TILE_STATUS_DEFINED;
+      break;
 
-         tc->tex_face = face;
-         tc->tex_level = level;
-         tc->tex_z = z;
-      }
+   case LP_TILE_STATUS_UNDEFINED:
+      /* get new tile data from transfer */
+      lp_get_tile_rgba_soa(pt, x, y, tile->color);
+      tile->status = LP_TILE_STATUS_DEFINED;
+      break;
 
-      /* get tile from the transfer (view into texture) */
-      pipe_get_tile_rgba(tc->tex_trans,
-                         tile_x, tile_y, TILE_SIZE, TILE_SIZE,
-                         (float *) tile->data.color);
-      tile->x = tile_x;
-      tile->y = tile_y;
-      tile->z = z;
-      tile->face = face;
-      tile->level = level;
+   case LP_TILE_STATUS_DEFINED:
+      /* nothing to do */
+      break;
    }
 
-   return tile;
+   return tile->color;
 }
 
 
@@ -598,25 +279,21 @@ void
 lp_tile_cache_clear(struct llvmpipe_tile_cache *tc, const float *rgba,
                     uint clearValue)
 {
-   uint pos;
+   struct pipe_transfer *pt = tc->transfer;
+   const unsigned w = pt->width;
+   const unsigned h = pt->height;
+   unsigned x, y, chan;
 
-   tc->clear_color[0] = rgba[0];
-   tc->clear_color[1] = rgba[1];
-   tc->clear_color[2] = rgba[2];
-   tc->clear_color[3] = rgba[3];
+   for(chan = 0; chan < 4; ++chan)
+      tc->clear_color[chan] = float_to_ubyte(rgba[chan]);
 
    tc->clear_val = clearValue;
 
-#if TILE_CLEAR_OPTIMIZATION
-   /* set flags to indicate all the tiles are cleared */
-   memset(tc->clear_flags, 255, sizeof(tc->clear_flags));
-#else
-   /* disable the optimization */
-   memset(tc->clear_flags, 0, sizeof(tc->clear_flags));
-#endif
-
-   for (pos = 0; pos < NUM_ENTRIES; pos++) {
-      struct llvmpipe_cached_tile *tile = tc->entries + pos;
-      tile->x = tile->y = -1;
+   /* push the tile to all positions marked as clear */
+   for (y = 0; y < h; y += TILE_SIZE) {
+      for (x = 0; x < w; x += TILE_SIZE) {
+         struct llvmpipe_cached_tile *tile = &tc->entries[y/TILE_SIZE][x/TILE_SIZE];
+         tile->status = LP_TILE_STATUS_CLEAR;
+      }
    }
 }