Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79afff79 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Generate Vulkan framework from Vulkan registry"

parents 2093f571 751a7dcc
Loading
Loading
Loading
Loading
+344 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
#
# Copyright 2019 The Android Open Source Project
#
# 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.
#
# This script provides the functions required for generating the
# vulkan api framework directly from the vulkan registry (vk.xml)

import os
import generator_common as gencom

def isInstanceDispatchTableEntry(functionName):
  if functionName == 'vkEnumerateDeviceLayerProperties': # deprecated, unused internally - @dbd33bc
    return False
  if gencom.gencom.isFunctionExported(functionName) and gencom.isInstanceDispatched(functionName):
    return True
  return False

def isDeviceDispatchTableEntry(functionName):
  if gencom.gencom.isFunctionExported(functionName) and gencom.gencom.isDeviceDispatched(functionName):
    return True
  return False

def api_genh():

  header = """#ifndef LIBVULKAN_API_GEN_H
#define LIBVULKAN_API_GEN_H

#include <vulkan/vulkan.h>

#include <bitset>

#include "driver_gen.h"

namespace vulkan {
namespace api {

"""

  tail = """
bool InitDispatchTable(
    VkInstance instance,
    PFN_vkGetInstanceProcAddr get_proc,
    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
bool InitDispatchTable(
    VkDevice dev,
    PFN_vkGetDeviceProcAddr get_proc,
    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);

}  // namespace api
}  // namespace vulkan

#endif  // LIBVULKAN_API_GEN_H
"""
  genfile = os.path.join(os.path.dirname(__file__),'..','libvulkan','api_gen2.h')
  with open(genfile, 'w') as f:
    instanceDispatchTableEntries = []
    deviceDispatchTableEntries = []
    for commands in gencom.allCommandsList:
      if commands not in gencom.aliasDict:
        if gencom.isInstanceDispatchTableEntry(commands):
          instanceDispatchTableEntries.append('PFN_'+commands+' '+commands[2:]+';')
        elif gencom.isDeviceDispatchTableEntry(commands):
          deviceDispatchTableEntries.append('PFN_'+commands+' '+commands[2:]+';')

    f.write (gencom.copyright)
    f.write (gencom.warning)
    f.write (header)
    f.write ('struct InstanceDispatchTable {\n')
    gencom.clang_off(f,1)
    for functions in instanceDispatchTableEntries:
      f.write(gencom.clang_off_spaces + functions + '\n')
    gencom.clang_on(f,1)
    f.write ('};\n\n')

    f.write ('struct DeviceDispatchTable {\n')
    gencom.clang_off(f,1)
    for functions in deviceDispatchTableEntries:
      f.write(gencom.clang_off_spaces + functions + '\n')
    gencom.clang_on(f,1)
    f.write ('};\n')

    f.write (tail)
    f.close()

def defineInitProc(name, f):
  f.write ('#define UNLIKELY(expr) __builtin_expect((expr), 0)\n')
  f.write ('\n')
  f.write ("""#define INIT_PROC(required, obj, proc)                                 \\
    do {                                                               \\
        data.""" + name + """.proc =                                           \\
            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\
        if (UNLIKELY(required && !data.""" + name + """.proc)) {               \\
            ALOGE("missing " #obj " proc: vk" #proc);                  \\
            success = false;                                           \\
        }                                                              \\
    } while (0)\n\n""")

def defineInitProcExt(f):
  f.write ('// Exported extension functions may be invoked even when their extensions\n')
  f.write ('// are disabled.  Dispatch to stubs when that happens.\n')
  f.write ("""#define INIT_PROC_EXT(ext, required, obj, proc)  \\
    do {                                         \\
        if (extensions[driver::ProcHook::ext])   \\
            INIT_PROC(required, obj, proc);      \\
        else                                     \\
            data.dispatch.proc = disabled##proc; \\
    } while (0)\n\n""")

def defineExtensionStub(functionName, f):
  if functionName in gencom.extensionsDict and gencom.isFunctionExported(functionName):
    extname = gencom.extensionsDict[functionName]
    base_name = functionName[2:]
    pList = gencom.paramDict[functionName]
    firstParam = pList[0][0] + pList[0][1]
    tailParams = [x[0][:-1] for x in pList[1:]]
    tailP = ', '.join(tailParams)
    f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[functionName] + ' disabled' + base_name + '(' + firstParam + ', ' + tailP + ') {\n')
    f.write (gencom.clang_off_spaces)
    f.write ('driver::Logger(' + pList[0][1] + ').Err(' + pList[0][1] + ', \"' + extname + ' not enabled. Exported ' + functionName + ' not executed.\");\n')
    if gencom.returnTypeDict[functionName] != 'void':
      f.write(gencom.clang_off_spaces + 'return VK_SUCCESS;\n')
    f.write ('}\n\n')

def isIntercepted(functionName):
  if gencom.isFunctionSupported(functionName):
    if gencom.isGloballyDispatched(functionName):
      return True
    elif functionName == 'vkCreateDevice':
      return True
    elif functionName == 'vkEnumerateDeviceLayerProperties':
      return True
    elif functionName == 'vkEnumerateDeviceExtensionProperties':
      return True
    elif functionName == 'vkDestroyInstance':
      return True
    elif functionName == 'vkDestroyDevice':
      return True
  return False

def interceptInstanceProcAddr(functionName, f):
  indent = 1
  f.write (gencom.clang_off_spaces*indent + '// global functions\n' + gencom.clang_off_spaces*indent+ 'if (instance == VK_NULL_HANDLE) {\n')
  indent = indent + 1
  for cmds in gencom.allCommandsList:
    if gencom.isGloballyDispatched(cmds):
      f.write(gencom.clang_off_spaces*indent + 'if (strcmp(pName, \"' + cmds + '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + cmds[2:] + ');\n')

  f.write ('\n')
  f.write ("""        ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \\\"%s\\\") call", pName);
        return nullptr;
    }

    static const struct Hook {
        const char* name;
        PFN_vkVoidFunction proc;
    } hooks[] = {\n""")
  sortedCommandsList = sorted(gencom.allCommandsList)
  for cmds in sortedCommandsList:
    if gencom.isFunctionExported(cmds):
      if gencom.isGloballyDispatched(cmds):
        f.write (gencom.clang_off_spaces*2 + '{ \"' + cmds + '\", nullptr },\n')
      elif isIntercepted(cmds) or cmds == 'vkGetInstanceProcAddr' or gencom.isDeviceDispatched(cmds):
        f.write (gencom.clang_off_spaces*2 + '{ \"' + cmds + '\", reinterpret_cast<PFN_vkVoidFunction>(' + cmds[2:] + ') },\n')
  f.write (gencom.clang_off_spaces + """};
    // clang-format on
    constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
    auto hook = std::lower_bound(
        hooks, hooks + count, pName,
        [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
    if (hook < hooks + count && strcmp(hook->name, pName) == 0) {
        if (!hook->proc) {
            vulkan::driver::Logger(instance).Err(
                instance, "invalid vkGetInstanceProcAddr(%p, \\\"%s\\\") call",
                instance, pName);
        }
        return hook->proc;
    }
    // clang-format off\n\n""")

def interceptDeviceProcAddr(functionName, f):
  f.write (gencom.clang_off_spaces + """if (device == VK_NULL_HANDLE) {
        ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call");
        return nullptr;
    }\n\n""")
  f.write (gencom.clang_off_spaces + 'static const char* const known_non_device_names[] = {\n')
  sortedCommandsList = sorted(gencom.allCommandsList)
  for cmds in sortedCommandsList:
    if gencom.isFunctionSupported(cmds):
      if not gencom.isDeviceDispatched(cmds):
        f.write(gencom.clang_off_spaces*2 + '\"' + cmds + '\",\n')
  f.write(gencom.clang_off_spaces + '};\n')
  f.write(gencom.clang_off_spaces + """// clang-format on
    constexpr size_t count =
        sizeof(known_non_device_names) / sizeof(known_non_device_names[0]);
    if (!pName ||
        std::binary_search(
            known_non_device_names, known_non_device_names + count, pName,
            [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
        vulkan::driver::Logger(device).Err(
            device, "invalid vkGetDeviceProcAddr(%p, \\\"%s\\\") call", device,
            (pName) ? pName : "(null)");
        return nullptr;
    }
    // clang-format off\n\n""")
  for cmds in gencom.allCommandsList:
    if gencom.isDeviceDispatched(cmds):
      if isIntercepted(cmds) or cmds == 'vkGetDeviceProcAddr':
        f.write (gencom.clang_off_spaces + 'if (strcmp(pName, "' + cmds + '") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + cmds[2:] + ');\n')
  f.write ('\n')

def apiDispatch(functionName, f):
  assert not isIntercepted(functionName)

  f.write (gencom.clang_off_spaces)
  if gencom.returnTypeDict[functionName] != 'void':
    f.write ('return ')

  paramList = gencom.paramDict[functionName]
  p0 = paramList[0][1]
  f.write('GetData(' + p0 + ').dispatch.' + functionName[2:] + '(' + ', '.join(i[1] for i in paramList) + ');\n')


def api_gencpp():
  genfile = os.path.join(os.path.dirname(__file__),'..','libvulkan','api_gen2.cpp')
  header = """#include <log/log.h>
#include <string.h>

#include <algorithm>

// to catch mismatches between vulkan.h and this file
#undef VK_NO_PROTOTYPES
#include "api.h"

namespace vulkan {
namespace api {

"""
  with open(genfile, 'w') as f:
    f.write (gencom.copyright)
    f.write (gencom.warning)
    f.write ("""#include <log/log.h>
#include <string.h>

#include <algorithm>

// to catch mismatches between vulkan.h and this file
#undef VK_NO_PROTOTYPES
#include "api.h"

namespace vulkan {
namespace api {\n\n""")
    defineInitProc('dispatch',f)
    defineInitProcExt(f)
    f.write ('namespace {\n\n')
    gencom.clang_off(f,0)
    f.write ('\n')
    for cmds in gencom.allCommandsList:
      defineExtensionStub(cmds,f)
    gencom.clang_on(f,0)
    f.write ('\n}  // namespace\n\n')
    f.write ("""bool InitDispatchTable(
    VkInstance instance,
    PFN_vkGetInstanceProcAddr get_proc,
    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
    auto& data = GetData(instance);
    bool success = true;\n\n""")
    gencom.clang_off(f,1)
    for cmds in gencom.allCommandsList:
      if gencom.isInstanceDispatchTableEntry(cmds):
        gencom.initProc(cmds, f)
    gencom.clang_on(f,1)
    f.write ('\n')
    f.write ('    return success;\n}\n\n')
    f.write ("""bool InitDispatchTable(
    VkDevice dev,
    PFN_vkGetDeviceProcAddr get_proc,
    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
    auto& data = GetData(dev);
    bool success = true;\n\n""")

    gencom.clang_off(f,1)
    for cmds in gencom.allCommandsList:
      if gencom.isDeviceDispatchTableEntry(cmds):
        gencom.initProc(cmds, f)
    gencom.clang_on(f,1)
    f.write ('\n')
    f.write ('    return success;\n}\n\n')

    gencom.clang_off(f,0)

    f.write ('\nnamespace {\n\n')
    f.write('// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr\n')
    for cmds in gencom.allCommandsList:
      if gencom.isFunctionExported(cmds) and not isIntercepted(cmds):
        paramList = [''.join(i) for i in gencom.paramDict[cmds]]
        f.write ('VKAPI_ATTR '+gencom.returnTypeDict[cmds] + ' ' + cmds[2:] + '(' + ', '.join(paramList) + ');\n')

    f.write ('\n')

    for cmds in gencom.allCommandsList:
      if gencom.isFunctionExported(cmds) and not isIntercepted(cmds):
        paramList = [''.join(i) for i in gencom.paramDict[cmds]]
        f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[cmds] + ' ' + cmds[2:] + '(' + ', '.join(paramList) + ') {\n')
        if cmds == 'vkGetInstanceProcAddr':
          interceptInstanceProcAddr(cmds, f)
        elif cmds == 'vkGetDeviceProcAddr':
          interceptDeviceProcAddr(cmds, f)
        apiDispatch(cmds, f)
        f.write('}\n\n')
    f.write ("""\n}  // anonymous namespace

// clang-format on

}  // namespace api
}  // namespace vulkan

// clang-format off\n\n""")

    for cmds in gencom.allCommandsList:
      if gencom.isFunctionExported(cmds):
        paramList = [''.join(i) for i in gencom.paramDict[cmds]]
        f.write ('__attribute__((visibility("default")))\n')
        f.write ('VKAPI_ATTR ' + gencom.returnTypeDict[cmds] + ' ' + cmds + '(' + ', '.join(paramList) + ') {\n')
        f.write (gencom.clang_off_spaces)
        if gencom.returnTypeDict[cmds] != 'void':
          f.write ('return ')
        paramList = gencom.paramDict[cmds]
        f.write ('vulkan::api::' + cmds[2:] + '(' + ', '.join(i[1] for i in paramList) + ');\n')
        f.write ('}\n\n')

    gencom.clang_on(f, 0)
+26 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
#
# Copyright 2019 The Android Open Source Project
#
# 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.
#
# This script provides the main function for generating
# vulkan framework directly from the vulkan registry (vk.xml).

import generator_common as gencom
import api_generator as apigen

if __name__ == '__main__':
  gencom.parseVulkanRegistry()
  apigen.api_genh()
  apigen.api_gencpp()
+238 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
#
# Copyright 2019 The Android Open Source Project
#
# 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.
#
# This script provides the common functions for generating the
# vulkan framework directly from the vulkan registry (vk.xml).

copyright = """/*
 * Copyright 2016 The Android Open Source Project
 *
 * 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.
 */

"""

warning = '// WARNING: This file is generated. See ../README.md for instructions.\n\n'

blacklistedExtensions = [
    'VK_KHR_display',
    'VK_KHR_display_swapchain',
    'VK_KHR_mir_surface',
    'VK_KHR_xcb_surface',
    'VK_KHR_xlib_surface',
    'VK_KHR_wayland_surface',
    'VK_KHR_win32_surface',
    'VK_KHR_external_memory_win32',
    'VK_KHR_win32_keyed_mutex',
    'VK_KHR_external_semaphore_win32',
    'VK_KHR_external_fence_win32',
    'VK_EXT_acquire_xlib_display',
    'VK_EXT_direct_mode_display',
    'VK_EXT_display_surface_counter',
    'VK_EXT_display_control',
    'VK_FUCHSIA_imagepipe_surface',
    'VK_MVK_ios_surface',
    'VK_MVK_macos_surface',
    'VK_NN_vi_surface',
    'VK_NV_external_memory_win32',
    'VK_NV_win32_keyed_mutex',
    'VK_EXT_metal_surface', #not present in vulkan.api
    'VK_NVX_image_view_handle', #not present in vulkan.api
    'VK_NV_cooperative_matrix' #not present in vulkan.api
]

exportedExtensions = [
    'VK_KHR_surface',
    'VK_KHR_swapchain',
    'VK_KHR_android_surface',
    'VK_ANDROID_external_memory_android_hardware_buffer'
]

def isFunctionSupported(functionName):
  if functionName not in extensionsDict:
    return True
  else:
    if extensionsDict[functionName] not in blacklistedExtensions:
      return True
  return False

def isInstanceDispatched(functionName):
  return isFunctionSupported(functionName) and getDispatchTableType(functionName) == 'Instance'

def isDeviceDispatched(functionName):
  return isFunctionSupported(functionName) and getDispatchTableType(functionName) == 'Device'

def isGloballyDispatched(functionName):
  return isFunctionSupported(functionName) and getDispatchTableType(functionName) == 'Global'

def isExtensionExported(extensionName):
  if extensionName in exportedExtensions:
    return True
  return False

def isFunctionExported(functionName):
  if isFunctionSupported(functionName):
    if functionName in extensionsDict:
      return isExtensionExported(extensionsDict[functionName])
    return True
  return False

def getDispatchTableType(functionName):
  if functionName not in paramDict:
    return None

  switchCase = {
      'VkInstance ' : 'Instance',
      'VkPhysicalDevice ' : 'Instance',
      'VkDevice ' : 'Device',
      'VkQueue ' : 'Device',
      'VkCommandBuffer ' : 'Device'
  }

  if len(paramDict[functionName])>0:
    return switchCase.get(paramDict[functionName][0][0], 'Global')
  return 'Global'

def isInstanceDispatchTableEntry(functionName):
  if functionName == 'vkEnumerateDeviceLayerProperties': # deprecated, unused internally - @dbd33bc
    return False
  if isFunctionExported(functionName) and isInstanceDispatched(functionName):
    return True
  return False

def isDeviceDispatchTableEntry(functionName):
  if isFunctionExported(functionName) and isDeviceDispatched(functionName):
    return True
  return False


def clang_on(f, indent):
  f.write (clang_off_spaces * indent + '// clang-format on\n')

def clang_off(f, indent):
  f.write (clang_off_spaces * indent + '// clang-format off\n')

clang_off_spaces = ' '*4

parametersList = []
paramDict = {}
allCommandsList = []
extensionsDict = {}
returnTypeDict = {}
versionDict = {}
aliasDict = {}

def parseVulkanRegistry():
  import xml.etree.ElementTree as ET
  import os
  vulkan_registry = os.path.join(os.path.dirname(__file__),'..','..','..','..','external','vulkan-headers','registry','vk.xml')
  tree = ET.parse(vulkan_registry)
  root = tree.getroot()
  protoset = False
  fnName = ""
  fnType = ""
  for commands in root.iter('commands'):
    for command in commands:
      if command.tag == 'command':
        if protoset == True:
          paramDict[fnName] = parametersList.copy()
        parametersList.clear()
        protoset = False
        if command.get('alias') != None:
          alias = command.get('alias')
          fnName = command.get('name')
          aliasDict[fnName] = alias
          allCommandsList.append(fnName)
          paramDict[fnName] = paramDict[alias].copy()
        for params in command:
          if(params.tag == 'param'):
            paramtype = ""
            if params.text!=None:
              paramtype = params.text
            typeval = params.find('type')
            paramtype = paramtype + typeval.text
            if typeval.tail!=None:
              paramtype = paramtype + typeval.tail
            pname = params.find('name')
            paramname = pname.text
            if pname.tail != None:
              parametersList.append((paramtype,paramname,pname.tail))
            else:
              parametersList.append((paramtype,paramname))
          if params.tag == 'proto':
            for c in params:
              if c.tag == 'type':
                fnType = c.text
              if c.tag == 'name':
                fnName = c.text
                protoset = True
                allCommandsList.append(fnName)
                returnTypeDict[fnName] = fnType

  for exts in root.iter('extensions'):
    for extension in exts:
      apiversion = ""
      if extension.tag == 'extension':
        extname = extension.get('name')
        for req in extension:
          if req.get('feature')!=None:
            apiversion = req.get('feature')
          for commands in req:
            if commands.tag == 'command':
              commandname = commands.get('name')
              if commandname not in extensionsDict:
                extensionsDict[commandname] = extname
                if apiversion != "":
                  versionDict[commandname] = apiversion

  for feature in root.iter('feature'):
    apiversion = feature.get('name')
    for req in feature:
      for command in req:
        if command.tag == 'command':
          cmdName = command.get('name')
          if cmdName in allCommandsList:
            versionDict[cmdName] = apiversion


def initProc(name, f):
  if name in extensionsDict:
    f.write ('    INIT_PROC_EXT(' + extensionsDict[name][3:] + ', ')
  else:
    f.write ('    INIT_PROC(')

  if name in versionDict and versionDict[name] == 'VK_VERSION_1_1':
    f.write('false, ')
  else:
    f.write('true, ')

  if isInstanceDispatched(name):
    f.write('instance, ')
  else:
    f.write('dev, ')

  f.write(name[2:] + ');\n')