xref: /aosp_15_r20/external/mesa3d/src/mapi/glapi/gen/gl_marshal.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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 gl_XML
25import license
26import marshal_XML
27import sys
28import collections
29import apiexec
30
31header = """
32#include "context.h"
33#include "glthread_marshal.h"
34#include "bufferobj.h"
35#include "dispatch.h"
36
37#define COMPAT (ctx->API != API_OPENGL_CORE)
38
39UNUSED static inline int safe_mul(int a, int b)
40{
41    if (a < 0 || b < 0) return -1;
42    if (a == 0 || b == 0) return 0;
43    if (a > INT_MAX / b) return -1;
44    return a * b;
45}
46"""
47
48
49file_index = 0
50file_count = 1
51current_indent = 0
52
53
54def out(str):
55    if str:
56        print(' '*current_indent + str)
57    else:
58        print('')
59
60
61@contextlib.contextmanager
62def indent(delta = 3):
63    global current_indent
64    current_indent += delta
65    yield
66    current_indent -= delta
67
68
69class PrintCode(gl_XML.gl_print_base):
70    def __init__(self):
71        super(PrintCode, self).__init__()
72
73        self.name = 'gl_marshal.py'
74        self.license = license.bsd_license_template % (
75            'Copyright (C) 2012 Intel Corporation', 'INTEL CORPORATION')
76
77    def printRealHeader(self):
78        print(header)
79
80    def printRealFooter(self):
81        pass
82
83    def print_call(self, func, unmarshal=0):
84        ret = 'return ' if func.return_type != 'void' and not unmarshal else '';
85        call = 'CALL_{0}(ctx->Dispatch.Current, ({1}))'.format(
86            func.name, func.get_called_parameter_string())
87        out('{0}{1};'.format(ret, call))
88        if func.marshal_call_after and ret == '' and not unmarshal:
89            out(func.marshal_call_after);
90
91    def print_sync_body(self, func):
92        out('/* {0}: marshalled synchronously */'.format(func.name))
93        out('{0}{1} GLAPIENTRY'.format('static ' if func.marshal_is_static() else '', func.return_type))
94        out('_mesa_marshal_{0}({1})'.format(func.name, func.get_parameter_string()))
95        out('{')
96        with indent():
97            out('GET_CURRENT_CONTEXT(ctx);')
98            if func.marshal_call_before:
99                out(func.marshal_call_before);
100            out('_mesa_glthread_finish_before(ctx, "{0}");'.format(func.name))
101            self.print_call(func)
102        out('}')
103        out('')
104        out('')
105
106    def print_unmarshal_func(self, func, is_packed=False):
107        func.print_unmarshal_prototype(is_packed=is_packed)
108        out('{')
109        with indent():
110            for p in func.fixed_params:
111                type = func.get_marshal_type(p)
112
113                if p.count:
114                    p_decl = '{0} *{1} = cmd->{1};'.format(
115                            p.get_base_type_string(), p.name)
116                elif is_packed and func.packed_param_name == p.name:
117                    if func.packed_param_size == 0:
118                        p_decl = '{0} {1} = ({0})(uintptr_t)0;'.format(type, p.name)
119                    else:
120                        p_decl = '{0} {1} = ({0})(uintptr_t)cmd->{1};'.format(type, p.name)
121                else:
122                    p_decl = '{0} {1} = cmd->{1};'.format(type, p.name)
123
124                if not p_decl.startswith('const ') and p.count:
125                    # Declare all local function variables as const, even if
126                    # the original parameter is not const.
127                    p_decl = 'const ' + p_decl
128
129                out(p_decl)
130
131            if func.variable_params:
132                for p in func.variable_params:
133                    out('{0} *{1};'.format(
134                            p.get_base_type_string(), p.name))
135                out('const char *variable_data = (const char *) (cmd + 1);')
136                i = 1
137                for p in func.variable_params:
138                    out('{0} = ({1} *) variable_data;'.format(
139                            p.name, p.get_base_type_string()))
140
141                    if p.img_null_flag:
142                        out('if (cmd->{0}_null)'.format(p.name))
143                        with indent():
144                            out('{0} = NULL;'.format(p.name))
145                        if i < len(func.variable_params):
146                            out('else')
147                            with indent():
148                                out('variable_data += {0};'.format(p.size_string(False, marshal=1)))
149                    elif i < len(func.variable_params):
150                        out('variable_data += {0};'.format(p.size_string(False, marshal=1)))
151                    i += 1
152
153            self.print_call(func, unmarshal=1)
154            if func.variable_params:
155                out('return cmd->num_slots;')
156            else:
157                out('return align(sizeof({0}), 8) / 8;'.format(func.get_marshal_struct_name(is_packed)))
158        out('}')
159
160        if not is_packed and func.packed_fixed_params:
161            self.print_unmarshal_func(func, is_packed=True)
162
163    def print_marshal_async_code(self, func, is_packed=False):
164        struct = func.get_marshal_struct_name(is_packed)
165
166        if func.marshal_sync:
167            out('int cmd_size = sizeof({0});'.format(struct))
168
169            out('if ({0}) {{'.format(func.marshal_sync))
170            with indent():
171                out('_mesa_glthread_finish_before(ctx, "{0}");'.format(func.name))
172                self.print_call(func)
173                out('return;')
174            out('}')
175        else:
176            size_terms = ['sizeof({0})'.format(struct)]
177            for p in func.variable_params:
178                out('int {0}_size = {1};'.format(p.name, p.size_string(marshal=1)))
179                if p.img_null_flag:
180                    size_terms.append('({0} ? {0}_size : 0)'.format(p.name))
181                else:
182                    size_terms.append('{0}_size'.format(p.name))
183
184            out('int cmd_size = {0};'.format(' + '.join(size_terms)))
185
186            # Fall back to syncing if variable-length sizes can't be handled.
187            #
188            # Check that any counts for variable-length arguments might be < 0, in
189            # which case the command alloc or the memcpy would blow up before we
190            # get to the validation in Mesa core.
191            list = []
192            assert_size = False
193            for p in func.parameters:
194                if p.is_variable_length():
195                    if p.marshal_count:
196                        assert_size = True
197                    else:
198                        list.append('{0}_size < 0'.format(p.name))
199                        list.append('({0}_size > 0 && !{0})'.format(p.name))
200
201            if len(list) != 0:
202                list.append('(unsigned)cmd_size > MARSHAL_MAX_CMD_SIZE')
203
204                out('if (unlikely({0})) {{'.format(' || '.join(list)))
205                with indent():
206                    out('_mesa_glthread_finish_before(ctx, "{0}");'.format(func.name))
207                    self.print_call(func)
208                    out('return;')
209                out('}')
210            elif assert_size:
211                out('assert(cmd_size >= 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE);')
212
213        # Add the call into the batch.
214        dispatch_cmd = 'DISPATCH_CMD_{0}{1}'.format(func.name, '_packed' if is_packed else '')
215        if func.get_fixed_params(is_packed) or func.variable_params:
216            out('{0} *cmd = _mesa_glthread_allocate_command(ctx, {1}, cmd_size);'
217                .format(struct, dispatch_cmd))
218        else:
219            out('_mesa_glthread_allocate_command(ctx, {0}, cmd_size);'.format(dispatch_cmd))
220
221        if func.variable_params:
222            out('cmd->num_slots = align(cmd_size, 8) / 8;')
223
224        for p in func.get_fixed_params(is_packed):
225            type = func.get_marshal_type(p)
226
227            if p.count:
228                out('memcpy(cmd->{0}, {0}, {1});'.format(
229                        p.name, p.size_string()))
230            elif is_packed and p.name == func.packed_param_name:
231                out('cmd->{0} = (uintptr_t){0}; /* truncated */'.format(p.name))
232            elif type == 'GLenum8':
233                out('cmd->{0} = MIN2({0}, 0xff); /* clamped to 0xff (invalid enum) */'.format(p.name))
234            elif type == 'GLenum16':
235                out('cmd->{0} = MIN2({0}, 0xffff); /* clamped to 0xffff (invalid enum) */'.format(p.name))
236            elif type == 'GLclamped16i':
237                out('cmd->{0} = CLAMP({0}, INT16_MIN, INT16_MAX);'.format(p.name))
238            elif type == 'GLpacked16i':
239                out('cmd->{0} = {0} < 0 ? UINT16_MAX : MIN2({0}, UINT16_MAX);'.format(p.name))
240            else:
241                out('cmd->{0} = {0};'.format(p.name))
242
243        if func.variable_params:
244            out('char *variable_data = (char *) (cmd + 1);')
245            i = 1
246            for p in func.variable_params:
247                if p.img_null_flag:
248                    out('cmd->{0}_null = !{0};'.format(p.name))
249                    out('if (!cmd->{0}_null) {{'.format(p.name))
250                    with indent():
251                        out(('memcpy(variable_data, {0}, {0}_size);').format(p.name))
252                        if i < len(func.variable_params):
253                            out('variable_data += {0}_size;'.format(p.name))
254                    out('}')
255                else:
256                    out(('memcpy(variable_data, {0}, {0}_size);').format(p.name))
257                    if i < len(func.variable_params):
258                        out('variable_data += {0}_size;'.format(p.name))
259                i += 1
260
261    def print_async_body(self, func):
262        out('/* {0}: marshalled asynchronously */'.format(func.name))
263        func.print_struct()
264        self.print_unmarshal_func(func)
265
266        out('{0}{1} GLAPIENTRY'.format('static ' if func.marshal_is_static() else '', func.return_type))
267        out('_mesa_marshal_{0}({1})'.format(
268                func.name, func.get_parameter_string()))
269        out('{')
270        with indent():
271            out('GET_CURRENT_CONTEXT(ctx);')
272            if func.marshal_call_before:
273                out(func.marshal_call_before);
274
275            if func.packed_fixed_params:
276                if func.packed_param_size > 0:
277                    out('if (((uintptr_t){0} & 0x{1}) == (uintptr_t){0}) {{'
278                        .format(func.packed_param_name,
279                                'ff' * func.packed_param_size))
280                else:
281                    out('if (!{0}) {{'.format(func.packed_param_name))
282
283                with indent():
284                    self.print_marshal_async_code(func, is_packed=True)
285                out('} else {')
286                with indent():
287                    self.print_marshal_async_code(func)
288                out('}')
289            else:
290                self.print_marshal_async_code(func)
291
292            if func.marshal_call_after:
293                out(func.marshal_call_after)
294
295            # Uncomment this if you want to call _mesa_glthread_finish for debugging
296            #out('_mesa_glthread_finish(ctx);')
297
298            if func.return_type == 'GLboolean':
299                out('return GL_TRUE;')  # for glUnmapBuffer
300        out('}')
301        out('')
302        out('')
303
304    def print_init_marshal_table(self, functions):
305        out('void')
306        out('_mesa_glthread_init_dispatch%u(struct gl_context *ctx, '
307                                           'struct _glapi_table *table)' % file_index)
308        out('{')
309        with indent():
310            # Collect SET_* calls by the condition under which they should
311            # be called.
312            settings_by_condition = collections.defaultdict(lambda: [])
313
314            for func in functions:
315                condition = apiexec.get_api_condition(func)
316                if not condition:
317                    continue
318
319                if func.marshal_no_error:
320                    no_error_condition = '_mesa_is_no_error_enabled(ctx) && ({0})'.format(condition)
321                    error_condition = '!_mesa_is_no_error_enabled(ctx) && ({0})'.format(condition)
322                    settings_by_condition[no_error_condition].append(
323                        'SET_{0}(table, _mesa_marshal_{0}_no_error);'.format(func.name))
324                    settings_by_condition[error_condition].append(
325                        'SET_{0}(table, _mesa_marshal_{0});'.format(func.name))
326                else:
327                    settings_by_condition[condition].append(
328                        'SET_{0}(table, _mesa_marshal_{0});'.format(func.name))
329
330            # Print out an if statement for each unique condition, with
331            # the SET_* calls nested inside it.
332            for condition in sorted(settings_by_condition.keys()):
333                out('if ({0}) {{'.format(condition))
334                with indent():
335                    for setting in sorted(settings_by_condition[condition]):
336                        for line in setting.split('\n'):
337                            out(line)
338                out('}')
339        out('}')
340
341    def printBody(self, api):
342        # Don't generate marshal/unmarshal functions for skipped and custom functions
343        functions = [func for func in api.functionIterateAll()
344                     if func.marshal_flavor() not in ('skip', 'custom')]
345        # Divide the functions between files
346        func_per_file = len(functions) // file_count + 1
347        functions = functions[file_index*func_per_file:(file_index+1)*func_per_file]
348
349        for func in functions:
350            flavor = func.marshal_flavor()
351            if flavor == 'async':
352                self.print_async_body(func)
353            elif flavor == 'sync':
354                self.print_sync_body(func)
355            else:
356                assert False
357
358        # The first file will also set custom functions
359        if file_index == 0:
360            functions += [func for func in api.functionIterateAll()
361                          if func.marshal_flavor() == 'custom']
362
363        self.print_init_marshal_table(functions)
364
365
366def show_usage():
367    print('Usage: %s [file_name] [file_index] [total file count]' % sys.argv[0])
368    sys.exit(1)
369
370
371if __name__ == '__main__':
372    try:
373        file_name = sys.argv[1]
374        file_index = int(sys.argv[2])
375        file_count = int(sys.argv[3])
376        pointer_size = int(sys.argv[4])
377    except Exception:
378        show_usage()
379
380    printer = PrintCode()
381
382    assert pointer_size != 0
383    api = gl_XML.parse_GL_API(file_name, marshal_XML.marshal_item_factory(), pointer_size)
384    printer.Print(api)
385