import argparse
import functools
+import math
import os
import xml.etree.cElementTree as et
};
% for e in entrypoints:
+ % if e.alias:
+ <% continue %>
+ % endif
% if e.guard is not None:
#ifdef ${e.guard}
% endif
#include "radv_private.h"
-struct radv_entrypoint {
+struct string_map_entry {
uint32_t name;
uint32_t hash;
+ uint32_t num;
};
/* We use a big string constant to avoid lots of reloctions from the entry
*/
static const char strings[] =
-% for e in entrypoints:
- "${e.name}\\0"
+% for s in strmap.sorted_strings:
+ "${s.string}\\0"
% endfor
;
-static const struct radv_entrypoint entrypoints[] = {
-% for e in entrypoints:
- [${e.num}] = { ${offsets[e.num]}, ${'{:0=#8x}'.format(e.get_c_hash())} }, /* ${e.name} */
+static const struct string_map_entry string_map_entries[] = {
+% for s in strmap.sorted_strings:
+ { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */
+% endfor
+};
+
+/* Hash table stats:
+ * size ${len(strmap.sorted_strings)} entries
+ * collisions entries:
+% for i in xrange(10):
+ * ${i}${'+' if i == 9 else ' '} ${strmap.collisions[i]}
+% endfor
+ */
+
+#define none 0xffff
+static const uint16_t string_map[${strmap.hash_size}] = {
+% for e in strmap.mapping:
+ ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },
% endfor
};
% for layer in LAYERS:
% for e in entrypoints:
+ % if e.alias:
+ <% continue %>
+ % endif
% if e.guard is not None:
#ifdef ${e.guard}
% endif
return radv_layer.entrypoints[index];
}
-/* Hash table stats:
- * size ${hash_size} entries
- * collisions entries:
-% for i in xrange(10):
- * ${i}${'+' if i == 9 else ''} ${collisions[i]}
-% endfor
- */
-
-#define none ${'{:#x}'.format(none)}
-static const uint16_t map[] = {
-% for i in xrange(0, hash_size, 8):
- % for j in xrange(i, i + 8):
- ## This is 6 because the 0x is counted in the length
- % if mapping[j] & 0xffff == 0xffff:
- none,
- % else:
- ${'{:0=#6x}'.format(mapping[j] & 0xffff)},
- % endif
- % endfor
-% endfor
-};
-
/** Return true if the core version or extension in which the given entrypoint
* is defined is enabled.
*
return !device;
% elif e.core_version:
return instance && ${e.core_version.c_vk_version()} <= core_version;
- % elif e.extension:
- % if e.extension.type == 'instance':
- return instance && instance->${e.extension.name[3:]};
- % else:
- return instance && (!device || device->${e.extension.name[3:]});
- % endif
+ % elif e.extensions:
+ % for ext in e.extensions:
+ % if ext.type == 'instance':
+ if (instance && instance->${ext.name[3:]}) return true;
+ % else:
+ if (instance && (!device || device->${ext.name[3:]})) return true;
+ % endif
+ %endfor
+ return false;
% else:
return instance;
% endif
static int
radv_lookup_entrypoint(const char *name)
{
- static const uint32_t prime_factor = ${prime_factor};
- static const uint32_t prime_step = ${prime_step};
- const struct radv_entrypoint *e;
- uint32_t hash, h, i;
+ static const uint32_t prime_factor = ${strmap.prime_factor};
+ static const uint32_t prime_step = ${strmap.prime_step};
+ const struct string_map_entry *e;
+ uint32_t hash, h;
+ uint16_t i;
const char *p;
hash = 0;
for (p = name; *p; p++)
- hash = hash * prime_factor + *p;
+ hash = hash * prime_factor + *p;
h = hash;
- do {
- i = map[h & ${hash_mask}];
- if (i == none)
- return -1;
- e = &entrypoints[i];
- h += prime_step;
- } while (e->hash != hash);
-
- if (strcmp(name, strings + e->name) != 0)
- return -1;
-
- return i;
+ while (1) {
+ i = string_map[h & ${strmap.hash_mask}];
+ if (i == none)
+ return -1;
+ e = &string_map_entries[i];
+ if (e->hash == hash && strcmp(name, strings + e->name) == 0)
+ return e->num;
+ h += prime_step;
+ }
+
+ return -1;
}
void *
return radv_resolve_entrypoint(index);
}""", output_encoding='utf-8')
-NONE = 0xffff
-HASH_SIZE = 256
U32_MASK = 2**32 - 1
-HASH_MASK = HASH_SIZE - 1
PRIME_FACTOR = 5024183
PRIME_STEP = 19
-def cal_hash(name):
- """Calculate the same hash value that Mesa will calculate in C."""
- return functools.reduce(
- lambda h, c: (h * PRIME_FACTOR + ord(c)) & U32_MASK, name, 0)
+def round_to_pow2(x):
+ return 2**int(math.ceil(math.log(x, 2)))
+
+class StringIntMapEntry(object):
+ def __init__(self, string, num):
+ self.string = string
+ self.num = num
+
+ # Calculate the same hash value that we will calculate in C.
+ h = 0
+ for c in string:
+ h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
+ self.hash = h
+
+ self.offset = None
+
+class StringIntMap(object):
+ def __init__(self):
+ self.baked = False
+ self.strings = dict()
+
+ def add_string(self, string, num):
+ assert not self.baked
+ assert string not in self.strings
+ assert num >= 0 and num < 2**31
+ self.strings[string] = StringIntMapEntry(string, num)
+
+ def bake(self):
+ self.sorted_strings = \
+ sorted(self.strings.values(), key=lambda x: x.string)
+ offset = 0
+ for entry in self.sorted_strings:
+ entry.offset = offset
+ offset += len(entry.string) + 1
+
+ # Save off some values that we'll need in C
+ self.hash_size = round_to_pow2(len(self.strings) * 1.25)
+ self.hash_mask = self.hash_size - 1
+ self.prime_factor = PRIME_FACTOR
+ self.prime_step = PRIME_STEP
+
+ self.mapping = [-1] * self.hash_size
+ self.collisions = [0] * 10
+ for idx, s in enumerate(self.sorted_strings):
+ level = 0
+ h = s.hash
+ while self.mapping[h & self.hash_mask] >= 0:
+ h = h + PRIME_STEP
+ level = level + 1
+ self.collisions[min(level, 9)] += 1
+ self.mapping[h & self.hash_mask] = idx
EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
-class Entrypoint(object):
- def __init__(self, name, return_type, params, guard = None):
+class EntrypointBase(object):
+ def __init__(self, name):
self.name = name
- self.return_type = return_type
- self.params = params
- self.guard = guard
+ self.alias = None
+ self.guard = None
self.enabled = False
self.num = None
# Extensions which require this entrypoint
self.core_version = None
- self.extension = None
+ self.extensions = []
+
+class Entrypoint(EntrypointBase):
+ def __init__(self, name, return_type, params, guard = None):
+ super(Entrypoint, self).__init__(name)
+ self.return_type = return_type
+ self.params = params
+ self.guard = guard
self.device_command = len(params) > 0 and (params[0].type == 'VkDevice' or params[0].type == 'VkQueue' or params[0].type == 'VkCommandBuffer')
def prefixed_name(self, prefix):
def call_params(self):
return ', '.join(p.name for p in self.params)
- def get_c_hash(self):
- return cal_hash(self.name)
+class EntrypointAlias(EntrypointBase):
+ def __init__(self, name, entrypoint):
+ super(EntrypointAlias, self).__init__(name)
+ self.alias = entrypoint
+ self.device_command = entrypoint.device_command
+
+ def prefixed_name(self, prefix):
+ return self.alias.prefixed_name(prefix)
def get_entrypoints(doc, entrypoints_to_defines, start_index):
"""Extract the entry points from the registry."""
entrypoints = OrderedDict()
for command in doc.findall('./commands/command'):
- ret_type = command.find('./proto/type').text
- fullname = command.find('./proto/name').text
- params = [EntrypointParam(
- type = p.find('./type').text,
- name = p.find('./name').text,
- decl = ''.join(p.itertext())
- ) for p in command.findall('./param')]
- guard = entrypoints_to_defines.get(fullname)
- # They really need to be unique
- assert fullname not in entrypoints
- entrypoints[fullname] = Entrypoint(fullname, ret_type, params, guard)
-
- enabled_commands = set()
+ if 'alias' in command.attrib:
+ alias = command.attrib['name']
+ target = command.attrib['alias']
+ entrypoints[alias] = EntrypointAlias(alias, entrypoints[target])
+ else:
+ name = command.find('./proto/name').text
+ ret_type = command.find('./proto/type').text
+ params = [EntrypointParam(
+ type = p.find('./type').text,
+ name = p.find('./name').text,
+ decl = ''.join(p.itertext())
+ ) for p in command.findall('./param')]
+ guard = entrypoints_to_defines.get(name)
+ # They really need to be unique
+ assert name not in entrypoints
+ entrypoints[name] = Entrypoint(name, ret_type, params, guard)
+
for feature in doc.findall('./feature'):
assert feature.attrib['api'] == 'vulkan'
version = VkVersion(feature.attrib['number'])
e = entrypoints[command.attrib['name']]
e.enabled = True
assert e.core_version is None
- assert e.extension is None
- e.extension = ext
+ e.extensions.append(ext)
+
+ # if the base command is not supported by the driver yet, don't alias aliases
+ for e in entrypoints.values():
+ if e.alias and not e.alias.enabled:
+ e_clone = copy.deepcopy(e.alias)
+ e_clone.enabled = True
+ e_clone.name = e.name
+ entrypoints[e.name] = e_clone
return [e for e in entrypoints.itervalues() if e.enabled]
fullname = entrypoint.attrib['name']
entrypoints_to_defines[fullname] = define
+ for extension in doc.findall('./extensions/extension[@platform]'):
+ platform = extension.attrib['platform']
+ define = 'VK_USE_PLATFORM_' + platform.upper() + '_KHR'
+
+ for entrypoint in extension.findall('./require/command'):
+ fullname = entrypoint.attrib['name']
+ entrypoints_to_defines[fullname] = define
+
return entrypoints_to_defines
def gen_code(entrypoints):
"""Generate the C code."""
- i = 0
- offsets = []
- for e in entrypoints:
- offsets.append(i)
- i += len(e.name) + 1
-
- mapping = [NONE] * HASH_SIZE
- collisions = [0] * 10
+ strmap = StringIntMap()
for e in entrypoints:
- level = 0
- h = e.get_c_hash()
- while mapping[h & HASH_MASK] != NONE:
- h = h + PRIME_STEP
- level = level + 1
- if level > 9:
- collisions[9] += 1
- else:
- collisions[level] += 1
- mapping[h & HASH_MASK] = e.num
+ strmap.add_string(e.name, e.num)
+ strmap.bake()
return TEMPLATE_C.render(entrypoints=entrypoints,
LAYERS=LAYERS,
- offsets=offsets,
- collisions=collisions,
- mapping=mapping,
- hash_mask=HASH_MASK,
- prime_step=PRIME_STEP,
- prime_factor=PRIME_FACTOR,
- none=NONE,
- hash_size=HASH_SIZE,
+ strmap=strmap,
filename=os.path.basename(__file__))