1 /*
2 * Copyright © 2017 Ilia Mirkin
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 /**
25 * \file lower_cs_derived.cpp
26 *
27 * For hardware that does not support the gl_GlobalInvocationID and
28 * gl_LocalInvocationIndex system values, replace them with fresh
29 * globals. Note that we can't rely on gl_WorkGroupSize or
30 * gl_LocalGroupSizeARB being available, since they may only have been defined
31 * in a non-main shader.
32 *
33 * [ This can happen if only a secondary shader has the layout(local_size_*)
34 * declaration. ]
35 *
36 * This is meant to be run post-linking.
37 */
38
39 #include "glsl_symbol_table.h"
40 #include "ir_hierarchical_visitor.h"
41 #include "ir.h"
42 #include "ir_builder.h"
43 #include "linker.h"
44 #include "program/prog_statevars.h"
45 #include "builtin_functions.h"
46
47 using namespace ir_builder;
48
49 namespace {
50
51 class lower_cs_derived_visitor : public ir_hierarchical_visitor {
52 public:
lower_cs_derived_visitor(gl_linked_shader * shader)53 explicit lower_cs_derived_visitor(gl_linked_shader *shader)
54 : progress(false),
55 shader(shader),
56 local_size_variable(shader->Program->info.cs.local_size_variable),
57 gl_WorkGroupSize(NULL),
58 gl_WorkGroupID(NULL),
59 gl_LocalInvocationID(NULL),
60 gl_GlobalInvocationID(NULL),
61 gl_LocalInvocationIndex(NULL)
62 {
63 main_sig = _mesa_get_main_function_signature(shader->symbols);
64 assert(main_sig);
65 }
66
67 virtual ir_visitor_status visit(ir_dereference_variable *);
68
69 ir_variable *add_system_value(
70 int slot, const glsl_type *type, const char *name);
71 void find_sysvals();
72 void make_gl_GlobalInvocationID();
73 void make_gl_LocalInvocationIndex();
74
75 bool progress;
76
77 private:
78 gl_linked_shader *shader;
79 bool local_size_variable;
80 ir_function_signature *main_sig;
81
82 ir_rvalue *gl_WorkGroupSize;
83 ir_variable *gl_WorkGroupID;
84 ir_variable *gl_LocalInvocationID;
85
86 ir_variable *gl_GlobalInvocationID;
87 ir_variable *gl_LocalInvocationIndex;
88 };
89
90 } /* anonymous namespace */
91
92 ir_variable *
add_system_value(int slot,const glsl_type * type,const char * name)93 lower_cs_derived_visitor::add_system_value(
94 int slot, const glsl_type *type, const char *name)
95 {
96 ir_variable *var = new(shader) ir_variable(type, name, ir_var_system_value);
97 var->data.how_declared = ir_var_declared_implicitly;
98 var->data.read_only = true;
99 var->data.location = slot;
100 var->data.explicit_location = true;
101 var->data.explicit_index = 0;
102 shader->ir->push_head(var);
103
104 return var;
105 }
106
107 void
find_sysvals()108 lower_cs_derived_visitor::find_sysvals()
109 {
110 if (gl_WorkGroupSize != NULL)
111 return;
112
113 ir_variable *WorkGroupSize;
114 if (local_size_variable)
115 WorkGroupSize = shader->symbols->get_variable("gl_LocalGroupSizeARB");
116 else
117 WorkGroupSize = shader->symbols->get_variable("gl_WorkGroupSize");
118 if (WorkGroupSize)
119 gl_WorkGroupSize = new(shader) ir_dereference_variable(WorkGroupSize);
120 gl_WorkGroupID = shader->symbols->get_variable("gl_WorkGroupID");
121 gl_LocalInvocationID = shader->symbols->get_variable("gl_LocalInvocationID");
122
123 /*
124 * These may be missing due to either dead code elimination, or, in the
125 * case of the group size, due to the layout being declared in a non-main
126 * shader. Re-create them.
127 */
128
129 if (!gl_WorkGroupID)
130 gl_WorkGroupID = add_system_value(
131 SYSTEM_VALUE_WORK_GROUP_ID, glsl_type::uvec3_type, "gl_WorkGroupID");
132 if (!gl_LocalInvocationID)
133 gl_LocalInvocationID = add_system_value(
134 SYSTEM_VALUE_LOCAL_INVOCATION_ID, glsl_type::uvec3_type,
135 "gl_LocalInvocationID");
136 if (!WorkGroupSize) {
137 if (local_size_variable) {
138 gl_WorkGroupSize = new(shader) ir_dereference_variable(
139 add_system_value(
140 SYSTEM_VALUE_LOCAL_GROUP_SIZE, glsl_type::uvec3_type,
141 "gl_LocalGroupSizeARB"));
142 } else {
143 ir_constant_data data;
144 memset(&data, 0, sizeof(data));
145 for (int i = 0; i < 3; i++)
146 data.u[i] = shader->Program->info.cs.local_size[i];
147 gl_WorkGroupSize = new(shader) ir_constant(glsl_type::uvec3_type, &data);
148 }
149 }
150 }
151
152 void
make_gl_GlobalInvocationID()153 lower_cs_derived_visitor::make_gl_GlobalInvocationID()
154 {
155 if (gl_GlobalInvocationID != NULL)
156 return;
157
158 find_sysvals();
159
160 /* gl_GlobalInvocationID =
161 * gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID
162 */
163 gl_GlobalInvocationID = new(shader) ir_variable(
164 glsl_type::uvec3_type, "__GlobalInvocationID", ir_var_temporary);
165 shader->ir->push_head(gl_GlobalInvocationID);
166
167 ir_instruction *inst =
168 assign(gl_GlobalInvocationID,
169 add(mul(gl_WorkGroupID, gl_WorkGroupSize->clone(shader, NULL)),
170 gl_LocalInvocationID));
171 main_sig->body.push_head(inst);
172 }
173
174 void
make_gl_LocalInvocationIndex()175 lower_cs_derived_visitor::make_gl_LocalInvocationIndex()
176 {
177 if (gl_LocalInvocationIndex != NULL)
178 return;
179
180 find_sysvals();
181
182 /* gl_LocalInvocationIndex =
183 * gl_LocalInvocationID.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y +
184 * gl_LocalInvocationID.y * gl_WorkGroupSize.x +
185 * gl_LocalInvocationID.x;
186 */
187 gl_LocalInvocationIndex = new(shader)
188 ir_variable(glsl_type::uint_type, "__LocalInvocationIndex", ir_var_temporary);
189 shader->ir->push_head(gl_LocalInvocationIndex);
190
191 ir_expression *index_z =
192 mul(mul(swizzle_z(gl_LocalInvocationID), swizzle_x(gl_WorkGroupSize->clone(shader, NULL))),
193 swizzle_y(gl_WorkGroupSize->clone(shader, NULL)));
194 ir_expression *index_y =
195 mul(swizzle_y(gl_LocalInvocationID), swizzle_x(gl_WorkGroupSize->clone(shader, NULL)));
196 ir_expression *index_y_plus_z = add(index_y, index_z);
197 operand index_x(swizzle_x(gl_LocalInvocationID));
198 ir_expression *index_x_plus_y_plus_z = add(index_y_plus_z, index_x);
199 ir_instruction *inst =
200 assign(gl_LocalInvocationIndex, index_x_plus_y_plus_z);
201 main_sig->body.push_head(inst);
202 }
203
204 ir_visitor_status
visit(ir_dereference_variable * ir)205 lower_cs_derived_visitor::visit(ir_dereference_variable *ir)
206 {
207 if (ir->var->data.mode == ir_var_system_value &&
208 ir->var->data.location == SYSTEM_VALUE_GLOBAL_INVOCATION_ID) {
209 make_gl_GlobalInvocationID();
210 ir->var = gl_GlobalInvocationID;
211 progress = true;
212 }
213
214 if (ir->var->data.mode == ir_var_system_value &&
215 ir->var->data.location == SYSTEM_VALUE_LOCAL_INVOCATION_INDEX) {
216 make_gl_LocalInvocationIndex();
217 ir->var = gl_LocalInvocationIndex;
218 progress = true;
219 }
220
221 return visit_continue;
222 }
223
224 bool
lower_cs_derived(gl_linked_shader * shader)225 lower_cs_derived(gl_linked_shader *shader)
226 {
227 if (shader->Stage != MESA_SHADER_COMPUTE)
228 return false;
229
230 lower_cs_derived_visitor v(shader);
231 v.run(shader->ir);
232
233 return v.progress;
234 }
235