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