xref: /aosp_15_r20/external/mesa3d/src/mapi/glapi/gen/marshal_XML.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
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