1#!/usr/bin/python2
2# coding=utf-8
3# -*- Mode: Python; py-indent-offset: 4 -*-
4#
5# Copyright © 2012 Intel Corporation
6#
7# Based on code by Kristian Høgsberg <krh@bitplanet.net>,
8#   extracted from mesa/main/get.c
9#
10# Permission is hereby granted, free of charge, to any person obtaining a
11# copy of this software and associated documentation files (the "Software"),
12# to deal in the Software without restriction, including without limitation
13# on the rights to use, copy, modify, merge, publish, distribute, sub
14# license, and/or sell copies of the Software, and to permit persons to whom
15# the Software is furnished to do so, subject to the following conditions:
16#
17# The above copyright notice and this permission notice (including the next
18# paragraph) shall be included in all copies or substantial portions of the
19# Software.
20#
21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
24# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27# IN THE SOFTWARE.
28
29# Generate a C header file containing hash tables of glGet parameter
30# names for each GL API. The generated file is to be included by glGet.c
31
32import os, sys, imp, getopt
33from collections import defaultdict
34import get_hash_params
35
36cur_dir = os.path.dirname(sys.argv[0])
37param_desc_file = "%s/get_hash_params.py" % cur_dir
38
39GLAPI = "%s/../../mapi/glapi/gen" % cur_dir
40sys.path.append(GLAPI)
41import gl_XML
42
43prime_factor = 89
44prime_step = 281
45hash_table_size = 1024
46
47gl_apis=set(["GL", "GL_CORE", "GLES", "GLES2", "GLES3", "GLES31", "GLES32"])
48
49def print_header():
50   print "typedef const unsigned short table_t[%d];\n" % (hash_table_size)
51   print "static const int prime_factor = %d, prime_step = %d;\n" % \
52          (prime_factor, prime_step)
53
54def print_params(params):
55   print "static const struct value_desc values[] = {"
56   for p in params:
57      print "    { %s, %s }," % (p[0], p[1])
58
59   print "};\n"
60
61def api_name(api):
62   return "API_OPEN%s" % api
63
64# This must match gl_api enum in src/mesa/main/mtypes.h
65api_enum = [
66   'GL',
67   'GLES',
68   'GLES2',
69   'GL_CORE',
70   'GLES3', # Not in gl_api enum in mtypes.h
71   'GLES31', # Not in gl_api enum in mtypes.h
72   'GLES32', # Not in gl_api enum in mtypes.h
73]
74
75def api_index(api):
76    return api_enum.index(api)
77
78def table_name(api):
79   return "table_" + api_name(api)
80
81def print_table(api, table):
82   print "static table_t %s = {" % (table_name(api))
83
84   # convert sparse (index, value) table into a dense table
85   dense_table = [0] * hash_table_size
86   for i, v in table:
87      dense_table[i] = v
88
89   row_size = 4
90   for i in range(0, hash_table_size, row_size):
91      row = dense_table[i : i + row_size]
92      idx_val = ["%4d" % v for v in row]
93      print " " * 4 + ", ".join(idx_val) + ","
94
95   print "};\n"
96
97def print_tables(tables):
98   for table in tables:
99      print_table(table["apis"][0], table["indices"])
100
101   dense_tables = ['NULL'] * len(api_enum)
102   for table in tables:
103      tname = table_name(table["apis"][0])
104      for api in table["apis"]:
105         i = api_index(api)
106         dense_tables[i] = "&%s" % (tname)
107
108   print "static table_t *table_set[] = {"
109   for expr in dense_tables:
110      print "   %s," % expr
111   print "};\n"
112
113   print "#define table(api) (*table_set[api])"
114
115# Merge tables with matching parameter lists (i.e. GL and GL_CORE)
116def merge_tables(tables):
117   merged_tables = []
118   for api, indices in sorted(tables.items()):
119      matching_table = filter(lambda mt:mt["indices"] == indices,
120                              merged_tables)
121      if matching_table:
122         matching_table[0]["apis"].append(api)
123      else:
124         merged_tables.append({"apis": [api], "indices": indices})
125
126   return merged_tables
127
128def add_to_hash_table(table, hash_val, value):
129   while True:
130      index = hash_val & (hash_table_size - 1)
131      if index not in table:
132         table[index] = value
133         break
134      hash_val += prime_step
135
136def die(msg):
137   sys.stderr.write("%s: %s\n" % (program, msg))
138   exit(1)
139
140program = os.path.basename(sys.argv[0])
141
142def generate_hash_tables(enum_list, enabled_apis, param_descriptors):
143   tables = defaultdict(lambda:{})
144
145   # the first entry should be invalid, so that get.c:find_value can use
146   # its index for the 'enum not found' condition.
147   params = [[0, ""]]
148
149   for param_block in param_descriptors:
150      if set(["apis", "params"]) != set(param_block):
151         die("missing fields (%s) in param descriptor file (%s)" %
152               (", ".join(set(["apis", "params"]) - set(param_block)),
153                param_desc_file))
154
155      valid_apis = set(param_block["apis"])
156      if valid_apis - gl_apis:
157         die("unknown API(s) in param descriptor file (%s): %s\n" %
158               (param_desc_file, ",".join(valid_apis - gl_apis)))
159
160      if not (valid_apis & enabled_apis):
161         continue
162
163      valid_apis &= enabled_apis
164
165      for param in param_block["params"]:
166         enum_name = param[0]
167         enum_val = enum_list[enum_name].value
168         hash_val = enum_val * prime_factor
169
170         for api in valid_apis:
171            add_to_hash_table(tables[api], hash_val, len(params))
172            # Also add GLES2 items to the GLES3+ hash tables
173            if api == "GLES2":
174               add_to_hash_table(tables["GLES3"], hash_val, len(params))
175               add_to_hash_table(tables["GLES31"], hash_val, len(params))
176               add_to_hash_table(tables["GLES32"], hash_val, len(params))
177            # Also add GLES3 items to the GLES31+ hash tables
178            if api == "GLES3":
179               add_to_hash_table(tables["GLES31"], hash_val, len(params))
180               add_to_hash_table(tables["GLES32"], hash_val, len(params))
181            # Also add GLES31 items to the GLES32+ hash tables
182            if api == "GLES31":
183               add_to_hash_table(tables["GLES32"], hash_val, len(params))
184         params.append(["GL_" + enum_name, param[1]])
185
186   sorted_tables={}
187   for api, indices in tables.items():
188      sorted_tables[api] = sorted(indices.items())
189
190   return params, merge_tables(sorted_tables)
191
192
193def show_usage():
194   sys.stderr.write(
195"""Usage: %s [OPTIONS]
196  -f <file>          specify GL API XML file
197""" % (program))
198   exit(1)
199
200if __name__ == '__main__':
201   try:
202      (opts, args) = getopt.getopt(sys.argv[1:], "f:")
203   except Exception,e:
204      show_usage()
205
206   if len(args) != 0:
207      show_usage()
208
209   api_desc_file = ""
210
211   for opt_name, opt_val in opts:
212      if opt_name == "-f":
213         api_desc_file = opt_val
214
215   if not api_desc_file:
216      die("missing descriptor file (-f)\n")
217
218   # generate the code for all APIs
219   enabled_apis = set(["GLES", "GLES2", "GLES3", "GLES31", "GLES32",
220                       "GL", "GL_CORE"])
221
222   try:
223      api_desc = gl_XML.parse_GL_API(api_desc_file)
224   except Exception:
225      die("couldn't parse API specification file %s\n" % api_desc_file)
226
227   (params, hash_tables) = generate_hash_tables(api_desc.enums_by_name,
228                              enabled_apis, get_hash_params.descriptor)
229
230   print_header()
231   print_params(params)
232   print_tables(hash_tables)
233