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