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 "compiler/glsl_types.h" 48#include "ir.h" 49 50namespace { 51 52class lower_discard_flow_visitor : public ir_hierarchical_visitor { 53public: 54 lower_discard_flow_visitor(ir_variable *discarded) 55 : discarded(discarded) 56 { 57 mem_ctx = ralloc_parent(discarded); 58 } 59 60 ~lower_discard_flow_visitor() 61 { 62 } 63 64 ir_visitor_status visit(ir_loop_jump *ir); 65 ir_visitor_status visit_enter(ir_discard *ir); 66 ir_visitor_status visit_enter(ir_loop *ir); 67 ir_visitor_status visit_enter(ir_function_signature *ir); 68 69 ir_if *generate_discard_break(); 70 71 ir_variable *discarded; 72 void *mem_ctx; 73}; 74 75} /* anonymous namespace */ 76 77ir_visitor_status 78lower_discard_flow_visitor::visit(ir_loop_jump *ir) 79{ 80 if (ir->mode != ir_loop_jump::jump_continue) 81 return visit_continue; 82 83 ir->insert_before(generate_discard_break()); 84 85 return visit_continue; 86} 87 88ir_visitor_status 89lower_discard_flow_visitor::visit_enter(ir_discard *ir) 90{ 91 ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(discarded); 92 ir_rvalue *rhs; 93 if (ir->condition) { 94 /* discarded <- condition, use (var_ref discarded) as the condition */ 95 rhs = ir->condition; 96 ir->condition = new(mem_ctx) ir_dereference_variable(discarded); 97 } else { 98 rhs = new(mem_ctx) ir_constant(true); 99 } 100 ir_assignment *assign = new(mem_ctx) ir_assignment(lhs, rhs); 101 ir->insert_before(assign); 102 103 return visit_continue; 104} 105 106ir_visitor_status 107lower_discard_flow_visitor::visit_enter(ir_loop *ir) 108{ 109 ir->body_instructions.push_tail(generate_discard_break()); 110 111 return visit_continue; 112} 113 114ir_visitor_status 115lower_discard_flow_visitor::visit_enter(ir_function_signature *ir) 116{ 117 if (strcmp(ir->function_name(), "main") != 0) 118 return visit_continue; 119 120 ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(discarded); 121 ir_rvalue *rhs = new(mem_ctx) ir_constant(false); 122 ir_assignment *assign = new(mem_ctx) ir_assignment(lhs, rhs); 123 ir->body.push_head(assign); 124 125 return visit_continue; 126} 127 128ir_if * 129lower_discard_flow_visitor::generate_discard_break() 130{ 131 ir_rvalue *if_condition = new(mem_ctx) ir_dereference_variable(discarded); 132 ir_if *if_inst = new(mem_ctx) ir_if(if_condition); 133 134 ir_instruction *br = new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_break); 135 if_inst->then_instructions.push_tail(br); 136 137 return if_inst; 138} 139 140void 141lower_discard_flow(exec_list *ir) 142{ 143 void *mem_ctx = ir; 144 145 ir_variable *var = new(mem_ctx) ir_variable(glsl_type::bool_type, 146 "discarded", 147 ir_var_temporary); 148 149 ir->push_head(var); 150 151 lower_discard_flow_visitor v(var); 152 153 visit_list_elements(&v, ir); 154} 155