From 9ae47069b4a2b67e381b02d805f1ca74f31ea7b8 Mon Sep 17 00:00:00 2001 From: José Fonseca Date: Wed, 19 Aug 2009 20:42:50 +0100 Subject: llvmpipe: Code generate alpha testing and append to generated fragment shader. --- src/gallium/drivers/llvmpipe/Makefile | 1 + src/gallium/drivers/llvmpipe/SConscript | 1 + src/gallium/drivers/llvmpipe/lp_bld_alpha.c | 68 +++++++++ src/gallium/drivers/llvmpipe/lp_bld_alpha.h | 52 +++++++ src/gallium/drivers/llvmpipe/lp_quad_depth_test.c | 81 +---------- src/gallium/drivers/llvmpipe/lp_quad_fs.c | 25 ++-- src/gallium/drivers/llvmpipe/lp_state.h | 27 +++- src/gallium/drivers/llvmpipe/lp_state_derived.c | 5 + src/gallium/drivers/llvmpipe/lp_state_fs.c | 166 ++++++++++++++-------- 9 files changed, 279 insertions(+), 147 deletions(-) create mode 100644 src/gallium/drivers/llvmpipe/lp_bld_alpha.c create mode 100644 src/gallium/drivers/llvmpipe/lp_bld_alpha.h (limited to 'src/gallium/drivers') diff --git a/src/gallium/drivers/llvmpipe/Makefile b/src/gallium/drivers/llvmpipe/Makefile index 170eefd51a..96e0380497 100644 --- a/src/gallium/drivers/llvmpipe/Makefile +++ b/src/gallium/drivers/llvmpipe/Makefile @@ -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 \ diff --git a/src/gallium/drivers/llvmpipe/SConscript b/src/gallium/drivers/llvmpipe/SConscript index 97af1b95c3..68989792aa 100644 --- a/src/gallium/drivers/llvmpipe/SConscript +++ b/src/gallium/drivers/llvmpipe/SConscript @@ -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 index 0000000000..a3faa22b99 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_bld_alpha.c @@ -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 + */ + +#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 index 0000000000..f3fa8b6053 --- /dev/null +++ b/src/gallium/drivers/llvmpipe/lp_bld_alpha.h @@ -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 + */ + +#ifndef LP_BLD_ALPHA_H +#define LP_BLD_ALPHA_H + + +#include + +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 */ diff --git a/src/gallium/drivers/llvmpipe/lp_quad_depth_test.c b/src/gallium/drivers/llvmpipe/lp_quad_depth_test.c index fefb99c1ff..124301688f 100644 --- a/src/gallium/drivers/llvmpipe/lp_quad_depth_test.c +++ b/src/gallium/drivers/llvmpipe/lp_quad_depth_test.c @@ -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 && diff --git a/src/gallium/drivers/llvmpipe/lp_quad_fs.c b/src/gallium/drivers/llvmpipe/lp_quad_fs.c index 78b4e1bab6..2736efc956 100644 --- a/src/gallium/drivers/llvmpipe/lp_quad_fs.c +++ b/src/gallium/drivers/llvmpipe/lp_quad_fs.c @@ -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]) diff --git a/src/gallium/drivers/llvmpipe/lp_state.h b/src/gallium/drivers/llvmpipe/lp_state.h index f8b3793a59..db21096f21 100644 --- a/src/gallium/drivers/llvmpipe/lp_state.h +++ b/src/gallium/drivers/llvmpipe/lp_state.h @@ -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 ); diff --git a/src/gallium/drivers/llvmpipe/lp_state_derived.c b/src/gallium/drivers/llvmpipe/lp_state_derived.c index 79861b2d13..b42e6b1502 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_derived.c +++ b/src/gallium/drivers/llvmpipe/lp_state_derived.c @@ -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 | diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.c b/src/gallium/drivers/llvmpipe/lp_state_fs.c index ffcc8336b1..702be42916 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.c +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.c @@ -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; +} -- cgit v1.2.3