gallium: implement a glBitmap cache
authorBrian <brian.paul@tungstengraphics.com>
Fri, 28 Mar 2008 20:53:47 +0000 (14:53 -0600)
committerBrian <brian.paul@tungstengraphics.com>
Fri, 28 Mar 2008 20:53:47 +0000 (14:53 -0600)
The bitmap cache attempts to accumulate a series of glBitmap calls in a
buffer to effectively render a whole bunch of bitmaps at once.

The cache can be disabled, if needed, by setting UseBitmapCache=GL_FALSE.

src/mesa/state_tracker/st_atom.c
src/mesa/state_tracker/st_cb_bitmap.c
src/mesa/state_tracker/st_cb_bitmap.h
src/mesa/state_tracker/st_cb_flush.c
src/mesa/state_tracker/st_context.c
src/mesa/state_tracker/st_context.h

index 0e22a2fa6e74546563ac9bd883ae2ee8140566cf..40e4142631bf3b38cd1035ceacfd1ff2aa26bdf9 100644 (file)
@@ -32,6 +32,7 @@
 #include "pipe/p_defines.h"
 #include "st_context.h"
 #include "st_atom.h"
+#include "st_cb_bitmap.h"
 #include "st_program.h"
 
        
@@ -147,6 +148,8 @@ void st_validate_state( struct st_context *st )
    struct st_state_flags *state = &st->dirty;
    GLuint i;
 
+   st_flush_bitmap_cache(st);
+
    check_program_state( st );
 
    if (state->st == 0)
index 64b188242455cda4961247f53ac792a74b46a7dc..e46dcbf66106edcea266bd0e20b6112fd26c38a5 100644 (file)
 
 
 
+/**
+ * The bitmap cache attempts to accumulate multiple glBitmap calls in a
+ * buffer which is then rendered en mass upon a flush, state change, etc.
+ * A wide, short buffer is used to target the common case of a series
+ * of glBitmap calls being used to draw text.
+ */
+static GLboolean UseBitmapCache = 0*GL_TRUE;
+
+
+#define BITMAP_CACHE_WIDTH  512
+#define BITMAP_CACHE_HEIGHT 32
+
+struct bitmap_cache
+{
+   /** An I8 texture image: */
+   GLubyte buffer[BITMAP_CACHE_HEIGHT][BITMAP_CACHE_WIDTH];
+   GLboolean empty;
+   /** Window pos to render the cached image */
+   GLint xpos, ypos;
+   struct pipe_texture *texture;
+};
+
+
+
+
 /**
  * Make fragment program for glBitmap:
  *   Sample the texture and kill the fragment if the bit is 0.
@@ -390,16 +415,18 @@ setup_bitmap_vertex_data(struct st_context *st,
 static void
 draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
                  GLsizei width, GLsizei height,
-                 struct pipe_texture *pt,
-                 struct st_fragment_program *stfp)
+                 struct pipe_texture *pt)
 {
    struct st_context *st = ctx->st;
    struct pipe_context *pipe = ctx->st->pipe;
    struct cso_context *cso = ctx->st->cso_context;
+   struct st_fragment_program *stfp;
    GLuint maxSize;
 
+   stfp = combined_bitmap_fragment_program(ctx);
+
    /* limit checks */
-   /* XXX if DrawPixels image is larger than max texture size, break
+   /* XXX if the bitmap is larger than the max texture size, break
     * it up into chunks.
     */
    maxSize = 1 << (pipe->screen->get_param(pipe->screen, PIPE_CAP_MAX_TEXTURE_2D_LEVELS) - 1);
@@ -467,18 +494,185 @@ draw_bitmap_quad(GLcontext *ctx, GLint x, GLint y, GLfloat z,
 
 
 
+static void
+init_bitmap_cache(struct st_context *st)
+{
+   struct pipe_context *pipe = st->pipe;
+   struct pipe_screen *screen = pipe->screen;
+   enum pipe_format format;
+
+   st->bitmap.cache = CALLOC_STRUCT(bitmap_cache);
+   if (!st->bitmap.cache)
+      return;
+
+   /* find a usable texture format */
+   if (screen->is_format_supported(screen, PIPE_FORMAT_U_I8, PIPE_TEXTURE)) {
+      format = PIPE_FORMAT_U_I8;
+   }
+   else {
+      /* XXX support more formats */
+      assert(0);
+   }
+
+   st->bitmap.cache->texture
+      = st_texture_create(st, PIPE_TEXTURE_2D, format, 0,
+                          BITMAP_CACHE_WIDTH, BITMAP_CACHE_HEIGHT, 1, 0);
+   if (!st->bitmap.cache->texture) {
+      FREE(st->bitmap.cache);
+      st->bitmap.cache = NULL;
+      return;
+   }
+
+   st->bitmap.cache->empty = GL_TRUE;
+}
+
+
+/**
+ * If there's anything in the bitmap cache, draw/flush it now.
+ */
+void
+st_flush_bitmap_cache(struct st_context *st)
+{
+   if (!st->bitmap.cache->empty) {
+      struct pipe_context *pipe = st->pipe;
+      struct pipe_screen *screen = pipe->screen;
+      struct pipe_surface *surf;
+      void *dest;
+
+      /* update the texture map image */
+      surf = screen->get_tex_surface(screen, st->bitmap.cache->texture, 0, 0, 0);
+      dest = pipe_surface_map(surf);
+      memcpy(dest, st->bitmap.cache->buffer, sizeof(st->bitmap.cache->buffer));
+      pipe_surface_unmap(surf);
+      pipe_surface_reference(&surf, NULL);
+
+      pipe->texture_update(pipe, st->bitmap.cache->texture, 0, 0x1);
+
+      draw_bitmap_quad(st->ctx,
+                       st->bitmap.cache->xpos,
+                       st->bitmap.cache->ypos,
+                       st->ctx->Current.RasterPos[2],
+                       BITMAP_CACHE_WIDTH, BITMAP_CACHE_HEIGHT,
+                       st->bitmap.cache->texture);
+
+      memset(st->bitmap.cache->buffer, 0, sizeof(st->bitmap.cache->buffer));
+      st->bitmap.cache->empty = GL_TRUE;
+   }
+}
+
+
+/**
+ * Try to accumulate this glBitmap call in the bitmap cache.
+ * \return  GL_TRUE for success, GL_FALSE if bitmap is too large, etc.
+ */
+static GLboolean
+accum_bitmap(struct st_context *st,
+             GLint x, GLint y, GLsizei width, GLsizei height,
+             const struct gl_pixelstore_attrib *unpack,
+             const GLubyte *bitmap )
+{
+   int row, col;
+   int px = -999, py;
+
+   if (width > BITMAP_CACHE_WIDTH ||
+       height > BITMAP_CACHE_HEIGHT)
+      return GL_FALSE; /* too big to cache */
+
+   if (!st->bitmap.cache->empty) {
+      px = x - st->bitmap.cache->xpos;  /* pos in buffer */
+      py = y - st->bitmap.cache->ypos;
+      if (px < 0 || px + width > BITMAP_CACHE_WIDTH ||
+          py < 0 || py + height > BITMAP_CACHE_HEIGHT) {
+         /* This bitmap would extend beyond cache bounds,
+          * so flush and continue.
+          */
+         st_flush_bitmap_cache(st);
+      }
+   }
+
+   if (st->bitmap.cache->empty) {
+      /* Initialize.  Center bitmap vertically in the buffer. */
+      px = 0;
+      py = (BITMAP_CACHE_HEIGHT - height) / 2;
+      st->bitmap.cache->xpos = x;
+      st->bitmap.cache->ypos = y - py;
+      st->bitmap.cache->empty = GL_FALSE;
+   }
+
+   assert(px != -999);
+
+   /* XXX try to combine this code with code in make_bitmap_texture() */
+#define SET_PIXEL(COL, ROW) \
+   st->bitmap.cache->buffer[py + (ROW)][px + (COL)] = 0xff;
+
+   for (row = 0; row < height; row++) {
+      const GLubyte *src = (const GLubyte *) _mesa_image_address2d(unpack,
+                 bitmap, width, height, GL_COLOR_INDEX, GL_BITMAP, row, 0);
+
+      if (unpack->LsbFirst) {
+         /* Lsb first */
+         GLubyte mask = 1U << (unpack->SkipPixels & 0x7);
+         for (col = 0; col < width; col++) {
+
+            if (*src & mask) {
+               SET_PIXEL(col, row);
+            }
+
+            if (mask == 128U) {
+               src++;
+               mask = 1U;
+            }
+            else {
+               mask = mask << 1;
+            }
+         }
+
+         /* get ready for next row */
+         if (mask != 1)
+            src++;
+      }
+      else {
+         /* Msb first */
+         GLubyte mask = 128U >> (unpack->SkipPixels & 0x7);
+         for (col = 0; col < width; col++) {
+
+            if (*src & mask) {
+               SET_PIXEL(col, row);
+            }
+
+            if (mask == 1U) {
+               src++;
+               mask = 128U;
+            }
+            else {
+               mask = mask >> 1;
+            }
+         }
+
+         /* get ready for next row */
+         if (mask != 128)
+            src++;
+      }
+
+   } /* row */
+
+   return GL_TRUE; /* accumulated */
+}
+
+
+
+/**
+ * Called via ctx->Driver.Bitmap()
+ */
 static void
 st_Bitmap(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height,
           const struct gl_pixelstore_attrib *unpack, const GLubyte *bitmap )
 {
-   struct st_fragment_program *stfp;
    struct st_context *st = ctx->st;
    struct pipe_texture *pt;
 
    st_validate_state(st);
 
-   stfp = combined_bitmap_fragment_program(ctx);
-
    if (!st->bitmap.vs) {
       /* create pass-through vertex shader now */
       const uint semantic_names[] = { TGSI_SEMANTIC_POSITION,
@@ -491,26 +685,36 @@ st_Bitmap(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height,
                                                           &st->bitmap.vert_shader);
    }
 
-   st_validate_state(st);
+   if (UseBitmapCache && accum_bitmap(st, x, y, width, height, unpack, bitmap))
+      return;
 
    pt = make_bitmap_texture(ctx, width, height, unpack, bitmap);
    if (pt) {
       assert(pt->target == PIPE_TEXTURE_2D);
       draw_bitmap_quad(ctx, x, y, ctx->Current.RasterPos[2],
-                       width, height,
-                       pt, stfp);
+                       width, height, pt);
       pipe_texture_reference(&pt, NULL);
    }
 }
 
 
-
-void st_init_bitmap_functions(struct dd_function_table *functions)
+/** Per-context init */
+void
+st_init_bitmap_functions(struct dd_function_table *functions)
 {
    functions->Bitmap = st_Bitmap;
 }
 
 
+/** Per-context init */
+void
+st_init_bitmap(struct st_context *st)
+{
+   init_bitmap_cache(st);
+}
+
+
+/** Per-context tear-down */
 void
 st_destroy_bitmap(struct st_context *st)
 {
@@ -528,9 +732,15 @@ st_destroy_bitmap(struct st_context *st)
       pipe->delete_vs_state(pipe, st->bitmap.vs);
       st->bitmap.vs = NULL;
    }
+
    if (st->bitmap.vbuf) {
       pipe->winsys->buffer_destroy(pipe->winsys, st->bitmap.vbuf);
       st->bitmap.vbuf = NULL;
    }
-}
 
+   if (st->bitmap.cache) {
+      pipe_texture_release(&st->bitmap.cache->texture);
+      FREE(st->bitmap.cache);
+      st->bitmap.cache = NULL;
+   }
+}
index ac19e0ebb126053ab666af7b4b30f95c2f25a95f..aae11d34c9222f935adafc1116900ee814519d1f 100644 (file)
 extern void
 st_init_bitmap_functions(struct dd_function_table *functions);
 
+extern void
+st_init_bitmap(struct st_context *st);
 
 extern void
 st_destroy_bitmap(struct st_context *st);
 
+extern void
+st_flush_bitmap_cache(struct st_context *st);
+
 
 #endif /* ST_CB_BITMAP_H */
index a536a059bd5f9ca1d04f35d6582b6b5babb8ea97..e321b401e2d860c02d600dfdd2e4063d448788be 100644 (file)
@@ -35,6 +35,7 @@
 #include "main/macros.h"
 #include "main/context.h"
 #include "st_context.h"
+#include "st_cb_bitmap.h"
 #include "st_cb_flush.h"
 #include "st_cb_fbo.h"
 #include "st_public.h"
@@ -48,6 +49,8 @@ void st_flush( struct st_context *st, uint pipeFlushFlags,
 {
    FLUSH_VERTICES(st->ctx, 0);
 
+   st_flush_bitmap_cache(st);
+
    st->pipe->flush( st->pipe, pipeFlushFlags, fence );
 }
 
index 726e06d7c2b97c9b6d0e02d6044eaed528107e8a..a20195f2dea25d3f8d72aa688fbf57db5dbf3a2a 100644 (file)
@@ -109,6 +109,7 @@ st_create_context_priv( GLcontext *ctx, struct pipe_context *pipe )
    st->cso_context = cso_create_context(pipe);
 
    st_init_atoms( st );
+   st_init_bitmap(st);
    st_init_draw( st );
    st_init_generate_mipmap(st);
    st_init_blit(st);
index f235c194b7470d464c6bb346d7c7a4d7d9b24358..85e3d47e1aa37eaff1143c863278a6696f48290a 100644 (file)
@@ -42,6 +42,7 @@ struct cso_cache;
 struct cso_blend;
 struct gen_mipmap_state;
 struct blit_state;
+struct bitmap_cache;
 
 
 #define ST_NEW_MESA                    0x1 /* Mesa state has changed */
@@ -151,6 +152,7 @@ struct st_context
       void *vs;
       float vertices[4][3][4];  /**< vertex pos + color + texcoord */
       struct pipe_buffer *vbuf;
+      struct bitmap_cache *cache;
    } bitmap;
 
    /** for glClear */