llvmpipe: Code generate alpha testing and append to generated fragment shader.
authorJosé Fonseca <jfonseca@vmware.com>
Wed, 19 Aug 2009 19:42:50 +0000 (20:42 +0100)
committerJosé Fonseca <jfonseca@vmware.com>
Sat, 29 Aug 2009 08:21:37 +0000 (09:21 +0100)
src/gallium/drivers/llvmpipe/Makefile
src/gallium/drivers/llvmpipe/SConscript
src/gallium/drivers/llvmpipe/lp_bld_alpha.c [new file with mode: 0644]
src/gallium/drivers/llvmpipe/lp_bld_alpha.h [new file with mode: 0644]
src/gallium/drivers/llvmpipe/lp_quad_depth_test.c
src/gallium/drivers/llvmpipe/lp_quad_fs.c
src/gallium/drivers/llvmpipe/lp_state.h
src/gallium/drivers/llvmpipe/lp_state_derived.c
src/gallium/drivers/llvmpipe/lp_state_fs.c

index 170eefd51a4ca28d8c60bdbda9073ea6e3c7a6e4..96e0380497979e0f0e4e12e3aa5f5bec551f5c6e 100644 (file)
@@ -4,6 +4,7 @@ include $(TOP)/configs/current
 LIBNAME = llvmpipe
 
 C_SOURCES = \
+       lp_bld_alpha.c \
        lp_bld_arit.c \
        lp_bld_blend_aos.c \
        lp_bld_blend_soa.c \
index 97af1b95c34698bd93a5f90f45299f13c2ff1d44..68989792aaa0ec5e7ebbc3cfcd9c7723de1fa534 100644 (file)
@@ -8,6 +8,7 @@ env.ParseConfig('llvm-config --cppflags')
 llvmpipe = env.ConvenienceLibrary(
        target = 'llvmpipe',
        source = [
+               'lp_bld_alpha.c',
                'lp_bld_arit.c',
                'lp_bld_blend_aos.c',
                'lp_bld_blend_soa.c',
diff --git a/src/gallium/drivers/llvmpipe/lp_bld_alpha.c b/src/gallium/drivers/llvmpipe/lp_bld_alpha.c
new file mode 100644 (file)
index 0000000..a3faa22
--- /dev/null
@@ -0,0 +1,68 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * 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 VMWARE 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.
+ *
+ **************************************************************************/
+
+/**
+ * Alpha testing to LLVM IR translation.
+ *
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ */
+
+#include "pipe/p_state.h"
+
+#include "lp_bld_type.h"
+#include "lp_bld_const.h"
+#include "lp_bld_arit.h"
+#include "lp_bld_logic.h"
+#include "lp_bld_debug.h"
+#include "lp_bld_alpha.h"
+
+
+LLVMValueRef
+lp_build_alpha_test(LLVMBuilderRef builder,
+                    const struct pipe_alpha_state *state,
+                    union lp_type type,
+                    LLVMValueRef alpha,
+                    LLVMValueRef mask)
+{
+   struct lp_build_context bld;
+
+   lp_build_context_init(&bld, builder, type);
+
+   if(state->enabled) {
+      LLVMValueRef ref = lp_build_const_uni(type, state->ref_value);
+      LLVMValueRef test = lp_build_cmp(&bld, state->func, alpha, ref);
+
+      lp_build_name(test, "alpha_mask");
+
+      if(mask)
+         mask = LLVMBuildAnd(builder, mask, test, "");
+      else
+         mask = test;
+   }
+
+   return mask;
+}
diff --git a/src/gallium/drivers/llvmpipe/lp_bld_alpha.h b/src/gallium/drivers/llvmpipe/lp_bld_alpha.h
new file mode 100644 (file)
index 0000000..f3fa8b6
--- /dev/null
@@ -0,0 +1,52 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * 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 VMWARE 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.
+ *
+ **************************************************************************/
+
+/**
+ * Alpha testing to LLVM IR translation.
+ *
+ * @author Jose Fonseca <jfonseca@vmware.com>
+ */
+
+#ifndef LP_BLD_ALPHA_H
+#define LP_BLD_ALPHA_H
+
+
+#include <llvm-c/Core.h>  
+
+struct pipe_alpha_state;
+union lp_type;
+
+
+LLVMValueRef
+lp_build_alpha_test(LLVMBuilderRef builder,
+                    const struct pipe_alpha_state *state,
+                    union lp_type type,
+                    LLVMValueRef alpha,
+                    LLVMValueRef mask);
+
+
+#endif /* !LP_BLD_ALPHA_H */
index fefb99c1ffa87458adca71fb301c42c42809f3eb..124301688f797798c59ff921339dc6b97e25ecd2 100644 (file)
@@ -564,75 +564,6 @@ depth_stencil_test_quad(struct quad_stage *qs,
 }
 
 
-#define ALPHATEST( FUNC, COMP )                                         \
-   static int                                                          \
-   alpha_test_quads_##FUNC( struct quad_stage *qs,                      \
-                           struct quad_header *quads[],                 \
-                           unsigned nr )                                \
-   {                                                                    \
-      const float ref = qs->llvmpipe->depth_stencil->alpha.ref_value;   \
-      const uint cbuf = 0; /* only output[0].alpha is tested */         \
-      unsigned pass_nr = 0;                                             \
-      unsigned i;                                                       \
-                                                                        \
-      for (i = 0; i < nr; i++) {                                        \
-         const float *aaaa = quads[i]->output.color[cbuf][3];           \
-         unsigned passMask = 0;                                         \
-                                                                        \
-         if (!quads[i]->inout.mask)                                     \
-            continue;                                                   \
-                                                                        \
-         if (aaaa[0] COMP ref) passMask |= (1 << 0);                    \
-         if (aaaa[1] COMP ref) passMask |= (1 << 1);                    \
-         if (aaaa[2] COMP ref) passMask |= (1 << 2);                    \
-         if (aaaa[3] COMP ref) passMask |= (1 << 3);                    \
-                                                                        \
-         quads[i]->inout.mask &= passMask;                              \
-                                                                        \
-         if (quads[i]->inout.mask)                                      \
-            ++pass_nr;                                                  \
-      }                                                                 \
-                                                                        \
-      return pass_nr;                                                   \
-   }
-
-
-ALPHATEST( LESS,     < )
-ALPHATEST( EQUAL,    == )
-ALPHATEST( LEQUAL,   <= )
-ALPHATEST( GREATER,  > )
-ALPHATEST( NOTEQUAL, != )
-ALPHATEST( GEQUAL,   >= )
-
-
-/* XXX: Incorporate into shader using KILP.
- */
-static int
-alpha_test_quads(struct quad_stage *qs, 
-                 struct quad_header *quads[], 
-                 unsigned nr)
-{
-   switch (qs->llvmpipe->depth_stencil->alpha.func) {
-   case PIPE_FUNC_LESS:
-      return alpha_test_quads_LESS( qs, quads, nr );
-   case PIPE_FUNC_EQUAL:
-      return alpha_test_quads_EQUAL( qs, quads, nr );
-      break;
-   case PIPE_FUNC_LEQUAL:
-      return alpha_test_quads_LEQUAL( qs, quads, nr );
-   case PIPE_FUNC_GREATER:
-      return alpha_test_quads_GREATER( qs, quads, nr );
-   case PIPE_FUNC_NOTEQUAL:
-      return alpha_test_quads_NOTEQUAL( qs, quads, nr );
-   case PIPE_FUNC_GEQUAL:
-      return alpha_test_quads_GEQUAL( qs, quads, nr );
-   case PIPE_FUNC_ALWAYS:
-      return nr;
-   case PIPE_FUNC_NEVER:
-   default:
-      return 0;
-   }
-}
 
 static unsigned mask_count[0x8] = 
 {
@@ -659,10 +590,6 @@ depth_test_quads_fallback(struct quad_stage *qs,
    struct depth_data data;
 
 
-   if (qs->llvmpipe->depth_stencil->alpha.enabled) {
-      alpha_test_quads(qs, quads, nr);
-   }
-
    if (qs->llvmpipe->framebuffer.zsbuf && 
        (qs->llvmpipe->depth_stencil->depth.enabled ||
         qs->llvmpipe->depth_stencil->stencil[0].enabled)) {
@@ -801,8 +728,6 @@ choose_depth_test(struct quad_stage *qs,
 {
    boolean interp_depth = !qs->llvmpipe->fs->info.writes_z;
 
-   boolean alpha = qs->llvmpipe->depth_stencil->alpha.enabled;
-
    boolean depth = (qs->llvmpipe->framebuffer.zsbuf && 
                     qs->llvmpipe->depth_stencil->depth.enabled);
 
@@ -815,13 +740,11 @@ choose_depth_test(struct quad_stage *qs,
 
    qs->run = depth_test_quads_fallback;
 
-   if (!alpha &&
-       !depth &&
+   if (!depth &&
        !stencil) {
       qs->run = depth_noop;
    }
-   else if (!alpha && 
-            interp_depth && 
+   else if (interp_depth &&
             depth && 
             depthfunc == PIPE_FUNC_LESS && 
             depthwrite && 
index 78b4e1bab6d9f37243bae6ceb611804489e119ae..2736efc956a734e0bc9adb13bd958283e7e267ff 100644 (file)
@@ -76,10 +76,15 @@ shade_quad(struct quad_stage *qs, struct quad_header *quad)
 {
    struct quad_shade_stage *qss = quad_shade_stage( qs );
    struct llvmpipe_context *llvmpipe = qs->llvmpipe;
+   struct lp_fragment_shader *fs = llvmpipe->fs;
    void *constants;
    struct tgsi_sampler **samplers;
    unsigned chan_index;
 
+   assert(fs->current);
+   if(!fs->current)
+      return FALSE;
+
    constants = llvmpipe->mapped_constants[PIPE_SHADER_FRAGMENT];
    samplers = (struct tgsi_sampler **)llvmpipe->tgsi.frag_samplers_list;
 
@@ -87,16 +92,16 @@ shade_quad(struct quad_stage *qs, struct quad_header *quad)
       qss->mask[chan_index] = ~0;
 
    /* run shader */
-   llvmpipe->fs->jit_function( quad->input.x0,
-                               quad->input.y0,
-                               quad->coef->a0,
-                               quad->coef->dadx,
-                               quad->coef->dady,
-                               constants,
-                               qss->mask,
-                               quad->output.color,
-                               quad->output.depth,
-                               samplers);
+   fs->current->jit_function( quad->input.x0,
+                              quad->input.y0,
+                              quad->coef->a0,
+                              quad->coef->dadx,
+                              quad->coef->dady,
+                              constants,
+                              qss->mask,
+                              quad->output.color,
+                              quad->output.depth,
+                              samplers);
 
    for (chan_index = 0; chan_index < NUM_CHANNELS; ++chan_index)
       if(!qss->mask[chan_index])
index f8b3793a59500e662899dee41e18d1479b80b34e..db21096f215a52cf1cb42b2edf52790d7c7bf635 100644 (file)
@@ -70,6 +70,28 @@ typedef void
                      void *depth,
                      struct tgsi_sampler **samplers);
 
+
+struct lp_fragment_shader;
+
+
+/**
+ * Subclass of pipe_shader_state (though it doesn't really need to be).
+ *
+ * This is starting to look an awful lot like a quad pipeline stage...
+ */
+struct lp_fragment_shader_variant
+{
+   struct lp_fragment_shader *shader;
+   struct pipe_alpha_state alpha;
+
+   LLVMValueRef function;
+
+   lp_shader_fs_func jit_function;
+
+   struct lp_fragment_shader_variant *next;
+};
+
+
 /**
  * Subclass of pipe_shader_state (though it doesn't really need to be).
  *
@@ -83,9 +105,9 @@ struct lp_fragment_shader
 
    struct llvmpipe_screen *screen;
 
-   LLVMValueRef function;
+   struct lp_fragment_shader_variant *variants;
 
-   lp_shader_fs_func jit_function;
+   struct lp_fragment_shader_variant *current;
 };
 
 
@@ -183,6 +205,7 @@ void llvmpipe_set_vertex_buffers(struct pipe_context *,
                                  unsigned count,
                                  const struct pipe_vertex_buffer *);
 
+void llvmpipe_update_fs(struct llvmpipe_context *lp);
 
 void llvmpipe_update_derived( struct llvmpipe_context *llvmpipe );
 
index 79861b2d13371664a1b35d39562025f286ec9a0f..b42e6b15021d33f52e2e0a737dd7e363b0ebfbbb 100644 (file)
@@ -244,6 +244,11 @@ void llvmpipe_update_derived( struct llvmpipe_context *llvmpipe )
                           LP_NEW_FRAMEBUFFER))
       compute_cliprect(llvmpipe);
 
+   if (llvmpipe->dirty & (LP_NEW_FS |
+                          LP_NEW_DEPTH_STENCIL_ALPHA))
+      llvmpipe_update_fs( llvmpipe );
+
+
    if (llvmpipe->dirty & (LP_NEW_BLEND |
                           LP_NEW_DEPTH_STENCIL_ALPHA |
                           LP_NEW_FRAMEBUFFER |
index ffcc8336b1205511c4818652635d5a08fd70d179..702be429164cbadb97bda0a73eb3d7300820af5f 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "pipe/p_defines.h"
 #include "util/u_memory.h"
+#include "util/u_debug_dump.h"
 #include "pipe/internal/p_winsys_screen.h"
 #include "pipe/p_shader_tokens.h"
 #include "draw/draw_context.h"
@@ -36,6 +37,7 @@
 #include "tgsi/tgsi_parse.h"
 #include "lp_bld_type.h"
 #include "lp_bld_tgsi.h"
+#include "lp_bld_alpha.h"
 #include "lp_bld_swizzle.h"
 #include "lp_bld_debug.h"
 #include "lp_screen.h"
@@ -103,10 +105,12 @@ setup_pos_vector(LLVMBuilderRef builder,
 }
 
 
-static void
+static struct lp_fragment_shader_variant *
 shader_generate(struct llvmpipe_screen *screen,
-                struct lp_fragment_shader *shader)
+                struct lp_fragment_shader *shader,
+                const struct pipe_alpha_state *alpha)
 {
+   struct lp_fragment_shader_variant *variant;
    const struct tgsi_token *tokens = shader->base.tokens;
    union lp_type type;
    LLVMTypeRef elem_type;
@@ -129,10 +133,25 @@ shader_generate(struct llvmpipe_screen *screen,
    LLVMValueRef pos[NUM_CHANNELS];
    LLVMValueRef outputs[PIPE_MAX_SHADER_OUTPUTS][NUM_CHANNELS];
    LLVMValueRef mask;
+   LLVMValueRef fetch_texel;
    unsigned i;
    unsigned attrib;
    unsigned chan;
 
+#ifdef DEBUG
+   tgsi_dump(shader->base.tokens, 0);
+   debug_printf("alpha.enabled = %u\n", alpha->enabled);
+   debug_printf("alpha.func = %s\n", debug_dump_func(alpha->func, TRUE));
+   debug_printf("alpha.ref_value = %f\n", alpha->ref_value);
+#endif
+
+   variant = CALLOC_STRUCT(lp_fragment_shader_variant);
+   if(!variant)
+      return NULL;
+
+   variant->shader = shader;
+   memcpy(&variant->alpha, alpha, sizeof *alpha);
+
    type.value = 0;
    type.floating = TRUE; /* floating point values */
    type.sign = TRUE;     /* values are signed */
@@ -157,22 +176,22 @@ shader_generate(struct llvmpipe_screen *screen,
 
    func_type = LLVMFunctionType(LLVMVoidType(), arg_types, Elements(arg_types), 0);
 
-   shader->function = LLVMAddFunction(screen->module, "shader", func_type);
-   LLVMSetFunctionCallConv(shader->function, LLVMCCallConv);
+   variant->function = LLVMAddFunction(screen->module, "shader", func_type);
+   LLVMSetFunctionCallConv(variant->function, LLVMCCallConv);
    for(i = 0; i < Elements(arg_types); ++i)
       if(LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind)
-         LLVMAddAttribute(LLVMGetParam(shader->function, i), LLVMNoAliasAttribute);
-
-   x            = LLVMGetParam(shader->function, 0);
-   y            = LLVMGetParam(shader->function, 1);
-   a0_ptr       = LLVMGetParam(shader->function, 2);
-   dadx_ptr     = LLVMGetParam(shader->function, 3);
-   dady_ptr     = LLVMGetParam(shader->function, 4);
-   consts_ptr   = LLVMGetParam(shader->function, 5);
-   mask_ptr     = LLVMGetParam(shader->function, 6);
-   color_ptr    = LLVMGetParam(shader->function, 7);
-   depth_ptr    = LLVMGetParam(shader->function, 8);
-   samplers_ptr = LLVMGetParam(shader->function, 9);
+         LLVMAddAttribute(LLVMGetParam(variant->function, i), LLVMNoAliasAttribute);
+
+   x            = LLVMGetParam(variant->function, 0);
+   y            = LLVMGetParam(variant->function, 1);
+   a0_ptr       = LLVMGetParam(variant->function, 2);
+   dadx_ptr     = LLVMGetParam(variant->function, 3);
+   dady_ptr     = LLVMGetParam(variant->function, 4);
+   consts_ptr   = LLVMGetParam(variant->function, 5);
+   mask_ptr     = LLVMGetParam(variant->function, 6);
+   color_ptr    = LLVMGetParam(variant->function, 7);
+   depth_ptr    = LLVMGetParam(variant->function, 8);
+   samplers_ptr = LLVMGetParam(variant->function, 9);
 
    lp_build_name(x, "x");
    lp_build_name(y, "y");
@@ -185,7 +204,7 @@ shader_generate(struct llvmpipe_screen *screen,
    lp_build_name(depth_ptr, "depth");
    lp_build_name(samplers_ptr, "samplers");
 
-   block = LLVMAppendBasicBlock(shader->function, "entry");
+   block = LLVMAppendBasicBlock(variant->function, "entry");
    builder = LLVMCreateBuilder();
    LLVMPositionBuilderAtEnd(builder, block);
 
@@ -210,6 +229,12 @@ shader_generate(struct llvmpipe_screen *screen,
                   LLVMValueRef output_ptr = LLVMBuildGEP(builder, color_ptr, &index, 1, "");
                   lp_build_name(outputs[attrib][chan], "color%u.%c", attrib, "rgba"[chan]);
                   LLVMBuildStore(builder, outputs[attrib][chan], output_ptr);
+
+                  /* Alpha test */
+                  /* XXX: should the alpha reference value be passed separately? */
+                  if(cbuf == 0 && chan == 3)
+                     mask = lp_build_alpha_test(builder, alpha, type, outputs[attrib][chan], mask);
+
                   break;
                }
 
@@ -228,44 +253,16 @@ shader_generate(struct llvmpipe_screen *screen,
    LLVMBuildRetVoid(builder);;
 
    LLVMDisposeBuilder(builder);
-}
-
 
-void *
-llvmpipe_create_fs_state(struct pipe_context *pipe,
-                         const struct pipe_shader_state *templ)
-{
-   struct llvmpipe_screen *screen = llvmpipe_screen(pipe->screen);
-   struct lp_fragment_shader *shader;
-   LLVMValueRef fetch_texel;
-
-   shader = CALLOC_STRUCT(lp_fragment_shader);
-   if (!shader)
-      return NULL;
-
-   /* get/save the summary info for this shader */
-   tgsi_scan_shader(templ->tokens, &shader->info);
-
-   /* we need to keep a local copy of the tokens */
-   shader->base.tokens = tgsi_dup_tokens(templ->tokens);
-
-   shader->screen = screen;
+   LLVMRunFunctionPassManager(screen->pass, variant->function);
 
 #ifdef DEBUG
-   tgsi_dump(templ->tokens, 0);
-#endif
-
-   shader_generate(screen, shader);
-
-   LLVMRunFunctionPassManager(screen->pass, shader->function);
-
-#ifdef DEBUG
-   LLVMDumpValue(shader->function);
+   LLVMDumpValue(variant->function);
    debug_printf("\n");
 #endif
 
-   if(LLVMVerifyFunction(shader->function, LLVMPrintMessageAction)) {
-      LLVMDumpValue(shader->function);
+   if(LLVMVerifyFunction(variant->function, LLVMPrintMessageAction)) {
+      LLVMDumpValue(variant->function);
       abort();
    }
 
@@ -278,12 +275,38 @@ llvmpipe_create_fs_state(struct pipe_context *pipe,
       }
    }
 
-   shader->jit_function = (lp_shader_fs_func)LLVMGetPointerToGlobal(screen->engine, shader->function);
+   variant->jit_function = (lp_shader_fs_func)LLVMGetPointerToGlobal(screen->engine, variant->function);
 
 #ifdef DEBUG
-   lp_disassemble(shader->jit_function);
+   lp_disassemble(variant->jit_function);
 #endif
 
+   variant->next = shader->variants;
+   shader->variants = variant;
+
+   return variant;
+}
+
+
+void *
+llvmpipe_create_fs_state(struct pipe_context *pipe,
+                         const struct pipe_shader_state *templ)
+{
+   struct llvmpipe_screen *screen = llvmpipe_screen(pipe->screen);
+   struct lp_fragment_shader *shader;
+
+   shader = CALLOC_STRUCT(lp_fragment_shader);
+   if (!shader)
+      return NULL;
+
+   /* get/save the summary info for this shader */
+   tgsi_scan_shader(templ->tokens, &shader->info);
+
+   /* we need to keep a local copy of the tokens */
+   shader->base.tokens = tgsi_dup_tokens(templ->tokens);
+
+   shader->screen = screen;
+
    return shader;
 }
 
@@ -303,14 +326,24 @@ void
 llvmpipe_delete_fs_state(struct pipe_context *pipe, void *fs)
 {
    struct lp_fragment_shader *shader = fs;
+   struct lp_fragment_shader_variant *variant;
    struct llvmpipe_screen *screen = shader->screen;
 
    assert(fs != llvmpipe_context(pipe)->fs);
-   
-   if(shader->function) {
-      if(shader->jit_function)
-         LLVMFreeMachineCodeForFunction(screen->engine, shader->function);
-      LLVMDeleteFunction(shader->function);
+
+   variant = shader->variants;
+   while(variant) {
+      struct lp_fragment_shader_variant *next = variant->next;
+
+      if(variant->function) {
+         if(variant->jit_function)
+            LLVMFreeMachineCodeForFunction(screen->engine, variant->function);
+         LLVMDeleteFunction(variant->function);
+      }
+
+      FREE(variant);
+
+      variant = next;
    }
 
    FREE((void *) shader->base.tokens);
@@ -395,3 +428,24 @@ llvmpipe_set_constant_buffer(struct pipe_context *pipe,
 
    llvmpipe->dirty |= LP_NEW_CONSTANTS;
 }
+
+
+void llvmpipe_update_fs(struct llvmpipe_context *lp)
+{
+   struct lp_fragment_shader *shader = lp->fs;
+   const struct pipe_alpha_state *alpha = &lp->depth_stencil->alpha;
+   struct lp_fragment_shader_variant *variant;
+
+   variant = shader->variants;
+   while(variant) {
+      if(memcmp(&variant->alpha, alpha, sizeof *alpha) == 0)
+         break;
+
+      variant = variant->next;
+   }
+
+   if(!variant)
+      variant = shader_generate(shader->screen, shader, alpha);
+
+   shader->current = variant;
+}