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