intel: Add support for glBitmap as metaops using GL calls.
authorEric Anholt <eric@anholt.net>
Wed, 31 Dec 2008 08:02:43 +0000 (00:02 -0800)
committerEric Anholt <eric@anholt.net>
Wed, 31 Dec 2008 08:10:29 +0000 (00:10 -0800)
This lets us avoid software fallbacks when clients forget to turn some state
off (engine demo) or just do crazy things to test conformance (OGLC).

This should probably be brought into mesa generic code so other drivers can
make use of it.

Bug #19016.

src/mesa/drivers/dri/intel/intel_context.h
src/mesa/drivers/dri/intel/intel_pixel.c
src/mesa/drivers/dri/intel/intel_pixel.h
src/mesa/drivers/dri/intel/intel_pixel_bitmap.c

index ac08117bb4c084c569661e0339514444819e80ff..4100677750c69b588af86710642e07ddc8a6dc4b 100644 (file)
@@ -157,6 +157,16 @@ struct intel_context
       void (*debug_batch)(struct intel_context *intel);
    } vtbl;
 
+   struct {
+      struct gl_fragment_program *bitmap_fp;
+      struct gl_vertex_program *passthrough_vp;
+
+      struct gl_fragment_program *saved_fp;
+      GLboolean saved_fp_enable;
+      struct gl_vertex_program *saved_vp;
+      GLboolean saved_vp_enable;
+   } meta;
+
    GLint refcount;
    GLuint Fallback;
    GLuint NewGLState;
index 5702ad9bb575862ac3b659724c162633e3d0643d..91027d37e741e1ec252e3e4fe93a04b32d9b1ddb 100644 (file)
 
 #include "main/enums.h"
 #include "main/state.h"
+#include "main/context.h"
+#include "main/enable.h"
 #include "swrast/swrast.h"
+#include "shader/arbprogram.h"
+#include "shader/program.h"
 
 #include "intel_context.h"
 #include "intel_pixel.h"
@@ -167,6 +171,125 @@ intel_check_blit_format(struct intel_region * region,
    return GL_FALSE;
 }
 
+/**
+ * Set up a vertex program to pass through the position and first texcoord
+ * for pixel path.
+ */
+void
+intel_meta_set_passthrough_vertex_program(struct intel_context *intel)
+{
+   GLcontext *ctx = &intel->ctx;
+   static const char *vp =
+      "!!ARBvp1.0\n"
+      "TEMP vertexClip;\n"
+      "DP4 vertexClip.x, state.matrix.mvp.row[0], vertex.position;\n"
+      "DP4 vertexClip.y, state.matrix.mvp.row[1], vertex.position;\n"
+      "DP4 vertexClip.z, state.matrix.mvp.row[2], vertex.position;\n"
+      "DP4 vertexClip.w, state.matrix.mvp.row[3], vertex.position;\n"
+      "MOV result.position, vertexClip;\n"
+      "MOV result.texcoord[0], vertex.texcoord[0];\n"
+      "MOV result.color, vertex.color;\n"
+      "END\n";
+
+   assert(intel->meta.saved_vp == NULL);
+
+   _mesa_reference_vertprog(ctx, &intel->meta.saved_vp,
+                           ctx->VertexProgram.Current);
+   if (intel->meta.passthrough_vp == NULL) {
+      GLuint prog_name;
+      _mesa_GenPrograms(1, &prog_name);
+      _mesa_BindProgram(GL_VERTEX_PROGRAM_ARB, prog_name);
+      _mesa_ProgramStringARB(GL_VERTEX_PROGRAM_ARB,
+                            GL_PROGRAM_FORMAT_ASCII_ARB,
+                            strlen(vp), (const GLubyte *)vp);
+      _mesa_reference_vertprog(ctx, &intel->meta.passthrough_vp,
+                              ctx->VertexProgram.Current);
+      _mesa_DeletePrograms(1, &prog_name);
+   }
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+   _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+                           intel->meta.passthrough_vp);
+   ctx->Driver.BindProgram(ctx, GL_VERTEX_PROGRAM_ARB,
+                          &intel->meta.passthrough_vp->Base);
+
+   intel->meta.saved_vp_enable = ctx->VertexProgram.Enabled;
+   _mesa_Enable(GL_VERTEX_PROGRAM_ARB);
+}
+
+/**
+ * Restores the previous vertex program after
+ * intel_meta_set_passthrough_vertex_program()
+ */
+void
+intel_meta_restore_vertex_program(struct intel_context *intel)
+{
+   GLcontext *ctx = &intel->ctx;
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+   _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current,
+                           intel->meta.saved_vp);
+   _mesa_reference_vertprog(ctx, &intel->meta.saved_vp, NULL);
+   ctx->Driver.BindProgram(ctx, GL_VERTEX_PROGRAM_ARB,
+                          &ctx->VertexProgram.Current->Base);
+
+   if (!intel->meta.saved_vp_enable)
+      _mesa_Disable(GL_VERTEX_PROGRAM_ARB);
+}
+
+/**
+ * Binds the given program string to GL_FRAGMENT_PROGRAM_ARB, caching the
+ * program object.
+ */
+void
+intel_meta_set_fragment_program(struct intel_context *intel,
+                               struct gl_fragment_program **prog,
+                               const char *prog_string)
+{
+   GLcontext *ctx = &intel->ctx;
+   assert(intel->meta.saved_fp == NULL);
+
+   _mesa_reference_fragprog(ctx, &intel->meta.saved_fp,
+                           ctx->FragmentProgram.Current);
+   if (*prog == NULL) {
+      GLuint prog_name;
+      _mesa_GenPrograms(1, &prog_name);
+      _mesa_BindProgram(GL_FRAGMENT_PROGRAM_ARB, prog_name);
+      _mesa_ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
+                            GL_PROGRAM_FORMAT_ASCII_ARB,
+                            strlen(prog_string), (const GLubyte *)prog_string);
+      _mesa_reference_fragprog(ctx, prog, ctx->FragmentProgram.Current);
+      /* Note that DeletePrograms unbinds the program on us */
+      _mesa_DeletePrograms(1, &prog_name);
+   }
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+   _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, *prog);
+   ctx->Driver.BindProgram(ctx, GL_FRAGMENT_PROGRAM_ARB, &((*prog)->Base));
+
+   intel->meta.saved_fp_enable = ctx->FragmentProgram.Enabled;
+   _mesa_Enable(GL_FRAGMENT_PROGRAM_ARB);
+}
+
+/**
+ * Restores the previous fragment program after
+ * intel_meta_set_fragment_program()
+ */
+void
+intel_meta_restore_fragment_program(struct intel_context *intel)
+{
+   GLcontext *ctx = &intel->ctx;
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+   _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current,
+                           intel->meta.saved_fp);
+   _mesa_reference_fragprog(ctx, &intel->meta.saved_fp, NULL);
+   ctx->Driver.BindProgram(ctx, GL_FRAGMENT_PROGRAM_ARB,
+                          &ctx->FragmentProgram.Current->Base);
+
+   if (!intel->meta.saved_fp_enable)
+      _mesa_Disable(GL_FRAGMENT_PROGRAM_ARB);
+}
 
 void
 intelInitPixelFuncs(struct dd_function_table *functions)
@@ -181,3 +304,13 @@ intelInitPixelFuncs(struct dd_function_table *functions)
 #endif
    }
 }
+
+void
+intel_free_pixel_state(struct intel_context *intel)
+{
+   GLcontext *ctx = &intel->ctx;
+
+   _mesa_reference_vertprog(ctx, &intel->meta.passthrough_vp, NULL);
+   _mesa_reference_fragprog(ctx, &intel->meta.bitmap_fp, NULL);
+}
+
index 6fa6effe83529cba8d1716482a38628ccd5c45c3..9556efc71c2e2db72092dca34c99afc3acfbb334 100644 (file)
 
 void intelInitPixelFuncs(struct dd_function_table *functions);
 
+void intel_meta_set_passthrough_vertex_program(struct intel_context *intel);
+void intel_meta_restore_vertex_program(struct intel_context *intel);
+void intel_meta_set_fragment_program(struct intel_context *intel,
+                                    struct gl_fragment_program **prog,
+                                    const char *prog_string);
+void intel_meta_restore_fragment_program(struct intel_context *intel);
+void intel_free_pixel_state(struct intel_context *intel);
+
 GLboolean intel_check_blit_fragment_ops(GLcontext * ctx,
                                        GLboolean src_alpha_is_one);
 
index 5e0e0d29ca55c329635a64d6107161e39bfbcb7d..88e181a51f232b5657f68ad7330cb414b71a9f37 100644 (file)
 #include "main/mtypes.h"
 #include "main/macros.h"
 #include "main/bufferobj.h"
+#include "main/pixelstore.h"
 #include "main/state.h"
+#include "main/teximage.h"
+#include "main/texenv.h"
+#include "main/texobj.h"
+#include "main/texstate.h"
+#include "main/texparam.h"
+#include "main/matrix.h"
+#include "main/varray.h"
+#include "main/attrib.h"
+#include "main/enable.h"
+#include "shader/arbprogram.h"
+#include "glapi/dispatch.h"
 #include "swrast/swrast.h"
 
 #include "intel_screen.h"
@@ -87,6 +99,11 @@ static GLboolean test_bit( const GLubyte *src,
    return (src[bit/8] & (1<<(bit % 8))) ? 1 : 0;
 }
 
+static GLboolean test_msb_bit(const GLubyte *src, GLuint bit)
+{
+   return (src[bit/8] & (1<<(7 - (bit % 8)))) ? 1 : 0;
+}
+
 static void set_bit( GLubyte *dest,
                          GLuint bit )
 {
@@ -317,9 +334,187 @@ out:
    return GL_TRUE;
 }
 
+static GLboolean
+intel_texture_bitmap(GLcontext * ctx,
+                    GLint dst_x, GLint dst_y,
+                    GLsizei width, GLsizei height,
+                    const struct gl_pixelstore_attrib *unpack,
+                    const GLubyte *bitmap)
+{
+   struct intel_context *intel = intel_context(ctx);
+   static const char *fp =
+      "!!ARBfp1.0\n"
+      "TEMP val;\n"
+      "PARAM color=program.local[0];\n"
+      "TEX val, fragment.texcoord[0], texture[0], 2D;\n"
+      "ADD val, val.wwww, {-.5, -.5, -.5, -.5};\n"
+      "KIL val;\n"
+      "MOV result.color, color;\n"
+      "END\n";
+   GLuint texname;
+   GLfloat vertices[4][4];
+   GLfloat texcoords[4][2];
+   GLint old_active_texture;
+   GLubyte *unpacked_bitmap;
+   GLubyte *a8_bitmap;
+   int x, y;
+
+   /* We need a fragment program for the KIL effect */
+   if (!ctx->Extensions.ARB_fragment_program ||
+       !ctx->Extensions.ARB_vertex_program) {
+      if (INTEL_DEBUG & DEBUG_FALLBACKS)
+        fprintf(stderr,
+                "glBitmap fallback: No fragment/vertex program support\n");
+      return GL_FALSE;
+   }
+
+   /* We're going to mess with texturing with no regard to existing texture
+    * state, so if there is some set up we have to bail.
+    */
+   if (ctx->Texture._EnabledUnits != 0) {
+      if (INTEL_DEBUG & DEBUG_FALLBACKS)
+        fprintf(stderr, "glBitmap fallback: texturing enabled\n");
+      return GL_FALSE;
+   }
+
+   /* Can't do textured DrawPixels with a fragment program, unless we were
+    * to generate a new program that sampled our texture and put the results
+    * in the fragment color before the user's program started.
+    */
+   if (ctx->FragmentProgram.Enabled) {
+      if (INTEL_DEBUG & DEBUG_FALLBACKS)
+        fprintf(stderr, "glBitmap fallback: fragment program enabled\n");
+      return GL_FALSE;
+   }
+
+   if (ctx->VertexProgram.Enabled) {
+      if (INTEL_DEBUG & DEBUG_FALLBACKS)
+        fprintf(stderr, "glBitmap fallback: vertex program enabled\n");
+      return GL_FALSE;
+   }
+
+   /* Check that we can load in a texture this big. */
+   if (width > (1 << (ctx->Const.MaxTextureLevels - 1)) ||
+       height > (1 << (ctx->Const.MaxTextureLevels - 1))) {
+      if (INTEL_DEBUG & DEBUG_FALLBACKS)
+        fprintf(stderr, "glBitmap fallback: bitmap too large (%dx%d)\n",
+                width, height);
+      return GL_FALSE;
+   }
 
+   /* Convert the A1 bitmap to an A8 format suitable for glTexImage */
+   if (unpack->BufferObj->Name) {
+      bitmap = map_pbo(ctx, width, height, unpack, bitmap);
+      if (bitmap == NULL)
+        return GL_TRUE;        /* even though this is an error, we're done */
+   }
+   unpacked_bitmap = _mesa_unpack_bitmap(width, height, bitmap,
+                                        unpack);
+   a8_bitmap = _mesa_calloc(width * height);
+   for (y = 0; y < height; y++) {
+      for (x = 0; x < width; x++) {
+        if (test_msb_bit(unpacked_bitmap, ALIGN(width, 8) * y + x))
+           a8_bitmap[y * width + x] = 0xff;
+      }
+   }
+   _mesa_free(unpacked_bitmap);
+   if (unpack->BufferObj->Name) {
+      /* done with PBO so unmap it now */
+      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
+                              unpack->BufferObj);
+   }
 
+   /* Save GL state before we start setting up our drawing */
+   _mesa_PushAttrib(GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_CURRENT_BIT |
+                   GL_VIEWPORT_BIT);
+   _mesa_PushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT |
+                         GL_CLIENT_PIXEL_STORE_BIT);
+   old_active_texture = ctx->Texture.CurrentUnit;
+
+   _mesa_Disable(GL_POLYGON_STIPPLE);
+
+   /* Upload our bitmap data to an alpha texture */
+   _mesa_ActiveTextureARB(GL_TEXTURE0_ARB);
+   _mesa_Enable(GL_TEXTURE_2D);
+   _mesa_GenTextures(1, &texname);
+   _mesa_BindTexture(GL_TEXTURE_2D, texname);
+   _mesa_TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   _mesa_TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+   _mesa_PixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
+   _mesa_PixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+   _mesa_PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+   _mesa_PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+   _mesa_PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+   _mesa_PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+   _mesa_TexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
+                   GL_ALPHA, GL_UNSIGNED_BYTE, a8_bitmap);
+   _mesa_free(a8_bitmap);
+
+   _mesa_Viewport(0, 0, ctx->DrawBuffer->Width, ctx->DrawBuffer->Height);
+   _mesa_MatrixMode(GL_PROJECTION);
+   _mesa_PushMatrix();
+   _mesa_LoadIdentity();
+   _mesa_Ortho(0, ctx->DrawBuffer->Width, 0, ctx->DrawBuffer->Height, 1, -1);
+
+   _mesa_MatrixMode(GL_MODELVIEW);
+   _mesa_PushMatrix();
+   _mesa_LoadIdentity();
+
+   intel_meta_set_fragment_program(intel, &intel->meta.bitmap_fp, fp);
+   _mesa_ProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 0,
+                                    ctx->Current.RasterColor);
+   intel_meta_set_passthrough_vertex_program(intel);
+
+   vertices[0][0] = dst_x;
+   vertices[0][1] = dst_y;
+   vertices[0][2] = ctx->Current.RasterPos[2];
+   vertices[0][3] = 1.0;
+   vertices[1][0] = dst_x + width;
+   vertices[1][1] = dst_y;
+   vertices[1][2] = ctx->Current.RasterPos[2];
+   vertices[1][3] = 1.0;
+   vertices[2][0] = dst_x + width;
+   vertices[2][1] = dst_y + height;
+   vertices[2][2] = ctx->Current.RasterPos[2];
+   vertices[2][3] = 1.0;
+   vertices[3][0] = dst_x;
+   vertices[3][1] = dst_y + height;
+   vertices[3][2] = ctx->Current.RasterPos[2];
+   vertices[3][3] = 1.0;
+
+   texcoords[0][0] = 0.0;
+   texcoords[0][1] = 0.0;
+   texcoords[1][0] = 1.0;
+   texcoords[1][1] = 0.0;
+   texcoords[2][0] = 1.0;
+   texcoords[2][1] = 1.0;
+   texcoords[3][0] = 0.0;
+   texcoords[3][1] = 1.0;
+
+   _mesa_VertexPointer(4, GL_FLOAT, 4 * sizeof(GLfloat), &vertices);
+   _mesa_TexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), &texcoords);
+   _mesa_Enable(GL_VERTEX_ARRAY);
+   _mesa_Enable(GL_TEXTURE_COORD_ARRAY);
+   CALL_DrawArrays(ctx->Exec, (GL_TRIANGLE_FAN, 0, 4));
+
+   intel_meta_restore_fragment_program(intel);
+   intel_meta_restore_vertex_program(intel);
+
+   _mesa_MatrixMode(GL_PROJECTION);
+   _mesa_PopMatrix();
+   _mesa_MatrixMode(GL_MODELVIEW);
+   _mesa_PopMatrix();
+
+   _mesa_PopClientAttrib();
+   _mesa_Disable(GL_TEXTURE_2D); /* asserted that it was disabled at entry */
+   _mesa_ActiveTextureARB(GL_TEXTURE0_ARB + old_active_texture);
+   _mesa_PopAttrib();
+
+   _mesa_DeleteTextures(1, &texname);
 
+   return GL_TRUE;
+}
 
 /* There are a large number of possible ways to implement bitmap on
  * this hardware, most of them have some sort of drawback.  Here are a
@@ -352,6 +547,10 @@ intelBitmap(GLcontext * ctx,
                           unpack, pixels))
       return;
 
+   if (intel_texture_bitmap(ctx, x, y, width, height,
+                           unpack, pixels))
+      return;
+
    if (INTEL_DEBUG & DEBUG_PIXEL)
       _mesa_printf("%s: fallback to swrast\n", __FUNCTION__);