1
2# (C) Copyright IBM Corporation 2004, 2005
3# All Rights Reserved.
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and associated documentation files (the "Software"),
7# to deal in the Software without restriction, including without limitation
8# on the rights to use, copy, modify, merge, publish, distribute, sub
9# license, and/or sell copies of the Software, and to permit persons to whom
10# the Software is furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice (including the next
13# paragraph) shall be included in all copies or substantial portions of the
14# Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22# IN THE SOFTWARE.
23#
24# Authors:
25#    Ian Romanick <idr@us.ibm.com>
26
27import argparse
28import sys, string
29
30import gl_XML, glX_XML
31import license
32
33
34class glx_enum_function(object):
35    def __init__(self, func_name, enum_dict):
36        self.name = func_name
37        self.mode = 1
38        self.sig = None
39
40        # "enums" is a set of lists.  The element in the set is the
41        # value of the enum.  The list is the list of names for that
42        # value.  For example, [0x8126] = {"POINT_SIZE_MIN",
43        # "POINT_SIZE_MIN_ARB", "POINT_SIZE_MIN_EXT",
44        # "POINT_SIZE_MIN_SGIS"}.
45
46        self.enums = {}
47
48        # "count" is indexed by count values.  Each element of count
49        # is a list of index to "enums" that have that number of
50        # associated data elements.  For example, [4] =
51        # {GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION,
52        # GL_AMBIENT_AND_DIFFUSE} (the enum names are used here,
53        # but the actual hexadecimal values would be in the array).
54
55        self.count = {}
56
57
58        # Fill self.count and self.enums using the dictionary of enums
59        # that was passed in.  The generic Get functions (e.g.,
60        # GetBooleanv and friends) are handled specially here.  In
61        # the data the generic Get functions are referred to as "Get".
62
63        if func_name in ["GetIntegerv", "GetBooleanv", "GetFloatv", "GetDoublev"]:
64            match_name = "Get"
65        else:
66            match_name = func_name
67
68        mode_set = 0
69        for enum_name in enum_dict:
70            e = enum_dict[ enum_name ]
71
72            if match_name in e.functions:
73                [count, mode] = e.functions[ match_name ]
74
75                if mode_set and mode != self.mode:
76                    raise RuntimeError("Not all enums for %s have the same mode." % (func_name))
77
78                self.mode = mode
79
80                if e.value in self.enums:
81                    if e.name not in self.enums[ e.value ]:
82                        self.enums[ e.value ].append( e )
83                else:
84                    if count not in self.count:
85                        self.count[ count ] = []
86
87                    self.enums[ e.value ] = [ e ]
88                    self.count[ count ].append( e.value )
89
90
91        return
92
93
94    def signature( self ):
95        if self.sig == None:
96            self.sig = ""
97            for i in self.count:
98                if i == None:
99                    raise RuntimeError("i is None.  WTF?")
100
101                self.count[i].sort()
102                for e in self.count[i]:
103                    self.sig += "%04x,%d," % (e, i)
104
105        return self.sig
106
107
108    def is_set( self ):
109        return self.mode
110
111
112    def PrintUsingTable(self):
113        """Emit the body of the __gl*_size function using a pair
114        of look-up tables and a mask.  The mask is calculated such
115        that (e & mask) is unique for all the valid values of e for
116        this function.  The result of (e & mask) is used as an index
117        into the first look-up table.  If it matches e, then the
118        same entry of the second table is returned.  Otherwise zero
119        is returned.
120
121        It seems like this should cause better code to be generated.
122        However, on x86 at least, the resulting .o file is about 20%
123        larger then the switch-statment version.  I am leaving this
124        code in because the results may be different on other
125        platforms (e.g., PowerPC or x86-64)."""
126
127        return 0
128        count = 0
129        for a in self.enums:
130            count += 1
131
132        if -1 in self.count:
133            return 0
134
135        # Determine if there is some mask M, such that M = (2^N) - 1,
136        # that will generate unique values for all of the enums.
137
138        mask = 0
139        for i in [1, 2, 3, 4, 5, 6, 7, 8]:
140            mask = (1 << i) - 1
141
142            fail = 0;
143            for a in self.enums:
144                for b in self.enums:
145                    if a != b:
146                        if (a & mask) == (b & mask):
147                            fail = 1;
148
149            if not fail:
150                break;
151            else:
152                mask = 0
153
154        if (mask != 0) and (mask < (2 * count)):
155            masked_enums = {}
156            masked_count = {}
157
158            for i in range(0, mask + 1):
159                masked_enums[i] = "0";
160                masked_count[i] = 0;
161
162            for c in self.count:
163                for e in self.count[c]:
164                    i = e & mask
165                    enum_obj = self.enums[e][0]
166                    masked_enums[i] = '0x%04x /* %s */' % (e, enum_obj.name )
167                    masked_count[i] = c
168
169
170            print('    static const GLushort a[%u] = {' % (mask + 1))
171            for e in masked_enums:
172                print('        %s, ' % (masked_enums[e]))
173            print('    };')
174
175            print('    static const GLubyte b[%u] = {' % (mask + 1))
176            for c in masked_count:
177                print('        %u, ' % (masked_count[c]))
178            print('    };')
179
180            print('    const unsigned idx = (e & 0x%02xU);' % (mask))
181            print('')
182            print('    return (e == a[idx]) ? (GLint) b[idx] : 0;')
183            return 1;
184        else:
185            return 0;
186
187
188    def PrintUsingSwitch(self, name):
189        """Emit the body of the __gl*_size function using a
190        switch-statement."""
191
192        print('    switch( e ) {')
193
194        for c in sorted(self.count):
195            for e in self.count[c]:
196                first = 1
197
198                # There may be multiple enums with the same
199                # value.  This happens has extensions are
200                # promoted from vendor-specific or EXT to
201                # ARB and to the core.  Emit the first one as
202                # a case label, and emit the others as
203                # commented-out case labels.
204
205                list = {}
206                for enum_obj in self.enums[e]:
207                    list[ enum_obj.priority() ] = enum_obj.name
208
209                keys = sorted(list.keys())
210                for k in keys:
211                    j = list[k]
212                    if first:
213                        print('        case GL_%s:' % (j))
214                        first = 0
215                    else:
216                        print('/*      case GL_%s:*/' % (j))
217
218            if c == -1:
219                print('            return __gl%s_variable_size( e );' % (name))
220            else:
221                print('            return %u;' % (c))
222
223        print('        default: return 0;')
224        print('    }')
225
226
227    def Print(self, name):
228        print('_X_INTERNAL PURE FASTCALL GLint')
229        print('__gl%s_size( GLenum e )' % (name))
230        print('{')
231
232        if not self.PrintUsingTable():
233            self.PrintUsingSwitch(name)
234
235        print('}')
236        print('')
237
238
239class glx_server_enum_function(glx_enum_function):
240    def __init__(self, func, enum_dict):
241        glx_enum_function.__init__(self, func.name, enum_dict)
242
243        self.function = func
244        return
245
246
247    def signature( self ):
248        if self.sig == None:
249            sig = glx_enum_function.signature(self)
250
251            p = self.function.variable_length_parameter()
252            if p:
253                sig += "%u" % (p.size())
254
255            self.sig = sig
256
257        return self.sig;
258
259
260    def Print(self, name, printer):
261        f = self.function
262        printer.common_func_print_just_header( f )
263
264        fixup = []
265
266        foo = {}
267        for param_name in f.count_parameter_list:
268            o = f.offset_of( param_name )
269            foo[o] = param_name
270
271        for param_name in f.counter_list:
272            o = f.offset_of( param_name )
273            foo[o] = param_name
274
275        keys = sorted(foo.keys())
276        for o in keys:
277            p = f.parameters_by_name[ foo[o] ]
278
279            printer.common_emit_one_arg(p, "pc", 0)
280            fixup.append( p.name )
281
282
283        print('    GLsizei compsize;')
284        print('')
285
286        printer.common_emit_fixups(fixup)
287
288        print('')
289        print('    compsize = __gl%s_size(%s);' % (f.name, string.join(f.count_parameter_list, ",")))
290        p = f.variable_length_parameter()
291        print('    return safe_pad(%s);' % (p.size_string()))
292
293        print('}')
294        print('')
295
296
297class PrintGlxSizeStubs_common(gl_XML.gl_print_base):
298    do_get = (1 << 0)
299    do_set = (1 << 1)
300
301    def __init__(self, which_functions):
302        gl_XML.gl_print_base.__init__(self)
303
304        self.name = "glX_proto_size.py (from Mesa)"
305        self.license = license.bsd_license_template % ( "(C) Copyright IBM Corporation 2004", "IBM")
306
307        self.emit_set = ((which_functions & PrintGlxSizeStubs_common.do_set) != 0)
308        self.emit_get = ((which_functions & PrintGlxSizeStubs_common.do_get) != 0)
309        return
310
311
312class PrintGlxSizeStubs_c(PrintGlxSizeStubs_common):
313    def printRealHeader(self):
314        print('')
315        print('#include <X11/Xfuncproto.h>')
316        print('#include <GL/gl.h>')
317        if self.emit_get:
318            print('#include "indirect_size_get.h"')
319            print('#include "glxserver.h"')
320            print('#include "indirect_util.h"')
321
322        print('#include "indirect_size.h"')
323
324        print('')
325        self.printPure()
326        print('')
327        self.printFastcall()
328        print('')
329        print('')
330        print('#ifdef HAVE_FUNC_ATTRIBUTE_ALIAS')
331        print('#  define ALIAS2(from,to) \\')
332        print('    _X_INTERNAL PURE FASTCALL GLint __gl ## from ## _size( GLenum e ) \\')
333        print('        __attribute__ ((alias( # to )));')
334        print('#  define ALIAS(from,to) ALIAS2( from, __gl ## to ## _size )')
335        print('#else')
336        print('#  define ALIAS(from,to) \\')
337        print('    _X_INTERNAL PURE FASTCALL GLint __gl ## from ## _size( GLenum e ) \\')
338        print('    { return __gl ## to ## _size( e ); }')
339        print('#endif')
340        print('')
341        print('')
342
343
344    def printBody(self, api):
345        enum_sigs = {}
346        aliases = []
347
348        for func in api.functionIterateGlx():
349            ef = glx_enum_function( func.name, api.enums_by_name )
350            if len(ef.enums) == 0:
351                continue
352
353            if (ef.is_set() and self.emit_set) or (not ef.is_set() and self.emit_get):
354                sig = ef.signature()
355                if sig in enum_sigs:
356                    aliases.append( [func.name, enum_sigs[ sig ]] )
357                else:
358                    enum_sigs[ sig ] = func.name
359                    ef.Print( func.name )
360
361
362        for [alias_name, real_name] in aliases:
363            print('ALIAS( %s, %s )' % (alias_name, real_name))
364
365
366
367class PrintGlxSizeStubs_h(PrintGlxSizeStubs_common):
368    def printRealHeader(self):
369        print("""/**
370 * \\file
371 * Prototypes for functions used to determine the number of data elements in
372 * various GLX protocol messages.
373 *
374 * \\author Ian Romanick <idr@us.ibm.com>
375 */
376""")
377        print('#include <X11/Xfuncproto.h>')
378        print('')
379        self.printPure();
380        print('')
381        self.printFastcall();
382        print('')
383
384
385    def printBody(self, api):
386        for func in api.functionIterateGlx():
387            ef = glx_enum_function( func.name, api.enums_by_name )
388            if len(ef.enums) == 0:
389                continue
390
391            if (ef.is_set() and self.emit_set) or (not ef.is_set() and self.emit_get):
392                print('extern _X_INTERNAL PURE FASTCALL GLint __gl%s_size(GLenum);' % (func.name))
393
394
395class PrintGlxReqSize_common(gl_XML.gl_print_base):
396    """Common base class for PrintGlxSizeReq_h and PrintGlxSizeReq_h.
397
398    The main purpose of this common base class is to provide the infrastructure
399    for the derrived classes to iterate over the same set of functions.
400    """
401
402    def __init__(self):
403        gl_XML.gl_print_base.__init__(self)
404
405        self.name = "glX_proto_size.py (from Mesa)"
406        self.license = license.bsd_license_template % ( "(C) Copyright IBM Corporation 2005", "IBM")
407
408
409class PrintGlxReqSize_h(PrintGlxReqSize_common):
410    def __init__(self):
411        PrintGlxReqSize_common.__init__(self)
412        self.header_tag = "_INDIRECT_REQSIZE_H_"
413
414
415    def printRealHeader(self):
416        print('#include <X11/Xfuncproto.h>')
417        print('')
418        self.printPure()
419        print('')
420
421
422    def printBody(self, api):
423        for func in api.functionIterateGlx():
424            if not func.ignore and func.has_variable_size_request():
425                print('extern PURE _X_HIDDEN int __glX%sReqSize(const GLbyte *pc, Bool swap, int reqlen);' % (func.name))
426
427
428class PrintGlxReqSize_c(PrintGlxReqSize_common):
429    """Create the server-side 'request size' functions.
430
431    Create the server-side functions that are used to determine what the
432    size of a varible length command should be.  The server then uses
433    this value to determine if the incoming command packed it malformed.
434    """
435
436    def __init__(self):
437        PrintGlxReqSize_common.__init__(self)
438        self.counter_sigs = {}
439
440
441    def printRealHeader(self):
442        print('')
443        print('#include <GL/gl.h>')
444        print('#include "glxserver.h"')
445        print('#include "glxbyteorder.h"')
446        print('#include "indirect_size.h"')
447        print('#include "indirect_reqsize.h"')
448        print('')
449        print('#ifdef HAVE_FUNC_ATTRIBUTE_ALIAS')
450        print('#  define ALIAS2(from,to) \\')
451        print('    GLint __glX ## from ## ReqSize( const GLbyte * pc, Bool swap, int reqlen ) \\')
452        print('        __attribute__ ((alias( # to )));')
453        print('#  define ALIAS(from,to) ALIAS2( from, __glX ## to ## ReqSize )')
454        print('#else')
455        print('#  define ALIAS(from,to) \\')
456        print('    GLint __glX ## from ## ReqSize( const GLbyte * pc, Bool swap, int reqlen ) \\')
457        print('    { return __glX ## to ## ReqSize( pc, swap, reqlen ); }')
458        print('#endif')
459        print('')
460        print('')
461
462
463    def printBody(self, api):
464        aliases = []
465        enum_functions = {}
466        enum_sigs = {}
467
468        for func in api.functionIterateGlx():
469            if not func.has_variable_size_request(): continue
470
471            ef = glx_server_enum_function( func, api.enums_by_name )
472            if len(ef.enums) == 0: continue
473
474            sig = ef.signature()
475
476            if func.name not in enum_functions:
477                enum_functions[ func.name ] = sig
478
479            if sig not in enum_sigs:
480                enum_sigs[ sig ] = ef
481
482
483
484        for func in api.functionIterateGlx():
485            # Even though server-handcode fuctions are on "the
486            # list", and prototypes are generated for them, there
487            # isn't enough information to generate a size
488            # function.  If there was enough information, they
489            # probably wouldn't need to be handcoded in the first
490            # place!
491
492            if func.server_handcode: continue
493            if not func.has_variable_size_request(): continue
494
495            if func.name in enum_functions:
496                sig = enum_functions[func.name]
497                ef = enum_sigs[ sig ]
498
499                if ef.name != func.name:
500                    aliases.append( [func.name, ef.name] )
501                else:
502                    ef.Print( func.name, self )
503
504            elif func.images:
505                self.printPixelFunction(func)
506            elif func.has_variable_size_request():
507                a = self.printCountedFunction(func)
508                if a: aliases.append(a)
509
510
511        for [alias_name, real_name] in aliases:
512            print('ALIAS( %s, %s )' % (alias_name, real_name))
513
514        return
515
516
517    def common_emit_fixups(self, fixup):
518        """Utility function to emit conditional byte-swaps."""
519
520        if fixup:
521            print('    if (swap) {')
522            for name in fixup:
523                print('        %s = bswap_32(%s);' % (name, name))
524            print('    }')
525
526        return
527
528
529    def common_emit_one_arg(self, p, pc, adjust):
530        offset = p.offset
531        dst = p.string()
532        src = '(%s *)' % (p.type_string())
533        print('%-18s = *%11s(%s + %u);' % (dst, src, pc, offset + adjust));
534        return
535
536
537    def common_func_print_just_header(self, f):
538        print('int')
539        print('__glX%sReqSize( const GLbyte * pc, Bool swap, int reqlen )' % (f.name))
540        print('{')
541
542
543    def printPixelFunction(self, f):
544        self.common_func_print_just_header(f)
545
546        f.offset_of( f.parameters[0].name )
547        [dim, w, h, d, junk] = f.get_images()[0].get_dimensions()
548
549        print('    GLint row_length   = *  (GLint *)(pc +  4);')
550
551        if dim < 3:
552            fixup = ['row_length', 'skip_rows', 'alignment']
553            print('    GLint image_height = 0;')
554            print('    GLint skip_images  = 0;')
555            print('    GLint skip_rows    = *  (GLint *)(pc +  8);')
556            print('    GLint alignment    = *  (GLint *)(pc + 16);')
557        else:
558            fixup = ['row_length', 'image_height', 'skip_rows', 'skip_images', 'alignment']
559            print('    GLint image_height = *  (GLint *)(pc +  8);')
560            print('    GLint skip_rows    = *  (GLint *)(pc + 16);')
561            print('    GLint skip_images  = *  (GLint *)(pc + 20);')
562            print('    GLint alignment    = *  (GLint *)(pc + 32);')
563
564        img = f.images[0]
565        for p in f.parameterIterateGlxSend():
566            if p.name in [w, h, d, img.img_format, img.img_type, img.img_target]:
567                self.common_emit_one_arg(p, "pc", 0)
568                fixup.append( p.name )
569
570        print('')
571
572        self.common_emit_fixups(fixup)
573
574        if img.img_null_flag:
575            print('')
576            print('	   if (*(CARD32 *) (pc + %s))' % (img.offset - 4))
577            print('	       return 0;')
578
579        print('')
580        print('    return __glXImageSize(%s, %s, %s, %s, %s, %s,' % (img.img_format, img.img_type, img.img_target, w, h, d ))
581        print('                          image_height, row_length, skip_images,')
582        print('                          skip_rows, alignment);')
583        print('}')
584        print('')
585        return
586
587
588    def printCountedFunction(self, f):
589
590        sig = ""
591        offset = 0
592        fixup = []
593        params = []
594        size = ''
595        param_offsets = {}
596
597        # Calculate the offset of each counter parameter and the
598        # size string for the variable length parameter(s).  While
599        # that is being done, calculate a unique signature for this
600        # function.
601
602        for p in f.parameterIterateGlxSend():
603            if p.is_counter:
604                fixup.append( p.name )
605                params.append( p )
606            elif p.counter:
607                s = p.size()
608                if s == 0: s = 1
609
610                sig += "(%u,%u)" % (f.offset_of(p.counter), s)
611                if size == '':
612                    size = p.size_string()
613                else:
614                    size = "safe_add(%s, %s)" % (size, p.size_string())
615
616        # If the calculated signature matches a function that has
617        # already be emitted, don't emit this function.  Instead, add
618        # it to the list of function aliases.
619
620        if sig in self.counter_sigs:
621            n = self.counter_sigs[sig];
622            alias = [f.name, n]
623        else:
624            alias = None
625            self.counter_sigs[sig] = f.name
626
627            self.common_func_print_just_header(f)
628
629            for p in params:
630                self.common_emit_one_arg(p, "pc", 0)
631
632
633            print('')
634            self.common_emit_fixups(fixup)
635            print('')
636
637            print('    return safe_pad(%s);' % (size))
638            print('}')
639            print('')
640
641        return alias
642
643
644def _parser():
645    """Parse arguments and return a namespace."""
646    parser = argparse.ArgumentParser()
647    parser.set_defaults(which_functions=(PrintGlxSizeStubs_common.do_get |
648                                         PrintGlxSizeStubs_common.do_set))
649    parser.add_argument('-f',
650                        dest='filename',
651                        default='gl_API.xml',
652                        help='an XML file describing an OpenGL API.')
653    parser.add_argument('-m',
654                        dest='mode',
655                        choices=['size_c', 'size_h', 'reqsize_c', 'reqsize_h'],
656                        help='Which file to generate')
657    getset = parser.add_mutually_exclusive_group()
658    getset.add_argument('--only-get',
659                        dest='which_functions',
660                        action='store_const',
661                        const=PrintGlxSizeStubs_common.do_get,
662                        help='only emit "get-type" functions')
663    getset.add_argument('--only-set',
664                        dest='which_functions',
665                        action='store_const',
666                        const=PrintGlxSizeStubs_common.do_set,
667                        help='only emit "set-type" functions')
668    parser.add_argument('--header-tag',
669                        dest='header_tag',
670                        action='store',
671                        default=None,
672                        help='set header tag value')
673    return parser.parse_args()
674
675
676def main():
677    """Main function."""
678    args = _parser()
679
680    if args.mode == "size_c":
681        printer = PrintGlxSizeStubs_c(args.which_functions)
682    elif args.mode == "size_h":
683        printer = PrintGlxSizeStubs_h(args.which_functions)
684        if args.header_tag is not None:
685            printer.header_tag = args.header_tag
686    elif args.mode == "reqsize_c":
687        printer = PrintGlxReqSize_c()
688    elif args.mode == "reqsize_h":
689        printer = PrintGlxReqSize_h()
690
691    api = gl_XML.parse_GL_API(args.filename, glX_XML.glx_item_factory())
692
693    printer.Print(api)
694
695
696if __name__ == '__main__':
697    main()
698