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 /** @file lower_discard_flow.cpp
     25  *
     26  * Implements the GLSL 1.30 revision 9 rule for fragment shader
     27  * discard handling:
     28  *
     29  *     "Control flow exits the shader, and subsequent implicit or
     30  *      explicit derivatives are undefined when this control flow is
     31  *      non-uniform (meaning different fragments within the primitive
     32  *      take different control paths)."
     33  *
     34  * There seem to be two conflicting things here.  "Control flow exits
     35  * the shader" sounds like the discarded fragments should effectively
     36  * jump to the end of the shader, but that breaks derivatives in the
     37  * case of uniform control flow and causes rendering failure in the
     38  * bushes in Unigine Tropics.
     39  *
     40  * The question, then, is whether the intent was "loops stop at the
     41  * point that the only active channels left are discarded pixels" or
     42  * "discarded pixels become inactive at the point that control flow
     43  * returns to the top of a loop".  This implements the second
     44  * interpretation.
     45  */
     46 
     47 #include "glsl_types.h"
     48 #include "ir.h"
     49 #include "program/hash_table.h"
     50 
     51 class lower_discard_flow_visitor : public ir_hierarchical_visitor {
     52 public:
     53    lower_discard_flow_visitor(ir_variable *discarded)
     54    : discarded(discarded)
     55    {
     56       mem_ctx = ralloc_parent(discarded);
     57    }
     58 
     59    ~lower_discard_flow_visitor()
     60    {
     61    }
     62 
     63    ir_visitor_status visit_enter(ir_discard *ir);
     64    ir_visitor_status visit_enter(ir_loop_jump *ir);
     65    ir_visitor_status visit_enter(ir_loop *ir);
     66    ir_visitor_status visit_enter(ir_function_signature *ir);
     67 
     68    ir_if *generate_discard_break();
     69 
     70    ir_variable *discarded;
     71    void *mem_ctx;
     72 };
     73 
     74 ir_visitor_status
     75 lower_discard_flow_visitor::visit_enter(ir_loop_jump *ir)
     76 {
     77    if (ir->mode != ir_loop_jump::jump_continue)
     78       return visit_continue;
     79 
     80    ir->insert_before(generate_discard_break());
     81 
     82    return visit_continue;
     83 }
     84 
     85 ir_visitor_status
     86 lower_discard_flow_visitor::visit_enter(ir_discard *ir)
     87 {
     88    ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(discarded);
     89    ir_rvalue *rhs = new(mem_ctx) ir_constant(true);
     90    ir_assignment *assign = new(mem_ctx) ir_assignment(lhs, rhs);
     91    ir->insert_before(assign);
     92 
     93    return visit_continue;
     94 }
     95 
     96 ir_visitor_status
     97 lower_discard_flow_visitor::visit_enter(ir_loop *ir)
     98 {
     99    ir->body_instructions.push_tail(generate_discard_break());
    100 
    101    return visit_continue;
    102 }
    103 
    104 ir_visitor_status
    105 lower_discard_flow_visitor::visit_enter(ir_function_signature *ir)
    106 {
    107    if (strcmp(ir->function_name(), "main") != 0)
    108       return visit_continue;
    109 
    110    ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(discarded);
    111    ir_rvalue *rhs = new(mem_ctx) ir_constant(false);
    112    ir_assignment *assign = new(mem_ctx) ir_assignment(lhs, rhs);
    113    ir->body.push_head(assign);
    114 
    115    return visit_continue;
    116 }
    117 
    118 ir_if *
    119 lower_discard_flow_visitor::generate_discard_break()
    120 {
    121    ir_rvalue *if_condition = new(mem_ctx) ir_dereference_variable(discarded);
    122    ir_if *if_inst = new(mem_ctx) ir_if(if_condition);
    123 
    124    ir_instruction *br = new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_break);
    125    if_inst->then_instructions.push_tail(br);
    126 
    127    return if_inst;
    128 }
    129 
    130 void
    131 lower_discard_flow(exec_list *ir)
    132 {
    133    void *mem_ctx = ir;
    134 
    135    ir_variable *var = new(mem_ctx) ir_variable(glsl_type::bool_type,
    136 					       "discarded",
    137 					       ir_var_temporary);
    138 
    139    ir->push_head(var);
    140 
    141    lower_discard_flow_visitor v(var);
    142 
    143    visit_list_elements(&v, ir);
    144 }
    145