blob: 9f0381d4c8e6bd6c4f471c4a0f6dca5c37c5e764 [file] [log] [blame]
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2016 Valve Corporation
# Copyright (c) 2015-2016 LunarG, Inc.
# Copyright (c) 2015-2016 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.
#
# Author: Lenny Komow <lenny@lunarg.com>
#
# The API dump layer works by passing custom format strings to the ApiDumpGenerator. These format
# strings are C++ code, with 3-ish exceptions:
# * Anything beginning with @ will be expanded by the ApiDumpGenerator. These are used to allow
# iteration over various items within the Vulkan spec, usch as functions, enums, etc.
# * Anything surrounded by { and } will be substituted when the ApiDumpGenerator expands the @
# directives. This gives a way to get things like data types or names for anything that can
# be iterated over in an @ directive.
# * Curly braces must be doubled like {{ for a single curly brace to appear in the output code.
#
# The API dump uses separate format strings for each output file, but passes them to a common
# generator. This allows greater flexibility, as changing the output codegen means just changing
# the corresponding format string.
#
# Currently, the API dump layer generates the following files from the following strings:
# * api_dump.cpp: COMMON_CODEGEN - Provides all entrypoints for functions and dispatches the calls
# to the proper back end
# * api_dump_text.h: TEXT_CODEGEN - Provides the back end for dumping to a text file
#
import generator as gen
import re
import sys
import xml.etree;
COMMON_CODEGEN = """
/* Copyright (c) 2015-2016 Valve Corporation
* Copyright (c) 2015-2016 LunarG, Inc.
* Copyright (c) 2015-2016 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.
*
* Author: Lenny Komow <lenny@lunarg.com>
*/
/*
* This file is generated from the Khronos Vulkan XML API Registry.
*/
#include "api_dump_text.h"
//============================= Dump Functions ==============================//
@foreach function where('{funcReturn}' != 'void')
inline void dump_{funcName}(ApiDumpInstance& dump_inst, {funcReturn} result, {funcTypedParams})
{{
loader_platform_thread_lock_mutex(dump_inst.outputMutex());
switch(dump_inst.settings().format())
{{
case ApiDumpFormat::Text:
dump_text_{funcName}(dump_inst, result, {funcNamedParams});
break;
}}
loader_platform_thread_unlock_mutex(dump_inst.outputMutex());
}}
@end function
@foreach function where('{funcReturn}' == 'void')
inline void dump_{funcName}(ApiDumpInstance& dump_inst, {funcTypedParams})
{{
loader_platform_thread_lock_mutex(dump_inst.outputMutex());
switch(dump_inst.settings().format())
{{
case ApiDumpFormat::Text:
dump_text_{funcName}(dump_inst, {funcNamedParams});
break;
}}
loader_platform_thread_unlock_mutex(dump_inst.outputMutex());
}}
@end function
//============================= API EntryPoints =============================//
// Specifically implemented functions
@foreach function where('{funcName}' == 'vkCreateInstance')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
// Get the function pointer
VkLayerInstanceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
assert(chain_info->u.pLayerInfo != 0);
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
assert(fpGetInstanceProcAddr != 0);
PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance) fpGetInstanceProcAddr(NULL, "vkCreateInstance");
if(fpCreateInstance == NULL) {{
return VK_ERROR_INITIALIZATION_FAILED;
}}
// Call the function and create the dispatch table
chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
{funcReturn} result = fpCreateInstance({funcNamedParams});
if(result == VK_SUCCESS) {{
initInstanceTable(*pInstance, fpGetInstanceProcAddr);
}}
// Output the API dump
dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
return result;
}}
@end function
@foreach function where('{funcName}' == 'vkDestroyInstance')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
// Destroy the dispatch table
dispatch_key key = get_dispatch_key({funcDispatchParam});
instance_dispatch_table({funcDispatchParam})->DestroyInstance({funcNamedParams});
destroy_instance_dispatch_table(key);
// Output the API dump
dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
}}
@end function
@foreach function where('{funcName}' == 'vkCreateDevice')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
// Get the function pointer
VkLayerDeviceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
assert(chain_info->u.pLayerInfo != 0);
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice) fpGetInstanceProcAddr(NULL, "vkCreateDevice");
if(fpCreateDevice == NULL) {{
return VK_ERROR_INITIALIZATION_FAILED;
}}
// Call the function and create the dispatch table
chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
{funcReturn} result = fpCreateDevice({funcNamedParams});
if(result == VK_SUCCESS) {{
initDeviceTable(*pDevice, fpGetDeviceProcAddr);
}}
// Output the API dump
dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
return result;
}}
@end function
@foreach function where('{funcName}' == 'vkDestroyDevice')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
// Destroy the dispatch table
dispatch_key key = get_dispatch_key({funcDispatchParam});
device_dispatch_table({funcDispatchParam})->DestroyDevice({funcNamedParams});
destroy_device_dispatch_table(key);
// Output the API dump
dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
}}
@end function
@foreach function where('{funcName}' == 'vkEnumerateInstanceExtensionProperties')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
return util_GetExtensionProperties(0, NULL, pPropertyCount, pProperties);
}}
@end function
@foreach function where('{funcName}' == 'vkEnumerateInstanceLayerProperties')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
static const VkLayerProperties layerProperties[] = {{
{{
"VK_LAYER_LUNARG_api_dump",
VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), // specVersion
VK_MAKE_VERSION(0, 2, 0), // implementationVersion
"layer: api_dump",
}}
}};
return util_GetLayerProperties(ARRAY_SIZE(layerProperties), layerProperties, pPropertyCount, pProperties);
}}
@end function
@foreach function where('{funcName}' == 'vkEnumerateDeviceLayerProperties')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
static const VkLayerProperties layerProperties[] = {{
{{
"VK_LAYER_LUNARG_api_dump",
VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION),
VK_MAKE_VERSION(0, 2, 0),
"layer: api_dump",
}}
}};
return util_GetLayerProperties(ARRAY_SIZE(layerProperties), layerProperties, pPropertyCount, pProperties);
}}
@end function
@foreach function where('{funcName}' == 'vkQueuePresentKHR')
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
{funcReturn} result = device_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
ApiDumpInstance::current().nextFrame();
return result;
}}
@end function
// Autogen instance functions
@foreach function where('{funcType}' == 'instance' and '{funcReturn}' != 'void' and '{funcName}' not in ['vkCreateInstance', 'vkDestroyInstance', 'vkCreateDevice', 'vkGetInstanceProcAddr', 'vkEnumerateDeviceExtensionProperties', 'vkEnumerateDeviceLayerProperties'])
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
{funcReturn} result = instance_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
return result;
}}
@end function
@foreach function where('{funcType}' == 'instance' and '{funcReturn}' == 'void' and '{funcName}' not in ['vkCreateInstance', 'vkDestroyInstance', 'vkCreateDevice', 'vkGetInstanceProcAddr', 'vkEnumerateDeviceExtensionProperties', 'vkEnumerateDeviceLayerProperties'])
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
instance_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
}}
@end function
// Autogen device functions
@foreach function where('{funcType}' == 'device' and '{funcReturn}' != 'void' and '{funcName}' not in ['vkDestroyDevice', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkQueuePresentKHR', 'vkGetDeviceProcAddr'])
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
{funcReturn} result = device_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
dump_{funcName}(ApiDumpInstance::current(), result, {funcNamedParams});
return result;
}}
@end function
@foreach function where('{funcType}' == 'device' and '{funcReturn}' == 'void' and '{funcName}' not in ['vkDestroyDevice', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkGetDeviceProcAddr'])
VK_LAYER_EXPORT VKAPI_ATTR {funcReturn} VKAPI_CALL {funcName}({funcTypedParams})
{{
device_dispatch_table({funcDispatchParam})->{funcShortName}({funcNamedParams});
dump_{funcName}(ApiDumpInstance::current(), {funcNamedParams});
}}
@end function
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* pName)
{{
@foreach function where('{funcType}' == 'instance' and '{funcName}' not in [ 'vkEnumerateDeviceExtensionProperties' ])
if(strcmp(pName, "{funcName}") == 0)
return reinterpret_cast<PFN_vkVoidFunction>({funcName});
@end function
if(instance_dispatch_table(instance)->GetInstanceProcAddr == NULL)
return NULL;
return instance_dispatch_table(instance)->GetInstanceProcAddr(instance, pName);
}}
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName)
{{
@foreach function where('{funcType}' == 'device')
if(strcmp(pName, "{funcName}") == 0)
return reinterpret_cast<PFN_vkVoidFunction>({funcName});
@end function
if(device_dispatch_table(device)->GetDeviceProcAddr == NULL)
return NULL;
return device_dispatch_table(device)->GetDeviceProcAddr(device, pName);
}}
"""
TEXT_CODEGEN = """
/* Copyright (c) 2015-2016 Valve Corporation
* Copyright (c) 2015-2016 LunarG, Inc.
* Copyright (c) 2015-2016 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.
*
* Author: Lenny Komow <lenny@lunarg.com>
*/
/*
* This file is generated from the Khronos Vulkan XML API Registry.
*/
#pragma once
#include "api_dump.h"
@foreach struct
std::ostream& dump_text_{sctName}(const {sctName}& object, const ApiDumpSettings& settings, int indents);
@end struct
@foreach union
std::ostream& dump_text_{unName}(const {unName}& object, const ApiDumpSettings& settings, int indents);
@end union
//=========================== Type Implementations ==========================//
@foreach type where('{etyName}' != 'void')
inline std::ostream& dump_text_{etyName}({etyName} object, const ApiDumpSettings& settings, int indents)
{{
@if('{etyName}' != 'uint8_t')
return settings.stream() << object;
@end if
@if('{etyName}' == 'uint8_t')
return settings.stream() << (uint32_t) object;
@end if
}}
@end type
//========================= Basetype Implementations ========================//
@foreach basetype
inline std::ostream& dump_text_{baseName}({baseName} object, const ApiDumpSettings& settings, int indents)
{{
return settings.stream() << object;
}}
@end basetype
//======================= System Type Implementations =======================//
@foreach systype
inline std::ostream& dump_text_{sysName}(const {sysType} object, const ApiDumpSettings& settings, int indents)
{{
return settings.stream() << object;
}}
@end systype
//========================== Handle Implementations =========================//
@foreach handle
inline std::ostream& dump_text_{hdlName}(const {hdlName} object, const ApiDumpSettings& settings, int indents)
{{
if(settings.showAddress())
return settings.stream() << object;
else
return settings.stream() << "address";
}}
@end handle
//=========================== Enum Implementations ==========================//
@foreach enum
std::ostream& dump_text_{enumName}({enumName} object, const ApiDumpSettings& settings, int indents)
{{
switch((int64_t) object)
{{
@foreach option
case {optValue}:
settings.stream() << "{optName} (";
break;
@end option
default:
settings.stream() << "UNKNOWN (";
}}
return settings.stream() << object << ")";
}}
@end enum
//========================= Bitmask Implementations =========================//
@foreach bitmask
std::ostream& dump_text_{bitName}({bitName} object, const ApiDumpSettings& settings, int indents)
{{
bool is_first = true;
//settings.formatNameType(stream, indents, name, type_string) << object;
settings.stream() << object;
@foreach option
if(object & {optValue})
is_first = dump_text_bitmaskOption("{optName}", settings.stream(), is_first);
@end option
if(!is_first)
settings.stream() << ")";
return settings.stream();
}}
@end bitmask
//=========================== Flag Implementations ==========================//
@foreach flag where('{flagEnum}' != 'None')
inline std::ostream& dump_text_{flagName}({flagName} object, const ApiDumpSettings& settings, int indents)
{{
return dump_text_{flagEnum}(({flagEnum}) object, settings, indents);
}}
@end flag
@foreach flag where('{flagEnum}' == 'None')
inline std::ostream& dump_text_{flagName}({flagName} object, const ApiDumpSettings& settings, int indents)
{{
return settings.stream() << object;
}}
@end flag
//======================= Func Pointer Implementations ======================//
@foreach funcpointer
inline std::ostream& dump_text_{pfnName}({pfnName} object, const ApiDumpSettings& settings, int indents)
{{
if(settings.showAddress())
return settings.stream() << object;
else
return settings.stream() << "address";
}}
@end funcpointer
//========================== Struct Implementations =========================//
@foreach struct where('{sctName}' != 'VkShaderModuleCreateInfo')
std::ostream& dump_text_{sctName}(const {sctName}& object, const ApiDumpSettings& settings, int indents)
{{
if(settings.showAddress())
settings.stream() << &object << ":\\n";
else
settings.stream() << "address:\\n";
@foreach member
@if('{memCondition}' != 'None')
if({memCondition})
@end if
@if({memPtrLevel} == 0)
dump_text_value<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if({memPtrLevel} == 1 and '{memLength}' == 'None')
dump_text_pointer<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if({memPtrLevel} == 1 and '{memLength}' != 'None' and not {memLengthIsMember})
dump_text_array<const {memBaseType}>(object.{memName}, {memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if({memPtrLevel} == 1 and '{memLength}' != 'None' and {memLengthIsMember})
dump_text_array<const {memBaseType}>(object.{memName}, object.{memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if('{memCondition}' != 'None')
else
dump_text_special("UNUSED", settings, "{memType}", "{memName}", indents + 1);
@end if
@end member
return settings.stream();
}}
@end struct
@foreach struct where('{sctName}' == 'VkShaderModuleCreateInfo')
std::ostream& dump_text_{sctName}(const {sctName}& object, const ApiDumpSettings& settings, int indents)
{{
if(settings.showAddress())
settings.stream() << &object << ":\\n";
else
settings.stream() << "address:\\n";
@foreach member
@if('{memCondition}' != 'None')
if({memCondition})
@end if
@if({memPtrLevel} == 0)
dump_text_value<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if({memPtrLevel} == 1 and '{memLength}' == 'None')
dump_text_pointer<const {memBaseType}>(object.{memName}, settings, "{memType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if({memPtrLevel} == 1 and '{memLength}' != 'None' and not {memLengthIsMember} and '{memName}' != 'pCode')
dump_text_array<const {memBaseType}>(object.{memName}, {memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if({memPtrLevel} == 1 and '{memLength}' != 'None' and {memLengthIsMember} and '{memName}' != 'pCode')
dump_text_array<const {memBaseType}>(object.{memName}, object.{memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
@end if
@if('{memName}' == 'pCode')
if(settings.showShader())
dump_text_array<const {memBaseType}>(object.{memName}, object.{memLength}, settings, "{memType}", "{memChildType}", "{memName}", indents + 1, dump_text_{memTypeID});
else
dump_text_special("SHADER DATA", settings, "{memType}", "{memName}", indents + 1);
@end if
@if('{memCondition}' != 'None')
else
dump_text_special("UNUSED", settings, "{memType}", "{memName}", indents + 1);
@end if
@end member
return settings.stream();
}}
@end struct
//========================== Union Implementations ==========================//
@foreach union
std::ostream& dump_text_{unName}(const {unName}& object, const ApiDumpSettings& settings, int indents)
{{
if(settings.showAddress())
settings.stream() << &object << " (Union):\\n";
else
settings.stream() << "address (Union):\\n";
@foreach choice
@if({chcPtrLevel} == 0)
dump_text_value<const {chcBaseType}>(object.{chcName}, settings, "{chcType}", "{chcName}", indents + 1, dump_text_{chcTypeID});
@end if
@if({chcPtrLevel} == 1 and '{chcLength}' == 'None')
dump_text_pointer<const {chcBaseType}>(object.{chcName}, settings, "{chcType}", "{chcName}", indents + 1, dump_text_{chcTypeID});
@end if
@if({chcPtrLevel} == 1 and '{chcLength}' != 'None')
dump_text_array<const {chcBaseType}>(object.{chcName}, {chcLength}, settings, "{chcType}", "{chcChildType}", "{chcName}", indents + 1, dump_text_{chcTypeID});
@end if
@end choice
return settings.stream();
}}
@end union
//========================= Function Implementations ========================//
@foreach function where('{funcReturn}' != 'void')
std::ostream& dump_text_{funcName}(ApiDumpInstance& dump_inst, {funcReturn} result, {funcTypedParams})
{{
const ApiDumpSettings& settings(dump_inst.settings());
settings.stream() << "Thread " << dump_inst.threadID() << ", Frame " << dump_inst.frameCount() << ":\\n";
settings.stream() << "{funcName}({funcNamedParams}) returns {funcReturn} ";
dump_text_{funcReturn}(result, settings, 0) << ":\\n";
if(settings.showParams())
{{
@foreach parameter
@if({prmPtrLevel} == 0)
dump_text_value<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
@end if
@if({prmPtrLevel} == 1 and '{prmLength}' == 'None')
dump_text_pointer<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
@end if
@if({prmPtrLevel} == 1 and '{prmLength}' != 'None')
dump_text_array<const {prmBaseType}>({prmName}, {prmLength}, settings, "{prmType}", "{prmChildType}", "{prmName}", 1, dump_text_{prmTypeID});
@end if
@end parameter
}}
settings.shouldFlush() ? settings.stream() << std::endl : settings.stream() << "\\n";
return settings.stream();
}}
@end function
@foreach function where('{funcReturn}' == 'void')
std::ostream& dump_text_{funcName}(ApiDumpInstance& dump_inst, {funcTypedParams})
{{
const ApiDumpSettings& settings(dump_inst.settings());
settings.stream() << "Thread " << dump_inst.threadID() << ", Frame " << dump_inst.frameCount() << ":\\n";
settings.stream() << "{funcName}({funcNamedParams}) returns {funcReturn}:\\n";
if(settings.showParams())
{{
@foreach parameter
@if({prmPtrLevel} == 0)
dump_text_value<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
@end if
@if({prmPtrLevel} == 1 and '{prmLength}' == 'None')
dump_text_pointer<const {prmBaseType}>({prmName}, settings, "{prmType}", "{prmName}", 1, dump_text_{prmTypeID});
@end if
@if({prmPtrLevel} == 1 and '{prmLength}' != 'None')
dump_text_array<const {prmBaseType}>({prmName}, {prmLength}, settings, "{prmType}", "{prmChildType}", "{prmName}", 1, dump_text_{prmTypeID});
@end if
@end parameter
}}
settings.shouldFlush() ? settings.stream() << std::endl : settings.stream() << "\\n";
return settings.stream();
}}
@end function
"""
POINTER_TYPES = ['void', 'xcb_connection_t', 'Display', 'SECURITY_ATTRIBUTES', 'ANativeWindow']
VALIDITY_CHECKS = {
'VkBufferCreateInfo': {
'pQueueFamilyIndices': 'object.sharingMode == VK_SHARING_MODE_CONCURRENT',
},
'VkCommandBufferBeginInfo': {
'pInheritanceInfo': 'false', # No easy way to tell if this is a primary command buffer
},
'VkDescriptorSetLayoutBinding': {
'pImmutableSamplers':
'(object.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)',
},
'VkImageCreateInfo': {
'pQueueFamilyIndices': 'object.sharingMode == VK_SHARING_MODE_CONCURRENT',
},
'VkPipelineViewportStateCreateInfo': {
'pViewports': 'false', # No easy way to check if viewport state is dynamic
'pScissors': 'false', # No easy way to check if scissor state is dynamic
},
'VkSwapchainCreateInfoKHR': {
'pQueueFamilyIndices': 'object.imageSharingMode == VK_SHARING_MODE_CONCURRENT',
},
'VkWriteDescriptorSet': {
'pImageInfo':
'(object.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)',
'pBufferInfo':
'(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)',
'pTexelBufferView':
'(object.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) || ' +
'(object.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)',
},
}
class ApiDumpGeneratorOptions(gen.GeneratorOptions):
def __init__(self,
input = None,
filename = None,
directory = '.',
apiname = None,
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = None,
addExtensions = None,
removeExtensions = None,
sortProcedure = None,
prefixText = "",
genFuncPointers = True,
protectFile = True,
protectFeature = True,
protectProto = None,
protectProtoStr = None,
apicall = '',
apientry = '',
apientryp = '',
indentFuncProto = True,
indentFuncPointer = False,
alignFuncParam = 0):
gen.GeneratorOptions.__init__(self, filename, directory, apiname, profile,
versions, emitversions, defaultExtensions,
addExtensions, removeExtensions, sortProcedure)
self.input = input
self.prefixText = prefixText
self.genFuncPointers = genFuncPointers
self.protectFile = protectFile
self.protectFeature = protectFeature
self.protectProto = protectProto
self.protectProtoStr = protectProtoStr
self.apicall = apicall
self.apientry = apientry
self.apientryp = apientryp
self.indentFuncProto = indentFuncProto
self.indentFuncPointer = indentFuncPointer
self.alignFuncParam = alignFuncParam
class ApiDumpOutputGenerator(gen.OutputGenerator):
def __init__(self,
errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout,
registryFile = None):
gen.OutputGenerator.__init__(self, errFile, warnFile, diagFile)
self.format = None
self.constants = {}
self.extensions = set()
self.extFuncs = {}
self.extTypes = {}
self.includes = {}
self.basetypes = set()
self.bitmasks = set()
self.enums = set()
self.externalTypes = set()
self.flags = set()
self.funcPointers = set()
self.functions = set()
self.handles = set()
self.structs = set()
self.unions = set()
self.registryFile = registryFile
def beginFile(self, genOpts):
gen.OutputGenerator.beginFile(self, genOpts)
self.format = genOpts.input
if self.registryFile != None:
root = xml.etree.ElementTree.parse(self.registryFile)
else:
root = self.registry.reg
for node in root.find('extensions').findall('extension'):
ext = VulkanExtension(node)
self.extensions.add(ext)
for item in ext.vktypes:
self.extTypes[item] = ext
for item in ext.vkfuncs:
self.extFuncs[item] = ext
for node in self.registry.reg.findall('enums'):
if node.get('name') == 'API Constants':
for item in node.findall('enum'):
self.constants[item.get('name')] = item.get('value')
for node in self.registry.reg.find('types').findall('type'):
if node.get('category') == 'include':
self.includes[node.get('name')] = ''.join(node.itertext())
def endFile(self):
# Find all of the extensions that use the system types
self.sysTypes = set()
for node in self.registry.reg.find('types').findall('type'):
if node.get('category') == None and node.get('requires') in self.includes and node.get('requires') != 'vk_platform':
for extension in self.extTypes:
for structName in self.extTypes[extension].vktypes:
for struct in self.structs:
if struct.name == structName:
for member in struct.members:
if node.get('name') == member.baseType or node.get('name') + '*' == member.baseType:
sysType = VulkanSystemType(node.get('name'), self.extTypes[structName])
if sysType not in self.sysTypes:
self.sysTypes.add(sysType)
for funcName in self.extTypes[extension].vkfuncs:
for func in self.functions:
if func.name == funcName:
for param in func.parameters:
if node.get('name') == param.baseType or node.get('name') + '*' == param.baseType:
sysType = VulkanSystemType(node.get('name'), self.extFuncs[funcName])
if sysType not in self.sysTypes:
self.sysTypes.add(sysType)
# Find every @foreach, @if, and @end
forIter = re.finditer('(^\\s*\\@foreach\\s+[a-z]+(\\s+where\\(.*\\))?\\s*^)|(\\@foreach [a-z]+(\\s+where\\(.*\\))?\\b)', self.format, flags=re.MULTILINE)
ifIter = re.finditer('(^\\s*\\@if\\(.*\\)\\s*^)|(\\@if\\(.*\\))', self.format, flags=re.MULTILINE)
endIter = re.finditer('(^\\s*\\@end\\s+[a-z]+\\s*^)|(\\@end [a-z]+\\b)', self.format, flags=re.MULTILINE)
try:
nextFor = next(forIter)
except StopIteration:
nextFor = None
try:
nextIf = next(ifIter)
except StopIteration:
nextIf = None
try:
nextEnd = next(endIter)
except StopIteration:
nextEnd = None
# Match the beginnings to the ends
loops = []
unassignedControls = []
depth = 0
while nextFor != None or nextFor != None or nextEnd != None:
# If this is a @foreach
if nextFor != None and ((nextIf == None or nextFor.start() < nextIf.start()) and nextFor.start() < nextEnd.start()):
depth += 1
forType = re.search('(?<=\\s)[a-z]+', self.format[nextFor.start():nextFor.end()])
text = self.format[forType.start()+nextFor.start():forType.end()+nextFor.start()]
whereMatch = re.search('(?<=where\\().*(?=\\))', self.format[nextFor.start():nextFor.end()])
condition = None if whereMatch == None else self.format[whereMatch.start()+nextFor.start():whereMatch.end()+nextFor.start()]
unassignedControls.append((nextFor.start(), nextFor.end(), text, condition))
try:
nextFor = next(forIter)
except StopIteration:
nextFor = None
# If this is an @if
elif nextIf != None and nextIf.start() < nextEnd.start():
depth += 1
condMatch = re.search('(?<=if\\().*(?=\\))', self.format[nextIf.start():nextIf.end()])
condition = None if condMatch == None else self.format[condMatch.start()+nextIf.start():condMatch.end()+nextIf.start()]
unassignedControls.append((nextIf.start(), nextIf.end(), 'if', condition))
try:
nextIf = next(ifIter)
except StopIteration:
nextIf = None
# Else this is an @end
else:
depth -= 1
endType = re.search('(?<=\\s)[a-z]+', self.format[nextEnd.start():nextEnd.end()])
text = self.format[endType.start()+nextEnd.start():endType.end()+nextEnd.start()]
start = unassignedControls.pop(-1)
assert(start[2] == text)
item = Control(self.format, start[0:2], (nextEnd.start(), nextEnd.end()), text, start[3])
if len(loops) < 1 or depth < loops[-1][0]:
while len(loops) > 0 and depth < loops[-1][0]:
item.children.insert(0, loops.pop(-1)[1])
loops.append((depth, item))
else:
loops.append((depth, item))
try:
nextEnd = next(endIter)
except StopIteration:
nextEnd = None
# Expand each loop into its full form
lastIndex = 0
for _, loop in loops:
gen.write(self.format[lastIndex:loop.startPos[0]].format(**{}), file=self.outFile)
gen.write(self.expand(loop), file=self.outFile)
lastIndex = loop.endPos[1]
gen.write(self.format[lastIndex:-1].format(**{}), file=self.outFile)
gen.OutputGenerator.endFile(self)
def genCmd(self, cmd, name):
gen.OutputGenerator.genCmd(self, cmd, name)
self.functions.add(VulkanFunction(cmd.elem, self.constants))
# These are actually constants
def genEnum(self, enuminfo, name):
gen.OutputGenerator.genEnum(self, enuminfo, name)
# These are actually enums
def genGroup(self, groupinfo, groupName):
gen.OutputGenerator.genGroup(self, groupinfo, groupName)
if groupinfo.elem.get('type') == 'bitmask':
self.bitmasks.add(VulkanBitmask(groupinfo.elem, self.extensions))
elif groupinfo.elem.get('type') == 'enum':
self.enums.add(VulkanEnum(groupinfo.elem, self.extensions))
def genType(self, typeinfo, name):
gen.OutputGenerator.genType(self, typeinfo, name)
if typeinfo.elem.get('category') == 'struct':
self.structs.add(VulkanStruct(typeinfo.elem, self.constants))
elif typeinfo.elem.get('category') == 'basetype':
self.basetypes.add(VulkanBasetype(typeinfo.elem))
elif typeinfo.elem.get('category') == None and typeinfo.elem.get('requires') == 'vk_platform':
self.externalTypes.add(VulkanExternalType(typeinfo.elem))
elif typeinfo.elem.get('category') == 'handle':
self.handles.add(VulkanHandle(typeinfo.elem))
elif typeinfo.elem.get('category') == 'union':
self.unions.add(VulkanUnion(typeinfo.elem, self.constants))
elif typeinfo.elem.get('category') == 'bitmask':
self.flags.add(VulkanFlags(typeinfo.elem))
elif typeinfo.elem.get('category') == 'funcpointer':
self.funcPointers.add(VulkanFunctionPointer(typeinfo.elem))
def expand(self, loop, parents=[]):
# Figure out what we're dealing with
if loop.text == 'if':
subjects = [ Control.IfDummy() ]
elif loop.text == 'basetype':
subjects = self.basetypes
elif loop.text == 'bitmask':
subjects = self.bitmasks
elif loop.text == 'choice':
subjects = self.findByType([VulkanUnion], parents).choices
elif loop.text == 'enum':
subjects = self.enums
elif loop.text == 'extension':
subjects = self.extensions
elif loop.text == 'flag':
subjects = self.flags
elif loop.text == 'funcpointer':
subjects = self.funcPointers
elif loop.text == 'function':
subjects = self.functions
elif loop.text == 'handle':
subjects = self.handles
elif loop.text == 'option':
subjects = self.findByType([VulkanEnum, VulkanBitmask], parents).options
elif loop.text == 'member':
subjects = self.findByType([VulkanStruct], parents).members
elif loop.text == 'parameter':
subjects = self.findByType([VulkanFunction], parents).parameters
elif loop.text == 'struct':
subjects = self.structs
elif loop.text == 'systype':
subjects = self.sysTypes
elif loop.text == 'type':
subjects = self.externalTypes
elif loop.text == 'union':
subjects = self.unions
else:
assert(False)
# Generate the output string
out = ''
for item in subjects:
# Merge the values and the parent values
values = item.values().copy()
for parent in parents:
values.update(parent.values())
# Check if the condition is met
if loop.condition != None:
cond = eval(loop.condition.format(**values))
assert(cond == True or cond == False)
if not cond:
continue
# Check if an ifdef is needed
if item.name in self.extFuncs:
ext = self.extFuncs[item.name]
elif item.name in self.extTypes:
ext = self.extTypes[item.name]
elif item in self.sysTypes:
ext = item.ext
else:
ext = None
if ext != None and ext.guard != None:
out += '#if defined({})\n'.format(ext.guard)
# Format the string
lastIndex = loop.startPos[1]
for child in loop.children:
out += loop.fullString[lastIndex:child.startPos[0]].format(**values)
out += self.expand(child, parents=[item]+parents)
lastIndex = child.endPos[1]
out += loop.fullString[lastIndex:loop.endPos[0]].format(**values)
# Close the ifdef
if ext != None and ext.guard != None:
out += '#endif // {}\n'.format(ext.guard)
return out
def findByType(self, types, objects):
value = None
for item in objects:
for ty in types:
if isinstance(item, ty):
value = item
break
assert(value != None)
return value
class Control:
class IfDummy:
def __init__(self):
self.name = 'ifdummy'
def values(self):
return {}
def __init__(self, fullString, start, end, text, condition):
self.fullString = fullString
self.startPos = start
self.endPos = end
self.text = text
self.condition = condition
self.children = []
# Base class for VulkanStruct.Member and VulkanStruct.Parameter
class VulkanVariable:
def __init__(self, rootNode, constants):
# Set basic properties
self.name = rootNode.find('name').text # Variable name
self.typeID = rootNode.find('type').text # Typename, dereferenced and converted to a useable C++ token
self.baseType = self.typeID # Type, dereferenced to the non-pointer type
self.childType = None # Type, dereferenced to the non-pointer type (None if it isn't a pointer)
self.arrayLength = None # Length of the array, or None if it isn't an array
typeMatch = re.search('.+(?=' + self.name + ')', ''.join(rootNode.itertext()))
self.type = typeMatch.string[typeMatch.start():typeMatch.end()]
self.type = ' '.join(self.type.split())
bracketMatch = re.search('(?<=\\[)[a-zA-Z0-9_]+(?=\\])', ''.join(rootNode.itertext()))
if bracketMatch != None:
matchText = bracketMatch.string[bracketMatch.start():bracketMatch.end()]
self.childType = self.type
self.type += '[' + matchText + ']'
if matchText in constants:
self.arrayLength = constants[matchText]
else:
self.arrayLength = matchText
self.lengthMember = False
lengthString = rootNode.get('len')
lengths = []
if lengthString != None:
lengths = re.split(',', lengthString)
lengths = list(filter(('null-terminated').__ne__, lengths))
assert(len(lengths) <= 1)
if self.arrayLength == None and len(lengths) > 0:
self.childType = '*'.join(self.type.split('*')[0:-1])
self.arrayLength = lengths[0]
self.lengthMember = True
if self.arrayLength != None and self.arrayLength.startswith('latexmath'):
code = self.arrayLength[10:len(self.arrayLength)]
code = re.sub('\\[\\$', '', code)
code = re.sub('\\$\\]', '', code)
code = re.sub('\\\\(lceil|rceil)', '', code)
code = re.sub('{|}', '', code)
code = re.sub('\\\\mathit', '', code)
code = re.sub('\\\\over', '/', code)
self.arrayLength = code
# Dereference if necessary and handle members of variables
if self.arrayLength != None:
self.arrayLength = re.sub('::', '->', self.arrayLength)
sections = self.arrayLength.split('->')
if sections[-1][0] == 'p' and sections[0][1].isupper():
self.arrayLength = '*' + self.arrayLength
self.pointerLevels = len(re.findall('\\*|\\[', ''.join(rootNode.itertext())))
if self.typeID == 'char' and self.pointerLevels > 0:
self.baseType += '*'
self.pointerLevels -= 1
self.typeID = 'cstring'
elif self.typeID in POINTER_TYPES:
self.baseType += '*'
self.pointerLevels -= 1
assert(self.pointerLevels >= 0)
class VulkanBasetype:
def __init__(self, rootNode):
self.name = rootNode.get('name')
self.type = rootNode.get('type')
def values(self):
return {
'baseName': self.name,
'baseType': self.type,
}
class VulkanBitmask:
def __init__(self, rootNode, extensions):
self.name = rootNode.get('name')
self.type = rootNode.get('type')
# Read each value that the enum contains
self.options = []
for child in rootNode:
childName = child.get('name')
childValue = child.get('value')
childBitpos = child.get('bitpos')
childComment = child.get('comment')
if childName == None or (childValue == None and childBitpos == None):
continue
self.options.append(VulkanEnum.Option(childName, childValue, childBitpos, childComment))
for ext in extensions:
if self.name in ext.enumValues:
childName, childValue = ext.enumValues[self.name]
self.options.append(VulkanEnum.Option(childName, childValue, None, None))
def values(self):
return {
'bitName': self.name,
'bitType': self.type,
}
class VulkanEnum:
class Option:
def __init__(self, name, value, bitpos, comment):
self.name = name
self.comment = comment
if value == 0 or value == None:
value = 1 << int(bitpos)
self.value = value
def values(self):
return {
'optName': self.name,
'optValue': self.value,
'optComment': self.comment,
}
def __init__(self, rootNode, extensions):
self.name = rootNode.get('name')
self.type = rootNode.get('type')
# Read each value that the enum contains
self.options = []
for child in rootNode:
childName = child.get('name')
childValue = child.get('value')
childBitpos = child.get('bitpos')
childComment = child.get('comment')
if childName == None or (childValue == None and childBitpos == None):
continue
self.options.append(VulkanEnum.Option(childName, childValue, childBitpos, childComment))
for ext in extensions:
if self.name in ext.enumValues:
childName, childValue = ext.enumValues[self.name]
self.options.append(VulkanEnum.Option(childName, childValue, None, None))
def values(self):
return {
'enumName': self.name,
'enumType': self.type,
}
class VulkanExtension:
def __init__(self, rootNode):
self.name = rootNode.get('name')
self.number = int(rootNode.get('number'))
self.type = rootNode.get('type')
self.dependency = rootNode.get('requires')
self.guard = rootNode.get('protect')
self.supported = rootNode.get('supported')
self.vktypes = []
for ty in rootNode.find('require').findall('type'):
self.vktypes.append(ty.get('name'))
self.vkfuncs = []
for func in rootNode.find('require').findall('command'):
self.vkfuncs.append(func.get('name'))
self.constants = {}
self.enumValues = {}
for enum in rootNode.find('require').findall('enum'):
base = enum.get('extends')
name = enum.get('name')
value = enum.get('value')
bitpos = enum.get('bitpos')
offset = enum.get('offset')
if value == None and bitpos != None:
value = 1 << int(bitpos)
if offset != None:
offset = int(offset)
if base != None and offset != None:
enumValue = 1000000000 + 1000*(self.number - 1) + offset
if enum.get('dir') == '-':
enumValue = -enumValue;
self.enumValues[base] = (name, enumValue)
else:
self.constants[name] = value
def values(self):
return {
'extName': self.name,
'extNumber': self.number,
'extType': self.type,
'extDependency': self.dependency,
'extGuard': self.guard,
'extSupported': self.supported,
}
class VulkanExternalType:
def __init__(self, rootNode):
self.name = rootNode.get('name')
self.dependency = rootNode.get('requires')
def values(self):
return {
'etyName': self.name,
'etyDependency': self.dependency,
}
class VulkanFlags:
def __init__(self, rootNode):
self.name = rootNode.get('name')
self.type = rootNode.get('type')
self.enum = rootNode.get('requires')
def values(self):
return {
'flagName': self.name,
'flagType': self.type,
'flagEnum': self.enum,
}
class VulkanFunction:
class Parameter(VulkanVariable):
def __init__(self, rootNode, constants):
VulkanVariable.__init__(self, rootNode, constants)
self.text = ''.join(rootNode.itertext())
def values(self):
return {
'prmName': self.name,
'prmBaseType': self.baseType,
'prmTypeID': self.typeID,
'prmType': self.type,
'prmChildType': self.childType,
'prmPtrLevel': self.pointerLevels,
'prmLength': self.arrayLength,
}
def __init__(self, rootNode, constants):
self.name = rootNode.find('proto').find('name').text
self.returnType = rootNode.find('proto').find('type').text
self.parameters = []
self.namedParams = ''
self.typedParams = ''
for node in rootNode.findall('param'):
self.parameters.append(VulkanFunction.Parameter(node, constants))
self.namedParams += self.parameters[-1].name + ', '
self.typedParams += self.parameters[-1].text + ', '
if len(self.parameters) > 0:
self.namedParams = self.namedParams[0:-2]
self.typedParams = self.typedParams[0:-2]
if self.parameters[0].type in ['VkInstance', 'VkPhysicalDevice'] or self.name == 'vkCreateInstance':
self.type = 'instance'
else:
self.type = 'device'
def values(self):
return {
'funcName': self.name,
'funcShortName': self.name[2:len(self.name)],
'funcType': self.type,
'funcReturn': self.returnType,
'funcNamedParams': self.namedParams,
'funcTypedParams': self.typedParams,
'funcDispatchParam': self.parameters[0].name
}
class VulkanFunctionPointer:
def __init__(self, rootNode):
self.name = rootNode.get('name')
def values(self):
return {
'pfnName': self.name,
}
class VulkanHandle:
def __init__(self, rootNode):
self.name = rootNode.get('name')
self.type = rootNode.get('type')
self.parent = rootNode.get('parent')
def values(self):
return {
'hdlName': self.name,
'hdlType': self.type,
'hdlParent': self.parent,
}
class VulkanStruct:
class Member(VulkanVariable):
def __init__(self, rootNode, constants, parentName):
VulkanVariable.__init__(self, rootNode, constants)
# Search for a member condition
self.condition = None
if rootNode.get('noautovalidity') == 'true' and parentName in VALIDITY_CHECKS and self.name in VALIDITY_CHECKS[parentName]:
self.condition = VALIDITY_CHECKS[parentName][self.name]
def values(self):
return {
'memName': self.name,
'memBaseType': self.baseType,
'memTypeID': self.typeID,
'memType': self.type,
'memChildType': self.childType,
'memPtrLevel': self.pointerLevels,
'memLength': self.arrayLength,
'memLengthIsMember': self.lengthMember,
'memCondition': self.condition,
}
def __init__(self, rootNode, constants):
self.name = rootNode.get('name')
self.members = []
for node in rootNode.findall('member'):
self.members.append(VulkanStruct.Member(node, constants, self.name))
def values(self):
return {
'sctName': self.name,
}
class VulkanSystemType:
def __init__(self, name, ext):
self.name = name
self.type = self.name if name not in POINTER_TYPES else self.name + '*'
self.ext = ext
def __eq__(self, that):
return self.name == that.name and self.type == that.type
def __hash__(self):
return hash(self.name) | hash(self.type)
def values(self):
return {
'sysName': self.name,
'sysType': self.type,
}
class VulkanUnion:
class Choice(VulkanVariable):
def __init__(self, rootNode, constants):
VulkanVariable.__init__(self, rootNode, constants)
def values(self):
return {
'chcName': self.name,
'chcBaseType': self.baseType,
'chcTypeID': self.typeID,
'chcType': self.type,
'chcChildType': self.childType,
'chcPtrLevel': self.pointerLevels,
'chcLength': self.arrayLength,
#'chcLengthIsMember': self.lengthMember,
}
def __init__(self, rootNode, constants):
self.name = rootNode.get('name')
self.choices = []
for node in rootNode.findall('member'):
self.choices.append(VulkanUnion.Choice(node, constants))
def values(self):
return {
'unName': self.name,
}