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 "glsl_types.h"
    109 #include "ir.h"
    110 
    111 class lower_discard_visitor : public ir_hierarchical_visitor {
    112 public:
    113    lower_discard_visitor()
    114    {
    115       this->progress = false;
    116    }
    117 
    118    ir_visitor_status visit_leave(ir_if *);
    119 
    120    bool progress;
    121 };
    122 
    123 
    124 bool
    125 lower_discard(exec_list *instructions)
    126 {
    127    lower_discard_visitor v;
    128 
    129    visit_list_elements(&v, instructions);
    130 
    131    return v.progress;
    132 }
    133 
    134 
    135 static ir_discard *
    136 find_discard(exec_list &instructions)
    137 {
    138    foreach_list(n, &instructions) {
    139       ir_discard *ir = ((ir_instruction *) n)->as_discard();
    140       if (ir != NULL)
    141 	 return ir;
    142    }
    143    return NULL;
    144 }
    145 
    146 
    147 static void
    148 replace_discard(void *mem_ctx, ir_variable *var, ir_discard *ir)
    149 {
    150    ir_rvalue *condition = ir->condition;
    151 
    152    /* For unconditional discards, use "true" as the condition. */
    153    if (condition == NULL)
    154       condition = new(mem_ctx) ir_constant(true);
    155 
    156    ir_assignment *assignment =
    157       new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(var),
    158 				 condition, NULL);
    159 
    160    ir->replace_with(assignment);
    161 }
    162 
    163 
    164 ir_visitor_status
    165 lower_discard_visitor::visit_leave(ir_if *ir)
    166 {
    167    ir_discard *then_discard = find_discard(ir->then_instructions);
    168    ir_discard *else_discard = find_discard(ir->else_instructions);
    169 
    170    if (then_discard == NULL && else_discard == NULL)
    171       return visit_continue;
    172 
    173    void *mem_ctx = hieralloc_parent(ir);
    174 
    175    ir_variable *temp = new(mem_ctx) ir_variable(glsl_type::bool_type,
    176 						"discard_cond_temp",
    177 						ir_var_temporary);
    178    ir_assignment *temp_initializer =
    179       new(mem_ctx) ir_assignment(new(mem_ctx) ir_dereference_variable(temp),
    180 				 new(mem_ctx) ir_constant(false), NULL);
    181 
    182    ir->insert_before(temp);
    183    ir->insert_before(temp_initializer);
    184 
    185    if (then_discard != NULL)
    186       replace_discard(mem_ctx, temp, then_discard);
    187 
    188    if (else_discard != NULL)
    189       replace_discard(mem_ctx, temp, else_discard);
    190 
    191    ir_discard *discard = then_discard != NULL ? then_discard : else_discard;
    192    discard->condition = new(mem_ctx) ir_dereference_variable(temp);
    193    ir->insert_after(discard);
    194 
    195    this->progress = true;
    196 
    197    return visit_continue;
    198 }
    199