2 * Copyright ©2019 Collabora Ltd.
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:
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
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.
25 * \file lower_xfb_varying.cpp
30 #include "main/mtypes.h"
31 #include "glsl_symbol_table.h"
32 #include "util/strndup.h"
37 * Visitor that splices varying packing code before every return.
39 class lower_xfb_var_splicer
: public ir_hierarchical_visitor
42 explicit lower_xfb_var_splicer(void *mem_ctx
,
43 const exec_list
*instructions
);
45 virtual ir_visitor_status
visit_leave(ir_return
*ret
);
46 virtual ir_visitor_status
visit_leave(ir_function_signature
*sig
);
50 * Memory context used to allocate new instructions for the shader.
55 * Instructions that should be spliced into place before each return.
57 const exec_list
*instructions
;
60 } /* anonymous namespace */
63 lower_xfb_var_splicer::lower_xfb_var_splicer(void *mem_ctx
, const exec_list
*instructions
)
64 : mem_ctx(mem_ctx
), instructions(instructions
)
69 lower_xfb_var_splicer::visit_leave(ir_return
*ret
)
71 foreach_in_list(ir_instruction
, ir
, this->instructions
) {
72 ret
->insert_before(ir
->clone(this->mem_ctx
, NULL
));
74 return visit_continue
;
77 /** Insert a copy-back assignment at the end of the main() function */
79 lower_xfb_var_splicer::visit_leave(ir_function_signature
*sig
)
81 if (strcmp(sig
->function_name(), "main") != 0)
82 return visit_continue
;
84 if (((ir_instruction
*)sig
->body
.get_tail())->ir_type
== ir_type_return
)
85 return visit_continue
;
87 foreach_in_list(ir_instruction
, ir
, this->instructions
) {
88 sig
->body
.push_tail(ir
->clone(this->mem_ctx
, NULL
));
91 return visit_continue
;
95 get_field_name(const char *name
)
97 const char *first_dot
= strchr(name
, '.');
98 const char *first_square_bracket
= strchr(name
, '[');
101 if (!first_square_bracket
&& !first_dot
)
102 name_size
= strlen(name
);
103 else if ((!first_square_bracket
||
104 (first_dot
&& first_dot
< first_square_bracket
)))
105 name_size
= first_dot
- name
;
107 name_size
= first_square_bracket
- name
;
109 return strndup(name
, name_size
);
112 /* Generate a new name given the old xfb declaration string by replacing dots
113 * with '_', brackets with '@' and appending "-xfb" */
115 generate_new_name(void *mem_ctx
, const char *name
)
120 new_name
= ralloc_strdup(mem_ctx
, name
);
121 while (new_name
[i
]) {
122 if (new_name
[i
] == '.') {
124 } else if (new_name
[i
] == '[' || new_name
[i
] == ']') {
130 if (!ralloc_strcat(&new_name
, "-xfb")) {
131 ralloc_free(new_name
);
138 /* Get the dereference for the given variable name. The method is called
139 * recursively to parse array indices and struct members. */
143 struct gl_linked_shader
*shader
,
144 ir_dereference
**deref
,
145 const glsl_type
**type
)
147 if (name
[0] == '\0') {
149 return (*deref
!= NULL
);
150 } else if (name
[0] == '[') {
155 index
= strtol(name
+ 1, &endptr
, 10);
156 assert(*type
!= NULL
&& (*type
)->is_array() && endptr
[0] == ']');
157 *deref
= new(ctx
) ir_dereference_array(*deref
, new(ctx
) ir_constant(index
));
158 *type
= (*type
)->without_array();
159 return get_deref(ctx
, endptr
+ 1, shader
, deref
, type
);
160 } else if (name
[0] == '.') {
162 char *field
= get_field_name(name
+ 1);
164 assert(*type
!= NULL
&& (*type
)->is_struct() && field
!= NULL
);
165 *deref
= new(ctx
) ir_dereference_record(*deref
, field
);
166 *type
= (*type
)->field_type(field
);
167 assert(*type
!= glsl_type::error_type
);
168 name
+= 1 + strlen(field
);
170 return get_deref(ctx
, name
, shader
, deref
, type
);
172 /* Top level variable */
173 char *field
= get_field_name(name
);
174 ir_variable
*toplevel_var
;
176 toplevel_var
= shader
->symbols
->get_variable(field
);
177 name
+= strlen(field
);
179 if (toplevel_var
== NULL
) {
183 *deref
= new (ctx
) ir_dereference_variable(toplevel_var
);
184 *type
= toplevel_var
->type
;
185 return get_deref(ctx
, name
, shader
, deref
, type
);
190 lower_xfb_varying(void *mem_ctx
,
191 struct gl_linked_shader
*shader
,
192 const char *old_var_name
)
194 exec_list new_instructions
;
196 ir_dereference
*deref
= NULL
;
197 const glsl_type
*type
= NULL
;
199 if (!get_deref(mem_ctx
, old_var_name
, shader
, &deref
, &type
)) {
206 new_var_name
= generate_new_name(mem_ctx
, old_var_name
);
207 ir_variable
*new_variable
208 = new(mem_ctx
) ir_variable(type
, new_var_name
, ir_var_shader_out
);
209 new_variable
->data
.assigned
= true;
210 new_variable
->data
.used
= true;
211 shader
->ir
->push_head(new_variable
);
212 ralloc_free(new_var_name
);
214 ir_dereference
*lhs
= new(mem_ctx
) ir_dereference_variable(new_variable
);
215 ir_assignment
*new_assignment
= new(mem_ctx
) ir_assignment(lhs
, deref
);
216 new_instructions
.push_tail(new_assignment
);
218 lower_xfb_var_splicer
splicer(mem_ctx
, &new_instructions
);
219 visit_list_elements(&splicer
, shader
->ir
);