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 
50 namespace {
51 
52 class lower_discard_flow_visitor : public ir_hierarchical_visitor {
53 public:
lower_discard_flow_visitor(ir_variable * discarded)54    lower_discard_flow_visitor(ir_variable *discarded)
55    : discarded(discarded)
56    {
57       mem_ctx = ralloc_parent(discarded);
58    }
59 
~lower_discard_flow_visitor()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 
77 ir_visitor_status
visit(ir_loop_jump * ir)78 lower_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 
88 ir_visitor_status
visit_enter(ir_discard * ir)89 lower_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 
106 ir_visitor_status
visit_enter(ir_loop * ir)107 lower_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 
114 ir_visitor_status
visit_enter(ir_function_signature * ir)115 lower_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 
128 ir_if *
generate_discard_break()129 lower_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 
140 void
lower_discard_flow(exec_list * ir)141 lower_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