glsl/builtins: Add missing mix(genType, genType, bvec) built-ins.
[mesa.git] / src / glsl / builtins / tools / generate_builtins.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 from __future__ import with_statement
5
6 import re
7 import sys
8 from glob import glob
9 from os import path
10 from subprocess import Popen, PIPE
11 from sys import argv
12
13 # Local module: generator for texture lookup builtins
14 from texture_builtins import generate_texture_functions
15
16 builtins_dir = path.join(path.dirname(path.abspath(__file__)), "..")
17
18 # Get the path to the standalone GLSL compiler
19 if len(argv) != 2:
20 print "Usage:", argv[0], "<path to compiler>"
21 sys.exit(1)
22
23 compiler = argv[1]
24
25 # Read the files in builtins/ir/*...add them to the supplied dictionary.
26 def read_ir_files(fs):
27 for filename in glob(path.join(path.join(builtins_dir, 'ir'), '*.ir')):
28 function_name = path.basename(filename).split('.')[0]
29 with open(filename) as f:
30 fs[function_name] = f.read()
31
32 # Return a dictionary containing all builtin definitions (even generated)
33 def get_builtin_definitions():
34 fs = {}
35 generate_texture_functions(fs)
36 read_ir_files(fs)
37 return fs
38
39 def stringify(s):
40 # Work around MSVC's 65535 byte limit by outputting an array of characters
41 # rather than actual string literals.
42 if len(s) >= 65535:
43 #t = "/* Warning: length " + repr(len(s)) + " too large */\n"
44 t = ""
45 for c in re.sub('\s\s+', ' ', s):
46 if c == '\n':
47 t += '\n'
48 else:
49 t += "'" + c + "',"
50 return '{' + t[:-1] + '}'
51
52 t = s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n"\n "')
53 return ' "' + t + '"\n'
54
55 def write_function_definitions():
56 fs = get_builtin_definitions()
57 for k, v in sorted(fs.iteritems()):
58 print 'static const char builtin_' + k + '[] ='
59 print stringify(v), ';'
60
61 def run_compiler(args):
62 command = [compiler, '--dump-lir'] + args
63 p = Popen(command, 1, stdout=PIPE, shell=False)
64 output = p.communicate()[0]
65
66 # Clean up output a bit by killing whitespace before a closing paren.
67 kill_paren_whitespace = re.compile(r'[ \n]*\)', re.MULTILINE)
68 output = kill_paren_whitespace.sub(')', output)
69
70 # Also toss any duplicate newlines
71 output = output.replace('\n\n', '\n')
72
73 return (output, p.returncode)
74
75 def write_profile(filename, profile):
76 (proto_ir, returncode) = run_compiler([filename])
77
78 if returncode != 0:
79 print '#error builtins profile', profile, 'failed to compile'
80 return
81
82 # Kill any global variable declarations. We don't want them.
83 kill_globals = re.compile(r'^\(declare.*\n', re.MULTILINE)
84 proto_ir = kill_globals.sub('', proto_ir)
85
86 print 'static const char prototypes_for_' + profile + '[] ='
87 print stringify(proto_ir), ';'
88
89 # Print a table of all the functions (not signatures) referenced.
90 # This is done so we can avoid bothering with a hash table in the C++ code.
91
92 function_names = set()
93 for func in re.finditer(r'\(function (.+)\n', proto_ir):
94 function_names.add(func.group(1))
95
96 print 'static const char *functions_for_' + profile + ' [] = {'
97 for func in sorted(function_names):
98 print ' builtin_' + func + ','
99 print '};'
100
101 def write_profiles():
102 profiles = get_profile_list()
103 for (filename, profile) in profiles:
104 write_profile(filename, profile)
105
106 def get_profile_list():
107 profile_files = []
108 for extension in ['frag', 'vert']:
109 path_glob = path.join(
110 path.join(builtins_dir, 'profiles'), '*.' + extension)
111 profile_files.extend(glob(path_glob))
112 profiles = []
113 for pfile in sorted(profile_files):
114 profiles.append((pfile, path.basename(pfile).replace('.', '_')))
115 return profiles
116
117 if __name__ == "__main__":
118 print """/* DO NOT MODIFY - automatically generated by generate_builtins.py */
119 /*
120 * Copyright © 2010 Intel Corporation
121 *
122 * Permission is hereby granted, free of charge, to any person obtaining a
123 * copy of this software and associated documentation files (the "Software"),
124 * to deal in the Software without restriction, including without limitation
125 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
126 * and/or sell copies of the Software, and to permit persons to whom the
127 * Software is furnished to do so, subject to the following conditions:
128 *
129 * The above copyright notice and this permission notice (including the next
130 * paragraph) shall be included in all copies or substantial portions of the
131 * Software.
132 *
133 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
134 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
135 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
136 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
137 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
138 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
139 * DEALINGS IN THE SOFTWARE.
140 */
141
142 #include <stdio.h>
143 #include "main/core.h" /* for struct gl_shader */
144 #include "glsl_parser_extras.h"
145 #include "ir_reader.h"
146 #include "program.h"
147 #include "ast.h"
148
149 extern "C" struct gl_shader *
150 _mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type);
151
152 gl_shader *
153 read_builtins(GLenum target, const char *protos, const char **functions, unsigned count)
154 {
155 struct gl_context fakeCtx;
156 fakeCtx.API = API_OPENGL;
157 fakeCtx.Const.GLSLVersion = 130;
158 fakeCtx.Extensions.ARB_ES2_compatibility = true;
159 gl_shader *sh = _mesa_new_shader(NULL, 0, target);
160 struct _mesa_glsl_parse_state *st =
161 new(sh) _mesa_glsl_parse_state(&fakeCtx, target, sh);
162
163 st->language_version = 130;
164 st->symbols->language_version = 130;
165 st->ARB_texture_rectangle_enable = true;
166 st->EXT_texture_array_enable = true;
167 st->OES_EGL_image_external_enable = true;
168 _mesa_glsl_initialize_types(st);
169
170 sh->ir = new(sh) exec_list;
171 sh->symbols = st->symbols;
172
173 /* Read the IR containing the prototypes */
174 _mesa_glsl_read_ir(st, sh->ir, protos, true);
175
176 /* Read ALL the function bodies, telling the IR reader not to scan for
177 * prototypes (we've already created them). The IR reader will skip any
178 * signature that does not already exist as a prototype.
179 */
180 for (unsigned i = 0; i < count; i++) {
181 _mesa_glsl_read_ir(st, sh->ir, functions[i], false);
182
183 if (st->error) {
184 printf("error reading builtin: %.35s ...\\n", functions[i]);
185 printf("Info log:\\n%s\\n", st->info_log);
186 ralloc_free(sh);
187 return NULL;
188 }
189 }
190
191 reparent_ir(sh->ir, sh);
192 delete st;
193
194 return sh;
195 }
196 """
197
198 write_function_definitions()
199 write_profiles()
200
201 profiles = get_profile_list()
202
203 print 'static gl_shader *builtin_profiles[%d];' % len(profiles)
204
205 print """
206 void *builtin_mem_ctx = NULL;
207
208 void
209 _mesa_glsl_release_functions(void)
210 {
211 ralloc_free(builtin_mem_ctx);
212 builtin_mem_ctx = NULL;
213 memset(builtin_profiles, 0, sizeof(builtin_profiles));
214 }
215
216 static void
217 _mesa_read_profile(struct _mesa_glsl_parse_state *state,
218 int profile_index,
219 const char *prototypes,
220 const char **functions,
221 int count)
222 {
223 gl_shader *sh = builtin_profiles[profile_index];
224
225 if (sh == NULL) {
226 sh = read_builtins(GL_VERTEX_SHADER, prototypes, functions, count);
227 ralloc_steal(builtin_mem_ctx, sh);
228 builtin_profiles[profile_index] = sh;
229 }
230
231 state->builtins_to_link[state->num_builtins_to_link] = sh;
232 state->num_builtins_to_link++;
233 }
234
235 void
236 _mesa_glsl_initialize_functions(struct _mesa_glsl_parse_state *state)
237 {
238 /* If we've already initialized the built-ins, bail early. */
239 if (state->num_builtins_to_link > 0)
240 return;
241
242 if (builtin_mem_ctx == NULL) {
243 builtin_mem_ctx = ralloc_context(NULL); // "GLSL built-in functions"
244 memset(&builtin_profiles, 0, sizeof(builtin_profiles));
245 }
246 """
247
248 i = 0
249 for (filename, profile) in profiles:
250 if profile.endswith('_vert'):
251 check = 'state->target == vertex_shader && '
252 elif profile.endswith('_frag'):
253 check = 'state->target == fragment_shader && '
254
255 version = re.sub(r'_(vert|frag)$', '', profile)
256 if version.isdigit():
257 check += 'state->language_version == ' + version
258 else: # an extension name
259 check += 'state->' + version + '_enable'
260
261 print ' if (' + check + ') {'
262 print ' _mesa_read_profile(state, %d,' % i
263 print ' prototypes_for_' + profile + ','
264 print ' functions_for_' + profile + ','
265 print ' Elements(functions_for_' + profile + '));'
266 print ' }'
267 print
268 i = i + 1
269 print '}'
270