From: Paul Berry Date: Wed, 7 Nov 2012 21:03:13 +0000 (-0800) Subject: glapi: Generate GL API marshalling code from the XML. X-Git-Url: https://git.libre-soc.org/?p=mesa.git;a=commitdiff_plain;h=05dd4a1104ea815e4627bab0fbd35d9abe49f024 glapi: Generate GL API marshalling code from the XML. This is not yet used in the build, just generated. v2: Add missing build dependencies. v3: Avoid mixing declarations and code, remove logic for avoiding emitting code that the compiler's optimizer can deal with anyway. v4: (Timothy Arceri) move safe_mul() genereation here from a later patch. Acked-by: Timothy Arceri Acked-by: Marek Olšák Tested-by: Dieter Nützel Tested-by: Mike Lothian --- diff --git a/src/mapi/glapi/gen/Makefile.am b/src/mapi/glapi/gen/Makefile.am index 079b1bb225e..1375855858f 100644 --- a/src/mapi/glapi/gen/Makefile.am +++ b/src/mapi/glapi/gen/Makefile.am @@ -54,6 +54,8 @@ BUILT_SOURCES = \ $(MESA_GLAPI_ASM_OUTPUTS) \ $(MESA_DIR)/main/enums.c \ $(MESA_DIR)/main/api_exec.c \ + $(MESA_DIR)/main/marshal_generated.c \ + $(MESA_DIR)/main/marshal_generated.h \ $(MESA_DIR)/main/dispatch.h \ $(MESA_DIR)/main/remap_helper.h \ $(MESA_GLX_DIR)/indirect.c \ @@ -287,6 +289,12 @@ $(MESA_DIR)/main/enums.c: gl_enums.py $(srcdir)/../registry/gl.xml $(MESA_DIR)/main/api_exec.c: gl_genexec.py apiexec.py $(COMMON) $(PYTHON_GEN) $(srcdir)/gl_genexec.py -f $(srcdir)/gl_and_es_API.xml > $@ +$(MESA_DIR)/main/marshal_generated.c: gl_marshal.py marshal_XML.py $(COMMON) + $(PYTHON_GEN) $< -f $(srcdir)/gl_and_es_API.xml > $@ + +$(MESA_DIR)/main/marshal_generated.h: gl_marshal_h.py marshal_XML.py $(COMMON) + $(PYTHON_GEN) $< -f $(srcdir)/gl_and_es_API.xml > $@ + $(MESA_DIR)/main/dispatch.h: gl_table.py $(COMMON) $(PYTHON_GEN) $(srcdir)/gl_table.py -f $(srcdir)/gl_and_es_API.xml -m remap_table > $@ diff --git a/src/mapi/glapi/gen/gl_marshal.py b/src/mapi/glapi/gen/gl_marshal.py new file mode 100644 index 00000000000..b7e05acb133 --- /dev/null +++ b/src/mapi/glapi/gen/gl_marshal.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python + +# 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. + +import contextlib +import getopt +import gl_XML +import license +import marshal_XML +import sys + +header = """ +#include "api_exec.h" +#include "context.h" +#include "dispatch.h" +#include "glthread.h" +#include "marshal.h" +#include "marshal_generated.h" +""" + + +current_indent = 0 + + +def out(str): + if str: + print ' '*current_indent + str + else: + print '' + + +@contextlib.contextmanager +def indent(delta = 3): + global current_indent + current_indent += delta + yield + current_indent -= delta + + +class PrintCode(gl_XML.gl_print_base): + def __init__(self): + super(PrintCode, self).__init__() + + self.name = 'gl_marshal.py' + self.license = license.bsd_license_template % ( + 'Copyright (C) 2012 Intel Corporation', 'INTEL CORPORATION') + + def printRealHeader(self): + print header + print '#include ' + print + print 'static _X_INLINE int safe_mul(int a, int b)' + print '{' + print ' if (a < 0 || b < 0) return -1;' + print ' if (a == 0 || b == 0) return 0;' + print ' if (a > INT_MAX / b) return -1;' + print ' return a * b;' + print '}' + print + + def printRealFooter(self): + pass + + def print_sync_call(self, func): + call = 'CALL_{0}(ctx->CurrentServerDispatch, ({1}))'.format( + func.name, func.get_called_parameter_string()) + if func.return_type == 'void': + out('{0};'.format(call)) + else: + out('return {0};'.format(call)) + + def print_sync_dispatch(self, func): + out('_mesa_glthread_finish(ctx);') + self.print_sync_call(func) + + def print_sync_body(self, func): + out('/* {0}: marshalled synchronously */'.format(func.name)) + out('static {0} GLAPIENTRY'.format(func.return_type)) + out('_mesa_marshal_{0}({1})'.format(func.name, func.get_parameter_string())) + out('{') + with indent(): + out('GET_CURRENT_CONTEXT(ctx);') + out('_mesa_glthread_finish(ctx);') + out('debug_print_sync("{0}");'.format(func.name)) + self.print_sync_call(func) + out('}') + out('') + out('') + + def print_async_dispatch(self, func): + out('cmd = _mesa_glthread_allocate_command(ctx, ' + 'DISPATCH_CMD_{0}, cmd_size);'.format(func.name)) + for p in func.fixed_params: + if p.count: + out('memcpy(cmd->{0}, {0}, {1});'.format( + p.name, p.size_string())) + else: + out('cmd->{0} = {0};'.format(p.name)) + if func.variable_params: + out('char *variable_data = (char *) (cmd + 1);') + for p in func.variable_params: + out(('memcpy(variable_data, {0}, {1});').format( + p.name, p.size_string(False))) + out('variable_data += {0};'.format( + p.size_string(False))) + if not func.fixed_params and not func.variable_params: + out('(void) cmd;\n') + out('_mesa_post_marshal_hook(ctx);') + + def print_async_struct(self, func): + out('struct marshal_cmd_{0}'.format(func.name)) + out('{') + with indent(): + out('struct marshal_cmd_base cmd_base;') + for p in func.fixed_params: + if p.count: + out('{0} {1}[{2}];'.format( + p.get_base_type_string(), p.name, p.count)) + else: + out('{0} {1};'.format(p.type_string(), p.name)) + for p in func.variable_params: + if p.count_scale != 1: + out(('/* Next {0} bytes are ' + '{1} {2}[{3}][{4}] */').format( + p.size_string(), p.get_base_type_string(), + p.name, p.counter, p.count_scale)) + else: + out(('/* Next {0} bytes are ' + '{1} {2}[{3}] */').format( + p.size_string(), p.get_base_type_string(), + p.name, p.counter)) + out('};') + + def print_async_unmarshal(self, func): + out('static inline void') + out(('_mesa_unmarshal_{0}(struct gl_context *ctx, ' + 'const struct marshal_cmd_{0} *cmd)').format(func.name)) + out('{') + with indent(): + for p in func.fixed_params: + if p.count: + out('const {0} * {1} = cmd->{1};'.format( + p.get_base_type_string(), p.name)) + else: + out('const {0} {1} = cmd->{1};'.format( + p.type_string(), p.name)) + if func.variable_params: + for p in func.variable_params: + out('const {0} * {1};'.format( + p.get_base_type_string(), p.name)) + out('const char *variable_data = (const char *) (cmd + 1);') + for p in func.variable_params: + out('{0} = (const {1} *) variable_data;'.format( + p.name, p.get_base_type_string())) + out('variable_data += {0};'.format(p.size_string(False))) + self.print_sync_call(func) + out('}') + + def print_async_marshal(self, func): + out('static void GLAPIENTRY') + out('_mesa_marshal_{0}({1})'.format( + func.name, func.get_parameter_string())) + out('{') + with indent(): + out('GET_CURRENT_CONTEXT(ctx);') + struct = 'struct marshal_cmd_{0}'.format(func.name) + size_terms = ['sizeof({0})'.format(struct)] + for p in func.variable_params: + size_terms.append(p.size_string()) + out('size_t cmd_size = {0};'.format(' + '.join(size_terms))) + out('{0} *cmd;'.format(struct)) + + out('debug_print_marshal("{0}");'.format(func.name)) + + out('if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {') + with indent(): + self.print_async_dispatch(func) + out('} else {') + with indent(): + self.print_sync_dispatch(func) + out('}') + + out('}') + + def print_async_body(self, func): + out('/* {0}: marshalled asynchronously */'.format(func.name)) + self.print_async_struct(func) + self.print_async_unmarshal(func) + self.print_async_marshal(func) + out('') + out('') + + def print_unmarshal_dispatch_cmd(self, api): + out('size_t') + out('_mesa_unmarshal_dispatch_cmd(struct gl_context *ctx, ' + 'const void *cmd)') + out('{') + with indent(): + out('const struct marshal_cmd_base *cmd_base = cmd;') + out('switch (cmd_base->cmd_id) {') + for func in api.functionIterateAll(): + flavor = func.marshal_flavor() + if flavor in ('skip', 'sync'): + continue + out('case DISPATCH_CMD_{0}:'.format(func.name)) + with indent(): + out('debug_print_unmarshal("{0}");'.format(func.name)) + out(('_mesa_unmarshal_{0}(ctx, (const struct marshal_cmd_{0} *)' + ' cmd);').format(func.name)) + out('break;') + out('default:') + with indent(): + out('assert(!"Unrecognized command ID");') + out('break;') + out('}') + out('') + out('return cmd_base->cmd_size;') + out('}') + out('') + out('') + + def print_create_marshal_table(self, api): + out('struct _glapi_table *') + out('_mesa_create_marshal_table(const struct gl_context *ctx)') + out('{') + with indent(): + out('struct _glapi_table *table;') + out('') + out('table = _mesa_alloc_dispatch_table();') + out('if (table == NULL)') + with indent(): + out('return NULL;') + out('') + for func in api.functionIterateAll(): + if func.marshal_flavor() == 'skip': + continue + out('SET_{0}(table, _mesa_marshal_{0});'.format(func.name)) + out('') + out('return table;') + out('}') + out('') + out('') + + def printBody(self, api): + async_funcs = [] + for func in api.functionIterateAll(): + flavor = func.marshal_flavor() + if flavor in ('skip', 'custom'): + continue + elif flavor == 'async': + self.print_async_body(func) + async_funcs.append(func) + elif flavor == 'sync': + self.print_sync_body(func) + self.print_unmarshal_dispatch_cmd(api) + self.print_create_marshal_table(api) + + +def show_usage(): + print 'Usage: %s [-f input_file_name]' % sys.argv[0] + sys.exit(1) + + +if __name__ == '__main__': + file_name = 'gl_API.xml' + + try: + (args, trail) = getopt.getopt(sys.argv[1:], 'm:f:') + except Exception,e: + show_usage() + + for (arg,val) in args: + if arg == '-f': + file_name = val + + printer = PrintCode() + + api = gl_XML.parse_GL_API(file_name, marshal_XML.marshal_item_factory()) + printer.Print(api) diff --git a/src/mapi/glapi/gen/gl_marshal_h.py b/src/mapi/glapi/gen/gl_marshal_h.py new file mode 100644 index 00000000000..6e39148d29a --- /dev/null +++ b/src/mapi/glapi/gen/gl_marshal_h.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +# 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. + +import getopt +import gl_XML +import license +import marshal_XML +import sys + + +header = """ +#ifndef MARSHAL_GENERATABLE_H +#define MARSHAL_GENERATABLE_H +""" + +footer = """ +#endif /* MARSHAL_GENERATABLE_H */ +""" + + +class PrintCode(gl_XML.gl_print_base): + def __init__(self): + super(PrintCode, self).__init__() + + self.name = 'gl_marshal_h.py' + self.license = license.bsd_license_template % ( + 'Copyright (C) 2012 Intel Corporation', 'INTEL CORPORATION') + + def printRealHeader(self): + print header + + def printRealFooter(self): + print footer + + def printBody(self, api): + print 'enum marshal_dispatch_cmd_id' + print '{' + for func in api.functionIterateAll(): + flavor = func.marshal_flavor() + if flavor in ('skip', 'sync'): + continue + print ' DISPATCH_CMD_{0},'.format(func.name) + print '};' + + +def show_usage(): + print 'Usage: %s [-f input_file_name]' % sys.argv[0] + sys.exit(1) + + +if __name__ == '__main__': + file_name = 'gl_API.xml' + + try: + (args, trail) = getopt.getopt(sys.argv[1:], 'm:f:') + except Exception,e: + show_usage() + + for (arg,val) in args: + if arg == '-f': + file_name = val + + printer = PrintCode() + + api = gl_XML.parse_GL_API(file_name, marshal_XML.marshal_item_factory()) + printer.Print(api) diff --git a/src/mapi/glapi/gen/marshal_XML.py b/src/mapi/glapi/gen/marshal_XML.py new file mode 100644 index 00000000000..e8ddb7ff8f6 --- /dev/null +++ b/src/mapi/glapi/gen/marshal_XML.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +# 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 + + +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): + 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') + + 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 != None: + if self.marshal == 'draw': + # TODO: as a temporary measure, do draw functions + # synchronously, since they may access client memory + # via vertex attribute pointers. + return 'sync' + 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): + return 'sync' + if p.count_parameter_list: + # Parameter size is determined by enums; haven't + # written logic to handle this yet. TODO: fix. + return 'sync' + if p.img_null_flag: + # Caller is allowed to pass NULL for this parameter; + # haven't written logic to handle this yet. TODO: + # fix. + return 'sync' + return 'async' diff --git a/src/mesa/Android.gen.mk b/src/mesa/Android.gen.mk index a985f0a64b1..a1e58a6d3b8 100644 --- a/src/mesa/Android.gen.mk +++ b/src/mesa/Android.gen.mk @@ -113,6 +113,18 @@ $(intermediates)/main/api_exec.c: PRIVATE_XML := -f $(glapi)/gl_and_es_API.xml $(intermediates)/main/api_exec.c: $(dispatch_deps) $(call es-gen) +$(intermediates)/main/marshal_generated.c: PRIVATE_SCRIPT := $(MESA_PYTHON2) $(glapi)/gl_marshal.py +$(intermediates)/main/marshal_generated.c: PRIVATE_XML := -f $(glapi)/gl_and_es_API.xml + +$(intermediates)/main/marshal_generated.c: $(dispatch_deps) + $(call es-gen) + +$(intermediates)/main/marshal_generated.h: PRIVATE_SCRIPT := $(MESA_PYTHON2) $(glapi)/gl_marshal_h.py +$(intermediates)/main/marshal_generated.h: PRIVATE_XML := -f $(glapi)/gl_and_es_API.xml + +$(intermediates)/main/marshal_generated.h: $(dispatch_deps) + $(call es-gen) + GET_HASH_GEN := $(LOCAL_PATH)/main/get_hash_generator.py $(intermediates)/main/get_hash.h: PRIVATE_SCRIPT := $(MESA_PYTHON2) $(GET_HASH_GEN) diff --git a/src/mesa/SConscript b/src/mesa/SConscript index e375de6ee77..9398ddb13bf 100644 --- a/src/mesa/SConscript +++ b/src/mesa/SConscript @@ -118,6 +118,22 @@ if env['platform'] not in ('cygwin', 'darwin', 'windows', 'haiku'): env.Prepend(CPPPATH = [matypes[0].dir]) +# The marshal_generated.c file is generated from the GL/ES API.xml file +env.CodeGenerate( + target = 'main/marshal_generated.c', + script = GLAPI + 'gen/gl_marshal.py', + source = GLAPI + 'gen/gl_and_es_API.xml', + command = python_cmd + ' $SCRIPT -f $SOURCE > $TARGET' + ) + +# The marshal_generated.h file is generated from the GL/ES API.xml file +env.CodeGenerate( + target = 'main/marshal_generated.h', + script = GLAPI + 'gen/gl_marshal_h.py', + source = GLAPI + 'gen/gl_and_es_API.xml', + command = python_cmd + ' $SCRIPT -f $SOURCE > $TARGET' + ) + # # Libraries # diff --git a/src/mesa/main/.gitignore b/src/mesa/main/.gitignore index a4da567c94e..836d8f104a8 100644 --- a/src/mesa/main/.gitignore +++ b/src/mesa/main/.gitignore @@ -8,3 +8,5 @@ format_info.h format_info.c format_pack.c format_unpack.c +marshal_generated.c +marshal_generated.h