101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2010 Intel Corporation
301e04c3fSmrg *
401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
501e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
601e04c3fSmrg * to deal in the Software without restriction, including without limitation
701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
901e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1001e04c3fSmrg *
1101e04c3fSmrg * The above copyright notice and this permission notice (including the next
1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1301e04c3fSmrg * Software.
1401e04c3fSmrg *
1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2101e04c3fSmrg * DEALINGS IN THE SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg/** @file lower_discard_flow.cpp
2501e04c3fSmrg *
2601e04c3fSmrg * Implements the GLSL 1.30 revision 9 rule for fragment shader
2701e04c3fSmrg * discard handling:
2801e04c3fSmrg *
2901e04c3fSmrg *     "Control flow exits the shader, and subsequent implicit or
3001e04c3fSmrg *      explicit derivatives are undefined when this control flow is
3101e04c3fSmrg *      non-uniform (meaning different fragments within the primitive
3201e04c3fSmrg *      take different control paths)."
3301e04c3fSmrg *
3401e04c3fSmrg * There seem to be two conflicting things here.  "Control flow exits
3501e04c3fSmrg * the shader" sounds like the discarded fragments should effectively
3601e04c3fSmrg * jump to the end of the shader, but that breaks derivatives in the
3701e04c3fSmrg * case of uniform control flow and causes rendering failure in the
3801e04c3fSmrg * bushes in Unigine Tropics.
3901e04c3fSmrg *
4001e04c3fSmrg * The question, then, is whether the intent was "loops stop at the
4101e04c3fSmrg * point that the only active channels left are discarded pixels" or
4201e04c3fSmrg * "discarded pixels become inactive at the point that control flow
4301e04c3fSmrg * returns to the top of a loop".  This implements the second
4401e04c3fSmrg * interpretation.
4501e04c3fSmrg */
4601e04c3fSmrg
4701e04c3fSmrg#include "compiler/glsl_types.h"
4801e04c3fSmrg#include "ir.h"
4901e04c3fSmrg
5001e04c3fSmrgnamespace {
5101e04c3fSmrg
5201e04c3fSmrgclass lower_discard_flow_visitor : public ir_hierarchical_visitor {
5301e04c3fSmrgpublic:
5401e04c3fSmrg   lower_discard_flow_visitor(ir_variable *discarded)
5501e04c3fSmrg   : discarded(discarded)
5601e04c3fSmrg   {
5701e04c3fSmrg      mem_ctx = ralloc_parent(discarded);
5801e04c3fSmrg   }
5901e04c3fSmrg
6001e04c3fSmrg   ~lower_discard_flow_visitor()
6101e04c3fSmrg   {
6201e04c3fSmrg   }
6301e04c3fSmrg
6401e04c3fSmrg   ir_visitor_status visit(ir_loop_jump *ir);
6501e04c3fSmrg   ir_visitor_status visit_enter(ir_discard *ir);
6601e04c3fSmrg   ir_visitor_status visit_enter(ir_loop *ir);
6701e04c3fSmrg   ir_visitor_status visit_enter(ir_function_signature *ir);
6801e04c3fSmrg
6901e04c3fSmrg   ir_if *generate_discard_break();
7001e04c3fSmrg
7101e04c3fSmrg   ir_variable *discarded;
7201e04c3fSmrg   void *mem_ctx;
7301e04c3fSmrg};
7401e04c3fSmrg
7501e04c3fSmrg} /* anonymous namespace */
7601e04c3fSmrg
7701e04c3fSmrgir_visitor_status
7801e04c3fSmrglower_discard_flow_visitor::visit(ir_loop_jump *ir)
7901e04c3fSmrg{
8001e04c3fSmrg   if (ir->mode != ir_loop_jump::jump_continue)
8101e04c3fSmrg      return visit_continue;
8201e04c3fSmrg
8301e04c3fSmrg   ir->insert_before(generate_discard_break());
8401e04c3fSmrg
8501e04c3fSmrg   return visit_continue;
8601e04c3fSmrg}
8701e04c3fSmrg
8801e04c3fSmrgir_visitor_status
8901e04c3fSmrglower_discard_flow_visitor::visit_enter(ir_discard *ir)
9001e04c3fSmrg{
9101e04c3fSmrg   ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(discarded);
9201e04c3fSmrg   ir_rvalue *rhs;
9301e04c3fSmrg   if (ir->condition) {
9401e04c3fSmrg      /* discarded <- condition, use (var_ref discarded) as the condition */
9501e04c3fSmrg      rhs = ir->condition;
9601e04c3fSmrg      ir->condition = new(mem_ctx) ir_dereference_variable(discarded);
9701e04c3fSmrg   } else {
9801e04c3fSmrg      rhs = new(mem_ctx) ir_constant(true);
9901e04c3fSmrg   }
10001e04c3fSmrg   ir_assignment *assign = new(mem_ctx) ir_assignment(lhs, rhs);
10101e04c3fSmrg   ir->insert_before(assign);
10201e04c3fSmrg
10301e04c3fSmrg   return visit_continue;
10401e04c3fSmrg}
10501e04c3fSmrg
10601e04c3fSmrgir_visitor_status
10701e04c3fSmrglower_discard_flow_visitor::visit_enter(ir_loop *ir)
10801e04c3fSmrg{
10901e04c3fSmrg   ir->body_instructions.push_tail(generate_discard_break());
11001e04c3fSmrg
11101e04c3fSmrg   return visit_continue;
11201e04c3fSmrg}
11301e04c3fSmrg
11401e04c3fSmrgir_visitor_status
11501e04c3fSmrglower_discard_flow_visitor::visit_enter(ir_function_signature *ir)
11601e04c3fSmrg{
11701e04c3fSmrg   if (strcmp(ir->function_name(), "main") != 0)
11801e04c3fSmrg      return visit_continue;
11901e04c3fSmrg
12001e04c3fSmrg   ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(discarded);
12101e04c3fSmrg   ir_rvalue *rhs = new(mem_ctx) ir_constant(false);
12201e04c3fSmrg   ir_assignment *assign = new(mem_ctx) ir_assignment(lhs, rhs);
12301e04c3fSmrg   ir->body.push_head(assign);
12401e04c3fSmrg
12501e04c3fSmrg   return visit_continue;
12601e04c3fSmrg}
12701e04c3fSmrg
12801e04c3fSmrgir_if *
12901e04c3fSmrglower_discard_flow_visitor::generate_discard_break()
13001e04c3fSmrg{
13101e04c3fSmrg   ir_rvalue *if_condition = new(mem_ctx) ir_dereference_variable(discarded);
13201e04c3fSmrg   ir_if *if_inst = new(mem_ctx) ir_if(if_condition);
13301e04c3fSmrg
13401e04c3fSmrg   ir_instruction *br = new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_break);
13501e04c3fSmrg   if_inst->then_instructions.push_tail(br);
13601e04c3fSmrg
13701e04c3fSmrg   return if_inst;
13801e04c3fSmrg}
13901e04c3fSmrg
14001e04c3fSmrgvoid
14101e04c3fSmrglower_discard_flow(exec_list *ir)
14201e04c3fSmrg{
14301e04c3fSmrg   void *mem_ctx = ir;
14401e04c3fSmrg
14501e04c3fSmrg   ir_variable *var = new(mem_ctx) ir_variable(glsl_type::bool_type,
14601e04c3fSmrg					       "discarded",
14701e04c3fSmrg					       ir_var_temporary);
14801e04c3fSmrg
14901e04c3fSmrg   ir->push_head(var);
15001e04c3fSmrg
15101e04c3fSmrg   lower_discard_flow_visitor v(var);
15201e04c3fSmrg
15301e04c3fSmrg   visit_list_elements(&v, ir);
15401e04c3fSmrg}
155