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