gallium: antialiased line drawing
authorBrian <brian.paul@tungstengraphics.com>
Mon, 18 Feb 2008 23:19:05 +0000 (16:19 -0700)
committerBrian <brian.paul@tungstengraphics.com>
Mon, 18 Feb 2008 23:19:05 +0000 (16:19 -0700)
New draw/prim stage: draw_aaline.  When installed, lines are replaced by
textured quads to do antialiasing.  The current user-defined fragment shader
is modified to do a texture fetch and modulate fragment alpha.

src/gallium/auxiliary/draw/Makefile
src/gallium/auxiliary/draw/draw_aaline.c [new file with mode: 0644]
src/gallium/auxiliary/draw/draw_context.c
src/gallium/auxiliary/draw/draw_context.h
src/gallium/auxiliary/draw/draw_prim.c
src/gallium/auxiliary/draw/draw_private.h
src/gallium/auxiliary/draw/draw_validate.c
src/gallium/auxiliary/tgsi/Makefile
src/gallium/drivers/softpipe/sp_context.c
src/gallium/drivers/softpipe/sp_state_derived.c

index c56b63d85b84a1967f0ed99d5b7eca177dd57090..c8000cbe9cb2638075ff29422010e78b1f02d52e 100644 (file)
@@ -4,6 +4,7 @@ include $(TOP)/configs/current
 LIBNAME = draw
 
 DRIVER_SOURCES = \
+       draw_aaline.c \
        draw_clip.c \
        draw_vs_exec.c \
        draw_vs_sse.c \
diff --git a/src/gallium/auxiliary/draw/draw_aaline.c b/src/gallium/auxiliary/draw/draw_aaline.c
new file mode 100644 (file)
index 0000000..f1fee4f
--- /dev/null
@@ -0,0 +1,832 @@
+/**************************************************************************
+ * 
+ * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * 
+ **************************************************************************/
+
+/**
+ * AA line stage:  AA lines are converted to texture mapped triangles.
+ *
+ * Authors:  Brian Paul
+ */
+
+
+#include "pipe/p_util.h"
+#include "pipe/p_inlines.h"
+#include "pipe/p_context.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_shader_tokens.h"
+
+#include "tgsi/util/tgsi_transform.h"
+#include "tgsi/util/tgsi_dump.h"
+
+#include "draw_context.h"
+#include "draw_private.h"
+
+
+/**
+ * Max texture level for the alpha texture used for antialiasing
+ */
+#define MAX_TEXTURE_LEVEL  5   /* 32 x 32 */
+
+
+/**
+ * Subclass of pipe_shader_state to carry extra fragment shader info.
+ */
+struct aaline_fragment_shader
+{
+   struct pipe_shader_state state;
+   void *driver_fs;
+   void *aaline_fs;
+   void *aapoint_fs; /* not yet */
+   void *sprite_fs; /* not yet */
+};
+
+
+/**
+ * Subclass of draw_stage
+ */
+struct aaline_stage
+{
+   struct draw_stage stage;
+
+   float half_line_width;
+
+   /** For AA lines, this is the vertex attrib slot for the new texcoords */
+   uint tex_slot;
+
+   void *sampler_cso;
+   struct pipe_texture *texture;
+   uint sampler_unit;
+
+
+   /*
+    * Currently bound state
+    */
+   struct aaline_fragment_shader *fs;
+   struct {
+      void *sampler[PIPE_MAX_SAMPLERS];
+      struct pipe_texture *texture[PIPE_MAX_SAMPLERS];
+   } state;
+
+   /*
+    * Driver interface/override functions
+    */
+   void * (*driver_create_fs_state)(struct pipe_context *,
+                                    const struct pipe_shader_state *);
+   void (*driver_bind_fs_state)(struct pipe_context *, void *);
+   void (*driver_delete_fs_state)(struct pipe_context *, void *);
+
+   void (*driver_bind_sampler_state)(struct pipe_context *, unsigned, void *);
+
+   void (*driver_set_sampler_texture)(struct pipe_context *,
+                                      unsigned sampler,
+                                      struct pipe_texture *);
+
+   struct pipe_context *pipe;
+};
+
+
+
+/**
+ * Subclass of tgsi_transform_context, used for transforming the
+ * user's fragment shader to add the special AA instructions.
+ */
+struct aa_transform_context {
+   struct tgsi_transform_context base;
+   uint tempsUsed;  /**< bitmask */
+   int colorOutput; /**< which output is the primary color */
+   int maxSampler;  /**< max sampler index found */
+   int maxInput, maxGeneric;  /**< max input index found */
+   int colorTemp, texTemp;  /**< temp registers */
+   boolean firstInstruction;
+};
+
+
+/**
+ * TGSI declaration transform callback.
+ * Look for a free sampler, a free input attrib, and two free temp regs.
+ */
+static void
+aa_transform_decl(struct tgsi_transform_context *ctx,
+                  struct tgsi_full_declaration *decl)
+{
+   struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
+
+   if (decl->Declaration.File == TGSI_FILE_OUTPUT &&
+       decl->Semantic.SemanticName == TGSI_SEMANTIC_COLOR &&
+       decl->Semantic.SemanticIndex == 0) {
+      aactx->colorOutput = decl->u.DeclarationRange.First;
+   }
+   else if (decl->Declaration.File == TGSI_FILE_SAMPLER) {
+      if (decl->u.DeclarationRange.Last > aactx->maxSampler)
+         aactx->maxSampler = decl->u.DeclarationRange.Last + 1;
+   }
+   else if (decl->Declaration.File == TGSI_FILE_INPUT) {
+      if (decl->u.DeclarationRange.Last > aactx->maxInput)
+         aactx->maxInput = decl->u.DeclarationRange.Last;
+      if (decl->Semantic.SemanticName == TGSI_SEMANTIC_GENERIC  &&
+          decl->Semantic.SemanticIndex > aactx->maxGeneric) {
+         aactx->maxGeneric = decl->Semantic.SemanticIndex;
+      }
+   }
+   else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
+      uint i;
+      for (i = decl->u.DeclarationRange.First;
+           i <= decl->u.DeclarationRange.Last; i++) {
+         aactx->tempsUsed |= (1 << i);
+      }
+   }
+
+   ctx->emit_declaration(ctx, decl);
+}
+
+
+/**
+ * TGSI instruction transform callback.
+ * Replace writes to result.color w/ a temp reg.
+ * Upon END instruction, insert texture sampling code for antialiasing.
+ */
+static void
+aa_transform_inst(struct tgsi_transform_context *ctx,
+                  struct tgsi_full_instruction *inst)
+{
+   struct aa_transform_context *aactx = (struct aa_transform_context *) ctx;
+
+   if (aactx->firstInstruction) {
+      /* emit our new declarations before the first instruction */
+
+      struct tgsi_full_declaration decl;
+      uint i;
+
+      /* find two free temp regs */
+      for (i = 0; i < 32; i++) {
+         if ((aactx->tempsUsed & (1 << i)) == 0) {
+            /* found a free temp */
+            if (aactx->colorTemp < 0)
+               aactx->colorTemp  = i;
+            else if (aactx->texTemp < 0)
+               aactx->texTemp  = i;
+            else
+               break;
+         }
+      }
+      assert(aactx->colorTemp >= 0);
+      assert(aactx->texTemp >= 0);
+
+      /* declare new generic input/texcoord */
+      decl = tgsi_default_full_declaration();
+      decl.Declaration.File = TGSI_FILE_INPUT;
+      decl.Declaration.Semantic = 1;
+      decl.Semantic.SemanticName = TGSI_SEMANTIC_GENERIC;
+      decl.Semantic.SemanticIndex = aactx->maxGeneric + 1;
+      decl.Declaration.Interpolate = 1;
+      /* XXX this could be linear... */
+      decl.Interpolation.Interpolate = TGSI_INTERPOLATE_PERSPECTIVE;
+      decl.u.DeclarationRange.First = 
+      decl.u.DeclarationRange.Last = aactx->maxInput + 1;
+      ctx->emit_declaration(ctx, &decl);
+
+      /* declare new sampler */
+      decl = tgsi_default_full_declaration();
+      decl.Declaration.File = TGSI_FILE_SAMPLER;
+      decl.u.DeclarationRange.First = 
+      decl.u.DeclarationRange.Last = aactx->maxSampler + 1;
+      ctx->emit_declaration(ctx, &decl);
+
+      /* declare new temp regs */
+      decl = tgsi_default_full_declaration();
+      decl.Declaration.File = TGSI_FILE_TEMPORARY;
+      decl.u.DeclarationRange.First = 
+      decl.u.DeclarationRange.Last = aactx->texTemp;
+      ctx->emit_declaration(ctx, &decl);
+
+      decl = tgsi_default_full_declaration();
+      decl.Declaration.File = TGSI_FILE_TEMPORARY;
+      decl.u.DeclarationRange.First = 
+      decl.u.DeclarationRange.Last = aactx->colorTemp;
+      ctx->emit_declaration(ctx, &decl);
+
+      aactx->firstInstruction = FALSE;
+   }
+
+   if (inst->Instruction.Opcode == TGSI_OPCODE_END &&
+       aactx->colorOutput != -1) {
+      struct tgsi_full_instruction newInst;
+
+      /* TEX */
+      newInst = tgsi_default_full_instruction();
+      newInst.Instruction.Opcode = TGSI_OPCODE_TEX;
+      newInst.Instruction.NumDstRegs = 1;
+      newInst.FullDstRegisters[0].DstRegister.File = TGSI_FILE_TEMPORARY;
+      newInst.FullDstRegisters[0].DstRegister.Index = aactx->texTemp;
+      newInst.Instruction.NumSrcRegs = 2;
+      newInst.InstructionExtTexture.Texture = TGSI_TEXTURE_2D;
+      newInst.FullSrcRegisters[0].SrcRegister.File = TGSI_FILE_INPUT;
+      newInst.FullSrcRegisters[0].SrcRegister.Index = aactx->maxInput + 1;
+      newInst.FullSrcRegisters[1].SrcRegister.File = TGSI_FILE_SAMPLER;
+      newInst.FullSrcRegisters[1].SrcRegister.Index = aactx->maxSampler + 1;
+
+      ctx->emit_instruction(ctx, &newInst);
+
+      /* MOV rgb */
+      newInst = tgsi_default_full_instruction();
+      newInst.Instruction.Opcode = TGSI_OPCODE_MOV;
+      newInst.Instruction.NumDstRegs = 1;
+      newInst.FullDstRegisters[0].DstRegister.File = TGSI_FILE_OUTPUT;
+      newInst.FullDstRegisters[0].DstRegister.Index = aactx->colorOutput;
+      newInst.FullDstRegisters[0].DstRegister.WriteMask = TGSI_WRITEMASK_XYZ;
+      newInst.Instruction.NumSrcRegs = 1;
+      newInst.FullSrcRegisters[0].SrcRegister.File = TGSI_FILE_TEMPORARY;
+      newInst.FullSrcRegisters[0].SrcRegister.Index = aactx->colorTemp;
+      ctx->emit_instruction(ctx, &newInst);
+
+      /* MUL alpha */
+      newInst = tgsi_default_full_instruction();
+      newInst.Instruction.Opcode = TGSI_OPCODE_MUL;
+      newInst.Instruction.NumDstRegs = 1;
+      newInst.FullDstRegisters[0].DstRegister.File = TGSI_FILE_OUTPUT;
+      newInst.FullDstRegisters[0].DstRegister.Index = aactx->colorOutput;
+      newInst.FullDstRegisters[0].DstRegister.WriteMask = TGSI_WRITEMASK_W;
+      newInst.Instruction.NumSrcRegs = 2;
+      newInst.FullSrcRegisters[0].SrcRegister.File = TGSI_FILE_TEMPORARY;
+      newInst.FullSrcRegisters[0].SrcRegister.Index = aactx->colorTemp;
+      newInst.FullSrcRegisters[1].SrcRegister.File = TGSI_FILE_TEMPORARY;
+      newInst.FullSrcRegisters[1].SrcRegister.Index = aactx->texTemp;
+      ctx->emit_instruction(ctx, &newInst);
+
+      /* END */
+      newInst = tgsi_default_full_instruction();
+      newInst.Instruction.Opcode = TGSI_OPCODE_END;
+      newInst.Instruction.NumDstRegs = 0;
+      newInst.Instruction.NumSrcRegs = 0;
+      ctx->emit_instruction(ctx, &newInst);
+   }
+   else {
+      /* Not an END instruction.
+       * Look for writes to result.color and replace with colorTemp reg.
+       */
+      uint i;
+
+      for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
+         struct tgsi_full_dst_register *dst = &inst->FullDstRegisters[i];
+         if (dst->DstRegister.File == TGSI_FILE_OUTPUT &&
+             dst->DstRegister.Index == aactx->colorOutput) {
+            dst->DstRegister.File = TGSI_FILE_TEMPORARY;
+            dst->DstRegister.Index = aactx->colorTemp;
+         }
+      }
+
+      ctx->emit_instruction(ctx, inst);
+   }
+}
+
+
+/**
+ * Generate the frag shader we'll use for drawing AA lines.
+ * This will be the user's shader plus some texture/modulate instructions.
+ */
+static void
+generate_aaline_fs(struct aaline_stage *aaline)
+{
+   const struct pipe_shader_state *orig_fs = &aaline->fs->state;
+   struct draw_context *draw = aaline->stage.draw;
+   struct pipe_shader_state aaline_fs;
+   struct aa_transform_context transform;
+
+#define MAX 1000
+
+   aaline_fs = *orig_fs; /* copy to init */
+   aaline_fs.tokens = MALLOC(sizeof(struct tgsi_token) * MAX);
+
+   memset(&transform, 0, sizeof(transform));
+   transform.colorOutput = -1;
+   transform.maxSampler = -1;
+   transform.maxInput = -1;
+   transform.maxGeneric = -1;
+   transform.colorTemp = -1;
+   transform.texTemp = -1;
+   transform.firstInstruction = TRUE;
+   transform.base.transform_instruction = aa_transform_inst;
+   transform.base.transform_declaration = aa_transform_decl;
+
+   tgsi_transform_shader(orig_fs->tokens,
+                         (struct tgsi_token *) aaline_fs.tokens,
+                         MAX, &transform.base);
+
+#if 0 /* DEBUG */
+   tgsi_dump(orig_fs->tokens, 0);
+   tgsi_dump(aaline_fs.tokens, 0);
+#endif
+
+   aaline_fs.input_semantic_name[aaline_fs.num_inputs] = TGSI_SEMANTIC_GENERIC;
+   aaline_fs.input_semantic_index[aaline_fs.num_inputs] = transform.maxGeneric + 1;
+   aaline_fs.num_inputs++;
+
+   aaline->fs->aaline_fs
+      = aaline->driver_create_fs_state(aaline->pipe, &aaline_fs);
+
+   /* advertise the extra post-transform vertex attributes which will have
+    * the texcoords.
+    */
+   draw->extra_vp_outputs.semantic_name = TGSI_SEMANTIC_GENERIC;
+   draw->extra_vp_outputs.semantic_index = transform.maxGeneric + 1;
+}
+
+
+/**
+ * Create the texture map we'll use for antialiasing the lines.
+ */
+static void
+aaline_create_texture(struct aaline_stage *aaline)
+{
+   struct pipe_context *pipe = aaline->pipe;
+   struct pipe_texture texTemp;
+   uint level;
+
+   memset(&texTemp, 0, sizeof(texTemp));
+   texTemp.target = PIPE_TEXTURE_2D;
+   texTemp.format = PIPE_FORMAT_U_A8; /* XXX verify supported by driver! */
+   texTemp.last_level = MAX_TEXTURE_LEVEL;
+   texTemp.width[0] = 1 << MAX_TEXTURE_LEVEL;
+   texTemp.height[0] = 1 << MAX_TEXTURE_LEVEL;
+   texTemp.depth[0] = 1;
+   texTemp.cpp = 1;
+
+   aaline->texture = pipe->texture_create(pipe, &texTemp);
+
+   /* Fill in mipmap images.
+    * Basically each level is solid opaque, except for the outermost
+    * texels which are zero.  Special case the 1x1 and 2x2 levels.
+    */
+   for (level = 0; level <= MAX_TEXTURE_LEVEL; level++) {
+      struct pipe_surface *surface;
+      const uint size = aaline->texture->width[level];
+      ubyte *data;
+      uint i, j;
+
+      assert(aaline->texture->width[level] == aaline->texture->height[level]);
+
+      surface = pipe->get_tex_surface(pipe, aaline->texture, 0, level, 0);
+      data = pipe_surface_map(surface);
+
+      for (i = 0; i < size; i++) {
+         for (j = 0; j < size; j++) {
+            uint d;
+            if (size == 1) {
+               d = 255;
+            }
+            else if (size == 2) {
+               d = 200; /* tuneable */
+            }
+            else if (i == 0 || j == 0 || i == size - 1 || j == size - 1) {
+               d = 0;
+            }
+            else {
+               d = 255;
+            }
+            data[i * surface->pitch + j] = d;
+         }
+      }
+
+      /* unmap */
+      pipe_surface_unmap(surface);
+      pipe_surface_reference(&surface, NULL);
+   }
+}
+
+
+/**
+ * Create the sampler CSO that'll be used for antialiasing.
+ * By using a mipmapped texture, we don't have to generate a different
+ * texture image for each line size.
+ */
+static void
+aaline_create_sampler(struct aaline_stage *aaline)
+{
+   struct pipe_sampler_state sampler;
+   struct pipe_context *pipe = aaline->pipe;
+
+   memset(&sampler, 0, sizeof(sampler));
+   sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
+   sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
+   sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
+   sampler.min_mip_filter = PIPE_TEX_MIPFILTER_LINEAR;
+   sampler.min_img_filter = PIPE_TEX_FILTER_LINEAR;
+   sampler.mag_img_filter = PIPE_TEX_FILTER_LINEAR;
+   sampler.normalized_coords = 1;
+   sampler.min_lod = 0.0f;
+   sampler.max_lod = MAX_TEXTURE_LEVEL;
+
+   aaline->sampler_cso = pipe->create_sampler_state(pipe, &sampler);
+}
+
+
+/**
+ * When we're about to draw our first AA line in a batch, this function is
+ * called to tell the driver to bind our modified fragment shader.
+ */
+static void
+bind_aaline_fragment_shader(struct aaline_stage *aaline)
+{
+   if (!aaline->fs->aaline_fs) {
+      generate_aaline_fs(aaline);
+   }
+   aaline->driver_bind_fs_state(aaline->pipe, aaline->fs->aaline_fs);
+}
+
+
+
+static INLINE struct aaline_stage *
+aaline_stage( struct draw_stage *stage )
+{
+   return (struct aaline_stage *) stage;
+}
+
+
+static void
+passthrough_point(struct draw_stage *stage, struct prim_header *header)
+{
+   stage->next->point(stage->next, header);
+}
+
+
+static void
+passthrough_tri(struct draw_stage *stage, struct prim_header *header)
+{
+   stage->next->tri(stage->next, header);
+}
+
+
+/**
+ * Draw a wide line by drawing a quad, using geometry which will
+ * fullfill GL's antialiased line requirements.
+ */
+static void
+aaline_line(struct draw_stage *stage, struct prim_header *header)
+{
+   const struct aaline_stage *aaline = aaline_stage(stage);
+   const float half_width = aaline->half_line_width;
+   struct prim_header tri;
+   struct vertex_header *v[8];
+   uint texPos = aaline->tex_slot;
+   float *pos, *tex;
+   float dx = header->v[1]->data[0][0] - header->v[0]->data[0][0];
+   float dy = header->v[1]->data[0][1] - header->v[0]->data[0][1];
+   float a = atan2(dy, dx);
+   float c_a = cos(a), s_a = sin(a);
+   uint i;
+
+   /* XXX the ends of lines aren't quite perfect yet, but probably passable */
+   dx = 0.5 * half_width;
+   dy = half_width;
+
+   /* allocate/dup new verts */
+   for (i = 0; i < 8; i++) {
+      v[i] = dup_vert(stage, header->v[i/4], i);
+   }
+
+   /*
+    * Quad strip for line from v0 to v1 (*=endpoints):
+    *
+    *  1   3                     5   7
+    *  +---+---------------------+---+
+    *  |                             |
+    *  | *v0                     v1* |
+    *  |                             |
+    *  +---+---------------------+---+
+    *  0   2                     4   6
+    */
+
+   /* new verts */
+   pos = v[0]->data[0];
+   pos[0] += (-dx * c_a -  dy * s_a);
+   pos[1] += (-dx * s_a +  dy * c_a);
+
+   pos = v[1]->data[0];
+   pos[0] += (-dx * c_a - -dy * s_a);
+   pos[1] += (-dx * s_a + -dy * c_a);
+
+   pos = v[2]->data[0];
+   pos[0] += ( dx * c_a -  dy * s_a);
+   pos[1] += ( dx * s_a +  dy * c_a);
+
+   pos = v[3]->data[0];
+   pos[0] += ( dx * c_a - -dy * s_a);
+   pos[1] += ( dx * s_a + -dy * c_a);
+
+   pos = v[4]->data[0];
+   pos[0] += (-dx * c_a -  dy * s_a);
+   pos[1] += (-dx * s_a +  dy * c_a);
+
+   pos = v[5]->data[0];
+   pos[0] += (-dx * c_a - -dy * s_a);
+   pos[1] += (-dx * s_a + -dy * c_a);
+
+   pos = v[6]->data[0];
+   pos[0] += ( dx * c_a -  dy * s_a);
+   pos[1] += ( dx * s_a +  dy * c_a);
+
+   pos = v[7]->data[0];
+   pos[0] += ( dx * c_a - -dy * s_a);
+   pos[1] += ( dx * s_a + -dy * c_a);
+
+   /* new texcoords */
+   tex = v[0]->data[texPos];
+   ASSIGN_4V(tex, 0, 0, 0, 1);
+
+   tex = v[1]->data[texPos];
+   ASSIGN_4V(tex, 0, 1, 0, 1);
+
+   tex = v[2]->data[texPos];
+   ASSIGN_4V(tex, .5, 0, 0, 1);
+
+   tex = v[3]->data[texPos];
+   ASSIGN_4V(tex, .5, 1, 0, 1);
+
+   tex = v[4]->data[texPos];
+   ASSIGN_4V(tex, .5, 0, 0, 1);
+
+   tex = v[5]->data[texPos];
+   ASSIGN_4V(tex, .5, 1, 0, 1);
+
+   tex = v[6]->data[texPos];
+   ASSIGN_4V(tex, 1, 0, 0, 1);
+
+   tex = v[7]->data[texPos];
+   ASSIGN_4V(tex, 1, 1, 0, 1);
+
+   /* emit 6 tris for the quad strip */
+   tri.v[0] = v[2];  tri.v[1] = v[1];  tri.v[2] = v[0];
+   stage->next->tri( stage->next, &tri );
+
+   tri.v[0] = v[3];  tri.v[1] = v[1];  tri.v[2] = v[2];
+   stage->next->tri( stage->next, &tri );
+
+   tri.v[0] = v[4];  tri.v[1] = v[3];  tri.v[2] = v[2];
+   stage->next->tri( stage->next, &tri );
+
+   tri.v[0] = v[5];  tri.v[1] = v[3];  tri.v[2] = v[4];
+   stage->next->tri( stage->next, &tri );
+
+   tri.v[0] = v[6];  tri.v[1] = v[5];  tri.v[2] = v[4];
+   stage->next->tri( stage->next, &tri );
+
+   tri.v[0] = v[7];  tri.v[1] = v[5];  tri.v[2] = v[6];
+   stage->next->tri( stage->next, &tri );
+}
+
+
+static void
+aaline_first_line(struct draw_stage *stage, struct prim_header *header)
+{
+   auto struct aaline_stage *aaline = aaline_stage(stage);
+   struct draw_context *draw = stage->draw;
+   struct pipe_context *pipe = aaline->pipe;
+
+   assert(draw->rasterizer->line_smooth);
+
+   if (draw->rasterizer->line_width <= 3.0)
+      aaline->half_line_width = 1.5f;
+   else
+      aaline->half_line_width = 0.5f * draw->rasterizer->line_width;
+
+   aaline->tex_slot = draw->num_vs_outputs;
+   assert(aaline->tex_slot > 0); /* output[0] is vertex pos */
+   draw->extra_vp_outputs.slot = aaline->tex_slot;
+
+   /*
+    * Bind our fragprog, sampler and texture
+    */
+   bind_aaline_fragment_shader(aaline);
+
+   aaline->driver_bind_sampler_state(pipe, aaline->sampler_unit, aaline->sampler_cso);
+   aaline->driver_set_sampler_texture(pipe, aaline->sampler_unit, aaline->texture);
+
+   /* now really draw first line */
+   stage->line = aaline_line;
+   stage->line(stage, header);
+}
+
+
+static void
+aaline_flush(struct draw_stage *stage, unsigned flags)
+{
+   struct draw_context *draw = stage->draw;
+   struct aaline_stage *aaline = aaline_stage(stage);
+   struct pipe_context *pipe = aaline->pipe;
+
+   stage->line = aaline_first_line;
+   stage->next->flush( stage->next, flags );
+
+   /* restore original frag shader */
+   aaline->driver_bind_fs_state(pipe, aaline->fs->driver_fs);
+
+   /* XXX restore original texture, sampler state */
+   aaline->driver_bind_sampler_state(pipe, aaline->sampler_unit,
+                                 aaline->state.sampler[aaline->sampler_unit]);
+   aaline->driver_set_sampler_texture(pipe, aaline->sampler_unit,
+                                 aaline->state.texture[aaline->sampler_unit]);
+
+   draw->extra_vp_outputs.slot = 0;
+}
+
+
+static void
+aaline_reset_stipple_counter(struct draw_stage *stage)
+{
+   stage->next->reset_stipple_counter( stage->next );
+}
+
+
+static void
+aaline_destroy(struct draw_stage *stage)
+{
+   draw_free_temp_verts( stage );
+   FREE( stage );
+}
+
+
+static struct aaline_stage *
+draw_aaline_stage(struct draw_context *draw)
+{
+   struct aaline_stage *aaline = CALLOC_STRUCT(aaline_stage);
+
+   draw_alloc_temp_verts( &aaline->stage, 8 );
+
+   aaline->stage.draw = draw;
+   aaline->stage.next = NULL;
+   aaline->stage.point = passthrough_point;
+   aaline->stage.line = aaline_first_line;
+   aaline->stage.tri = passthrough_tri;
+   aaline->stage.flush = aaline_flush;
+   aaline->stage.reset_stipple_counter = aaline_reset_stipple_counter;
+   aaline->stage.destroy = aaline_destroy;
+
+   return aaline;
+}
+
+
+/*
+ * XXX temporary? solution to mapping a pipe_context to a aaline_stage.
+ */
+
+#define MAX_CONTEXTS 10
+
+static struct pipe_context *Pipe[MAX_CONTEXTS];
+static struct aaline_stage *Stage[MAX_CONTEXTS];
+static uint NumContexts;
+
+static void
+add_aa_pipe_context(struct pipe_context *pipe, struct aaline_stage *aa)
+{
+   assert(NumContexts < MAX_CONTEXTS);
+   Pipe[NumContexts] = pipe;
+   Stage[NumContexts] = aa;
+   NumContexts++;
+}
+
+static struct aaline_stage *
+aaline_stage_from_pipe(struct pipe_context *pipe)
+{
+   uint i;
+   for (i = 0; i < NumContexts; i++) {
+      if (Pipe[i] == pipe)
+         return Stage[i];
+   }
+   assert(0);
+   return NULL;
+}
+
+
+/**
+ * This function overrides the driver's create_fs_state() function and
+ * will typically be called by the state tracker.
+ */
+static void *
+aaline_create_fs_state(struct pipe_context *pipe,
+                       const struct pipe_shader_state *fs)
+{
+   struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
+   struct aaline_fragment_shader *aafs = CALLOC_STRUCT(aaline_fragment_shader);
+
+   if (aafs) {
+      aafs->state = *fs;
+
+      /* pass-through */
+      aafs->driver_fs = aaline->driver_create_fs_state(aaline->pipe, fs);
+   }
+
+   return aafs;
+}
+
+
+static void
+aaline_bind_fs_state(struct pipe_context *pipe, void *fs)
+{
+   struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
+   struct aaline_fragment_shader *aafs = (struct aaline_fragment_shader *) fs;
+   /* save current */
+   aaline->fs = aafs;
+   /* pass-through */
+   aaline->driver_bind_fs_state(aaline->pipe, aafs->driver_fs);
+}
+
+
+static void
+aaline_delete_fs_state(struct pipe_context *pipe, void *fs)
+{
+   struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
+   struct aaline_fragment_shader *aafs = (struct aaline_fragment_shader *) fs;
+   /* pass-through */
+   aaline->driver_delete_fs_state(aaline->pipe, aafs->driver_fs);
+   FREE(aafs);
+}
+
+
+static void
+aaline_bind_sampler_state(struct pipe_context *pipe,
+                          unsigned unit, void *sampler)
+{
+   struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
+   /* save current */
+   aaline->state.sampler[unit] = sampler;
+   /* pass-through */
+   aaline->driver_bind_sampler_state(aaline->pipe, unit, sampler);
+}
+
+
+static void
+aaline_set_sampler_texture(struct pipe_context *pipe,
+                           unsigned sampler, struct pipe_texture *texture)
+{
+   struct aaline_stage *aaline = aaline_stage_from_pipe(pipe);
+   /* save current */
+   aaline->state.texture[sampler] = texture;
+   /* pass-through */
+   aaline->driver_set_sampler_texture(aaline->pipe, sampler, texture);
+}
+
+
+/**
+ * Called by drivers that want to install this AA line prim stage
+ * into the draw module's pipeline.  This will not be used if the
+ * hardware has native support for AA lines.
+ */
+void
+draw_install_aaline_stage(struct draw_context *draw, struct pipe_context *pipe)
+{
+   struct aaline_stage *aaline;
+
+   /*
+    * Create / install AA line drawing / prim stage
+    */
+   aaline = draw_aaline_stage( draw );
+   assert(aaline);
+   draw->pipeline.aaline = &aaline->stage;
+
+   aaline->pipe = pipe;
+
+   /* create special texture, sampler state */
+   aaline_create_texture(aaline);
+   aaline_create_sampler(aaline);
+
+   /* save original driver functions */
+   aaline->driver_create_fs_state = pipe->create_fs_state;
+   aaline->driver_bind_fs_state = pipe->bind_fs_state;
+   aaline->driver_delete_fs_state = pipe->delete_fs_state;
+
+   aaline->driver_bind_sampler_state = pipe->bind_sampler_state;
+   aaline->driver_set_sampler_texture = pipe->set_sampler_texture;
+
+   /* override the driver's functions */
+   pipe->create_fs_state = aaline_create_fs_state;
+   pipe->bind_fs_state = aaline_bind_fs_state;
+   pipe->delete_fs_state = aaline_delete_fs_state;
+
+   pipe->bind_sampler_state = aaline_bind_sampler_state;
+   pipe->set_sampler_texture = aaline_set_sampler_texture;
+
+   add_aa_pipe_context(pipe, aaline);
+}
index 4be3830316954b609e16107fe3d7f39bdf661d1b..a7f3b4aecb24df506a82eb192e027faa2ad95480 100644 (file)
@@ -103,6 +103,8 @@ void draw_destroy( struct draw_context *draw )
    draw->pipeline.flatshade->destroy( draw->pipeline.flatshade );
    draw->pipeline.cull->destroy( draw->pipeline.cull );
    draw->pipeline.validate->destroy( draw->pipeline.validate );
+   if (draw->pipeline.aaline)
+      draw->pipeline.aaline->destroy( draw->pipeline.aaline );
    if (draw->pipeline.rasterize)
       draw->pipeline.rasterize->destroy( draw->pipeline.rasterize );
    tgsi_exec_machine_free_data(&draw->machine);
@@ -239,6 +241,26 @@ draw_convert_wide_lines(struct draw_context *draw, boolean enable)
 }
 
 
+/**
+ * The draw module may sometimes generate vertices with extra attributes
+ * (such as texcoords for AA lines).  The driver can call this function
+ * to find those attributes.
+ */
+int
+draw_find_vs_output(struct draw_context *draw,
+                    uint semantic_name, uint semantic_index)
+{
+   /* XXX there may be more than one extra vertex attrib.
+    * For example, simulated gl_FragCoord and gl_PointCoord.
+    */
+   if (draw->extra_vp_outputs.semantic_name == semantic_name &&
+       draw->extra_vp_outputs.semantic_index == semantic_index) {
+      return draw->extra_vp_outputs.slot;
+   }
+   return 0;
+}
+
+
 /**
  * Allocate space for temporary post-transform vertices, such as for clipping.
  */
index ddeb184497aeec4fa8d3b8ba6cd63804d66b4524..82035aa8ada112d4a920023db4ddb0d93b818e5a 100644 (file)
@@ -41,6 +41,7 @@
 #include "pipe/p_state.h"
 
 
+struct pipe_context;
 struct vertex_buffer;
 struct vertex_info;
 struct draw_context;
@@ -93,6 +94,20 @@ void draw_convert_wide_points(struct draw_context *draw, boolean enable);
 
 void draw_convert_wide_lines(struct draw_context *draw, boolean enable);
 
+boolean draw_use_sse(struct draw_context *draw);
+
+void
+draw_install_aaline_stage(struct draw_context *draw, struct pipe_context *pipe);
+
+
+int
+draw_find_vs_output(struct draw_context *draw,
+                    uint semantic_name, uint semantic_index);
+
+
+/*
+ * Vertex shader functions
+ */
 
 struct draw_vertex_shader *
 draw_create_vertex_shader(struct draw_context *draw,
@@ -102,7 +117,11 @@ void draw_bind_vertex_shader(struct draw_context *draw,
 void draw_delete_vertex_shader(struct draw_context *draw,
                                struct draw_vertex_shader *dvs);
 
-boolean draw_use_sse(struct draw_context *draw);
+
+
+/*
+ * Vertex data functions
+ */
 
 void draw_set_vertex_buffer(struct draw_context *draw,
                            unsigned attr,
index 51e2242719a194738a1d54bc6904e5706a0e2d5d..dd9a848863746bb8e0042e218293be629475bf23 100644 (file)
@@ -121,11 +121,15 @@ static void draw_prim_queue_flush( struct draw_context *draw )
 
 void draw_do_flush( struct draw_context *draw, unsigned flags )
 {
+   static boolean flushing = FALSE;
+
    if (0)
       debug_printf("Flushing with %d verts, %d prims\n",
                    draw->vs.queue_nr,
                    draw->pq.queue_nr );
 
+   if (!flushing) {
+      flushing = TRUE;
 
    if (flags >= DRAW_FLUSH_SHADER_QUEUE) {
       if (draw->vs.queue_nr)
@@ -146,6 +150,9 @@ void draw_do_flush( struct draw_context *draw, unsigned flags )
         }
       }    
    }
+
+      flushing = FALSE;
+   }
 }
 
 
index bc11259cb2199e033332332f306e2b6a3a24f670..dd75f9aefc3211d3ca876471dd92325752a88064 100644 (file)
@@ -48,6 +48,7 @@
 #include "tgsi/exec/tgsi_exec.h"
 
 
+struct pipe_context;
 struct gallivm_prog;
 struct gallivm_cpu_engine;
 
@@ -179,6 +180,7 @@ struct draw_context
       struct draw_stage *offset;
       struct draw_stage *unfilled;
       struct draw_stage *stipple;
+      struct draw_stage *aaline;
       struct draw_stage *wide;
       struct draw_stage *rasterize;
    } pipeline;
@@ -212,9 +214,17 @@ struct draw_context
    unsigned nr_planes;
 
    boolean convert_wide_points; /**< convert wide points to tris? */
-   boolean convert_wide_lines;  /**< convert side lines to tris? */
+   boolean convert_wide_lines;  /**< convert wide lines to tris? */
    boolean use_sse;
 
+   /* If a prim stage introduces new vertex attributes, they'll be stored here
+    */
+   struct {
+      uint semantic_name;
+      uint semantic_index;
+      int slot;
+   } extra_vp_outputs;
+
    unsigned reduced_prim;
 
    /** TGSI program interpreter runtime state */
index 4375ebabbc420b5ec2430b0b138a46e70eccbb0b..97e40e372b826ddfe02e65dfeafddff1d083f7c7 100644 (file)
@@ -58,7 +58,13 @@ static struct draw_stage *validate_pipeline( struct draw_stage *stage )
     * shorter pipelines for lines & points.
     */
 
-   if ((draw->rasterizer->line_width != 1.0 && draw->convert_wide_lines) ||
+   if (draw->rasterizer->line_smooth && draw->pipeline.aaline) {
+      draw->pipeline.aaline->next = next;
+      next = draw->pipeline.aaline;
+   }
+
+   if ((draw->rasterizer->line_width != 1.0 && draw->convert_wide_lines
+        && !draw->rasterizer->line_smooth) ||
        (draw->rasterizer->point_size != 1.0 && draw->convert_wide_points) ||
        draw->rasterizer->point_sprite) {
       draw->pipeline.wide->next = next;
index c10ab396d8da888f0db9b1bd58b5288d6c253f59..8bb62b2a0a5cb0e9503c3dc91639699da275d902 100644 (file)
@@ -10,6 +10,7 @@ DRIVER_SOURCES = \
        util/tgsi_build.c \
        util/tgsi_dump.c \
        util/tgsi_parse.c \
+       util/tgsi_transform.c \
        util/tgsi_util.c
 
 C_SOURCES = \
index 5e98f190bbfc8a6720a21ceb47ace10872d43789..254c6adca4404e089a4ac03cd57028c5f8a198ec 100644 (file)
@@ -327,6 +327,9 @@ struct pipe_context *softpipe_create( struct pipe_winsys *pipe_winsys,
       draw_set_rasterize_stage(softpipe->draw, softpipe->setup);
    }
 
+   /* enable aaline stage */
+   draw_install_aaline_stage(softpipe->draw, &softpipe->pipe);
+
    sp_init_surface_functions(softpipe);
 
    return &softpipe->pipe;
index 9d8fd8b750f2f59f583b41140afb5f8a79ee6e89..f9f2c5eaa8f84987524fe365993f0f1b017328ca 100644 (file)
@@ -44,7 +44,8 @@
  * condition that users shouldn't hit anyway.
  */
 static int
-find_vs_output(const struct pipe_shader_state *vs,
+find_vs_output(struct softpipe_context *sp,
+               const struct pipe_shader_state *vs,
                uint semantic_name,
                uint semantic_index)
 {
@@ -54,7 +55,9 @@ find_vs_output(const struct pipe_shader_state *vs,
           vs->output_semantic_index[i] == semantic_index)
          return i;
    }
-   return 0;
+
+   /* See if the draw module is introducing a new attribute... */
+   return draw_find_vs_output(sp->draw, semantic_name, semantic_index);
 }
 
 
@@ -111,24 +114,24 @@ softpipe_get_vertex_info(struct softpipe_context *softpipe)
          int src;
          switch (fs->input_semantic_name[i]) {
          case TGSI_SEMANTIC_POSITION:
-            src = find_vs_output(vs, TGSI_SEMANTIC_POSITION, 0);
+            src = find_vs_output(softpipe, vs, TGSI_SEMANTIC_POSITION, 0);
             draw_emit_vertex_attr(vinfo, EMIT_4F, INTERP_POS, src);
             break;
 
          case TGSI_SEMANTIC_COLOR:
-            src = find_vs_output(vs, TGSI_SEMANTIC_COLOR, 
+            src = find_vs_output(softpipe, vs, TGSI_SEMANTIC_COLOR, 
                                  fs->input_semantic_index[i]);
             draw_emit_vertex_attr(vinfo, EMIT_4F, colorInterp, src);
             break;
 
          case TGSI_SEMANTIC_FOG:
-            src = find_vs_output(vs, TGSI_SEMANTIC_FOG, 0);
+            src = find_vs_output(softpipe, vs, TGSI_SEMANTIC_FOG, 0);
             draw_emit_vertex_attr(vinfo, EMIT_4F, INTERP_PERSPECTIVE, src);
             break;
 
          case TGSI_SEMANTIC_GENERIC:
             /* this includes texcoords and varying vars */
-            src = find_vs_output(vs, TGSI_SEMANTIC_GENERIC,
+            src = find_vs_output(softpipe, vs, TGSI_SEMANTIC_GENERIC,
                                  fs->input_semantic_index[i]);
             draw_emit_vertex_attr(vinfo, EMIT_4F, INTERP_PERSPECTIVE, src);
             break;
@@ -138,7 +141,7 @@ softpipe_get_vertex_info(struct softpipe_context *softpipe)
          }
       }
 
-      softpipe->psize_slot = find_vs_output(vs, TGSI_SEMANTIC_PSIZE, 0);
+      softpipe->psize_slot = find_vs_output(softpipe, vs, TGSI_SEMANTIC_PSIZE, 0);
       if (softpipe->psize_slot > 0) {
          draw_emit_vertex_attr(vinfo, EMIT_4F, INTERP_CONSTANT,
                                softpipe->psize_slot);