1 2# Copyright (C) 2012 Intel Corporation 3# 4# Permission is hereby granted, free of charge, to any person obtaining a 5# copy of this software and associated documentation files (the "Software"), 6# to deal in the Software without restriction, including without limitation 7# the rights to use, copy, modify, merge, publish, distribute, sublicense, 8# and/or sell copies of the Software, and to permit persons to whom the 9# Software is furnished to do so, subject to the following conditions: 10# 11# The above copyright notice and this permission notice (including the next 12# paragraph) shall be included in all copies or substantial portions of the 13# Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21# IN THE SOFTWARE. 22 23# marshal_XML.py: factory for interpreting XML for the purpose of 24# building thread marshalling code. 25 26import gl_XML 27import sys 28import copy 29import typeexpr 30 31def pot_align(base, pot_alignment): 32 return (base + pot_alignment - 1) & ~(pot_alignment - 1); 33 34 35class marshal_item_factory(gl_XML.gl_item_factory): 36 """Factory to create objects derived from gl_item containing 37 information necessary to generate thread marshalling code.""" 38 39 def create_function(self, element, context): 40 return marshal_function(element, context) 41 42 43class marshal_function(gl_XML.gl_function): 44 # We decrease the type size when it's safe, such as when the maximum value 45 # and all greater values are invalid. 46 def get_marshal_type(self, param): 47 type = param.type_string() 48 49 if ('Draw' in self.name and 50 ('Arrays' in self.name or 51 'Elements' in self.name or 52 'TransformFeedback' in self.name)): 53 if (type, param.name) == ('GLenum', 'mode'): 54 return 'GLenum8' 55 56 if (type, param.name) == ('GLenum', 'type'): 57 return 'GLindextype' 58 59 if type == 'GLenum': 60 return 'GLenum16' # clamped to 0xffff (always invalid enum) 61 62 if self.is_vertex_pointer_call: 63 if (type, param.name) == ('GLsizei', 'stride'): 64 return 'GLclamped16i' 65 66 if (type, param.name) == ('GLint', 'size'): 67 return 'GLpacked16i' 68 69 # glVertexAttrib*Pointer(index) 70 # glVertexArrayVertexBuffer(bindingindex) 71 if ((type, param.name) == ('GLuint', 'index') or 72 (type, param.name) == ('GLuint', 'bindingindex')): 73 return 'GLenum8' # clamped to 0xff 74 75 return type 76 77 def get_type_size(self, param): 78 type = self.get_marshal_type(param) 79 80 if type.find('*') != -1: 81 return 8; 82 83 mapping = { 84 'GLboolean': 1, 85 'GLbyte': 1, 86 'GLubyte': 1, 87 'GLenum8': 1, # clamped by glthread 88 'GLindextype': 1, 89 'GLenum16': 2, # clamped by glthread 90 'GLshort': 2, 91 'GLushort': 2, 92 'GLhalfNV': 2, 93 'GLclamped16i': 2, # clamped by glthread 94 'GLpacked16i': 2, # clamped by glthread 95 'GLint': 4, 96 'GLuint': 4, 97 'GLbitfield': 4, 98 'GLsizei': 4, 99 'GLfloat': 4, 100 'GLclampf': 4, 101 'GLfixed': 4, 102 'GLclampx': 4, 103 'GLhandleARB': 4, 104 'int': 4, 105 'float': 4, 106 'GLintptr': self.context.pointer_size, 107 'GLsizeiptr': self.context.pointer_size, 108 'GLsync': self.context.pointer_size, 109 'GLDEBUGPROC': self.context.pointer_size, 110 'GLdouble': 8, 111 'GLclampd': 8, 112 'GLint64': 8, 113 'GLuint64': 8, 114 'GLuint64EXT': 8, 115 } 116 val = mapping.get(type, 9999) 117 if val == 9999: 118 print('Unhandled type in marshal_XML.get_type_size: "{0}"'.format(type), file=sys.stderr) 119 assert False 120 return val 121 122 def process_element(self, element): 123 # Do normal processing. 124 super(marshal_function, self).process_element(element) 125 126 # Only do further processing when we see the canonical 127 # function name. 128 if element.get('name') != self.name: 129 return 130 131 # Classify fixed and variable parameters. 132 self.fixed_params = [] 133 self.variable_params = [] 134 for p in self.parameters: 135 if p.is_padding: 136 continue 137 if p.is_variable_length(): 138 self.variable_params.append(p) 139 else: 140 self.fixed_params.append(p) 141 142 # Store the "marshal" attribute, if present. 143 self.marshal = element.get('marshal') 144 self.marshal_sync = element.get('marshal_sync') 145 self.marshal_call_before = element.get('marshal_call_before') 146 self.marshal_call_after = element.get('marshal_call_after') 147 self.marshal_struct = element.get('marshal_struct') 148 self.marshal_no_error = gl_XML.is_attr_true(element, 'marshal_no_error') 149 self.is_vertex_pointer_call = (self.name == 'InterleavedArrays' or 150 self.name.endswith('VertexBuffer') or 151 self.name.endswith('VertexBufferEXT') or 152 self.name.endswith('Pointer') or 153 self.name.endswith('PointerEXT') or 154 self.name.endswith('PointerOES') or 155 self.name.endswith('OffsetEXT')) 156 157 # marshal_sync means whether a function should be called to determine 158 # whether we should sync. 159 if self.marshal_sync: 160 # This is a case of a pointer with an unknown size. Move 161 # variable-sized pointer parameters to fixed parameters because 162 # they will be passed as-is if the marshal_sync function evaluates 163 # to true. 164 self.fixed_params = self.fixed_params + self.variable_params 165 self.variable_params = [] 166 167 # Sort the parameters, so that the marshal structure fields are sorted 168 # from smallest to biggest. 169 self.fixed_params = sorted(self.fixed_params, key=lambda p: self.get_type_size(p)) 170 171 # Compute the marshal structure size and the largest hole 172 self.struct_size = 2 # sizeof(struct marshal_cmd_base) 173 largest_hole = 0 174 175 for p in self.fixed_params: 176 type_size = self.get_type_size(p) 177 aligned_size = pot_align(self.struct_size, type_size) 178 largest_hole = max(aligned_size - self.struct_size, largest_hole) 179 self.struct_size = aligned_size 180 self.struct_size = self.struct_size + type_size 181 182 # Round down largest_hole to a power of two. 183 largest_hole = int(2 ** (largest_hole.bit_length() - 1)) 184 185 # Align the structure to 8 bytes. 186 aligned_size = pot_align(self.struct_size, 8) 187 padding_hole = aligned_size - self.struct_size 188 self.struct_size = aligned_size 189 190 # Determine whether to generate a packed version of gl*Pointer calls. 191 # If there is a hole in the cmd structure, the pointer/offset parameter 192 # can be truncated and stored in the hole to save 8 bytes per call. 193 # The version of the structure is determined at runtime based on 194 # whether the truncation doesn't change the value. This is common with 195 # VBOs because the pointer/offset is usually small. 196 # 197 # If there is no hole, the packed version completely removes 198 # the pointer/offset parameter and is used when the value is NULL/0 199 # to remove 8 bytes per call. This is common with VBOs. 200 self.packed_param_name = None 201 202 if (self.is_vertex_pointer_call and 203 # 32-bit CPUs only benefit if we remove the whole 8-byte slot, 204 # which means there must be exactly 4-byte padding after the 4-byte 205 # pointer/offset parameter. 206 (self.context.pointer_size != 4 or padding_hole == 4)): 207 for pname in ['pointer', 'offset']: 208 if pname in [p.name for p in self.fixed_params]: 209 self.packed_param_name = pname 210 211 assert self.packed_param_name 212 assert not self.variable_params 213 assert not self.marshal_sync 214 215 # Prepare the parameters for the packed version by replacing the type 216 # of the packed variable or removing it completely. 217 self.packed_fixed_params = [] 218 if self.packed_param_name: 219 for p in self.fixed_params: 220 if p.name == self.packed_param_name: 221 if largest_hole > 0: 222 # Select the truncated type. 223 type = ['GLubyte', 'GLushort', 'GLuint'][largest_hole.bit_length() - 1] 224 225 # Clone the parameter and change its type 226 new_param = copy.deepcopy(p) 227 new_param.type_expr = typeexpr.type_expression(type, self.context) 228 self.packed_fixed_params.append(new_param) 229 else: 230 self.packed_fixed_params.append(p) 231 self.packed_param_size = largest_hole 232 # Sort the parameters by size to move the truncated type into the hole. 233 self.packed_fixed_params = sorted(self.packed_fixed_params, key=lambda p: self.get_type_size(p)) 234 235 236 def get_fixed_params(self, is_packed): 237 return self.packed_fixed_params if is_packed else self.fixed_params 238 239 def marshal_flavor(self): 240 """Find out how this function should be marshalled between 241 client and server threads.""" 242 # If a "marshal" attribute was present, that overrides any 243 # determination that would otherwise be made by this function. 244 if self.marshal is not None: 245 return self.marshal 246 247 if self.exec_flavor == 'skip': 248 # Functions marked exec="skip" are not yet implemented in 249 # Mesa, so don't bother trying to marshal them. 250 return 'skip' 251 252 if self.return_type != 'void': 253 return 'sync' 254 for p in self.parameters: 255 if p.is_output: 256 return 'sync' 257 if (p.is_pointer() and not 258 (p.count or p.counter or p.marshal_count or p.marshal_large_count)): 259 return 'sync' 260 if p.count_parameter_list and not (p.marshal_count or p.marshal_large_count): 261 # Parameter size is determined by enums; haven't 262 # written logic to handle this yet. TODO: fix. 263 return 'sync' 264 return 'async' 265 266 def marshal_is_static(self): 267 return (self.marshal_flavor() != 'custom' and 268 self.name[0:8] != 'Internal' and 269 self.exec_flavor != 'beginend') 270 271 def print_struct(self, is_header=False, is_packed=False): 272 if (self.marshal_struct == 'public') == is_header: 273 print(self.get_marshal_struct_name(is_packed)) 274 print('{') 275 print(' struct marshal_cmd_base cmd_base;') 276 if self.variable_params: 277 print(' uint16_t num_slots;') 278 279 for p in self.get_fixed_params(is_packed): 280 if p.count: 281 print(' {0} {1}[{2}];'.format( 282 p.get_base_type_string(), p.name, p.count)) 283 else: 284 print(' {0} {1};'.format(self.get_marshal_type(p), p.name)) 285 286 for p in self.variable_params: 287 if p.img_null_flag: 288 print(' bool {0}_null; /* If set, no data follows ' 289 'for "{0}" */'.format(p.name)) 290 291 for p in self.variable_params: 292 if p.count_scale != 1: 293 print((' /* Next {0} bytes are ' 294 '{1} {2}[{3}][{4}] */').format( 295 p.size_string(marshal=1), p.get_base_type_string(), 296 p.name, p.counter, p.count_scale)) 297 else: 298 print((' /* Next {0} bytes are ' 299 '{1} {2}[{3}] */').format( 300 p.size_string(marshal=1), p.get_base_type_string(), 301 p.name, p.counter)) 302 print('};') 303 elif self.marshal_flavor() in ('custom', 'async'): 304 print('{0};'.format(self.get_marshal_struct_name(is_packed))) 305 306 if not is_packed and self.packed_fixed_params: 307 self.print_struct(is_header, True) 308 309 def get_marshal_struct_name(self, is_packed=False): 310 return 'struct marshal_cmd_{0}{1}'.format(self.name, '_packed' if is_packed else '') 311 312 def print_unmarshal_prototype(self, is_packed=False, suffix=''): 313 print(('uint32_t _mesa_unmarshal_{0}{1}(struct gl_context *ctx, ' 314 'const {2} *restrict cmd){3}') 315 .format(self.name, '_packed' if is_packed else '', 316 self.get_marshal_struct_name(is_packed), suffix)) 317