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 23import contextlib 24import getopt 25import gl_XML 26import license 27import marshal_XML 28import sys 29 30header = """ 31#include "api_exec.h" 32#include "glthread_marshal.h" 33#include "bufferobj.h" 34#include "dispatch.h" 35 36#define COMPAT (ctx->API != API_OPENGL_CORE) 37 38UNUSED static inline int safe_mul(int a, int b) 39{ 40 if (a < 0 || b < 0) return -1; 41 if (a == 0 || b == 0) return 0; 42 if (a > INT_MAX / b) return -1; 43 return a * b; 44} 45""" 46 47 48file_index = 0 49file_count = 1 50current_indent = 0 51 52 53def out(str): 54 if str: 55 print(' '*current_indent + str) 56 else: 57 print('') 58 59 60@contextlib.contextmanager 61def indent(delta = 3): 62 global current_indent 63 current_indent += delta 64 yield 65 current_indent -= delta 66 67 68class PrintCode(gl_XML.gl_print_base): 69 def __init__(self): 70 super(PrintCode, self).__init__() 71 72 self.name = 'gl_marshal.py' 73 self.license = license.bsd_license_template % ( 74 'Copyright (C) 2012 Intel Corporation', 'INTEL CORPORATION') 75 76 def printRealHeader(self): 77 print(header) 78 79 def printRealFooter(self): 80 pass 81 82 def print_sync_call(self, func, unmarshal = 0): 83 call = 'CALL_{0}(ctx->CurrentServerDispatch, ({1}))'.format( 84 func.name, func.get_called_parameter_string()) 85 if func.return_type == 'void' or unmarshal: 86 out('{0};'.format(call)) 87 if func.marshal_call_after and not unmarshal: 88 out(func.marshal_call_after); 89 else: 90 out('return {0};'.format(call)) 91 assert not func.marshal_call_after 92 93 def print_sync_body(self, func): 94 out('/* {0}: marshalled synchronously */'.format(func.name)) 95 out('{0} GLAPIENTRY'.format(func.return_type)) 96 out('_mesa_marshal_{0}({1})'.format(func.name, func.get_parameter_string())) 97 out('{') 98 with indent(): 99 out('GET_CURRENT_CONTEXT(ctx);') 100 out('_mesa_glthread_finish_before(ctx, "{0}");'.format(func.name)) 101 self.print_sync_call(func) 102 out('}') 103 out('') 104 out('') 105 106 def print_async_dispatch(self, func): 107 out('cmd = _mesa_glthread_allocate_command(ctx, ' 108 'DISPATCH_CMD_{0}, cmd_size);'.format(func.name)) 109 110 # We want glthread to ignore variable-sized parameters if the only thing 111 # we want is to pass the pointer parameter as-is, e.g. when a PBO is bound. 112 # Making it conditional on marshal_sync is kinda hacky, but it's the easiest 113 # path towards handling PBOs in glthread, which use marshal_sync to check whether 114 # a PBO is bound. 115 if func.marshal_sync: 116 fixed_params = func.fixed_params + func.variable_params 117 variable_params = [] 118 else: 119 fixed_params = func.fixed_params 120 variable_params = func.variable_params 121 122 for p in fixed_params: 123 if p.count: 124 out('memcpy(cmd->{0}, {0}, {1});'.format( 125 p.name, p.size_string())) 126 else: 127 out('cmd->{0} = {0};'.format(p.name)) 128 if variable_params: 129 out('char *variable_data = (char *) (cmd + 1);') 130 i = 1 131 for p in variable_params: 132 if p.img_null_flag: 133 out('cmd->{0}_null = !{0};'.format(p.name)) 134 out('if (!cmd->{0}_null) {{'.format(p.name)) 135 with indent(): 136 out(('memcpy(variable_data, {0}, {0}_size);').format(p.name)) 137 if i < len(variable_params): 138 out('variable_data += {0}_size;'.format(p.name)) 139 out('}') 140 else: 141 out(('memcpy(variable_data, {0}, {0}_size);').format(p.name)) 142 if i < len(variable_params): 143 out('variable_data += {0}_size;'.format(p.name)) 144 i += 1 145 146 if not fixed_params and not variable_params: 147 out('(void) cmd;') 148 149 if func.marshal_call_after: 150 out(func.marshal_call_after); 151 152 # Uncomment this if you want to call _mesa_glthread_finish for debugging 153 #out('_mesa_glthread_finish(ctx);') 154 155 def get_type_size(self, str): 156 if str.find('*') != -1: 157 return 8; 158 159 mapping = { 160 'GLboolean': 1, 161 'GLbyte': 1, 162 'GLubyte': 1, 163 'GLshort': 2, 164 'GLushort': 2, 165 'GLhalfNV': 2, 166 'GLenum': 4, 167 'GLint': 4, 168 'GLuint': 4, 169 'GLbitfield': 4, 170 'GLsizei': 4, 171 'GLfloat': 4, 172 'GLclampf': 4, 173 'GLfixed': 4, 174 'GLclampx': 4, 175 'GLhandleARB': 4, 176 'int': 4, 177 'float': 4, 178 'GLdouble': 8, 179 'GLclampd': 8, 180 'GLintptr': 8, 181 'GLsizeiptr': 8, 182 'GLint64': 8, 183 'GLuint64': 8, 184 'GLuint64EXT': 8, 185 'GLsync': 8, 186 } 187 val = mapping.get(str, 9999) 188 if val == 9999: 189 print('Unhandled type in gl_marshal.py.get_type_size: ' + str, file=sys.stderr) 190 return val 191 192 def print_async_struct(self, func): 193 if func.marshal_sync: 194 fixed_params = func.fixed_params + func.variable_params 195 variable_params = [] 196 else: 197 fixed_params = func.fixed_params 198 variable_params = func.variable_params 199 200 out('struct marshal_cmd_{0}'.format(func.name)) 201 out('{') 202 with indent(): 203 out('struct marshal_cmd_base cmd_base;') 204 205 # Sort the parameters according to their size to pack the structure optimally 206 for p in sorted(fixed_params, key=lambda p: self.get_type_size(p.type_string())): 207 if p.count: 208 out('{0} {1}[{2}];'.format( 209 p.get_base_type_string(), p.name, p.count)) 210 else: 211 out('{0} {1};'.format(p.type_string(), p.name)) 212 213 for p in variable_params: 214 if p.img_null_flag: 215 out('bool {0}_null; /* If set, no data follows ' 216 'for "{0}" */'.format(p.name)) 217 218 for p in variable_params: 219 if p.count_scale != 1: 220 out(('/* Next {0} bytes are ' 221 '{1} {2}[{3}][{4}] */').format( 222 p.size_string(marshal = 1), p.get_base_type_string(), 223 p.name, p.counter, p.count_scale)) 224 else: 225 out(('/* Next {0} bytes are ' 226 '{1} {2}[{3}] */').format( 227 p.size_string(marshal = 1), p.get_base_type_string(), 228 p.name, p.counter)) 229 out('};') 230 231 def print_async_unmarshal(self, func): 232 if func.marshal_sync: 233 fixed_params = func.fixed_params + func.variable_params 234 variable_params = [] 235 else: 236 fixed_params = func.fixed_params 237 variable_params = func.variable_params 238 239 out('uint32_t') 240 out(('_mesa_unmarshal_{0}(struct gl_context *ctx, ' 241 'const struct marshal_cmd_{0} *cmd, const uint64_t *last)').format(func.name)) 242 out('{') 243 with indent(): 244 for p in fixed_params: 245 if p.count: 246 p_decl = '{0} * {1} = cmd->{1};'.format( 247 p.get_base_type_string(), p.name) 248 else: 249 p_decl = '{0} {1} = cmd->{1};'.format( 250 p.type_string(), p.name) 251 252 if not p_decl.startswith('const ') and p.count: 253 # Declare all local function variables as const, even if 254 # the original parameter is not const. 255 p_decl = 'const ' + p_decl 256 257 out(p_decl) 258 259 if variable_params: 260 for p in variable_params: 261 out('{0} * {1};'.format( 262 p.get_base_type_string(), p.name)) 263 out('const char *variable_data = (const char *) (cmd + 1);') 264 i = 1 265 for p in variable_params: 266 out('{0} = ({1} *) variable_data;'.format( 267 p.name, p.get_base_type_string())) 268 269 if p.img_null_flag: 270 out('if (cmd->{0}_null)'.format(p.name)) 271 with indent(): 272 out('{0} = NULL;'.format(p.name)) 273 if i < len(variable_params): 274 out('else') 275 with indent(): 276 out('variable_data += {0};'.format(p.size_string(False, marshal = 1))) 277 elif i < len(variable_params): 278 out('variable_data += {0};'.format(p.size_string(False, marshal = 1))) 279 i += 1 280 281 self.print_sync_call(func, unmarshal = 1) 282 if variable_params: 283 out('return cmd->cmd_base.cmd_size;') 284 else: 285 struct = 'struct marshal_cmd_{0}'.format(func.name) 286 out('const unsigned cmd_size = (align(sizeof({0}), 8) / 8);'.format(struct)) 287 out('assert (cmd_size == cmd->cmd_base.cmd_size);') 288 out('return cmd_size;'.format(struct)) 289 out('}') 290 291 def validate_count_or_fallback(self, func): 292 # Check that any counts for variable-length arguments might be < 0, in 293 # which case the command alloc or the memcpy would blow up before we 294 # get to the validation in Mesa core. 295 list = [] 296 for p in func.parameters: 297 if p.is_variable_length(): 298 list.append('{0}_size < 0'.format(p.name)) 299 list.append('({0}_size > 0 && !{0})'.format(p.name)) 300 301 if len(list) == 0: 302 return 303 304 list.append('(unsigned)cmd_size > MARSHAL_MAX_CMD_SIZE') 305 306 out('if (unlikely({0})) {{'.format(' || '.join(list))) 307 with indent(): 308 out('_mesa_glthread_finish_before(ctx, "{0}");'.format(func.name)) 309 self.print_sync_call(func) 310 out('return;') 311 out('}') 312 313 def print_async_marshal(self, func): 314 out('{0} GLAPIENTRY'.format(func.return_type)) 315 out('_mesa_marshal_{0}({1})'.format( 316 func.name, func.get_parameter_string())) 317 out('{') 318 with indent(): 319 out('GET_CURRENT_CONTEXT(ctx);') 320 if not func.marshal_sync: 321 for p in func.variable_params: 322 out('int {0}_size = {1};'.format(p.name, p.size_string(marshal = 1))) 323 324 struct = 'struct marshal_cmd_{0}'.format(func.name) 325 size_terms = ['sizeof({0})'.format(struct)] 326 if not func.marshal_sync: 327 for p in func.variable_params: 328 if p.img_null_flag: 329 size_terms.append('({0} ? {0}_size : 0)'.format(p.name)) 330 else: 331 size_terms.append('{0}_size'.format(p.name)) 332 out('int cmd_size = {0};'.format(' + '.join(size_terms))) 333 out('{0} *cmd;'.format(struct)) 334 335 if func.marshal_sync: 336 out('if ({0}) {{'.format(func.marshal_sync)) 337 with indent(): 338 out('_mesa_glthread_finish_before(ctx, "{0}");'.format(func.name)) 339 self.print_sync_call(func) 340 out('return;') 341 out('}') 342 else: 343 self.validate_count_or_fallback(func) 344 345 self.print_async_dispatch(func) 346 if func.return_type == 'GLboolean': 347 out('return GL_TRUE;') # for glUnmapBuffer 348 out('}') 349 350 def print_async_body(self, func): 351 out('/* {0}: marshalled asynchronously */'.format(func.name)) 352 self.print_async_struct(func) 353 self.print_async_unmarshal(func) 354 self.print_async_marshal(func) 355 out('') 356 out('') 357 358 def print_unmarshal_dispatch_cmd(self, api): 359 out('const _mesa_unmarshal_func _mesa_unmarshal_dispatch[NUM_DISPATCH_CMD] = {') 360 with indent(): 361 for func in api.functionIterateAll(): 362 flavor = func.marshal_flavor() 363 if flavor in ('skip', 'sync'): 364 continue 365 out('[DISPATCH_CMD_{0}] = (_mesa_unmarshal_func)_mesa_unmarshal_{0},'.format(func.name)) 366 out('};') 367 out('') 368 out('') 369 370 def print_create_marshal_table(self, api): 371 out('/* _mesa_create_marshal_table takes a long time to compile with -O2 */') 372 out('#if defined(__GNUC__) && !defined(__clang__)') 373 out('__attribute__((optimize("O1")))') 374 out('#endif') 375 out('struct _glapi_table *') 376 out('_mesa_create_marshal_table(const struct gl_context *ctx)') 377 out('{') 378 with indent(): 379 out('struct _glapi_table *table;') 380 out('') 381 out('table = _mesa_alloc_dispatch_table();') 382 out('if (table == NULL)') 383 with indent(): 384 out('return NULL;') 385 out('') 386 for func in api.functionIterateAll(): 387 if func.marshal_flavor() == 'skip': 388 continue 389 # Don't use the SET_* functions, because they increase compile time 390 # by 20 seconds (on Ryzen 1700X). 391 out('if (_gloffset_{0} >= 0)'.format(func.name)) 392 out(' ((_glapi_proc *)(table))[_gloffset_{0}] = (_glapi_proc)_mesa_marshal_{0};' 393 .format(func.name)) 394 out('') 395 out('return table;') 396 out('}') 397 out('') 398 out('') 399 400 def printBody(self, api): 401 # The first file only contains the dispatch tables 402 if file_index == 0: 403 self.print_unmarshal_dispatch_cmd(api) 404 self.print_create_marshal_table(api) 405 return 406 407 # The remaining files contain the marshal and unmarshal functions 408 func_per_file = (len(api.functionIterateAll()) // (file_count - 1)) + 1 409 i = -1 410 for func in api.functionIterateAll(): 411 i += 1 412 if i // func_per_file != (file_index - 1): 413 continue 414 415 flavor = func.marshal_flavor() 416 if flavor in ('skip', 'custom'): 417 continue 418 elif flavor == 'async': 419 self.print_async_body(func) 420 elif flavor == 'sync': 421 self.print_sync_body(func) 422 423 424def show_usage(): 425 print('Usage: %s [-f input_file_name]' % sys.argv[0]) 426 sys.exit(1) 427 428 429if __name__ == '__main__': 430 file_name = 'gl_API.xml' 431 432 try: 433 (args, trail) = getopt.getopt(sys.argv[1:], 'm:f:i:n:') 434 except Exception: 435 show_usage() 436 437 for (arg,val) in args: 438 if arg == '-f': 439 file_name = val 440 elif arg == '-i': 441 file_index = int(val) 442 elif arg == '-n': 443 file_count = int(val) 444 445 assert file_index < file_count 446 printer = PrintCode() 447 448 api = gl_XML.parse_GL_API(file_name, marshal_XML.marshal_item_factory()) 449 printer.Print(api) 450