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:
lower_discard_flow_visitor(ir_variable * discarded)53    lower_discard_flow_visitor(ir_variable *discarded)
54    : discarded(discarded)
55    {
56       mem_ctx = ralloc_parent(discarded);
57    }
58 
~lower_discard_flow_visitor()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
visit_enter(ir_loop_jump * ir)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
visit_enter(ir_discard * ir)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
visit_enter(ir_loop * ir)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
visit_enter(ir_function_signature * ir)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 *
generate_discard_break()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
lower_discard_flow(exec_list * ir)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