blob: de09e2186cc5a5af7e666a3e3f02cbd78ffa39d5 [file] [log] [blame]
# 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
import sys
import copy
import typeexpr
def pot_align(base, pot_alignment):
return (base + pot_alignment - 1) & ~(pot_alignment - 1);
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):
# We decrease the type size when it's safe, such as when the maximum value
# and all greater values are invalid.
def get_marshal_type(self, param):
type = param.type_string()
if ('Draw' in self.name and
('Arrays' in self.name or
'Elements' in self.name or
'TransformFeedback' in self.name)):
if (type, param.name) == ('GLenum', 'mode'):
return 'GLenum8'
if (type, param.name) == ('GLenum', 'type'):
return 'GLindextype'
if type == 'GLenum':
return 'GLenum16' # clamped to 0xffff (always invalid enum)
if self.is_vertex_pointer_call:
if (type, param.name) == ('GLsizei', 'stride'):
return 'GLclamped16i'
if (type, param.name) == ('GLint', 'size'):
return 'GLpacked16i'
# glVertexAttrib*Pointer(index)
# glVertexArrayVertexBuffer(bindingindex)
if ((type, param.name) == ('GLuint', 'index') or
(type, param.name) == ('GLuint', 'bindingindex')):
return 'GLenum8' # clamped to 0xff
return type
def get_type_size(self, param):
type = self.get_marshal_type(param)
if type.find('*') != -1:
return 8;
mapping = {
'GLboolean': 1,
'GLbyte': 1,
'GLubyte': 1,
'GLenum8': 1, # clamped by glthread
'GLindextype': 1,
'GLenum16': 2, # clamped by glthread
'GLshort': 2,
'GLushort': 2,
'GLhalfNV': 2,
'GLclamped16i': 2, # clamped by glthread
'GLpacked16i': 2, # clamped by glthread
'GLint': 4,
'GLuint': 4,
'GLbitfield': 4,
'GLsizei': 4,
'GLfloat': 4,
'GLclampf': 4,
'GLfixed': 4,
'GLclampx': 4,
'GLhandleARB': 4,
'int': 4,
'float': 4,
'GLintptr': self.context.pointer_size,
'GLsizeiptr': self.context.pointer_size,
'GLsync': self.context.pointer_size,
'GLDEBUGPROC': self.context.pointer_size,
'GLdouble': 8,
'GLclampd': 8,
'GLint64': 8,
'GLuint64': 8,
'GLuint64EXT': 8,
}
val = mapping.get(type, 9999)
if val == 9999:
print('Unhandled type in marshal_XML.get_type_size: "{0}"'.format(type), file=sys.stderr)
assert False
return val
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')
self.marshal_sync = element.get('marshal_sync')
self.marshal_call_before = element.get('marshal_call_before')
self.marshal_call_after = element.get('marshal_call_after')
self.marshal_struct = element.get('marshal_struct')
self.marshal_no_error = gl_XML.is_attr_true(element, 'marshal_no_error')
self.is_vertex_pointer_call = (self.name == 'InterleavedArrays' or
self.name.endswith('VertexBuffer') or
self.name.endswith('VertexBufferEXT') or
self.name.endswith('Pointer') or
self.name.endswith('PointerEXT') or
self.name.endswith('PointerOES') or
self.name.endswith('OffsetEXT'))
# marshal_sync means whether a function should be called to determine
# whether we should sync.
if self.marshal_sync:
# This is a case of a pointer with an unknown size. Move
# variable-sized pointer parameters to fixed parameters because
# they will be passed as-is if the marshal_sync function evaluates
# to true.
self.fixed_params = self.fixed_params + self.variable_params
self.variable_params = []
# Sort the parameters, so that the marshal structure fields are sorted
# from smallest to biggest.
self.fixed_params = sorted(self.fixed_params, key=lambda p: self.get_type_size(p))
# Compute the marshal structure size and the largest hole
self.struct_size = 2 # sizeof(struct marshal_cmd_base)
largest_hole = 0
for p in self.fixed_params:
type_size = self.get_type_size(p)
aligned_size = pot_align(self.struct_size, type_size)
largest_hole = max(aligned_size - self.struct_size, largest_hole)
self.struct_size = aligned_size
self.struct_size = self.struct_size + type_size
# Round down largest_hole to a power of two.
largest_hole = int(2 ** (largest_hole.bit_length() - 1))
# Align the structure to 8 bytes.
aligned_size = pot_align(self.struct_size, 8)
padding_hole = aligned_size - self.struct_size
self.struct_size = aligned_size
# Determine whether to generate a packed version of gl*Pointer calls.
# If there is a hole in the cmd structure, the pointer/offset parameter
# can be truncated and stored in the hole to save 8 bytes per call.
# The version of the structure is determined at runtime based on
# whether the truncation doesn't change the value. This is common with
# VBOs because the pointer/offset is usually small.
#
# If there is no hole, the packed version completely removes
# the pointer/offset parameter and is used when the value is NULL/0
# to remove 8 bytes per call. This is common with VBOs.
self.packed_param_name = None
if (self.is_vertex_pointer_call and
# 32-bit CPUs only benefit if we remove the whole 8-byte slot,
# which means there must be exactly 4-byte padding after the 4-byte
# pointer/offset parameter.
(self.context.pointer_size != 4 or padding_hole == 4)):
for pname in ['pointer', 'offset']:
if pname in [p.name for p in self.fixed_params]:
self.packed_param_name = pname
assert self.packed_param_name
assert not self.variable_params
assert not self.marshal_sync
# Prepare the parameters for the packed version by replacing the type
# of the packed variable or removing it completely.
self.packed_fixed_params = []
if self.packed_param_name:
for p in self.fixed_params:
if p.name == self.packed_param_name:
if largest_hole > 0:
# Select the truncated type.
type = ['GLubyte', 'GLushort', 'GLuint'][largest_hole.bit_length() - 1]
# Clone the parameter and change its type
new_param = copy.deepcopy(p)
new_param.type_expr = typeexpr.type_expression(type, self.context)
self.packed_fixed_params.append(new_param)
else:
self.packed_fixed_params.append(p)
self.packed_param_size = largest_hole
# Sort the parameters by size to move the truncated type into the hole.
self.packed_fixed_params = sorted(self.packed_fixed_params, key=lambda p: self.get_type_size(p))
def get_fixed_params(self, is_packed):
return self.packed_fixed_params if is_packed else self.fixed_params
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 is not None:
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 or p.marshal_count or p.marshal_large_count)):
return 'sync'
if p.count_parameter_list and not (p.marshal_count or p.marshal_large_count):
# Parameter size is determined by enums; haven't
# written logic to handle this yet. TODO: fix.
return 'sync'
return 'async'
def marshal_is_static(self):
return (self.marshal_flavor() != 'custom' and
self.name[0:8] != 'Internal' and
self.exec_flavor != 'beginend')
def print_struct(self, is_header=False, is_packed=False):
if (self.marshal_struct == 'public') == is_header:
print(self.get_marshal_struct_name(is_packed))
print('{')
print(' struct marshal_cmd_base cmd_base;')
if self.variable_params:
print(' uint16_t num_slots;')
for p in self.get_fixed_params(is_packed):
if p.count:
print(' {0} {1}[{2}];'.format(
p.get_base_type_string(), p.name, p.count))
else:
print(' {0} {1};'.format(self.get_marshal_type(p), p.name))
for p in self.variable_params:
if p.img_null_flag:
print(' bool {0}_null; /* If set, no data follows '
'for "{0}" */'.format(p.name))
for p in self.variable_params:
if p.count_scale != 1:
print((' /* Next {0} bytes are '
'{1} {2}[{3}][{4}] */').format(
p.size_string(marshal=1), p.get_base_type_string(),
p.name, p.counter, p.count_scale))
else:
print((' /* Next {0} bytes are '
'{1} {2}[{3}] */').format(
p.size_string(marshal=1), p.get_base_type_string(),
p.name, p.counter))
print('};')
elif self.marshal_flavor() in ('custom', 'async'):
print('{0};'.format(self.get_marshal_struct_name(is_packed)))
if not is_packed and self.packed_fixed_params:
self.print_struct(is_header, True)
def get_marshal_struct_name(self, is_packed=False):
return 'struct marshal_cmd_{0}{1}'.format(self.name, '_packed' if is_packed else '')
def print_unmarshal_prototype(self, is_packed=False, suffix=''):
print(('uint32_t _mesa_unmarshal_{0}{1}(struct gl_context *ctx, '
'const {2} *restrict cmd){3}')
.format(self.name, '_packed' if is_packed else '',
self.get_marshal_struct_name(is_packed), suffix))