1/* 2 * Copyright © 2021 Igalia S.L. 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 FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#include "nir.h" 25#include "nir_builder.h" 26 27/* Some hardware doesn't have a way to check if invocation was demoted, 28 * in such case we have to track it ourselves. 29 * OpIsHelperInvocationEXT is specified as: 30 * 31 * "An invocation is currently a helper invocation if it was originally 32 * invoked as a helper invocation or if it has been demoted to a helper 33 * invocation by OpDemoteToHelperInvocationEXT." 34 * 35 * Therefore we: 36 * - Set gl_IsHelperInvocationEXT = gl_HelperInvocation 37 * - Add "gl_IsHelperInvocationEXT = true" right before each demote 38 * - Add "gl_IsHelperInvocationEXT = gl_IsHelperInvocationEXT || condition" 39 * right before each demote_if 40 */ 41 42static bool 43nir_lower_load_and_store_is_helper(nir_builder *b, nir_instr *instr, void *data) 44{ 45 if (instr->type != nir_instr_type_intrinsic) 46 return false; 47 48 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); 49 nir_deref_instr *is_helper_deref = (nir_deref_instr*) data; 50 51 switch (intrin->intrinsic) { 52 case nir_intrinsic_demote: { 53 b->cursor = nir_before_instr(instr); 54 nir_store_deref(b, is_helper_deref, nir_imm_bool(b, true), 1); 55 return true; 56 } 57 case nir_intrinsic_demote_if: { 58 b->cursor = nir_before_instr(instr); 59 nir_ssa_def *current_is_helper = nir_load_deref(b, is_helper_deref); 60 nir_ssa_def *updated_is_helper = nir_ior(b, current_is_helper, intrin->src[0].ssa); 61 nir_store_deref(b, is_helper_deref, updated_is_helper, 1); 62 return true; 63 } 64 case nir_intrinsic_is_helper_invocation: { 65 b->cursor = nir_before_instr(instr); 66 nir_ssa_def *is_helper = nir_load_deref(b, is_helper_deref); 67 nir_ssa_def_rewrite_uses(&intrin->dest.ssa, is_helper); 68 nir_instr_remove_v(instr); 69 return true; 70 } 71 default: 72 return false; 73 } 74} 75 76static bool 77has_is_helper_invocation(nir_shader *shader) 78{ 79 nir_foreach_function(function, shader) { 80 if (!function->impl) 81 continue; 82 83 nir_foreach_block_safe(block, function->impl) { 84 nir_foreach_instr_safe(instr, block) { 85 if (instr->type != nir_instr_type_intrinsic) 86 continue; 87 88 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); 89 if (intrin->intrinsic == nir_intrinsic_is_helper_invocation) 90 return true; 91 } 92 } 93 } 94 95 return false; 96} 97 98bool 99nir_lower_is_helper_invocation(nir_shader *shader) 100{ 101 if (shader->info.stage != MESA_SHADER_FRAGMENT) 102 return false; 103 104 if (!has_is_helper_invocation(shader)) 105 return false; 106 107 nir_function_impl *entrypoint = nir_shader_get_entrypoint(shader); 108 109 nir_builder b; 110 nir_builder_init(&b, entrypoint); 111 b.cursor = nir_before_cf_list(&entrypoint->body); 112 113 nir_variable *is_helper = nir_local_variable_create(entrypoint, 114 glsl_bool_type(), 115 "gl_IsHelperInvocationEXT"); 116 117 nir_ssa_def *started_as_helper = nir_load_helper_invocation(&b, 32); 118 119 nir_deref_instr *is_helper_deref = nir_build_deref_var(&b, is_helper); 120 nir_store_deref(&b, is_helper_deref, started_as_helper, 1); 121 122 return nir_shader_instructions_pass(shader, 123 nir_lower_load_and_store_is_helper, 124 nir_metadata_all, 125 is_helper_deref); 126} 127