# Copyright (C) 2012 Intel Corporation # # 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 # the rights to use, copy, modify, merge, publish, distribute, sublicense, # 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 NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS 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. # marshal_XML.py: factory for interpreting XML for the purpose of # building thread marshalling code. import gl_XML import sys import copy import typeexpr def pot_align(base, pot_alignment): return (base + pot_alignment - 1) & ~(pot_alignment - 1); class marshal_item_factory(gl_XML.gl_item_factory): """Factory to create objects derived from gl_item containing information necessary to generate thread marshalling code.""" def create_function(self, element, context): return marshal_function(element, context) class marshal_function(gl_XML.gl_function): # We decrease the type size when it's safe, such as when the maximum value # and all greater values are invalid. def get_marshal_type(self, param): type = param.type_string() if ('Draw' in self.name and ('Arrays' in self.name or 'Elements' in self.name or 'TransformFeedback' in self.name)): if (type, param.name) == ('GLenum', 'mode'): return 'GLenum8' if (type, param.name) == ('GLenum', 'type'): return 'GLindextype' if type == 'GLenum': return 'GLenum16' # clamped to 0xffff (always invalid enum) if self.is_vertex_pointer_call: if (type, param.name) == ('GLsizei', 'stride'): return 'GLclamped16i' if (type, param.name) == ('GLint', 'size'): return 'GLpacked16i' # glVertexAttrib*Pointer(index) # glVertexArrayVertexBuffer(bindingindex) if ((type, param.name) == ('GLuint', 'index') or (type, param.name) == ('GLuint', 'bindingindex')): return 'GLenum8' # clamped to 0xff return type def get_type_size(self, param): type = self.get_marshal_type(param) if type.find('*') != -1: return 8; mapping = { 'GLboolean': 1, 'GLbyte': 1, 'GLubyte': 1, 'GLenum8': 1, # clamped by glthread 'GLindextype': 1, 'GLenum16': 2, # clamped by glthread 'GLshort': 2, 'GLushort': 2, 'GLhalfNV': 2, 'GLclamped16i': 2, # clamped by glthread 'GLpacked16i': 2, # clamped by glthread 'GLint': 4, 'GLuint': 4, 'GLbitfield': 4, 'GLsizei': 4, 'GLfloat': 4, 'GLclampf': 4, 'GLfixed': 4, 'GLclampx': 4, 'GLhandleARB': 4, 'int': 4, 'float': 4, 'GLintptr': self.context.pointer_size, 'GLsizeiptr': self.context.pointer_size, 'GLsync': self.context.pointer_size, 'GLDEBUGPROC': self.context.pointer_size, 'GLdouble': 8, 'GLclampd': 8, 'GLint64': 8, 'GLuint64': 8, 'GLuint64EXT': 8, } val = mapping.get(type, 9999) if val == 9999: print('Unhandled type in marshal_XML.get_type_size: "{0}"'.format(type), file=sys.stderr) assert False return val def process_element(self, element): # Do normal processing. super(marshal_function, self).process_element(element) # Only do further processing when we see the canonical # function name. if element.get('name') != self.name: return # Classify fixed and variable parameters. self.fixed_params = [] self.variable_params = [] for p in self.parameters: if p.is_padding: continue if p.is_variable_length(): self.variable_params.append(p) else: self.fixed_params.append(p) # Store the "marshal" attribute, if present. self.marshal = element.get('marshal') self.marshal_sync = element.get('marshal_sync') self.marshal_call_before = element.get('marshal_call_before') self.marshal_call_after = element.get('marshal_call_after') self.marshal_struct = element.get('marshal_struct') self.marshal_no_error = gl_XML.is_attr_true(element, 'marshal_no_error') self.is_vertex_pointer_call = (self.name == 'InterleavedArrays' or self.name.endswith('VertexBuffer') or self.name.endswith('VertexBufferEXT') or self.name.endswith('Pointer') or self.name.endswith('PointerEXT') or self.name.endswith('PointerOES') or self.name.endswith('OffsetEXT')) # marshal_sync means whether a function should be called to determine # whether we should sync. if self.marshal_sync: # This is a case of a pointer with an unknown size. Move # variable-sized pointer parameters to fixed parameters because # they will be passed as-is if the marshal_sync function evaluates # to true. self.fixed_params = self.fixed_params + self.variable_params self.variable_params = [] # Sort the parameters, so that the marshal structure fields are sorted # from smallest to biggest. self.fixed_params = sorted(self.fixed_params, key=lambda p: self.get_type_size(p)) # Compute the marshal structure size and the largest hole self.struct_size = 2 # sizeof(struct marshal_cmd_base) largest_hole = 0 for p in self.fixed_params: type_size = self.get_type_size(p) aligned_size = pot_align(self.struct_size, type_size) largest_hole = max(aligned_size - self.struct_size, largest_hole) self.struct_size = aligned_size self.struct_size = self.struct_size + type_size # Round down largest_hole to a power of two. largest_hole = int(2 ** (largest_hole.bit_length() - 1)) # Align the structure to 8 bytes. aligned_size = pot_align(self.struct_size, 8) padding_hole = aligned_size - self.struct_size self.struct_size = aligned_size # Determine whether to generate a packed version of gl*Pointer calls. # If there is a hole in the cmd structure, the pointer/offset parameter # can be truncated and stored in the hole to save 8 bytes per call. # The version of the structure is determined at runtime based on # whether the truncation doesn't change the value. This is common with # VBOs because the pointer/offset is usually small. # # If there is no hole, the packed version completely removes # the pointer/offset parameter and is used when the value is NULL/0 # to remove 8 bytes per call. This is common with VBOs. self.packed_param_name = None if (self.is_vertex_pointer_call and # 32-bit CPUs only benefit if we remove the whole 8-byte slot, # which means there must be exactly 4-byte padding after the 4-byte # pointer/offset parameter. (self.context.pointer_size != 4 or padding_hole == 4)): for pname in ['pointer', 'offset']: if pname in [p.name for p in self.fixed_params]: self.packed_param_name = pname assert self.packed_param_name assert not self.variable_params assert not self.marshal_sync # Prepare the parameters for the packed version by replacing the type # of the packed variable or removing it completely. self.packed_fixed_params = [] if self.packed_param_name: for p in self.fixed_params: if p.name == self.packed_param_name: if largest_hole > 0: # Select the truncated type. type = ['GLubyte', 'GLushort', 'GLuint'][largest_hole.bit_length() - 1] # Clone the parameter and change its type new_param = copy.deepcopy(p) new_param.type_expr = typeexpr.type_expression(type, self.context) self.packed_fixed_params.append(new_param) else: self.packed_fixed_params.append(p) self.packed_param_size = largest_hole # Sort the parameters by size to move the truncated type into the hole. self.packed_fixed_params = sorted(self.packed_fixed_params, key=lambda p: self.get_type_size(p)) def get_fixed_params(self, is_packed): return self.packed_fixed_params if is_packed else self.fixed_params def marshal_flavor(self): """Find out how this function should be marshalled between client and server threads.""" # If a "marshal" attribute was present, that overrides any # determination that would otherwise be made by this function. if self.marshal is not None: return self.marshal if self.exec_flavor == 'skip': # Functions marked exec="skip" are not yet implemented in # Mesa, so don't bother trying to marshal them. return 'skip' if self.return_type != 'void': return 'sync' for p in self.parameters: if p.is_output: return 'sync' if (p.is_pointer() and not (p.count or p.counter or p.marshal_count or p.marshal_large_count)): return 'sync' if p.count_parameter_list and not (p.marshal_count or p.marshal_large_count): # Parameter size is determined by enums; haven't # written logic to handle this yet. TODO: fix. return 'sync' return 'async' def marshal_is_static(self): return (self.marshal_flavor() != 'custom' and self.name[0:8] != 'Internal' and self.exec_flavor != 'beginend') def print_struct(self, is_header=False, is_packed=False): if (self.marshal_struct == 'public') == is_header: print(self.get_marshal_struct_name(is_packed)) print('{') print(' struct marshal_cmd_base cmd_base;') if self.variable_params: print(' uint16_t num_slots;') for p in self.get_fixed_params(is_packed): if p.count: print(' {0} {1}[{2}];'.format( p.get_base_type_string(), p.name, p.count)) else: print(' {0} {1};'.format(self.get_marshal_type(p), p.name)) for p in self.variable_params: if p.img_null_flag: print(' bool {0}_null; /* If set, no data follows ' 'for "{0}" */'.format(p.name)) for p in self.variable_params: if p.count_scale != 1: print((' /* Next {0} bytes are ' '{1} {2}[{3}][{4}] */').format( p.size_string(marshal=1), p.get_base_type_string(), p.name, p.counter, p.count_scale)) else: print((' /* Next {0} bytes are ' '{1} {2}[{3}] */').format( p.size_string(marshal=1), p.get_base_type_string(), p.name, p.counter)) print('};') elif self.marshal_flavor() in ('custom', 'async'): print('{0};'.format(self.get_marshal_struct_name(is_packed))) if not is_packed and self.packed_fixed_params: self.print_struct(is_header, True) def get_marshal_struct_name(self, is_packed=False): return 'struct marshal_cmd_{0}{1}'.format(self.name, '_packed' if is_packed else '') def print_unmarshal_prototype(self, is_packed=False, suffix=''): print(('uint32_t _mesa_unmarshal_{0}{1}(struct gl_context *ctx, ' 'const {2} *restrict cmd){3}') .format(self.name, '_packed' if is_packed else '', self.get_marshal_struct_name(is_packed), suffix))