| # Copyright (c) 2018 The Android Open Source Project |
| # Copyright (c) 2018 Google Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| from typing import Dict, Optional, List, Set, Union |
| from xml.etree.ElementTree import Element |
| |
| from generator import noneStr |
| |
| from copy import copy |
| from string import whitespace |
| |
| # Holds information about core Vulkan objects |
| # and the API calls that are used to create/destroy each one. |
| class HandleInfo(object): |
| def __init__(self, name, createApis, destroyApis): |
| self.name = name |
| self.createApis = createApis |
| self.destroyApis = destroyApis |
| |
| def isCreateApi(self, apiName): |
| return apiName == self.createApis or (apiName in self.createApis) |
| |
| def isDestroyApi(self, apiName): |
| if self.destroyApis is None: |
| return False |
| return apiName == self.destroyApis or (apiName in self.destroyApis) |
| |
| DISPATCHABLE_HANDLE_TYPES = [ |
| "VkInstance", |
| "VkPhysicalDevice", |
| "VkDevice", |
| "VkQueue", |
| "VkCommandBuffer", |
| ] |
| |
| NON_DISPATCHABLE_HANDLE_TYPES = [ |
| "VkDeviceMemory", |
| "VkBuffer", |
| "VkBufferView", |
| "VkImage", |
| "VkImageView", |
| "VkShaderModule", |
| "VkDescriptorPool", |
| "VkDescriptorSetLayout", |
| "VkDescriptorSet", |
| "VkSampler", |
| "VkPipeline", |
| "VkPipelineLayout", |
| "VkRenderPass", |
| "VkFramebuffer", |
| "VkPipelineCache", |
| "VkCommandPool", |
| "VkFence", |
| "VkSemaphore", |
| "VkEvent", |
| "VkQueryPool", |
| "VkSamplerYcbcrConversion", |
| "VkSamplerYcbcrConversionKHR", |
| "VkDescriptorUpdateTemplate", |
| "VkSurfaceKHR", |
| "VkSwapchainKHR", |
| "VkDisplayKHR", |
| "VkDisplayModeKHR", |
| "VkObjectTableNVX", |
| "VkIndirectCommandsLayoutNVX", |
| "VkValidationCacheEXT", |
| "VkDebugReportCallbackEXT", |
| "VkDebugUtilsMessengerEXT", |
| "VkAccelerationStructureNV", |
| "VkIndirectCommandsLayoutNV", |
| "VkAccelerationStructureKHR", |
| ] |
| |
| CUSTOM_HANDLE_CREATE_TYPES = [ |
| "VkPhysicalDevice", |
| "VkQueue", |
| "VkPipeline", |
| "VkDeviceMemory", |
| "VkDescriptorSet", |
| "VkCommandBuffer", |
| "VkRenderPass", |
| ] |
| |
| HANDLE_TYPES = list(sorted(list(set(DISPATCHABLE_HANDLE_TYPES + |
| NON_DISPATCHABLE_HANDLE_TYPES + CUSTOM_HANDLE_CREATE_TYPES)))) |
| |
| HANDLE_INFO = {} |
| |
| for h in HANDLE_TYPES: |
| if h in CUSTOM_HANDLE_CREATE_TYPES: |
| if h == "VkPhysicalDevice": |
| HANDLE_INFO[h] = \ |
| HandleInfo( |
| "VkPhysicalDevice", |
| "vkEnumeratePhysicalDevices", None) |
| if h == "VkQueue": |
| HANDLE_INFO[h] = \ |
| HandleInfo( |
| "VkQueue", |
| "vkGetDeviceQueue", None) |
| if h == "VkPipeline": |
| HANDLE_INFO[h] = \ |
| HandleInfo( |
| "VkPipeline", |
| ["vkCreateGraphicsPipelines", "vkCreateComputePipelines"], |
| "vkDestroyPipeline") |
| if h == "VkDeviceMemory": |
| HANDLE_INFO[h] = \ |
| HandleInfo("VkDeviceMemory", |
| "vkAllocateMemory", ["vkFreeMemory", "vkFreeMemorySyncGOOGLE"]) |
| if h == "VkDescriptorSet": |
| HANDLE_INFO[h] = \ |
| HandleInfo("VkDescriptorSet", "vkAllocateDescriptorSets", |
| "vkFreeDescriptorSets") |
| if h == "VkCommandBuffer": |
| HANDLE_INFO[h] = \ |
| HandleInfo("VkCommandBuffer", "vkAllocateCommandBuffers", |
| "vkFreeCommandBuffers") |
| if h == "VkRenderPass": |
| HANDLE_INFO[h] = \ |
| HandleInfo( |
| "VkRenderPass", |
| ["vkCreateRenderPass", "vkCreateRenderPass2", "vkCreateRenderPass2KHR"], |
| "vkDestroyRenderPass") |
| else: |
| HANDLE_INFO[h] = \ |
| HandleInfo(h, "vkCreate" + h[2:], "vkDestroy" + h[2:]) |
| |
| EXCLUDED_APIS = [ |
| "vkEnumeratePhysicalDeviceGroups", |
| ] |
| |
| EXPLICITLY_ABI_PORTABLE_TYPES = [ |
| "VkResult", |
| "VkBool32", |
| "VkSampleMask", |
| "VkFlags", |
| "VkDeviceSize", |
| ] |
| |
| EXPLICITLY_ABI_NON_PORTABLE_TYPES = [ |
| "size_t" |
| ] |
| |
| NON_ABI_PORTABLE_TYPE_CATEGORIES = [ |
| "handle", |
| "funcpointer", |
| ] |
| |
| DEVICE_MEMORY_INFO_KEYS = [ |
| "devicememoryhandle", |
| "devicememoryoffset", |
| "devicememorysize", |
| "devicememorytypeindex", |
| "devicememorytypebits", |
| ] |
| |
| TRIVIAL_TRANSFORMED_TYPES = [ |
| "VkPhysicalDeviceExternalImageFormatInfo", |
| "VkPhysicalDeviceExternalBufferInfo", |
| "VkExternalMemoryImageCreateInfo", |
| "VkExternalMemoryBufferCreateInfo", |
| "VkExportMemoryAllocateInfo", |
| "VkExternalImageFormatProperties", |
| "VkExternalBufferProperties", |
| ] |
| |
| NON_TRIVIAL_TRANSFORMED_TYPES = [ |
| "VkExternalMemoryProperties", |
| "VkImageCreateInfo", |
| ] |
| |
| TRANSFORMED_TYPES = TRIVIAL_TRANSFORMED_TYPES + NON_TRIVIAL_TRANSFORMED_TYPES |
| |
| # Holds information about a Vulkan type instance (i.e., not a type definition). |
| # Type instances are used as struct field definitions or function parameters, |
| # to be later fed to code generation. |
| # VulkanType instances can be constructed in two ways: |
| # 1. From an XML tag with <type> / <param> tags in vk.xml, |
| # using makeVulkanTypeFromXMLTag |
| # 2. User-defined instances with makeVulkanTypeSimple. |
| class VulkanType(object): |
| |
| def __init__(self): |
| self.parent: Optional[VulkanType] = None |
| self.typeName: str = "" |
| |
| self.isTransformed = False |
| |
| self.paramName: Optional[str] = None |
| |
| self.lenExpr: Optional[str] = None # Value of the `len` attribute in the spec |
| self.isOptional: bool = False |
| self.optionalStr: Optional[str] = None # Value of the `optional` attribute in the spec |
| |
| self.isConst = False |
| |
| # "" means it's not a static array, otherwise this is the total size of |
| # all elements. e.g. staticArrExpr of "x[3][2][8]" will be "((3)*(2)*(8))". |
| self.staticArrExpr = "" |
| # "" means it's not a static array, otherwise it's the raw expression |
| # of static array size, which can be one-dimensional or multi-dimensional. |
| self.rawStaticArrExpr = "" |
| |
| self.pointerIndirectionLevels = 0 # 0 means not pointer |
| self.isPointerToConstPointer = False |
| |
| self.primitiveEncodingSize = None |
| |
| self.deviceMemoryInfoParameterIndices = None |
| |
| # Annotations |
| # Environment annotation for binding current |
| # variables to sub-structures |
| self.binds = {} |
| |
| # Device memory annotations |
| |
| # self.deviceMemoryAttrib/Val stores |
| # device memory info attributes from the XML. |
| # devicememoryhandle |
| # devicememoryoffset |
| # devicememorysize |
| # devicememorytypeindex |
| # devicememorytypebits |
| self.deviceMemoryAttrib = None |
| self.deviceMemoryVal = None |
| |
| # Filter annotations |
| self.filterVar = None |
| self.filterVals = None |
| self.filterFunc = None |
| self.filterOtherwise = None |
| |
| # Stream feature |
| self.streamFeature = None |
| |
| # All other annotations |
| self.attribs = {} |
| |
| self.nonDispatchableHandleCreate = False |
| self.nonDispatchableHandleDestroy = False |
| self.dispatchHandle = False |
| self.dispatchableHandleCreate = False |
| self.dispatchableHandleDestroy = False |
| |
| |
| def __str__(self,): |
| return ("(vulkantype %s %s paramName %s len %s optional? %s " |
| "staticArrExpr %s)") % ( |
| self.typeName + ("*" * self.pointerIndirectionLevels) + |
| ("ptr2constptr" if self.isPointerToConstPointer else ""), "const" |
| if self.isConst else "nonconst", self.paramName, self.lenExpr, |
| self.isOptional, self.staticArrExpr) |
| |
| def isString(self): |
| return self.pointerIndirectionLevels == 1 and (self.typeName == "char") |
| |
| def isArrayOfStrings(self): |
| return self.isPointerToConstPointer and (self.typeName == "char") |
| |
| def primEncodingSize(self): |
| return self.primitiveEncodingSize |
| |
| # Utility functions to make codegen life easier. |
| # This method derives the correct "count" expression if possible. |
| # Otherwise, returns None or "null-terminated" if a string. |
| def getLengthExpression(self): |
| if self.staticArrExpr != "": |
| return self.staticArrExpr |
| if self.lenExpr: |
| return self.lenExpr |
| return None |
| |
| # Can we just pass this to functions expecting T* |
| def accessibleAsPointer(self): |
| if self.staticArrExpr != "": |
| return True |
| if self.pointerIndirectionLevels > 0: |
| return True |
| return False |
| |
| # Rough attempt to infer where a type could be an output. |
| # Good for inferring which things need to be marshaled in |
| # versus marshaled out for Vulkan API calls |
| def possiblyOutput(self,): |
| return self.pointerIndirectionLevels > 0 and (not self.isConst) |
| |
| def isVoidWithNoSize(self,): |
| return self.typeName == "void" and self.pointerIndirectionLevels == 0 |
| |
| def getCopy(self,): |
| return copy(self) |
| |
| def getTransformed(self, isConstChoice=None, ptrIndirectionChoice=None): |
| res = self.getCopy() |
| |
| if isConstChoice is not None: |
| res.isConst = isConstChoice |
| if ptrIndirectionChoice is not None: |
| res.pointerIndirectionLevels = ptrIndirectionChoice |
| |
| return res |
| |
| def getWithCustomName(self): |
| return self.getTransformed( |
| ptrIndirectionChoice=self.pointerIndirectionLevels + 1) |
| |
| def getForAddressAccess(self): |
| return self.getTransformed( |
| ptrIndirectionChoice=self.pointerIndirectionLevels + 1) |
| |
| def getForValueAccess(self): |
| if self.typeName == "void" and self.pointerIndirectionLevels == 1: |
| asUint8Type = self.getCopy() |
| asUint8Type.typeName = "uint8_t" |
| return asUint8Type.getForValueAccess() |
| return self.getTransformed( |
| ptrIndirectionChoice=self.pointerIndirectionLevels - 1) |
| |
| def getForNonConstAccess(self): |
| return self.getTransformed(isConstChoice=False) |
| |
| def withModifiedName(self, newName): |
| res = self.getCopy() |
| res.paramName = newName |
| return res |
| |
| def isNextPointer(self): |
| return self.paramName == "pNext" |
| |
| def isSigned(self): |
| return self.typeName in ["int", "int8_t", "int16_t", "int32_t", "int64_t"] |
| |
| def isEnum(self, typeInfo): |
| return typeInfo.categoryOf(self.typeName) == "enum" |
| |
| def isBitmask(self, typeInfo): |
| return typeInfo.categoryOf(self.typeName) == "enum" |
| |
| # Only deals with 'core' handle types here. |
| def isDispatchableHandleType(self): |
| return self.typeName in DISPATCHABLE_HANDLE_TYPES |
| |
| def isNonDispatchableHandleType(self): |
| return self.typeName in NON_DISPATCHABLE_HANDLE_TYPES |
| |
| def isHandleType(self): |
| return self.isDispatchableHandleType() or \ |
| self.isNonDispatchableHandleType() |
| |
| def isCreatedBy(self, api): |
| if self.typeName in HANDLE_INFO.keys(): |
| nonKhrRes = HANDLE_INFO[self.typeName].isCreateApi(api.name) |
| if nonKhrRes: |
| return True |
| if len(api.name) > 3 and "KHR" == api.name[-3:]: |
| return HANDLE_INFO[self.typeName].isCreateApi(api.name[:-3]) |
| |
| if self.typeName == "VkImage" and api.name == "vkCreateImageWithRequirementsGOOGLE": |
| return True |
| |
| if self.typeName == "VkBuffer" and api.name == "vkCreateBufferWithRequirementsGOOGLE": |
| return True |
| |
| return False |
| |
| def isDestroyedBy(self, api): |
| if self.typeName in HANDLE_INFO.keys(): |
| nonKhrRes = HANDLE_INFO[self.typeName].isDestroyApi(api.name) |
| if nonKhrRes: |
| return True |
| if len(api.name) > 3 and "KHR" == api.name[-3:]: |
| return HANDLE_INFO[self.typeName].isDestroyApi(api.name[:-3]) |
| |
| return False |
| |
| def isSimpleValueType(self, typeInfo): |
| if typeInfo.isCompoundType(self.typeName): |
| return False |
| if self.isString() or self.isArrayOfStrings(): |
| return False |
| if self.staticArrExpr or self.pointerIndirectionLevels > 0: |
| return False |
| return True |
| |
| def getStructEnumExpr(self,): |
| return None |
| |
| def getPrintFormatSpecifier(self): |
| kKnownTypePrintFormatSpecifiers = { |
| 'float': '%f', |
| 'int': '%d', |
| 'int32_t': '%d', |
| 'size_t': '%ld', |
| 'uint16_t': '%d', |
| 'uint32_t': '%d', |
| 'uint64_t': '%ld', |
| 'VkBool32': '%d', |
| 'VkDeviceSize': '%ld', |
| 'VkFormat': '%d', |
| 'VkImageLayout': '%d', |
| } |
| |
| if self.pointerIndirectionLevels > 0 or self.isHandleType(): |
| return '%p' |
| |
| if self.typeName in kKnownTypePrintFormatSpecifiers: |
| return kKnownTypePrintFormatSpecifiers[self.typeName] |
| |
| if self.typeName.endswith('Flags'): |
| # Based on `typedef uint32_t VkFlags;` |
| return '%d' |
| |
| return None |
| def isOptionalPointer(self) -> bool: |
| return self.isOptional and \ |
| self.pointerIndirectionLevels > 0 and \ |
| (not self.isNextPointer()) |
| |
| |
| # Is an S-expression w/ the following spec: |
| # From https://gist.github.com/pib/240957 |
| class Atom(object): |
| def __init__(self, name): |
| self.name = name |
| def __repr__(self,): |
| return self.name |
| |
| def parse_sexp(sexp): |
| atom_end = set('()"\'') | set(whitespace) |
| stack, i, length = [[]], 0, len(sexp) |
| while i < length: |
| c = sexp[i] |
| |
| reading = type(stack[-1]) |
| if reading == list: |
| if c == '(': stack.append([]) |
| elif c == ')': |
| stack[-2].append(stack.pop()) |
| if stack[-1][0] == ('quote',): stack[-2].append(stack.pop()) |
| elif c == '"': stack.append('') |
| elif c == "'": stack.append([('quote',)]) |
| elif c in whitespace: pass |
| else: stack.append(Atom(c)) |
| elif reading == str: |
| if c == '"': |
| stack[-2].append(stack.pop()) |
| if stack[-1][0] == ('quote',): stack[-2].append(stack.pop()) |
| elif c == '\\': |
| i += 1 |
| stack[-1] += sexp[i] |
| else: stack[-1] += c |
| elif reading == Atom: |
| if c in atom_end: |
| atom = stack.pop() |
| if atom.name[0].isdigit(): stack[-1].append(eval(atom.name)) |
| else: stack[-1].append(atom) |
| if stack[-1][0] == ('quote',): stack[-2].append(stack.pop()) |
| continue |
| else: stack[-1] = Atom(stack[-1].name + c) |
| i += 1 |
| |
| return stack.pop() |
| |
| class FuncExprVal(object): |
| def __init__(self, val): |
| self.val = val |
| def __repr__(self,): |
| return self.val.__repr__() |
| |
| class FuncExpr(object): |
| def __init__(self, name, args): |
| self.name = name |
| self.args = args |
| def __repr__(self,): |
| if len(self.args) == 0: |
| return "(%s)" % (self.name.__repr__()) |
| else: |
| return "(%s %s)" % (self.name.__repr__(), " ".join(map(lambda x: x.__repr__(), self.args))) |
| |
| class FuncLambda(object): |
| def __init__(self, vs, body): |
| self.vs = vs |
| self.body = body |
| def __repr__(self,): |
| return "(L (%s) %s)" % (" ".join(map(lambda x: x.__repr__(), self.vs)), self.body.__repr__()) |
| |
| class FuncLambdaParam(object): |
| def __init__(self, name, typ): |
| self.name = name |
| self.typ = typ |
| def __repr__(self,): |
| return "%s : %s" % (self.name, self.typ) |
| |
| def parse_func_expr(parsed_sexp): |
| if len(parsed_sexp) != 1: |
| print("Error: parsed # expressions != 1: %d" % (len(parsed_sexp))) |
| raise |
| |
| e = parsed_sexp[0] |
| |
| def parse_lambda_param(e): |
| return FuncLambdaParam(e[0].name, e[1].name) |
| |
| def parse_one(exp): |
| if list == type(exp): |
| if "lambda" == exp[0].__repr__(): |
| return FuncLambda(list(map(parse_lambda_param, exp[1])), parse_one(exp[2])) |
| else: |
| return FuncExpr(exp[0], list(map(parse_one, exp[1:]))) |
| else: |
| return FuncExprVal(exp) |
| |
| return parse_one(e) |
| |
| def parseFilterFuncExpr(expr): |
| res = parse_func_expr(parse_sexp(expr)) |
| print("parseFilterFuncExpr: parsed %s" % res) |
| return res |
| |
| def parseLetBodyExpr(expr): |
| res = parse_func_expr(parse_sexp(expr)) |
| print("parseLetBodyExpr: parsed %s" % res) |
| return res |
| |
| |
| def makeVulkanTypeFromXMLTag(typeInfo, tag: Element) -> VulkanType: |
| res = VulkanType() |
| |
| # Process the length expression |
| |
| if tag.attrib.get("len") is not None: |
| lengths = tag.attrib.get("len").split(",") |
| res.lenExpr = lengths[0] |
| |
| # Calculate static array expression |
| |
| nametag = tag.find("name") |
| enumtag = tag.find("enum") |
| |
| if enumtag is not None: |
| res.staticArrExpr = enumtag.text |
| elif nametag is not None: |
| res.rawStaticArrExpr = noneStr(nametag.tail) |
| |
| dimensions = res.rawStaticArrExpr.count('[') |
| if dimensions == 1: |
| res.staticArrExpr = res.rawStaticArrExpr[1:-1] |
| elif dimensions > 1: |
| arraySizes = res.rawStaticArrExpr[1:-1].split('][') |
| res.staticArrExpr = '(' + \ |
| '*'.join(f'({size})' for size in arraySizes) + ')' |
| |
| # Determine const |
| |
| beforeTypePart = noneStr(tag.text) |
| |
| if "const" in beforeTypePart: |
| res.isConst = True |
| |
| # Calculate type and pointer info |
| for elem in tag: |
| if elem.tag == "name": |
| res.paramName = elem.text |
| if elem.tag == "type": |
| duringTypePart = noneStr(elem.text) |
| afterTypePart = noneStr(elem.tail) |
| # Now we know enough to fill some stuff in |
| res.typeName = duringTypePart |
| |
| if res.typeName in TRANSFORMED_TYPES: |
| res.isTransformed = True |
| |
| # This only handles pointerIndirectionLevels == 2 |
| # along with optional constant pointer for the inner part. |
| for c in afterTypePart: |
| if c == "*": |
| res.pointerIndirectionLevels += 1 |
| if "const" in afterTypePart and res.pointerIndirectionLevels == 2: |
| res.isPointerToConstPointer = True |
| |
| # If void*, treat like it's not a pointer |
| # if duringTypePart == "void": |
| # res.pointerIndirectionLevels -= 1 |
| |
| # Calculate optionality (based on validitygenerator.py) |
| if tag.attrib.get("optional") is not None: |
| res.isOptional = True |
| res.optionalStr = tag.attrib.get("optional") |
| |
| # If no validity is being generated, it usually means that |
| # validity is complex and not absolute, so let's say yes. |
| if tag.attrib.get("noautovalidity") is not None: |
| res.isOptional = True |
| |
| # If this is a structure extension, it is optional. |
| if tag.attrib.get("structextends") is not None: |
| res.isOptional = True |
| |
| # If this is a pNext pointer, it is optional. |
| if res.paramName == "pNext": |
| res.isOptional = True |
| |
| res.primitiveEncodingSize = typeInfo.getPrimitiveEncodingSize(res.typeName) |
| |
| # Annotations: Environment binds |
| if tag.attrib.get("binds") is not None: |
| bindPairs = map(lambda x: x.strip(), tag.attrib.get("binds").split(",")) |
| bindPairsSplit = map(lambda p: p.split(":"), bindPairs) |
| res.binds = dict(map(lambda sp: (sp[0].strip(), sp[1].strip()), bindPairsSplit)) |
| |
| # Annotations: Device memory |
| for k in DEVICE_MEMORY_INFO_KEYS: |
| if tag.attrib.get(k) is not None: |
| res.deviceMemoryAttrib = k |
| res.deviceMemoryVal = tag.attrib.get(k) |
| break |
| |
| # Annotations: Filters |
| if tag.attrib.get("filterVar") is not None: |
| res.filterVar = tag.attrib.get("filterVar").strip() |
| |
| if tag.attrib.get("filterVals") is not None: |
| res.filterVals = \ |
| list(map(lambda v: v.strip(), |
| tag.attrib.get("filterVals").strip().split(","))) |
| print("Filtervals: %s" % res.filterVals) |
| |
| if tag.attrib.get("filterFunc") is not None: |
| res.filterFunc = parseFilterFuncExpr(tag.attrib.get("filterFunc")) |
| |
| if tag.attrib.get("filterOtherwise") is not None: |
| res.Otherwise = tag.attrib.get("filterOtherwise") |
| |
| # store all other attribs here |
| res.attribs = dict(tag.attrib) |
| |
| return res |
| |
| |
| def makeVulkanTypeSimple(isConst, |
| typeName, |
| ptrIndirectionLevels, |
| paramName=None): |
| res = VulkanType() |
| |
| res.typeName = typeName |
| res.isConst = isConst |
| res.pointerIndirectionLevels = ptrIndirectionLevels |
| res.isPointerToConstPointer = False |
| res.paramName = paramName |
| res.primitiveEncodingSize = None |
| |
| return res |
| |
| # A class for holding the parameter indices corresponding to various |
| # attributes about a VkDeviceMemory, such as the handle, size, offset, etc. |
| class DeviceMemoryInfoParameterIndices(object): |
| def __init__(self, handle, offset, size, typeIndex, typeBits): |
| self.handle = handle |
| self.offset = offset |
| self.size = size |
| self.typeIndex = typeIndex |
| self.typeBits = typeBits |
| |
| # initializes DeviceMemoryInfoParameterIndices for each |
| # abstract VkDeviceMemory encountered over |parameters| |
| def initDeviceMemoryInfoParameterIndices(parameters): |
| |
| use = False |
| deviceMemoryInfoById = {} |
| |
| for (i, p) in enumerate(parameters): |
| a = p.deviceMemoryAttrib |
| if not a: |
| continue |
| |
| if a in DEVICE_MEMORY_INFO_KEYS: |
| use = True |
| deviceMemoryInfoById[p.deviceMemoryVal] = DeviceMemoryInfoParameterIndices( |
| None, None, None, None, None) |
| |
| for (i, p) in enumerate(parameters): |
| a = p.deviceMemoryAttrib |
| if not a: |
| continue |
| |
| info = deviceMemoryInfoById[p.deviceMemoryVal] |
| |
| if a == "devicememoryhandle": |
| info.handle = i |
| if a == "devicememoryoffset": |
| info.offset = i |
| if a == "devicememorysize": |
| info.size = i |
| if a == "devicememorytypeindex": |
| info.typeIndex = i |
| if a == "devicememorytypebits": |
| info.typeBits = i |
| |
| if not use: |
| return None |
| |
| return deviceMemoryInfoById |
| |
| # Classes for describing aggregate types (unions, structs) and API calls. |
| class VulkanCompoundType(object): |
| |
| def __init__(self, name: str, members: List[VulkanType], isUnion=False, structEnumExpr=None, structExtendsExpr=None, feature=None, initialEnv={}, optional=None): |
| self.name: str = name |
| self.typeName: str = name |
| self.members: List[VulkanType] = members |
| self.environment = initialEnv |
| self.isUnion = isUnion |
| self.structEnumExpr = structEnumExpr |
| self.structExtendsExpr = structExtendsExpr |
| self.feature = feature |
| self.deviceMemoryInfoParameterIndices = initDeviceMemoryInfoParameterIndices(self.members) |
| self.isTransformed = name in TRANSFORMED_TYPES |
| self.copy = None |
| self.optionalStr = optional |
| |
| def initCopies(self): |
| self.copy = self |
| |
| for m in self.members: |
| m.parent = self.copy |
| |
| def getMember(self, memberName) -> Optional[VulkanType]: |
| for m in self.members: |
| if m.paramName == memberName: |
| return m |
| return None |
| |
| def getStructEnumExpr(self,): |
| return self.structEnumExpr |
| |
| class VulkanAPI(object): |
| |
| def __init__(self, name: str, retType: VulkanType, parameters: list[VulkanType], origName=None): |
| self.name: str = name |
| self.origName = name |
| self.retType: VulkanType = retType |
| self.parameters: List[VulkanType] = parameters |
| |
| self.deviceMemoryInfoParameterIndices = initDeviceMemoryInfoParameterIndices(self.parameters) |
| |
| self.copy = None |
| |
| self.isTransformed = name in TRANSFORMED_TYPES |
| |
| if origName: |
| self.origName = origName |
| |
| def initCopies(self): |
| self.copy = self |
| |
| for m in self.parameters: |
| m.parent = self.copy |
| |
| def getCopy(self,): |
| return copy(self) |
| |
| def getParameter(self, parameterName): |
| for p in self.parameters: |
| if p.paramName == parameterName: |
| return p |
| return None |
| |
| def withModifiedName(self, newName): |
| res = VulkanAPI(newName, self.retType, self.parameters) |
| return res |
| |
| def getRetVarExpr(self): |
| if self.retType.typeName == "void": |
| return None |
| return "%s_%s_return" % (self.name, self.retType.typeName) |
| |
| def getRetTypeExpr(self): |
| return self.retType.typeName |
| |
| def withCustomParameters(self, customParams): |
| res = self.getCopy() |
| res.parameters = customParams |
| return res |
| |
| def withCustomReturnType(self, retType): |
| res = self.getCopy() |
| res.retType = retType |
| return res |
| |
| # Whether or not special handling of virtual elements |
| # such as VkDeviceMemory is needed. |
| def vulkanTypeNeedsTransform(structOrApi): |
| return structOrApi.deviceMemoryInfoParameterIndices != None |
| |
| def vulkanTypeGetNeededTransformTypes(structOrApi): |
| res = [] |
| if structOrApi.deviceMemoryInfoParameterIndices != None: |
| res.append("devicememory") |
| return res |
| |
| def vulkanTypeforEachSubType(structOrApi, f): |
| toLoop = None |
| if type(structOrApi) == VulkanCompoundType: |
| toLoop = structOrApi.members |
| if type(structOrApi) == VulkanAPI: |
| toLoop = structOrApi.parameters |
| |
| for (i, x) in enumerate(toLoop): |
| f(i, x) |
| |
| # Parses everything about Vulkan types into a Python readable format. |
| class VulkanTypeInfo(object): |
| |
| def __init__(self, generator): |
| self.generator = generator |
| self.categories: Set[str] = set([]) |
| |
| # Tracks what Vulkan type is part of what category. |
| self.typeCategories: Dict[str, str] = {} |
| |
| # Tracks the primitive encoding size for each type, if applicable. |
| self.encodingSizes: Dict[str, Optional[int]] = {} |
| |
| self.structs: Dict[str, VulkanCompoundType] = {} |
| self.apis: Dict[str, VulkanAPI] = {} |
| |
| # Maps bitmask types to the enum type used for the flags |
| # E.g. "VkImageAspectFlags" -> "VkImageAspectFlagBits" |
| self.bitmasks: Dict[str, str] = {} |
| |
| # Maps all enum names to their values. |
| # For aliases, the value is the name of the canonical enum |
| self.enumValues: Dict[str, Union[int, str]] = {} |
| |
| self.feature = None |
| |
| def initType(self, name: str, category: str): |
| self.categories.add(category) |
| self.typeCategories[name] = category |
| self.encodingSizes[name] = self.setPrimitiveEncodingSize(name) |
| |
| def categoryOf(self, name): |
| return self.typeCategories[name] |
| |
| def getPrimitiveEncodingSize(self, name): |
| return self.encodingSizes[name] |
| |
| # Queries relating to categories of Vulkan types. |
| def isHandleType(self, name): |
| return self.typeCategories.get(name) == "handle" |
| |
| def isCompoundType(self, name: str): |
| return self.typeCategories.get(name) in ["struct", "union"] |
| |
| # Gets the best size in bytes |
| # for encoding/decoding a particular Vulkan type. |
| # If not applicable, returns None. |
| def setPrimitiveEncodingSize(self, name: str) -> Optional[int]: |
| baseEncodingSizes = { |
| "void": 8, |
| "char": 1, |
| "float": 4, |
| "uint8_t": 1, |
| "uint16_t": 2, |
| "uint32_t": 4, |
| "uint64_t": 8, |
| "int": 4, |
| "int8_t": 1, |
| "int16_t": 2, |
| "int32_t": 4, |
| "int64_t": 8, |
| "size_t": 8, |
| "ssize_t": 8, |
| "VkBool32": 4, |
| } |
| |
| if name in baseEncodingSizes: |
| return baseEncodingSizes[name] |
| |
| category = self.typeCategories[name] |
| |
| if category in [None, "api", "include", "define", "struct", "union"]: |
| return None |
| |
| # Handles are pointers so they must be 8 bytes. Basetype includes VkDeviceSize which is 8 bytes. |
| if category in ["handle", "basetype", "funcpointer"]: |
| return 8 |
| |
| if category in ["enum", "bitmask"]: |
| return 4 |
| |
| def isNonAbiPortableType(self, typeName): |
| if typeName in EXPLICITLY_ABI_PORTABLE_TYPES: |
| return False |
| |
| if typeName in EXPLICITLY_ABI_NON_PORTABLE_TYPES: |
| return True |
| |
| category = self.typeCategories[typeName] |
| return category in NON_ABI_PORTABLE_TYPE_CATEGORIES |
| |
| def onBeginFeature(self, featureName, featureType): |
| self.feature = featureName |
| |
| def onEndFeature(self): |
| self.feature = None |
| |
| def onGenType(self, typeinfo, name, alias): |
| category = typeinfo.elem.get("category") |
| self.initType(name, category) |
| |
| if category in ["struct", "union"]: |
| self.onGenStruct(typeinfo, name, alias) |
| |
| if category == "bitmask": |
| self.bitmasks[name] = typeinfo.elem.get("requires") |
| |
| def onGenStruct(self, typeinfo, typeName, alias): |
| if not alias: |
| members: List[VulkanType] = [] |
| |
| structExtendsExpr = typeinfo.elem.get("structextends") |
| |
| structEnumExpr = None |
| |
| initialEnv = {} |
| envStr = typeinfo.elem.get("exists") |
| if envStr != None: |
| comma_separated = envStr.split(",") |
| name_type_pairs = map(lambda cs: tuple(map(lambda t: t.strip(), cs.split(":"))), comma_separated) |
| for (name, typ) in name_type_pairs: |
| initialEnv[name] = { |
| "type" : typ, |
| "binding" : None, |
| "structmember" : False, |
| "body" : None, |
| } |
| |
| letenvStr = typeinfo.elem.get("let") |
| if letenvStr != None: |
| comma_separated = letenvStr.split(",") |
| name_body_pairs = map(lambda cs: tuple(map(lambda t: t.strip(), cs.split(":"))), comma_separated) |
| for (name, body) in name_body_pairs: |
| initialEnv[name] = { |
| "type" : "uint32_t", |
| "binding" : name, |
| "structmember" : False, |
| "body" : parseLetBodyExpr(body) |
| } |
| |
| for member in typeinfo.elem.findall(".//member"): |
| vulkanType = makeVulkanTypeFromXMLTag(self, member) |
| initialEnv[vulkanType.paramName] = { |
| "type": vulkanType.typeName, |
| "binding": vulkanType.paramName, |
| "structmember": True, |
| "body": None, |
| } |
| members.append(vulkanType) |
| if vulkanType.typeName == "VkStructureType" and \ |
| member.get("values"): |
| structEnumExpr = member.get("values") |
| |
| self.structs[typeName] = \ |
| VulkanCompoundType( |
| typeName, |
| members, |
| isUnion = self.categoryOf(typeName) == "union", |
| structEnumExpr = structEnumExpr, |
| structExtendsExpr = structExtendsExpr, |
| feature = self.feature, |
| initialEnv = initialEnv, |
| optional = typeinfo.elem.get("optional", None)) |
| self.structs[typeName].initCopies() |
| |
| def onGenGroup(self, groupinfo, groupName, _alias=None): |
| self.initType(groupName, "enum") |
| enums = groupinfo.elem.findall("enum") |
| for enum in enums: |
| intVal, strVal = self.generator.enumToValue(enum, True) |
| self.enumValues[enum.get('name')] = intVal if intVal is not None else strVal |
| |
| |
| def onGenEnum(self, enuminfo, name: str, alias): |
| self.initType(name, "enum") |
| value: str = enuminfo.elem.get("value") |
| if value and value.isdigit(): |
| self.enumValues[name] = int(value) |
| elif value and value[0] == '"' and value[-1] == '"': |
| self.enumValues[name] = value[1:-1] |
| elif alias is not None: |
| self.enumValues[name] = alias |
| else: |
| # There's about a dozen cases of using the bitwise NOT operator (e.g.: `(~0U)`, `(~0ULL)`) |
| # to concisely represent large values. Just ignore them for now. |
| # In the future, we can add a lookup table to convert these to int |
| return |
| |
| def onGenCmd(self, cmdinfo, name, _alias): |
| self.initType(name, "api") |
| |
| proto = cmdinfo.elem.find("proto") |
| params = cmdinfo.elem.findall("param") |
| |
| self.apis[name] = \ |
| VulkanAPI( |
| name, |
| makeVulkanTypeFromXMLTag(self, proto), |
| list(map(lambda p: makeVulkanTypeFromXMLTag(self, p), |
| params))) |
| self.apis[name].initCopies() |
| |
| def onEnd(self): |
| pass |
| |
| def hasNullOptionalStringFeature(forEachType): |
| return (hasattr(forEachType, "onCheckWithNullOptionalStringFeature")) and \ |
| (hasattr(forEachType, "endCheckWithNullOptionalStringFeature")) and \ |
| (hasattr(forEachType, "finalCheckWithNullOptionalStringFeature")) |
| |
| |
| # General function to iterate over a vulkan type and call code that processes |
| # each of its sub-components, if any. |
| def iterateVulkanType(typeInfo: VulkanTypeInfo, vulkanType: VulkanType, forEachType): |
| if not vulkanType.isArrayOfStrings(): |
| if vulkanType.isPointerToConstPointer: |
| return False |
| |
| forEachType.registerTypeInfo(typeInfo) |
| |
| needCheck = vulkanType.isOptionalPointer() |
| |
| if typeInfo.isCompoundType(vulkanType.typeName) and not vulkanType.isNextPointer(): |
| |
| if needCheck: |
| forEachType.onCheck(vulkanType) |
| |
| forEachType.onCompoundType(vulkanType) |
| |
| if needCheck: |
| forEachType.endCheck(vulkanType) |
| |
| else: |
| if vulkanType.isString(): |
| if needCheck and hasNullOptionalStringFeature(forEachType): |
| forEachType.onCheckWithNullOptionalStringFeature(vulkanType) |
| forEachType.onString(vulkanType) |
| forEachType.endCheckWithNullOptionalStringFeature(vulkanType) |
| forEachType.onString(vulkanType) |
| forEachType.finalCheckWithNullOptionalStringFeature(vulkanType) |
| elif needCheck: |
| forEachType.onCheck(vulkanType) |
| forEachType.onString(vulkanType) |
| forEachType.endCheck(vulkanType) |
| else: |
| forEachType.onString(vulkanType) |
| |
| elif vulkanType.isArrayOfStrings(): |
| forEachType.onStringArray(vulkanType) |
| |
| elif vulkanType.staticArrExpr: |
| forEachType.onStaticArr(vulkanType) |
| |
| elif vulkanType.isNextPointer(): |
| if needCheck: |
| forEachType.onCheck(vulkanType) |
| forEachType.onStructExtension(vulkanType) |
| if needCheck: |
| forEachType.endCheck(vulkanType) |
| |
| elif vulkanType.pointerIndirectionLevels > 0: |
| if needCheck: |
| forEachType.onCheck(vulkanType) |
| forEachType.onPointer(vulkanType) |
| if needCheck: |
| forEachType.endCheck(vulkanType) |
| |
| else: |
| forEachType.onValue(vulkanType) |
| |
| return True |
| |
| class VulkanTypeIterator(object): |
| def __init__(self,): |
| self.typeInfo = None |
| |
| def registerTypeInfo(self, typeInfo): |
| self.typeInfo = typeInfo |
| |
| def vulkanTypeGetStructFieldLengthInfo(structInfo, vulkanType): |
| def getSpecialCaseVulkanStructFieldLength(structInfo, vulkanType): |
| cases = [ |
| { |
| "structName": "VkShaderModuleCreateInfo", |
| "field": "pCode", |
| "lenExpr": "codeSize", |
| "postprocess": lambda expr: "(%s / 4)" % expr |
| }, |
| { |
| "structName": "VkPipelineMultisampleStateCreateInfo", |
| "field": "pSampleMask", |
| "lenExpr": "rasterizationSamples", |
| "postprocess": lambda expr: "(((%s) + 31) / 32)" % expr |
| }, |
| ] |
| |
| for c in cases: |
| if (structInfo.name, vulkanType.paramName) == (c["structName"], c["field"]): |
| return c |
| |
| return None |
| |
| specialCaseAccess = getSpecialCaseVulkanStructFieldLength(structInfo, vulkanType) |
| |
| if specialCaseAccess is not None: |
| return specialCaseAccess |
| |
| lenExpr = vulkanType.getLengthExpression() |
| |
| if lenExpr is None: |
| return None |
| |
| return { |
| "structName": structInfo.name, |
| "field": vulkanType.typeName, |
| "lenExpr": lenExpr, |
| "postprocess": lambda expr: expr} |
| |
| |
| class VulkanTypeProtobufInfo(object): |
| def __init__(self, typeInfo, structInfo, vulkanType): |
| self.needsMessage = typeInfo.isCompoundType(vulkanType.typeName) |
| self.isRepeatedString = vulkanType.isArrayOfStrings() |
| self.isString = vulkanType.isString() or ( |
| vulkanType.typeName == "char" and (vulkanType.staticArrExpr != "")) |
| |
| if structInfo is not None: |
| self.lengthInfo = vulkanTypeGetStructFieldLengthInfo( |
| structInfo, vulkanType) |
| else: |
| self.lengthInfo = vulkanType.getLengthExpression() |
| |
| self.protobufType = None |
| self.origTypeCategory = typeInfo.categoryOf(vulkanType.typeName) |
| |
| self.isExtensionStruct = \ |
| vulkanType.typeName == "void" and \ |
| vulkanType.pointerIndirectionLevels > 0 and \ |
| vulkanType.paramName == "pNext" |
| |
| if self.needsMessage: |
| return |
| |
| if typeInfo.categoryOf(vulkanType.typeName) in ["enum", "bitmask"]: |
| self.protobufType = "uint32" |
| |
| if typeInfo.categoryOf(vulkanType.typeName) in ["funcpointer", "handle", "define"]: |
| self.protobufType = "uint64" |
| |
| if typeInfo.categoryOf(vulkanType.typeName) in ["basetype"]: |
| baseTypeMapping = { |
| "VkFlags" : "uint32", |
| "VkBool32" : "uint32", |
| "VkDeviceSize" : "uint64", |
| "VkSampleMask" : "uint32", |
| } |
| self.protobufType = baseTypeMapping[vulkanType.typeName] |
| |
| if typeInfo.categoryOf(vulkanType.typeName) == None: |
| |
| otherTypeMapping = { |
| "void" : "uint64", |
| "char" : "uint8", |
| "size_t" : "uint64", |
| "float" : "float", |
| "uint8_t" : "uint32", |
| "uint16_t" : "uint32", |
| "int32_t" : "int32", |
| "uint32_t" : "uint32", |
| "uint64_t" : "uint64", |
| "VkDeviceSize" : "uint64", |
| "VkSampleMask" : "uint32", |
| } |
| |
| if vulkanType.typeName in otherTypeMapping: |
| self.protobufType = otherTypeMapping[vulkanType.typeName] |
| else: |
| self.protobufType = "uint64" |
| |
| |
| protobufCTypeMapping = { |
| "uint8" : "uint8_t", |
| "uint32" : "uint32_t", |
| "int32" : "int32_t", |
| "uint64" : "uint64_t", |
| "float" : "float", |
| "string" : "const char*", |
| } |
| |
| self.protobufCType = protobufCTypeMapping[self.protobufType] |
| |