| |
| # (C) Copyright IBM Corporation 2004, 2005 |
| # All Rights Reserved. |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a |
| # copy of this software and associated documentation files (the "Software"), |
| # to deal in the Software without restriction, including without limitation |
| # on the rights to use, copy, modify, merge, publish, distribute, sub |
| # license, and/or sell copies of the Software, and to permit persons to whom |
| # the Software is furnished to do so, subject to the following conditions: |
| # |
| # The above copyright notice and this permission notice (including the next |
| # paragraph) shall be included in all copies or substantial portions of the |
| # Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
| # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| # IN THE SOFTWARE. |
| # |
| # Authors: |
| # Ian Romanick <idr@us.ibm.com> |
| |
| import gl_XML |
| import license |
| import sys, getopt, string |
| |
| |
| class glx_item_factory(gl_XML.gl_item_factory): |
| """Factory to create GLX protocol oriented objects derived from gl_item.""" |
| |
| def create_function(self, element, context): |
| return glx_function(element, context) |
| |
| def create_enum(self, element, context, category): |
| return glx_enum(element, context, category) |
| |
| def create_api(self): |
| return glx_api(self) |
| |
| |
| class glx_enum(gl_XML.gl_enum): |
| def __init__(self, element, context, category): |
| gl_XML.gl_enum.__init__(self, element, context, category) |
| |
| self.functions = {} |
| |
| for child in element.getchildren(): |
| if child.tag == "size": |
| n = child.get( "name" ) |
| c = child.get( "count" ) |
| m = child.get( "mode", "set" ) |
| |
| if not c: |
| c = self.default_count |
| else: |
| c = int(c) |
| |
| if m == "get": |
| mode = 0 |
| else: |
| mode = 1 |
| |
| if n not in self.functions: |
| self.functions[ n ] = [c, mode] |
| |
| return |
| |
| |
| class glx_function(gl_XML.gl_function): |
| def __init__(self, element, context): |
| self.glx_rop = 0 |
| self.glx_sop = 0 |
| self.glx_vendorpriv = 0 |
| |
| self.glx_vendorpriv_names = [] |
| |
| # If this is set to true, it means that GLdouble parameters should be |
| # written to the GLX protocol packet in the order they appear in the |
| # prototype. This is different from the "classic" ordering. In the |
| # classic ordering GLdoubles are written to the protocol packet first, |
| # followed by non-doubles. NV_vertex_program was the first extension |
| # to break with this tradition. |
| |
| self.glx_doubles_in_order = 0 |
| |
| self.vectorequiv = None |
| self.output = None |
| self.can_be_large = 0 |
| self.reply_always_array = 0 |
| self.dimensions_in_reply = 0 |
| self.img_reset = None |
| |
| self.server_handcode = 0 |
| self.client_handcode = 0 |
| self.ignore = 0 |
| |
| self.count_parameter_list = [] |
| self.counter_list = [] |
| self.parameters_by_name = {} |
| self.offsets_calculated = 0 |
| |
| gl_XML.gl_function.__init__(self, element, context) |
| return |
| |
| |
| def process_element(self, element): |
| gl_XML.gl_function.process_element(self, element) |
| |
| # If the function already has a vector equivalent set, don't |
| # set it again. This can happen if an alias to a function |
| # appears after the function that it aliases. |
| |
| if not self.vectorequiv: |
| self.vectorequiv = element.get("vectorequiv") |
| |
| |
| name = element.get("name") |
| if name == self.name: |
| for param in self.parameters: |
| self.parameters_by_name[ param.name ] = param |
| |
| if len(param.count_parameter_list): |
| self.count_parameter_list.extend( param.count_parameter_list ) |
| |
| if param.counter and param.counter not in self.counter_list: |
| self.counter_list.append(param.counter) |
| |
| |
| for child in element.getchildren(): |
| if child.tag == "glx": |
| rop = child.get( 'rop' ) |
| sop = child.get( 'sop' ) |
| vop = child.get( 'vendorpriv' ) |
| |
| if rop: |
| self.glx_rop = int(rop) |
| |
| if sop: |
| self.glx_sop = int(sop) |
| |
| if vop: |
| self.glx_vendorpriv = int(vop) |
| self.glx_vendorpriv_names.append(name) |
| |
| self.img_reset = child.get( 'img_reset' ) |
| |
| # The 'handcode' attribute can be one of 'true', |
| # 'false', 'client', or 'server'. |
| |
| handcode = child.get( 'handcode', 'false' ) |
| if handcode == "false": |
| self.server_handcode = 0 |
| self.client_handcode = 0 |
| elif handcode == "true": |
| self.server_handcode = 1 |
| self.client_handcode = 1 |
| elif handcode == "client": |
| self.server_handcode = 0 |
| self.client_handcode = 1 |
| elif handcode == "server": |
| self.server_handcode = 1 |
| self.client_handcode = 0 |
| else: |
| raise RuntimeError('Invalid handcode mode "%s" in function "%s".' % (handcode, self.name)) |
| |
| self.ignore = gl_XML.is_attr_true( child, 'ignore' ) |
| self.can_be_large = gl_XML.is_attr_true( child, 'large' ) |
| self.glx_doubles_in_order = gl_XML.is_attr_true( child, 'doubles_in_order' ) |
| self.reply_always_array = gl_XML.is_attr_true( child, 'always_array' ) |
| self.dimensions_in_reply = gl_XML.is_attr_true( child, 'dimensions_in_reply' ) |
| |
| |
| # Do some validation of the GLX protocol information. As |
| # new tests are discovered, they should be added here. |
| |
| for param in self.parameters: |
| if param.is_output and self.glx_rop != 0: |
| raise RuntimeError("Render / RenderLarge commands cannot have outputs (%s)." % (self.name)) |
| |
| return |
| |
| |
| def has_variable_size_request(self): |
| """Determine if the GLX request packet is variable sized. |
| |
| The GLX request packet is variable sized in several common |
| situations. |
| |
| 1. The function has a non-output parameter that is counted |
| by another parameter (e.g., the 'textures' parameter of |
| glDeleteTextures). |
| |
| 2. The function has a non-output parameter whose count is |
| determined by another parameter that is an enum (e.g., the |
| 'params' parameter of glLightfv). |
| |
| 3. The function has a non-output parameter that is an |
| image. |
| |
| 4. The function must be hand-coded on the server. |
| """ |
| |
| if self.glx_rop == 0: |
| return 0 |
| |
| if self.server_handcode or self.images: |
| return 1 |
| |
| for param in self.parameters: |
| if not param.is_output: |
| if param.counter or len(param.count_parameter_list): |
| return 1 |
| |
| return 0 |
| |
| |
| def variable_length_parameter(self): |
| for param in self.parameters: |
| if not param.is_output: |
| if param.counter or len(param.count_parameter_list): |
| return param |
| |
| return None |
| |
| |
| def calculate_offsets(self): |
| if not self.offsets_calculated: |
| # Calculate the offset of the first function parameter |
| # in the GLX command packet. This byte offset is |
| # measured from the end of the Render / RenderLarge |
| # header. The offset for all non-pixel commends is |
| # zero. The offset for pixel commands depends on the |
| # number of dimensions of the pixel data. |
| |
| if len(self.images) and not self.images[0].is_output: |
| [dim, junk, junk, junk, junk] = self.images[0].get_dimensions() |
| |
| # The base size is the size of the pixel pack info |
| # header used by images with the specified number |
| # of dimensions. |
| |
| if dim <= 2: |
| offset = 20 |
| elif dim <= 4: |
| offset = 36 |
| else: |
| raise RuntimeError('Invalid number of dimensions %u for parameter "%s" in function "%s".' % (dim, self.image.name, self.name)) |
| else: |
| offset = 0 |
| |
| for param in self.parameterIterateGlxSend(): |
| if param.img_null_flag: |
| offset += 4 |
| |
| if param.name != self.img_reset: |
| param.offset = offset |
| if not param.is_variable_length() and not param.is_client_only: |
| offset += param.size() |
| |
| if self.pad_after( param ): |
| offset += 4 |
| |
| |
| self.offsets_calculated = 1 |
| return |
| |
| |
| def offset_of(self, param_name): |
| self.calculate_offsets() |
| return self.parameters_by_name[ param_name ].offset |
| |
| |
| def parameterIterateGlxSend(self, include_variable_parameters = 1): |
| """Create an iterator for parameters in GLX request order.""" |
| |
| # The parameter lists are usually quite short, so it's easier |
| # (i.e., less code) to just generate a new list with the |
| # required elements than it is to create a new iterator class. |
| |
| temp = [ [], [], [] ] |
| for param in self.parameters: |
| if param.is_output: continue |
| |
| if param.is_variable_length(): |
| temp[2].append( param ) |
| elif not self.glx_doubles_in_order and param.is_64_bit(): |
| temp[0].append( param ) |
| else: |
| temp[1].append( param ) |
| |
| parameters = temp[0] |
| parameters.extend( temp[1] ) |
| if include_variable_parameters: |
| parameters.extend( temp[2] ) |
| return iter(parameters) |
| |
| |
| def parameterIterateCounters(self): |
| temp = [] |
| for name in self.counter_list: |
| temp.append( self.parameters_by_name[ name ] ) |
| |
| return iter(temp) |
| |
| |
| def parameterIterateOutputs(self): |
| temp = [] |
| for p in self.parameters: |
| if p.is_output: |
| temp.append( p ) |
| |
| return temp |
| |
| |
| def command_fixed_length(self): |
| """Return the length, in bytes as an integer, of the |
| fixed-size portion of the command.""" |
| |
| if len(self.parameters) == 0: |
| return 0 |
| |
| self.calculate_offsets() |
| |
| size = 0 |
| for param in self.parameterIterateGlxSend(0): |
| if param.name != self.img_reset and not param.is_client_only: |
| if size == 0: |
| size = param.offset + param.size() |
| else: |
| size += param.size() |
| |
| if self.pad_after( param ): |
| size += 4 |
| |
| for param in self.images: |
| if param.img_null_flag or param.is_output: |
| size += 4 |
| |
| return size |
| |
| |
| def command_variable_length(self): |
| """Return the length, as a string, of the variable-sized |
| portion of the command.""" |
| |
| size_string = "" |
| for p in self.parameterIterateGlxSend(): |
| if (not p.is_output) and (p.is_variable_length() or p.is_image()): |
| # FIXME Replace the 1 in the size_string call |
| # FIXME w/0 to eliminate some un-needed parnes |
| # FIXME This would already be done, but it |
| # FIXME adds some extra diffs to the generated |
| # FIXME code. |
| |
| size_string = size_string + " + safe_pad(%s)" % (p.size_string(1)) |
| |
| return size_string |
| |
| |
| def command_length(self): |
| size = self.command_fixed_length() |
| |
| if self.glx_rop != 0: |
| size += 4 |
| |
| size = ((size + 3) & ~3) |
| return "%u%s" % (size, self.command_variable_length()) |
| |
| |
| def opcode_real_value(self): |
| """Get the true numeric value of the GLX opcode |
| |
| Behaves similarly to opcode_value, except for |
| X_GLXVendorPrivate and X_GLXVendorPrivateWithReply commands. |
| In these cases the value for the GLX opcode field (i.e., |
| 16 for X_GLXVendorPrivate or 17 for |
| X_GLXVendorPrivateWithReply) is returned. For other 'single' |
| commands, the opcode for the command (e.g., 101 for |
| X_GLsop_NewList) is returned.""" |
| |
| if self.glx_vendorpriv != 0: |
| if self.needs_reply(): |
| return 17 |
| else: |
| return 16 |
| else: |
| return self.opcode_value() |
| |
| |
| def opcode_value(self): |
| """Get the unique protocol opcode for the glXFunction""" |
| |
| if (self.glx_rop == 0) and self.vectorequiv: |
| equiv = self.context.functions_by_name[ self.vectorequiv ] |
| self.glx_rop = equiv.glx_rop |
| |
| |
| if self.glx_rop != 0: |
| return self.glx_rop |
| elif self.glx_sop != 0: |
| return self.glx_sop |
| elif self.glx_vendorpriv != 0: |
| return self.glx_vendorpriv |
| else: |
| return -1 |
| |
| |
| def opcode_rop_basename(self): |
| """Return either the name to be used for GLX protocol enum. |
| |
| Returns either the name of the function or the name of the |
| name of the equivalent vector (e.g., glVertex3fv for |
| glVertex3f) function.""" |
| |
| if self.vectorequiv == None: |
| return self.name |
| else: |
| return self.vectorequiv |
| |
| |
| def opcode_name(self): |
| """Get the unique protocol enum name for the glXFunction""" |
| |
| if (self.glx_rop == 0) and self.vectorequiv: |
| equiv = self.context.functions_by_name[ self.vectorequiv ] |
| self.glx_rop = equiv.glx_rop |
| self.glx_doubles_in_order = equiv.glx_doubles_in_order |
| |
| |
| if self.glx_rop != 0: |
| return "X_GLrop_%s" % (self.opcode_rop_basename()) |
| elif self.glx_sop != 0: |
| return "X_GLsop_%s" % (self.name) |
| elif self.glx_vendorpriv != 0: |
| return "X_GLvop_%s" % (self.name) |
| else: |
| raise RuntimeError('Function "%s" has no opcode.' % (self.name)) |
| |
| |
| def opcode_vendor_name(self, name): |
| if name in self.glx_vendorpriv_names: |
| return "X_GLvop_%s" % (name) |
| else: |
| raise RuntimeError('Function "%s" has no VendorPrivate opcode.' % (name)) |
| |
| |
| def opcode_real_name(self): |
| """Get the true protocol enum name for the GLX opcode |
| |
| Behaves similarly to opcode_name, except for |
| X_GLXVendorPrivate and X_GLXVendorPrivateWithReply commands. |
| In these cases the string 'X_GLXVendorPrivate' or |
| 'X_GLXVendorPrivateWithReply' is returned. For other |
| single or render commands 'X_GLsop' or 'X_GLrop' plus the |
| name of the function returned.""" |
| |
| if self.glx_vendorpriv != 0: |
| if self.needs_reply(): |
| return "X_GLXVendorPrivateWithReply" |
| else: |
| return "X_GLXVendorPrivate" |
| else: |
| return self.opcode_name() |
| |
| |
| def needs_reply(self): |
| try: |
| x = self._needs_reply |
| except Exception: |
| x = 0 |
| if self.return_type != 'void': |
| x = 1 |
| |
| for param in self.parameters: |
| if param.is_output: |
| x = 1 |
| break |
| |
| self._needs_reply = x |
| |
| return x |
| |
| |
| def pad_after(self, p): |
| """Returns the name of the field inserted after the |
| specified field to pad out the command header.""" |
| |
| for image in self.images: |
| if image.img_pad_dimensions: |
| if not image.height: |
| if p.name == image.width: |
| return "height" |
| elif p.name == image.img_xoff: |
| return "yoffset" |
| elif not image.extent: |
| if p.name == image.depth: |
| # Should this be "size4d"? |
| return "extent" |
| elif p.name == image.img_zoff: |
| return "woffset" |
| |
| return None |
| |
| |
| def has_different_protocol(self, name): |
| """Returns true if the named version of the function uses different protocol from the other versions. |
| |
| Some functions, such as glDeleteTextures and |
| glDeleteTexturesEXT are functionally identical, but have |
| different protocol. This function returns true if the |
| named function is an alias name and that named version uses |
| different protocol from the function that is aliased. |
| """ |
| |
| return (name in self.glx_vendorpriv_names) and self.glx_sop |
| |
| |
| def static_glx_name(self, name): |
| if self.has_different_protocol(name): |
| for n in self.glx_vendorpriv_names: |
| if n in self.static_entry_points: |
| return n |
| |
| return self.static_name(name) |
| |
| |
| def client_supported_for_indirect(self): |
| """Returns true if the function is supported on the client |
| side for indirect rendering.""" |
| |
| 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) |
| |
| |
| class glx_function_iterator(object): |
| """Class to iterate over a list of glXFunctions""" |
| |
| def __init__(self, context): |
| self.iterator = context.functionIterateByOffset() |
| return |
| |
| |
| def __iter__(self): |
| return self |
| |
| |
| def __next__(self): |
| while True: |
| f = next(self.iterator) |
| |
| if f.client_supported_for_indirect(): |
| return f |
| |
| next = __next__ |
| |
| |
| class glx_api(gl_XML.gl_api): |
| def functionIterateGlx(self): |
| return glx_function_iterator(self) |
| |