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 gl_XML
28import license
29import sys, getopt, string
30
31
32class glx_item_factory(gl_XML.gl_item_factory):
33    """Factory to create GLX protocol oriented objects derived from gl_item."""
34
35    def create_function(self, element, context):
36        return glx_function(element, context)
37
38    def create_enum(self, element, context, category):
39        return glx_enum(element, context, category)
40
41    def create_api(self):
42        return glx_api(self)
43
44
45class glx_enum(gl_XML.gl_enum):
46    def __init__(self, element, context, category):
47        gl_XML.gl_enum.__init__(self, element, context, category)
48
49        self.functions = {}
50
51        for child in element:
52            if child.tag == "size":
53                n = child.get( "name" )
54                c = child.get( "count" )
55                m = child.get( "mode", "set" )
56
57                if not c:
58                    c = self.default_count
59                else:
60                    c = int(c)
61
62                if m == "get":
63                    mode = 0
64                else:
65                    mode = 1
66
67                if n not in self.functions:
68                    self.functions[ n ] = [c, mode]
69
70        return
71
72
73class glx_function(gl_XML.gl_function):
74    def __init__(self, element, context):
75        self.glx_rop = 0
76        self.glx_sop = 0
77        self.glx_vendorpriv = 0
78
79        self.glx_vendorpriv_names = []
80
81        # If this is set to true, it means that GLdouble parameters should be
82        # written to the GLX protocol packet in the order they appear in the
83        # prototype.  This is different from the "classic" ordering.  In the
84        # classic ordering GLdoubles are written to the protocol packet first,
85        # followed by non-doubles.  NV_vertex_program was the first extension
86        # to break with this tradition.
87
88        self.glx_doubles_in_order = 0
89
90        self.vectorequiv = None
91        self.output = None
92        self.can_be_large = 0
93        self.reply_always_array = 0
94        self.dimensions_in_reply = 0
95        self.img_reset = None
96
97        self.server_handcode = 0
98        self.client_handcode = 0
99        self.ignore = 0
100
101        self.count_parameter_list = []
102        self.counter_list = []
103        self.parameters_by_name = {}
104        self.offsets_calculated = 0
105
106        gl_XML.gl_function.__init__(self, element, context)
107        return
108
109
110    def process_element(self, element):
111        gl_XML.gl_function.process_element(self, element)
112
113        # If the function already has a vector equivalent set, don't
114        # set it again.  This can happen if an alias to a function
115        # appears after the function that it aliases.
116
117        if not self.vectorequiv:
118            self.vectorequiv = element.get("vectorequiv")
119
120
121        name = element.get("name")
122        if name == self.name:
123            for param in self.parameters:
124                self.parameters_by_name[ param.name ] = param
125
126                if len(param.count_parameter_list):
127                    self.count_parameter_list.extend( param.count_parameter_list )
128
129                if param.counter and param.counter not in self.counter_list:
130                    self.counter_list.append(param.counter)
131
132
133        for child in element:
134            if child.tag == "glx":
135                rop = child.get( 'rop' )
136                sop = child.get( 'sop' )
137                vop = child.get( 'vendorpriv' )
138
139                if rop:
140                    self.glx_rop = int(rop)
141
142                if sop:
143                    self.glx_sop = int(sop)
144
145                if vop:
146                    self.glx_vendorpriv = int(vop)
147                    self.glx_vendorpriv_names.append(name)
148
149                self.img_reset = child.get( 'img_reset' )
150
151                # The 'handcode' attribute can be one of 'true',
152                # 'false', 'client', or 'server'.
153
154                handcode = child.get( 'handcode', 'false' )
155                if handcode == "false":
156                    self.server_handcode = 0
157                    self.client_handcode = 0
158                elif handcode == "true":
159                    self.server_handcode = 1
160                    self.client_handcode = 1
161                elif handcode == "client":
162                    self.server_handcode = 0
163                    self.client_handcode = 1
164                elif handcode == "server":
165                    self.server_handcode = 1
166                    self.client_handcode = 0
167                else:
168                    raise RuntimeError('Invalid handcode mode "%s" in function "%s".' % (handcode, self.name))
169
170                self.ignore               = gl_XML.is_attr_true( child, 'ignore' )
171                self.can_be_large         = gl_XML.is_attr_true( child, 'large' )
172                self.glx_doubles_in_order = gl_XML.is_attr_true( child, 'doubles_in_order' )
173                self.reply_always_array   = gl_XML.is_attr_true( child, 'always_array' )
174                self.dimensions_in_reply  = gl_XML.is_attr_true( child, 'dimensions_in_reply' )
175
176
177        # Do some validation of the GLX protocol information.  As
178        # new tests are discovered, they should be added here.
179
180        for param in self.parameters:
181            if param.is_output and self.glx_rop != 0:
182                raise RuntimeError("Render / RenderLarge commands cannot have outputs (%s)." % (self.name))
183
184        return
185
186
187    def has_variable_size_request(self):
188        """Determine if the GLX request packet is variable sized.
189
190        The GLX request packet is variable sized in several common
191        situations.
192
193        1. The function has a non-output parameter that is counted
194           by another parameter (e.g., the 'textures' parameter of
195           glDeleteTextures).
196
197        2. The function has a non-output parameter whose count is
198           determined by another parameter that is an enum (e.g., the
199           'params' parameter of glLightfv).
200
201        3. The function has a non-output parameter that is an
202           image.
203
204        4. The function must be hand-coded on the server.
205        """
206
207        if self.glx_rop == 0:
208            return 0
209
210        if self.server_handcode or self.images:
211            return 1
212
213        for param in self.parameters:
214            if not param.is_output:
215                if param.counter or len(param.count_parameter_list):
216                    return 1
217
218        return 0
219
220
221    def variable_length_parameter(self):
222        for param in self.parameters:
223            if not param.is_output:
224                if param.counter or len(param.count_parameter_list):
225                    return param
226
227        return None
228
229
230    def calculate_offsets(self):
231        if not self.offsets_calculated:
232            # Calculate the offset of the first function parameter
233            # in the GLX command packet.  This byte offset is
234            # measured from the end of the Render / RenderLarge
235            # header.  The offset for all non-pixel commends is
236            # zero.  The offset for pixel commands depends on the
237            # number of dimensions of the pixel data.
238
239            if len(self.images) and not self.images[0].is_output:
240                [dim, junk, junk, junk, junk] = self.images[0].get_dimensions()
241
242                # The base size is the size of the pixel pack info
243                # header used by images with the specified number
244                # of dimensions.
245
246                if dim <=  2:
247                    offset = 20
248                elif dim <= 4:
249                    offset = 36
250                else:
251                    raise RuntimeError('Invalid number of dimensions %u for parameter "%s" in function "%s".' % (dim, self.image.name, self.name))
252            else:
253                offset = 0
254
255            for param in self.parameterIterateGlxSend():
256                if param.img_null_flag:
257                    offset += 4
258
259                if param.name != self.img_reset:
260                    param.offset = offset
261                    if not param.is_variable_length() and not param.is_client_only:
262                        offset += param.size()
263
264                if self.pad_after( param ):
265                    offset += 4
266
267
268            self.offsets_calculated = 1
269        return
270
271
272    def offset_of(self, param_name):
273        self.calculate_offsets()
274        return self.parameters_by_name[ param_name ].offset
275
276
277    def parameterIterateGlxSend(self, include_variable_parameters = 1):
278        """Create an iterator for parameters in GLX request order."""
279
280        # The parameter lists are usually quite short, so it's easier
281        # (i.e., less code) to just generate a new list with the
282        # required elements than it is to create a new iterator class.
283
284        temp = [ [],  [], [] ]
285        for param in self.parameters:
286            if param.is_output: continue
287
288            if param.is_variable_length():
289                temp[2].append( param )
290            elif not self.glx_doubles_in_order and param.is_64_bit():
291                temp[0].append( param )
292            else:
293                temp[1].append( param )
294
295        parameters = temp[0]
296        parameters.extend( temp[1] )
297        if include_variable_parameters:
298            parameters.extend( temp[2] )
299        return iter(parameters)
300
301
302    def parameterIterateCounters(self):
303        temp = []
304        for name in self.counter_list:
305            temp.append( self.parameters_by_name[ name ] )
306
307        return iter(temp)
308
309
310    def parameterIterateOutputs(self):
311        temp = []
312        for p in self.parameters:
313            if p.is_output:
314                temp.append( p )
315
316        return temp
317
318
319    def command_fixed_length(self):
320        """Return the length, in bytes as an integer, of the
321        fixed-size portion of the command."""
322
323        if len(self.parameters) == 0:
324            return 0
325
326        self.calculate_offsets()
327
328        size = 0
329        for param in self.parameterIterateGlxSend(0):
330            if param.name != self.img_reset and not param.is_client_only:
331                if size == 0:
332                    size = param.offset + param.size()
333                else:
334                    size += param.size()
335
336                if self.pad_after( param ):
337                    size += 4
338
339        for param in self.images:
340            if param.img_null_flag or param.is_output:
341                size += 4
342
343        return size
344
345
346    def command_variable_length(self):
347        """Return the length, as a string, of the variable-sized
348        portion of the command."""
349
350        size_string = ""
351        for p in self.parameterIterateGlxSend():
352            if (not p.is_output) and (p.is_variable_length() or p.is_image()):
353                # FIXME Replace the 1 in the size_string call
354                # FIXME w/0 to eliminate some un-needed parnes
355                # FIXME This would already be done, but it
356                # FIXME adds some extra diffs to the generated
357                # FIXME code.
358
359                size_string = size_string + " + safe_pad(%s)" % (p.size_string(1))
360
361        return size_string
362
363
364    def command_length(self):
365        size = self.command_fixed_length()
366
367        if self.glx_rop != 0:
368            size += 4
369
370        size = ((size + 3) & ~3)
371        return "%u%s" % (size, self.command_variable_length())
372
373
374    def opcode_real_value(self):
375        """Get the true numeric value of the GLX opcode
376
377        Behaves similarly to opcode_value, except for
378        X_GLXVendorPrivate and X_GLXVendorPrivateWithReply commands.
379        In these cases the value for the GLX opcode field (i.e.,
380        16 for X_GLXVendorPrivate or 17 for
381        X_GLXVendorPrivateWithReply) is returned.  For other 'single'
382        commands, the opcode for the command (e.g., 101 for
383        X_GLsop_NewList) is returned."""
384
385        if self.glx_vendorpriv != 0:
386            if self.needs_reply():
387                return 17
388            else:
389                return 16
390        else:
391            return self.opcode_value()
392
393
394    def opcode_value(self):
395        """Get the unique protocol opcode for the glXFunction"""
396
397        if (self.glx_rop == 0) and self.vectorequiv:
398            equiv = self.context.functions_by_name[ self.vectorequiv ]
399            self.glx_rop = equiv.glx_rop
400
401
402        if self.glx_rop != 0:
403            return self.glx_rop
404        elif self.glx_sop != 0:
405            return self.glx_sop
406        elif self.glx_vendorpriv != 0:
407            return self.glx_vendorpriv
408        else:
409            return -1
410
411
412    def opcode_rop_basename(self):
413        """Return either the name to be used for GLX protocol enum.
414
415        Returns either the name of the function or the name of the
416        name of the equivalent vector (e.g., glVertex3fv for
417        glVertex3f) function."""
418
419        if self.vectorequiv == None:
420            return self.name
421        else:
422            return self.vectorequiv
423
424
425    def opcode_name(self):
426        """Get the unique protocol enum name for the glXFunction"""
427
428        if (self.glx_rop == 0) and self.vectorequiv:
429            equiv = self.context.functions_by_name[ self.vectorequiv ]
430            self.glx_rop = equiv.glx_rop
431            self.glx_doubles_in_order = equiv.glx_doubles_in_order
432
433
434        if self.glx_rop != 0:
435            return "X_GLrop_%s" % (self.opcode_rop_basename())
436        elif self.glx_sop != 0:
437            return "X_GLsop_%s" % (self.name)
438        elif self.glx_vendorpriv != 0:
439            return "X_GLvop_%s" % (self.name)
440        else:
441            raise RuntimeError('Function "%s" has no opcode.' % (self.name))
442
443
444    def opcode_vendor_name(self, name):
445        if name in self.glx_vendorpriv_names:
446            return "X_GLvop_%s" % (name)
447        else:
448            raise RuntimeError('Function "%s" has no VendorPrivate opcode.' % (name))
449
450
451    def opcode_real_name(self):
452        """Get the true protocol enum name for the GLX opcode
453
454        Behaves similarly to opcode_name, except for
455        X_GLXVendorPrivate and X_GLXVendorPrivateWithReply commands.
456        In these cases the string 'X_GLXVendorPrivate' or
457        'X_GLXVendorPrivateWithReply' is returned.  For other
458        single or render commands 'X_GLsop' or 'X_GLrop' plus the
459        name of the function returned."""
460
461        if self.glx_vendorpriv != 0:
462            if self.needs_reply():
463                return "X_GLXVendorPrivateWithReply"
464            else:
465                return "X_GLXVendorPrivate"
466        else:
467            return self.opcode_name()
468
469
470    def needs_reply(self):
471        try:
472            x = self._needs_reply
473        except Exception:
474            x = 0
475            if self.return_type != 'void':
476                x = 1
477
478            for param in self.parameters:
479                if param.is_output:
480                    x = 1
481                    break
482
483            self._needs_reply = x
484
485        return x
486
487
488    def pad_after(self, p):
489        """Returns the name of the field inserted after the
490        specified field to pad out the command header."""
491
492        for image in self.images:
493            if image.img_pad_dimensions:
494                if not image.height:
495                    if p.name == image.width:
496                        return "height"
497                    elif p.name == image.img_xoff:
498                        return "yoffset"
499                elif not image.extent:
500                    if p.name == image.depth:
501                        # Should this be "size4d"?
502                        return "extent"
503                    elif p.name == image.img_zoff:
504                        return "woffset"
505
506        return None
507
508
509    def has_different_protocol(self, name):
510        """Returns true if the named version of the function uses different protocol from the other versions.
511
512        Some functions, such as glDeleteTextures and
513        glDeleteTexturesEXT are functionally identical, but have
514        different protocol.  This function returns true if the
515        named function is an alias name and that named version uses
516        different protocol from the function that is aliased.
517        """
518
519        return (name in self.glx_vendorpriv_names) and self.glx_sop
520
521
522    def static_glx_name(self, name):
523        if self.has_different_protocol(name):
524            for n in self.glx_vendorpriv_names:
525                if n in self.static_entry_points:
526                    return n
527
528        return self.static_name(name)
529
530
531    def client_supported_for_indirect(self):
532        """Returns true if the function is supported on the client
533        side for indirect rendering."""
534
535        return not self.ignore and (self.offset != -1) and (self.glx_rop or self.glx_sop or self.glx_vendorpriv or self.vectorequiv or self.client_handcode)
536
537
538class glx_function_iterator(object):
539    """Class to iterate over a list of glXFunctions"""
540
541    def __init__(self, context):
542        self.iterator = context.functionIterateByOffset()
543        return
544
545
546    def __iter__(self):
547        return self
548
549
550    def __next__(self):
551        while True:
552            f = next(self.iterator)
553
554            if f.client_supported_for_indirect():
555                return f
556
557    next = __next__
558
559
560class glx_api(gl_XML.gl_api):
561    def functionIterateGlx(self):
562        return glx_function_iterator(self)
563
564