Home | History | Annotate | Download | only in glsl
      1 /*
      2  * Copyright  2010 Intel Corporation
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 /**
     25  * \file lower_discard.cpp
     26  *
     27  * This pass moves discards out of if-statements.
     28  *
     29  * Case 1: The "then" branch contains a conditional discard:
     30  * ---------------------------------------------------------
     31  *
     32  *    if (cond1) {
     33  *	 s1;
     34  *	 discard cond2;
     35  *	 s2;
     36  *    } else {
     37  *	 s3;
     38  *    }
     39  *
     40  * becomes:
     41  *
     42  *    temp = false;
     43  *    if (cond1) {
     44  *	 s1;
     45  *	 temp = cond2;
     46  *	 s2;
     47  *    } else {
     48  *	 s3;
     49  *    }
     50  *    discard temp;
     51  *
     52  * Case 2: The "else" branch contains a conditional discard:
     53  * ---------------------------------------------------------
     54  *
     55  *    if (cond1) {
     56  *	 s1;
     57  *    } else {
     58  *	 s2;
     59  *	 discard cond2;
     60  *	 s3;
     61  *    }
     62  *
     63  * becomes:
     64  *
     65  *    temp = false;
     66  *    if (cond1) {
     67  *	 s1;
     68  *    } else {
     69  *	 s2;
     70  *	 temp = cond2;
     71  *	 s3;
     72  *    }
     73  *    discard temp;
     74  *
     75  * Case 3: Both branches contain a conditional discard:
     76  * ----------------------------------------------------
     77  *
     78  *    if (cond1) {
     79  *	 s1;
     80  *	 discard cond2;
     81  *	 s2;
     82  *    } else {
     83  *	 s3;
     84  *	 discard cond3;
     85  *	 s4;
     86  *    }
     87  *
     88  * becomes:
     89  *
     90  *    temp = false;
     91  *    if (cond1) {
     92  *	 s1;
     93  *	 temp = cond2;
     94  *	 s2;
     95  *    } else {
     96  *	 s3;
     97  *	 temp = cond3;
     98  *	 s4;
     99  *    }
    100  *    discard temp;
    101  *
    102  * If there are multiple conditional discards, we need only deal with one of
    103  * them.  Repeatedly applying this pass will take care of the others.
    104  *
    105  * Unconditional discards are treated as having a condition of "true".
    106  */
    107 
    108 #include "compiler/glsl_types.h"
    109 #include "ir.h"
    110 
    111 namespace {
    112 
    113 class lower_discard_visitor : public ir_hierarchical_visitor {
    114 public:
    115    lower_discard_visitor()
    116    {
    117       this->progress = false;
    118    }
    119 
    120    ir_visitor_status visit_leave(ir_if *);
    121 
    122    bool progress;
    123 };
    124 
    125 } /* anonymous namespace */
    126 
    127 bool
    128 lower_discard(exec_list *instructions)
    129 {
    130    lower_discard_visitor v;
    131 
    132    visit_list_elements(&v, instructions);
    133 
    134    return v.progress;
    135 }
    136 
    137 
    138 static ir_discard *
    139 find_discard(exec_list &instructions)
    140 {
    141    foreach_in_list(ir_instruction, node, &instructions) {
    142       ir_discard *ir = node->as_discard();
    143       if (ir != NULL)
    144 	 return ir;
    145    }
    146    return NULL;
    147 }
    148 
    149 
    150 static void
    151 replace_discard(void *mem_ctx, ir_variable *var, ir_discard *ir)
    152 {
    153    ir_rvalue *condition = ir->condition;
    154 
    155    /* For unconditional discards, use "true" as the condition. */
    156    if (condition == NULL)
    157       condition = new(mem_ctx) ir_constant(true);
    158 
    159    ir_assignment *assignment =
    160       new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(var),
    161 				 condition, NULL);
    162 
    163    ir->replace_with(assignment);
    164 }
    165 
    166 
    167 ir_visitor_status
    168 lower_discard_visitor::visit_leave(ir_if *ir)
    169 {
    170    ir_discard *then_discard = find_discard(ir->then_instructions);
    171    ir_discard *else_discard = find_discard(ir->else_instructions);
    172 
    173    if (then_discard == NULL && else_discard == NULL)
    174       return visit_continue;
    175 
    176    void *mem_ctx = ralloc_parent(ir);
    177 
    178    ir_variable *temp = new(mem_ctx) ir_variable(glsl_type::bool_type,
    179 						"discard_cond_temp",
    180 						ir_var_temporary);
    181    ir_assignment *temp_initializer =
    182       new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(temp),
    183 				 new(mem_ctx) ir_constant(false), NULL);
    184 
    185    ir->insert_before(temp);
    186    ir->insert_before(temp_initializer);
    187 
    188    if (then_discard != NULL)
    189       replace_discard(mem_ctx, temp, then_discard);
    190 
    191    if (else_discard != NULL)
    192       replace_discard(mem_ctx, temp, else_discard);
    193 
    194    ir_discard *discard = then_discard != NULL ? then_discard : else_discard;
    195    discard->condition = new(mem_ctx) ir_dereference_variable(temp);
    196    ir->insert_after(discard);
    197 
    198    this->progress = true;
    199 
    200    return visit_continue;
    201 }
    202