| #!/usr/bin/python3 -i |
| # |
| # Copyright (c) 2017-2019 The Khronos Group Inc. |
| # Copyright (c) 2017-2019 Valve Corporation |
| # Copyright (c) 2017-2019 LunarG, 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(s): Mark Young <marky@lunarg.com> |
| # |
| # Purpose: This file utilizes the content formatted in the |
| # automatic_source_generator.py class to produce the |
| # generated source code for the API Dump layer. |
| |
| import os |
| import re |
| import sys |
| from automatic_source_generator import * |
| from collections import namedtuple |
| |
| # The following commands should not be generated for the layer |
| MANUALLY_DEFINED_IN_LAYER = [ |
| 'xrCreateInstance', |
| 'xrDestroyInstance', |
| ] |
| |
| DONT_GEN_IN_LAYER = [ |
| 'xrEnumerateApiLayerProperties', |
| 'xrEnumerateInstanceExtensionProperties', |
| ] |
| |
| # ApiDumpGeneratorOptions - subclass of AutomaticSourceGeneratorOptions. |
| |
| |
| class ApiDumpGeneratorOptions(AutomaticSourceGeneratorOptions): |
| def __init__(self, |
| conventions=None, |
| filename=None, |
| directory='.', |
| apiname=None, |
| profile=None, |
| versions='.*', |
| emitversions='.*', |
| defaultExtensions=None, |
| addExtensions=None, |
| removeExtensions=None, |
| emitExtensions=None, |
| sortProcedure=regSortFeatures, |
| prefixText="", |
| genFuncPointers=True, |
| protectFile=True, |
| protectFeature=True, |
| protectProto=None, |
| protectProtoStr=None, |
| apicall='', |
| apientry='', |
| apientryp='', |
| indentFuncProto=True, |
| indentFuncPointer=False, |
| alignFuncParam=0, |
| genEnumBeginEndRange=False): |
| AutomaticSourceGeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile, |
| versions, emitversions, defaultExtensions, |
| addExtensions, removeExtensions, |
| emitExtensions, sortProcedure) |
| |
| # ApiDumpOutputGenerator - subclass of AutomaticSourceOutputGenerator. |
| |
| |
| class ApiDumpOutputGenerator(AutomaticSourceOutputGenerator): |
| """Generate API Dump layer source using XML element attributes from registry""" |
| |
| def __init__(self, |
| errFile=sys.stderr, |
| warnFile=sys.stderr, |
| diagFile=sys.stdout): |
| AutomaticSourceOutputGenerator.__init__( |
| self, errFile, warnFile, diagFile) |
| |
| # Override the base class header warning so the comment indicates this file. |
| # self the AutomaticSourceOutputGenerator object |
| def outputGeneratedHeaderWarning(self): |
| # File Comment |
| generated_warning = '// *********** THIS FILE IS GENERATED - DO NOT EDIT ***********\n' |
| generated_warning += '// See api_dump_generator.py for modifications\n' |
| generated_warning += '// ************************************************************\n' |
| write(generated_warning, file=self.outFile) |
| |
| # Call the base class to properly begin the file, and then add |
| # the file-specific header information. |
| # self the ApiDumpOutputGenerator object |
| # gen_opts the ApiDumpGeneratorOptions object |
| def beginFile(self, genOpts): |
| AutomaticSourceOutputGenerator.beginFile(self, genOpts) |
| preamble = '' |
| if self.genOpts.filename == 'xr_generated_api_dump.hpp': |
| preamble += '#pragma once\n\n' |
| preamble += '#include <string>\n' |
| preamble += '#include <vector>\n' |
| preamble += '#include <tuple>\n\n' |
| preamble += '#include "api_layer_platform_defines.h"\n' |
| preamble += '#include <openxr/openxr.h>\n' |
| preamble += '#include <openxr/openxr_platform.h>\n\n' |
| preamble += '#include "xr_generated_dispatch_table.h"\n' |
| elif self.genOpts.filename == 'xr_generated_api_dump.cpp': |
| preamble += '#include <cstring>\n' |
| preamble += '#include <mutex>\n' |
| preamble += '#include <sstream>\n' |
| preamble += '#include <iomanip>\n' |
| preamble += '#include <unordered_map>\n\n' |
| preamble += '#include "xr_generated_api_dump.hpp"\n' |
| preamble += '#include "xr_utils.h"\n' |
| write(preamble, file=self.outFile) |
| |
| # Write out all the information for the appropriate file, |
| # and then call down to the base class to wrap everything up. |
| # self the ApiDumpOutputGenerator object |
| def endFile(self): |
| file_data = '' |
| if self.genOpts.filename == 'xr_generated_api_dump.hpp': |
| file_data += self.outputLayerHeaderPrototypes() |
| file_data += self.outputApiDumpExterns() |
| |
| elif self.genOpts.filename == 'xr_generated_api_dump.cpp': |
| file_data += self.outputApiDumpMapMutexItems() |
| file_data += self.writeApiDumpUnionStructFuncs() |
| file_data += self.outputLayerCommands() |
| |
| write(file_data, file=self.outFile) |
| |
| # Finish processing in superclass |
| AutomaticSourceOutputGenerator.endFile(self) |
| |
| # Output the externs required by the manual code to work with the API Dump |
| # gnerated code. |
| # self the ApiDumpOutputGenerator object |
| def outputApiDumpExterns(self): |
| externs = '\n// Externs for API dump\n' |
| for handle in self.api_handles: |
| base_handle_name = undecorate(handle.name) |
| if handle.protect_value is not None: |
| externs += '#if %s\n' % handle.protect_string |
| externs += 'extern std::unordered_map<%s, XrGeneratedDispatchTable*> g_%s_dispatch_map;\n' % ( |
| handle.name, base_handle_name) |
| externs += 'extern std::mutex g_%s_dispatch_mutex;\n' % base_handle_name |
| if handle.protect_value is not None: |
| externs += '#endif // %s\n' % handle.protect_string |
| externs += 'void ApiDumpCleanUpMapsForTable(XrGeneratedDispatchTable *table);\n' |
| return externs |
| |
| # Output the externs manually implemented by the API Dump layer so that the generated code |
| # can access them. |
| # self the ApiDumpOutputGenerator object |
| def outputLayerHeaderPrototypes(self): |
| generated_prototypes = '// Layer\'s xrGetInstanceProcAddr\n' |
| generated_prototypes += 'XrResult ApiDumpLayerXrGetInstanceProcAddr(XrInstance instance,\n' |
| generated_prototypes += ' const char* name, PFN_xrVoidFunction* function);\n\n' |
| generated_prototypes += '// Api Dump Log Command\n' |
| generated_prototypes += 'bool ApiDumpLayerRecordContent(std::vector<std::tuple<std::string, std::string, std::string>> contents);\n\n' |
| generated_prototypes += '// Api Dump Manual Functions\n' |
| generated_prototypes += 'XrInstance FindInstanceFromDispatchTable(XrGeneratedDispatchTable* dispatch_table);\n' |
| generated_prototypes += 'XrResult ApiDumpLayerXrCreateInstance(const XrInstanceCreateInfo *info,\n' |
| generated_prototypes += ' XrInstance *instance);\n' |
| generated_prototypes += 'XrResult ApiDumpLayerXrDestroyInstance(XrInstance instance);\n' |
| generated_prototypes += '\n//Dump utility functions\n' |
| generated_prototypes += 'bool ApiDumpDecodeNextChain(XrGeneratedDispatchTable* gen_dispatch_table, const void* value, std::string prefix,\n' |
| generated_prototypes += ' std::vector<std::tuple<std::string, std::string, std::string>> &contents);\n' |
| generated_prototypes += '\n// Union/Structure Output Helper function prototypes\n' |
| for xr_union in self.api_unions: |
| if xr_union.protect_value: |
| generated_prototypes += '#if %s\n' % xr_union.protect_string |
| generated_prototypes += 'bool ApiDumpOutputXrUnion(XrGeneratedDispatchTable* gen_dispatch_table, const %s* value,\n' % xr_union.name |
| generated_prototypes += ' std::string prefix, std::string type_string, bool is_pointer,\n' |
| generated_prototypes += ' std::vector<std::tuple<std::string, std::string, std::string>> &contents);\n' |
| if xr_union.protect_value: |
| generated_prototypes += '#endif // %s\n' % xr_union.protect_string |
| for xr_struct in self.api_structures: |
| if xr_struct.protect_value: |
| generated_prototypes += '#if %s\n' % xr_struct.protect_string |
| generated_prototypes += 'bool ApiDumpOutputXrStruct(XrGeneratedDispatchTable* gen_dispatch_table, const %s* value,\n' % xr_struct.name |
| generated_prototypes += ' std::string prefix, std::string type_string, bool is_pointer,\n' |
| generated_prototypes += ' std::vector<std::tuple<std::string, std::string, std::string>> &contents);\n' |
| if xr_struct.protect_value: |
| generated_prototypes += '#endif // %s\n' % xr_struct.protect_string |
| return generated_prototypes |
| |
| # Output the unordered_map's required to track all the data we need per handle type. Also, create |
| # a mutex that allows us to access the unordered_maps in a thread-safe manner. Finally, wrap it |
| # all up by creating utility functions for cleaning up a dispatch table when it's instance has |
| # been deleted. |
| # self the ApiDumpOutputGenerator object |
| def outputApiDumpMapMutexItems(self): |
| maps_mutexes = '' |
| for handle in self.api_handles: |
| base_handle_name = undecorate(handle.name) |
| if handle.protect_value: |
| maps_mutexes += '#if %s\n' % handle.protect_string |
| maps_mutexes += 'std::unordered_map<%s, XrGeneratedDispatchTable*> g_%s_dispatch_map;\n' % ( |
| handle.name, base_handle_name) |
| maps_mutexes += 'std::mutex g_%s_dispatch_mutex;\n' % base_handle_name |
| if handle.protect_value: |
| maps_mutexes += '#endif // %s\n' % handle.protect_string |
| maps_mutexes += '\n' |
| maps_mutexes += '// Template function to reduce duplicating the map locking, searching, and deleting.`\n' |
| maps_mutexes += 'template <typename MapType>\n' |
| maps_mutexes += 'void eraseAllTableMapElements(MapType &search_map, std::mutex &mutex, XrGeneratedDispatchTable *search_value) {\n' |
| maps_mutexes += ' std::unique_lock<std::mutex> lock(mutex);\n' |
| maps_mutexes += ' for (auto it = search_map.begin(); it != search_map.end();) {\n' |
| maps_mutexes += ' if (it->second == search_value) {\n' |
| maps_mutexes += ' search_map.erase(it++);\n' |
| maps_mutexes += ' } else {\n' |
| maps_mutexes += ' ++it;\n' |
| maps_mutexes += ' }\n' |
| maps_mutexes += ' }\n' |
| maps_mutexes += '}\n' |
| maps_mutexes += '\n' |
| maps_mutexes += '// Function used to clean up any residual map values that point to an instance prior to that\n' |
| maps_mutexes += '// instance being deleted.\n' |
| maps_mutexes += 'void ApiDumpCleanUpMapsForTable(XrGeneratedDispatchTable *table) {\n' |
| # Call each handle's erase utility function using the template we defined above. |
| for handle in self.api_handles: |
| base_handle_name = undecorate(handle.name) |
| if handle.protect_value: |
| maps_mutexes += '#if %s\n' % handle.protect_string |
| maps_mutexes += ' eraseAllTableMapElements<std::unordered_map<%s, XrGeneratedDispatchTable*>>' % handle.name |
| maps_mutexes += '(g_%s_dispatch_map, g_%s_dispatch_mutex, table);\n' % ( |
| base_handle_name, base_handle_name) |
| if handle.protect_value: |
| maps_mutexes += '#endif // %s\n' % handle.protect_string |
| maps_mutexes += '}\n' |
| maps_mutexes += '\n' |
| return maps_mutexes |
| |
| # Generate a short version of the parameter name that we can use as a variable. |
| # self the ApiDumpOutputGenerator object |
| # param_name the name of the parameter to parse |
| def generateShortParamVariableName(self, param_name): |
| # Remove any structure dereferences or array brackets to make the |
| # full name of the parameter something we can use as a variable. |
| # Then cut it to no more than 3 word blocks to make sure it's a |
| # unique enough name. |
| short_param_name = param_name |
| short_param_name = short_param_name.replace(".", "_") |
| short_param_name = short_param_name.replace("->", "_") |
| # The short name is no more than everything after the 3rd |
| # underscore from the end of the parameter name. |
| underscore_pos = short_param_name.rfind("_") |
| if underscore_pos > 0: |
| underscore_pos = short_param_name.rfind("_", 0, underscore_pos - 1) |
| if underscore_pos > 0: |
| underscore_pos = short_param_name.rfind( |
| "_", 0, underscore_pos - 1) |
| short_param_name = short_param_name[underscore_pos + 1:] |
| # Replace the array brackets after we get the short name because |
| # we don't want too short of a name |
| short_param_name = short_param_name.replace("[", "_") |
| short_param_name = short_param_name.replace("]", "_") |
| short_param_name = short_param_name.replace("__", "_") |
| return short_param_name |
| |
| # Return the part of a parameter prior to a pointer/structure dereference. |
| # self the ApiDumpOutputGenerator object |
| # param_name the name of the parameter to parse |
| def getParamPrefix(self, param_name): |
| period_pos = param_name.rfind(".") |
| arrow_pos = param_name.rfind(">") |
| param_name_prefix = '' |
| if arrow_pos > period_pos: |
| param_name_prefix = param_name[:arrow_pos + 1] |
| elif period_pos > arrow_pos: |
| param_name_prefix = param_name[:period_pos + 1] |
| return param_name_prefix |
| |
| # We don't know what to do with external graphics handles, so we need |
| # to detect them here. |
| # self the ApiDumpOutputGenerator object |
| # name the name of the type to check |
| def isExternalGraphicsApiHandle(self, name): |
| if (name.startswith('Vk') or name.startswith('GLX') or name.startswith('ID3D') or |
| name.startswith('wl_') or name.startswith('Mir') or name.startswith('xcb_') or |
| name == 'HGLRC' or name == 'HDC'): |
| return True |
| return False |
| |
| # Output a single entry's C++ output code. This will generate the final resulting |
| # entry in the Api Dump content vector which is used to record the data to a file. |
| # self the ApiDumpOutputGenerator object |
| # indent the number of "tabs" to space in for the resulting C+ code. |
| # allow_deref Boolean indicating if we want to allow a dereference |
| # member_param the structure from automatic_source_generator for the member or parameter. |
| # base_type the base type of the parameter being recorded |
| # description a description of the member/parameter |
| # full_name a full name of the parameter in C++ parlance including any structure/union/pointer dereferences |
| # cdecl the C-style declaration for the parameter |
| def outputSingleEntry(self, indent, allow_deref, member_param, base_type, description, full_name, cdecl): |
| |
| # Initialization of internal variables |
| int_short_param_name = '' |
| is_standard_type = False |
| is_char = False |
| use_stream = False |
| pointer_count = member_param.pointer_count |
| is_array = member_param.is_array |
| |
| # If this is an array defined by a pointer, we need to drop the pointer count for it |
| if is_array and not member_param.is_static_array and len(member_param.pointer_count_var) > 0: |
| pointer_count = pointer_count - 1 |
| |
| full_type = cdecl[0:cdecl.rfind(' ')].strip() |
| if member_param.is_static_array: |
| full_type += '*' |
| |
| # Only attempt to dereference an object at this point if it's a const and |
| # not a pointer. |
| # NOTE: Structures/Unions should be handled in other utility calls. This |
| # is only encountered if it can't extract the information through |
| # those if it's a type that could be expanded. |
| can_dereference = member_param.is_const or not pointer_count > 0 |
| if not allow_deref: |
| can_dereference = False |
| |
| # We can't dereference a void, so don't even try. |
| # NOTE: The 'next' chain is actually handled elsewhere. |
| if 'void' == base_type: |
| can_dereference = False |
| |
| # Is this one of the standard types? |
| if ('char' == base_type[0:4].lower() or 'float' == base_type[0:5].lower() or |
| 'double' == base_type[0:6].lower() or 'unsigned ' in base_type.lower() or |
| 'uint' in base_type.lower()): |
| is_standard_type = True |
| # Characters are a special case because we can write them directly out. As long as |
| # they're a character array or pointer. If they're a pointer, just drop one from it |
| # since we don't want to write the pointer, but the contents of it |
| if 'char' == base_type[0:4].lower(): |
| is_char = True |
| if not can_dereference or ((is_array and pointer_count > 0) or pointer_count > 1): |
| use_stream = True |
| elif pointer_count > 0: |
| pointer_count -= 1 |
| else: |
| # If it's a non-character standard type, we want to output the information |
| # using a string stream instead of the standard output path. |
| use_stream = True |
| |
| # If this is a pointer and not a character, or it's a handle, we also want to |
| # use a stream. This is because we can output hex values through a stream. |
| if (self.isHandle(base_type) or self.isExternalGraphicsApiHandle(base_type) or |
| ((is_array or pointer_count > 0) and not is_char)): |
| use_stream = True |
| |
| # Create a short name for use as a variable |
| int_short_param_name = self.generateShortParamVariableName( |
| member_param.name) |
| if is_array and not can_dereference: |
| int_short_param_name += '_array' |
| |
| write_string = '' |
| |
| if base_type == 'GUID': |
| write_string += '{\n' |
| indent = indent + 1 |
| write_string += self.writeIndent(indent) |
| write_string += 'std::ostringstream oss_%s;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::uppercase;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s.width(8);\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::hex << %s.Data1 << \'-\';\n' % (int_short_param_name, full_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s.width(4);\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::hex << %s.Data2 << \'-\';\n' % (int_short_param_name, full_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s.width(4);\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::hex << %s.Data3 << \'-\';\n' % (int_short_param_name, full_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s.width(2);\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::hex\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[0])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[1])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << \'-\'\n' |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[2])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[3])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[4])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[5])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[6])\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += ' << static_cast<short>(%s.Data4[7]);\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::nouppercase;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s' % (full_type, description) |
| write_string += ', oss_%s.str()));\n' % int_short_param_name |
| indent = indent - 1 |
| write_string += self.writeIndent(indent) |
| write_string += '}\n' |
| elif base_type == 'LUID': |
| write_string += self.writeIndent(indent) |
| write_string += '{\n' |
| indent = indent + 1 |
| write_string += self.writeIndent(indent) |
| write_string += 'std::ostringstream oss_%s;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::uppercase;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s.width(8);\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::hex << %s.LowPart;\n' % (int_short_param_name, full_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::hex << %s.HighPart;\n' % (int_short_param_name, full_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << std::nouppercase;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s' % (full_type, description) |
| write_string += ', oss_%s.str()));\n' % int_short_param_name |
| indent = indent - 1 |
| write_string += self.writeIndent(indent) |
| write_string += '}\n' |
| elif base_type == 'LARGE_INTEGER': |
| # Unbeknownst to XR, this is actually a union. Append '.QuadPart' to get the entirety |
| write_string += self.writeIndent(indent) |
| write_string += 'std::ostringstream oss_%s;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'oss_%s << ' % int_short_param_name |
| write_string += 'std::hex << reinterpret_cast<const void*>( (' |
| if pointer_count > 0: # Ignore can_dereference, must deref to access union member |
| write_string += '*' * pointer_count |
| write_string += '%s).QuadPart );\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s' % (full_type, description) |
| write_string += ', oss_%s.str()));\n' % int_short_param_name |
| elif base_type == 'timespec': |
| # Unbeknownst to XR, this is actually a struct. |
| write_string += self.writeIndent(indent) |
| write_string += 'std::ostringstream oss_%s;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| deref = '' + '*' * pointer_count |
| |
| # Write whole seconds |
| write_string += 'oss_%s << (' % int_short_param_name |
| write_string += deref |
| write_string += '%s).tv_sec << ".";\n' % full_name |
| |
| # Write nanoseconds as a decimal |
| write_string += self.writeIndent(indent) |
| write_string += "oss_%s << std::setw(9) << std::setfill('0') << (" % int_short_param_name |
| write_string += deref |
| write_string += '%s).tv_nsec << "s";\n' % full_name |
| |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s' % ( |
| full_type, description) |
| write_string += ', oss_%s.str()));\n' % int_short_param_name |
| else: |
| if base_type == 'XrResult': |
| write_string += self.writeIndent(indent) |
| write_string += 'if (nullptr != gen_dispatch_table) {\n' |
| indent = indent + 1 |
| write_string += self.writeIndent(indent) |
| write_string += 'char %s_string[XR_MAX_RESULT_STRING_SIZE];\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'gen_dispatch_table->ResultToString(FindInstanceFromDispatchTable(gen_dispatch_table),\n' |
| write_string += self.writeIndent(indent) |
| write_string += ' %s, %s_string);\n' % (full_name, int_short_param_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s, %s_string));\n' % (full_type, description, int_short_param_name) |
| write_string += self.writeIndent(indent - 1) |
| write_string += '} else {\n' |
| write_string += self.writeIndent(indent) |
| elif base_type == 'XrStructureType': |
| write_string += self.writeIndent(indent) |
| write_string += 'if (nullptr != gen_dispatch_table) {\n' |
| indent = indent + 1 |
| write_string += self.writeIndent(indent) |
| write_string += 'char %s_string[XR_MAX_STRUCTURE_NAME_SIZE];\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| write_string += 'gen_dispatch_table->StructureTypeToString(FindInstanceFromDispatchTable(gen_dispatch_table),\n' |
| write_string += self.writeIndent(indent) |
| write_string += ' %s, %s_string);\n' % (full_name, int_short_param_name) |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s, %s_string));\n' % (full_type, description, int_short_param_name) |
| write_string += self.writeIndent(indent - 1) |
| write_string += '} else {\n' |
| write_string += self.writeIndent(indent) |
| |
| # If we're outputting using a string stream, determine the type of information |
| # we're generating and format it appropriately. |
| if use_stream: |
| write_string += self.writeIndent(indent) |
| write_string += 'std::ostringstream oss_%s;\n' % int_short_param_name |
| write_string += self.writeIndent(indent) |
| # Output the standard type information if we can, except for characters because the |
| # hex converter will try to use the string value in the char. |
| if is_standard_type and not is_char: |
| if 'float' in base_type: |
| precision = '32' |
| if '64' in base_type: |
| precision = '64' |
| elif '16' in base_type: |
| precision = '16' |
| write_string += 'oss_%s << std::setprecision(%s) << (' % (int_short_param_name, precision) |
| elif 'double' in base_type: |
| write_string += 'oss_%s << std::setprecision(64) << (' % int_short_param_name |
| else: |
| write_string += 'oss_%s << ' % int_short_param_name |
| if member_param.pointer_count == 0: |
| write_string += '"0x" << ' |
| write_string += 'std::hex << (' |
| else: |
| write_string += 'oss_%s << ' % int_short_param_name |
| write_string += 'std::hex << reinterpret_cast<const void*>(' |
| if can_dereference and pointer_count > 0: |
| write_string += '*' * pointer_count |
| write_string += '%s);\n' % full_name |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s' % (full_type, description) |
| write_string += ', oss_%s.str()));\n' % int_short_param_name |
| |
| else: |
| write_string += self.writeIndent(indent) |
| write_string += 'contents.push_back(std::make_tuple("%s", %s, ' % (full_type, description) |
| if not is_char: |
| write_string += 'std::to_string(' |
| if can_dereference: |
| write_string += '*' * pointer_count |
| write_string += '%s' % full_name |
| # Close std::to_string |
| if not is_char: |
| write_string += ')' |
| write_string += '));\n' |
| |
| if base_type == 'XrResult' or base_type == 'XrStructureType': |
| indent = indent - 1 |
| write_string += self.writeIndent(indent) |
| write_string += '}\n' |
| return write_string |
| |
| # Output a single parameter/member. |
| # self The ApiDumpOutputGenerator object |
| # base_type The base type of the parameter |
| # is_pointer Boolean indicating whether or not the contents of the arrays are pointers |
| # pointer_count The number of pointers per variable (void*[] would be one, void**[] would be two) |
| # member_param The structure from automatic_source_generator for the member or parameter. |
| # member_param_prefix The prefix to place in front of the member/param items |
| # member_param_name The prefixed name of this member/param |
| # has_prefix Boolean indicates that there's an incoming C++ prefix that needs to be added to the variable. |
| # prefix_string1 The first prefix string to add prior to writing out the variable information |
| # prefix_string1 The second prefix string to add prior to writing out the variable information |
| # expand Boolean indicates whether or not to try to expand/derefernce the contents of this parameter |
| # indent the number of "tabs" to space in for the resulting C+ code. |
| def writeExpandedMember(self, base_type, is_pointer, pointer_count, member_param, member_param_prefix, member_param_name, has_prefix, prefix_string1, prefix_string2, expand, indent): |
| member_string = '' |
| derefernce_str = '' |
| if not is_pointer: |
| derefernce_str = '&' |
| |
| prefix_string = '' |
| if has_prefix: |
| prefix_string += self.writeIndent(indent) |
| prefix_string += prefix_string1 |
| prefix_string += self.writeIndent(indent) |
| prefix_string += prefix_string2 |
| |
| # If it's a structure or union, we can also only expand it if it's not |
| # return-only |
| # If this is a structure or union, save that info for easier use later |
| is_struct_union = False |
| member_param_struct = self.getStruct(member_param.type) |
| member_param_union = self.getUnion(member_param.type) |
| if member_param_struct or member_param_union: |
| is_struct_union = True |
| |
| # Type for output structure call |
| full_type = member_param.cdecl[0:member_param.cdecl.rfind(' ')].strip() |
| pointer_string = 'false' |
| if is_pointer: |
| pointer_string = 'true' |
| if member_param.name == 'next': |
| member_string += prefix_string |
| member_string += self.writeIndent(indent) |
| member_string += '// Decode the next chain if it exists\n' |
| member_string += self.writeIndent(indent) |
| member_string += 'if (!ApiDumpDecodeNextChain(gen_dispatch_table, %s%s, %s, contents)) {\n' % (derefernce_str, |
| member_param_name, |
| member_param_prefix) |
| member_string += self.writeIndent(indent + 1) |
| member_string += 'throw std::invalid_argument("Invalid Operation");\n' |
| member_string += self.writeIndent(indent) |
| member_string += '}\n' |
| elif is_struct_union and expand: |
| member_string += prefix_string |
| member_string += self.writeIndent(indent) |
| # If it's optional, we still want to dump out NULL if it's present just so it's logged |
| if member_param.is_optional and is_pointer: |
| member_string += 'if (nullptr == %s) {\n' % member_param_name |
| member_string += self.outputSingleEntry(indent + 1, |
| False, |
| member_param, |
| base_type, |
| member_param_prefix, |
| member_param_name, |
| member_param.cdecl) |
| member_string += self.writeIndent(indent) |
| member_string += '} else ' |
| # Otherwise, if it's not NULL, print out the contents |
| if member_param_struct: |
| member_string += 'if (!ApiDumpOutputXrStruct(gen_dispatch_table, ' |
| else: |
| member_string += 'if (!ApiDumpOutputXrUnion(gen_dispatch_table, ' |
| member_string += '%s%s, %s, "%s", %s, contents)) {\n' % (derefernce_str, |
| member_param_name, |
| member_param_prefix, |
| full_type, |
| pointer_string) |
| member_string += self.writeIndent(indent + 1) |
| member_string += 'throw std::invalid_argument("Invalid Operation");\n' |
| member_string += self.writeIndent(indent) |
| member_string += '}\n' |
| else: |
| valid_extension_structs = None |
| if member_param_struct or member_param_union: |
| valid_extension_structs = member_param.valid_extension_structs |
| member_string += prefix_string |
| |
| tmp_member_param = self.MemberOrParam(type=member_param.type, |
| name=member_param.name, |
| is_const=member_param.is_const, |
| is_handle=member_param.is_handle, |
| is_bool=member_param.is_bool, |
| is_optional=member_param.is_optional, |
| no_auto_validity=member_param.no_auto_validity, |
| valid_extension_structs=valid_extension_structs, |
| is_array=member_param.is_array, |
| is_static_array=member_param.is_static_array, |
| static_array_sizes=member_param.static_array_sizes, |
| array_dimen=member_param.array_dimen, |
| array_count_var=member_param.array_count_var, |
| array_length_for=member_param.array_length_for, |
| pointer_count=pointer_count, |
| is_null_terminated=member_param.is_null_terminated, |
| pointer_count_var=member_param.pointer_count_var, |
| cdecl=member_param.cdecl, |
| values=member_param.values) |
| |
| if member_param.is_optional and member_param.is_const and member_param.pointer_count > 0: |
| member_string += self.writeIndent(indent) |
| member_string += 'if (nullptr == %s) {\n' % member_param_name |
| indent += 1 |
| member_string += self.outputSingleEntry(indent, |
| False, |
| tmp_member_param, |
| base_type, |
| member_param_prefix, |
| member_param_name, |
| member_param.cdecl) |
| member_string += self.writeIndent(indent - 1) |
| member_string += '} else {\n' |
| member_string += self.outputSingleEntry(indent, |
| True, |
| tmp_member_param, |
| base_type, |
| member_param_prefix, |
| member_param_name, |
| member_param.cdecl) |
| if member_param.is_optional and member_param.is_const and member_param.pointer_count > 0: |
| indent -= 1 |
| member_string += self.writeIndent(indent) |
| member_string += '}\n' |
| return member_string |
| |
| # Output an array of parameters/members. |
| # self The ApiDumpOutputGenerator object |
| # base_type The base type of the parameter |
| # is_pointer Boolean indicating whether or not the contents of the arrays are pointers |
| # pointer_count The number of pointers per variable (void*[] would be one, void**[] would be two) |
| # member_param The structure from automatic_source_generator for the member or parameter. |
| # array_param The member/parameter used to indicate the size of the array (or None) |
| # member_param_prefix The prefix to place in front of the member/param items |
| # member_param_name The prefixed name of this member/param |
| # has_prefix Boolean indicates that there's an incoming C++ prefix that needs to be added to the variable. |
| # prefix_string1 The first prefix string to add prior to writing out the variable information |
| # prefix_string1 The second prefix string to add prior to writing out the variable information |
| # indent the number of "tabs" to space in for the resulting C+ code. |
| def writeExpandedArray(self, base_type, is_pointer, pointer_count, member_param, array_param, member_param_prefix, member_param_name, has_prefix, prefix_string1, prefix_string2, indent): |
| member_array_string = '' |
| loop_count_name = '' |
| loop_param_name = '' |
| if self.isAllNumbers(array_param) or self.isAllUpperCase(array_param): |
| loop_count_name = array_param |
| else: |
| loop_count_name = '' |
| if has_prefix: |
| loop_count_name = 'value->' |
| loop_count_name += array_param |
| if has_prefix: |
| loop_param_name = 'value_' |
| loop_param_name += member_param.name.lower() |
| loop_param_name += '_inc' |
| member_array_string += self.writeIndent(indent) |
| member_array_string += prefix_string1 |
| prefix_string1 = '' |
| member_array_string += self.writeIndent(indent) |
| member_array_string += prefix_string2 |
| prefix_string2 = '' |
| member_array_string += self.outputSingleEntry(indent, |
| False, |
| member_param, |
| base_type, |
| member_param_prefix, |
| member_param_name, |
| member_param.cdecl) |
| member_array_string += self.writeIndent(indent) |
| member_array_string += 'for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (loop_param_name, |
| loop_param_name, |
| loop_count_name, |
| loop_param_name) |
| indent = indent + 1 |
| |
| # Make sure we set that we need a prefix (even if we didn't need one before) because |
| # of the array looping. |
| paranet_param_prefix = member_param_prefix |
| member_param_prefix = '%s_array_prefix' % member_param.name.lower() |
| prefix_string1 = 'std::string %s = %s;\n' % ( |
| member_param_prefix, paranet_param_prefix) |
| prefix_string2 += '%s += "[";\n' % member_param_prefix |
| prefix_string2 += self.writeIndent(indent) |
| prefix_string2 += '%s += std::to_string(%s);\n' % ( |
| member_param_prefix, loop_param_name) |
| prefix_string2 += self.writeIndent(indent) |
| prefix_string2 += '%s += "]' % member_param_prefix |
| prefix_string2 += '";\n' |
| member_param_name += "[%s]" % loop_param_name |
| array_dimen = member_param.array_dimen - 1 |
| static_array_sizes = [] |
| is_array = member_param.is_array |
| is_static_array = member_param.is_static_array |
| array_count_var = member_param.array_count_var |
| pointer_count_var = member_param.pointer_count_var |
| array_length_for = member_param.array_length_for |
| if array_dimen <= 0: |
| is_array = False |
| is_static_array = False |
| array_count_var = '' |
| pointer_count_var = '' |
| array_length_for = '' |
| else: |
| static_array_sizes = member_param.static_array_sizes[1:] |
| |
| tmp_member_param = self.MemberOrParam(type=member_param.type, |
| name=member_param.name, |
| is_const=member_param.is_const, |
| is_handle=member_param.is_handle, |
| is_bool=member_param.is_bool, |
| is_optional=member_param.is_optional, |
| no_auto_validity=member_param.no_auto_validity, |
| valid_extension_structs=member_param.valid_extension_structs, |
| is_array=is_array, |
| is_static_array=is_static_array, |
| static_array_sizes=static_array_sizes, |
| array_dimen=array_dimen, |
| array_count_var=array_count_var, |
| array_length_for=array_length_for, |
| pointer_count=pointer_count, |
| is_null_terminated=member_param.is_null_terminated, |
| pointer_count_var=pointer_count_var, |
| cdecl=member_param.cdecl, |
| values=member_param.values) |
| |
| member_array_string += self.writeExpandedMember(base_type, is_pointer, pointer_count, tmp_member_param, |
| member_param_prefix, member_param_name, True, prefix_string1, prefix_string2, True, indent) |
| indent = indent - 1 |
| member_array_string += self.writeIndent(indent) |
| member_array_string += '}\n' |
| return member_array_string |
| |
| # Output a single parameter or member based on whether it is an array or not |
| # self the ApiDumpOutputGenerator object |
| # member_param the structure from automatic_source_generator for the member or parameter. |
| # has_prefix Boolean indicates that there's an incoming C++ prefix that needs to be added to the variable. |
| # expand_parent Boolean indicating that the parent could or could not be expanded. |
| # indent the number of "tabs" to space in for the resulting C+ code. |
| def writeParamMember(self, member_param, has_prefix, expand_parent, indent): |
| member_param_string = '' |
| # Can only expand non-pointers or constant pointer values and only if we can |
| # expand the parent |
| can_expand = False |
| if expand_parent: |
| can_expand = member_param.is_const or member_param.pointer_count == 0 |
| |
| # If it's a structure or union, we can also only expand it if it's not |
| # return-only |
| member_param_struct = self.getStruct(member_param.type) |
| member_param_union = self.getUnion(member_param.type) |
| if ((member_param_struct and member_param_struct.returned_only) or |
| (member_param_union and member_param_union.returned_only)): |
| can_expand = False |
| elif member_param.type == 'void' and member_param.name != 'next': |
| can_expand = False |
| |
| is_pointer = False |
| is_array = member_param.is_static_array |
| base_type = self.getRawType(member_param.type) |
| array_param = '' |
| pointer_count = member_param.pointer_count |
| if len(member_param.array_count_var) != 0: |
| array_param = member_param.array_count_var |
| is_array = True |
| if pointer_count > 0: |
| is_pointer = True |
| elif len(member_param.pointer_count_var) != 0: |
| array_param = member_param.pointer_count_var |
| is_array = True |
| pointer_count -= 1 |
| if pointer_count > 0: |
| is_pointer = True |
| elif member_param.is_static_array: |
| array_param = member_param.static_array_sizes[0] |
| elif pointer_count > 0: |
| is_pointer = True |
| # If this is a character array, it's really a string. So, |
| # only treat it as an array if it also still has a pointer |
| # in addition to the array itself. |
| if base_type == 'char' and is_array and not is_pointer: |
| is_array = False |
| |
| member_param_prefix = '' |
| prefix_string1 = '' |
| prefix_string2 = '' |
| member_param_name = member_param.name |
| if has_prefix: |
| member_param_prefix = '%s_prefix' % member_param.name.lower() |
| prefix_string1 = 'std::string %s = prefix;\n' % member_param_prefix |
| prefix_string2 = '%s += "' % member_param_prefix |
| member_param_name = "value->%s" % member_param.name |
| prefix_string2 += '%s";\n' % member_param.name |
| else: |
| member_param_prefix = '"%s"' % member_param.name |
| |
| if can_expand and is_array: |
| is_relation_group = False |
| relation_group = None |
| # We only care about a relation group at this point if it's an array |
| # that isn't a pointer |
| if member_param_struct and pointer_count == 0: |
| # Check to see if this struct is the base of a relation group |
| for cur_rel_group in self.struct_relation_groups: |
| if cur_rel_group.generic_struct_name == member_param_struct.name: |
| relation_group = cur_rel_group |
| is_relation_group = True |
| break |
| # If this struct is the base of a relation group, check to see if this call really should go to any one of |
| # it's children instead of itself. |
| if is_relation_group: |
| member_param_string += self.writeIndent(indent) |
| decoded_var = '%s_decoded' % member_param.name.lower() |
| member_param_string += 'bool %s = false;\n' % decoded_var |
| for child in relation_group.child_struct_names: |
| child_struct = self.getStruct(child) |
| if child_struct.protect_value: |
| member_param_string += '#if %s\n' % child_struct.protect_string |
| member_param_string += self.writeIndent(indent) |
| member_param_string += 'if (%s[0].type == %s) {\n' % ( |
| member_param_name, self.genXrStructureType(child)) |
| member_param_string += self.writeExpandedArray(base_type, is_pointer, pointer_count, member_param, array_param, |
| member_param_prefix, member_param_name, has_prefix, prefix_string1, prefix_string2, indent + 1) |
| member_param_string += self.writeIndent(indent + 1) |
| member_param_string += '%s = true;\n' % decoded_var |
| member_param_string += self.writeIndent(indent) |
| member_param_string += '}\n' |
| if child_struct.protect_value: |
| member_param_string += '#endif // %s\n' % child_struct.protect_string |
| member_param_string += self.writeIndent(indent) |
| member_param_string += 'if (!%s) {\n' % decoded_var |
| indent += 1 |
| member_param_string += self.writeExpandedArray(base_type, is_pointer, pointer_count, member_param, |
| array_param, member_param_prefix, member_param_name, has_prefix, prefix_string1, prefix_string2, indent) |
| if is_relation_group: |
| indent -= 1 |
| member_param_string += self.writeIndent(indent) |
| member_param_string += '}\n' |
| else: |
| member_param_string += self.writeExpandedMember(base_type, is_pointer, pointer_count, member_param, |
| member_param_prefix, member_param_name, has_prefix, prefix_string1, prefix_string2, can_expand, indent) |
| return member_param_string |
| |
| # Generate the C++ output code for each member of a union or structure. |
| # self the ApiDumpOutputGenerator object |
| # union_struct the structure from automatic_source_generator for the XR union or structure. |
| # indent the number of "tabs" to space in for the resulting C+ code. |
| def writeUnionStructMembers(self, union_struct, indent): |
| struct_union_member = '' |
| for member in union_struct.members: |
| struct_union_member += self.writeParamMember( |
| member, True, True, indent) |
| return struct_union_member |
| |
| # Write the C++ Api Dump function for every union and structure we know about. |
| # self the ApiDumpOutputGenerator object |
| def writeApiDumpUnionStructFuncs(self): |
| struct_union_check = '\n// Union/Structure Output Helper functions\n' |
| for xr_union in self.api_unions: |
| if xr_union.protect_value: |
| struct_union_check += '#if %s\n' % xr_union.protect_string |
| struct_union_check += 'bool ApiDumpOutputXrUnion(XrGeneratedDispatchTable* gen_dispatch_table, const %s* value,\n' % xr_union.name |
| struct_union_check += ' std::string prefix, std::string type_string, bool is_pointer,\n' |
| struct_union_check += ' std::vector<std::tuple<std::string, std::string, std::string>> &contents) {\n' |
| struct_union_check += self.writeIndent(1) |
| struct_union_check += 'try {\n' |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += 'contents.push_back(std::make_tuple(type_string, prefix, PointerToHexString(value)));\n' |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += 'if (is_pointer) {\n' |
| struct_union_check += self.writeIndent(3) |
| struct_union_check += 'prefix += "->";\n' |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += '} else {\n' |
| struct_union_check += self.writeIndent(3) |
| struct_union_check += 'prefix += ".";\n' |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += '}\n' |
| struct_union_check += self.writeUnionStructMembers(xr_union, 2) |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += 'return true;\n' |
| struct_union_check += self.writeIndent(1) |
| struct_union_check += '} catch(...) {\n' |
| struct_union_check += self.writeIndent(1) |
| struct_union_check += '}\n' |
| struct_union_check += self.writeIndent(1) |
| struct_union_check += 'return false;\n' |
| struct_union_check += '}\n\n' |
| if xr_union.protect_value: |
| struct_union_check += '#endif // %s\n' % xr_union.protect_string |
| for xr_struct in self.api_structures: |
| if xr_struct.protect_value: |
| struct_union_check += '#if %s\n' % xr_struct.protect_string |
| struct_union_check += 'bool ApiDumpOutputXrStruct(XrGeneratedDispatchTable* gen_dispatch_table, const %s* value,\n' % xr_struct.name |
| struct_union_check += ' std::string prefix, std::string type_string, bool is_pointer,\n' |
| struct_union_check += ' std::vector<std::tuple<std::string, std::string, std::string>> &contents) {\n' |
| indent = 1 |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += 'try {\n' |
| indent = indent + 1 |
| is_relation_group = False |
| relation_group = None |
| # Check to see if this struct is the base of a relation group |
| for cur_rel_group in self.struct_relation_groups: |
| if cur_rel_group.generic_struct_name == xr_struct.name: |
| relation_group = cur_rel_group |
| is_relation_group = True |
| break |
| # If this struct is the base of a relation group, check to see if this call really should go to any one of |
| # it's children instead of itself. |
| if is_relation_group: |
| for child in relation_group.child_struct_names: |
| child_struct = self.getStruct(child) |
| if child_struct.protect_value: |
| struct_union_check += '#if %s\n' % child_struct.protect_string |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += 'if (value->type == %s) {\n' % self.genXrStructureType( |
| child) |
| struct_union_check += self.writeIndent(indent + 1) |
| struct_union_check += 'const %s* new_value = reinterpret_cast<const %s*>(value);\n' % ( |
| child, child) |
| struct_union_check += self.writeIndent(indent + 1) |
| struct_union_check += 'return ApiDumpOutputXrStruct(gen_dispatch_table, new_value, prefix, type_string, is_pointer, contents);\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += '}\n' |
| if child_struct.protect_value: |
| struct_union_check += '#endif // %s\n' % child_struct.protect_string |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += '// Fallback path - Just output generic information about the base struct\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += 'contents.push_back(std::make_tuple(type_string, prefix, PointerToHexString(value)));\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += 'if (is_pointer) {\n' |
| struct_union_check += self.writeIndent(indent + 1) |
| struct_union_check += 'prefix += "->";\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += '} else {\n' |
| struct_union_check += self.writeIndent(indent + 1) |
| struct_union_check += 'prefix += ".";\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += '}\n' |
| struct_union_check += self.writeUnionStructMembers( |
| xr_struct, indent) |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += 'return true;\n' |
| indent = indent - 1 |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += '} catch(...) {\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += '}\n' |
| struct_union_check += self.writeIndent(indent) |
| struct_union_check += 'return false;\n' |
| struct_union_check += '}\n' |
| if xr_struct.protect_value: |
| struct_union_check += '#endif // %s\n' % xr_struct.protect_string |
| struct_union_check += '\n' |
| struct_union_check += 'bool ApiDumpDecodeNextChain(XrGeneratedDispatchTable* gen_dispatch_table, const void* value, std::string prefix,\n' |
| struct_union_check += ' std::vector<std::tuple<std::string, std::string, std::string>> &contents) {\n' |
| struct_union_check += ' try {\n' |
| struct_union_check += ' contents.push_back(std::make_tuple("const void *", prefix, PointerToHexString(value)));\n' |
| struct_union_check += ' if (nullptr == value) {\n' |
| struct_union_check += ' return true;\n' |
| struct_union_check += ' }\n' |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += 'const XrBaseInStructure* next_header = reinterpret_cast<const XrBaseInStructure*>(value);\n' |
| # Validate the rest of this struct |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += 'switch (next_header->type) {\n' |
| for enum_tuple in self.api_enums: |
| if enum_tuple.name == 'XrStructureType': |
| if enum_tuple.protect_value: |
| struct_union_check += '#if %s\n' % enum_tuple.protect_string |
| for cur_value in enum_tuple.values: |
| struct_define_name = self.genXrStructureName( |
| cur_value.name) |
| if len(struct_define_name) > 0: |
| cur_struct = self.getStruct(struct_define_name) |
| if cur_struct.protect_value: |
| struct_union_check += '#if %s\n' % cur_struct.protect_string |
| struct_union_check += self.writeIndent(3) |
| struct_union_check += 'case %s:\n' % cur_value.name |
| struct_union_check += self.writeIndent(4) |
| struct_union_check += 'if (!ApiDumpOutputXrStruct(gen_dispatch_table, reinterpret_cast<const %s*>(value), prefix, "const %s*", true, contents)) {\n' % ( |
| struct_define_name, struct_define_name) |
| struct_union_check += self.writeIndent(5) |
| struct_union_check += 'return false;\n' |
| struct_union_check += self.writeIndent(4) |
| struct_union_check += '}\n' |
| struct_union_check += self.writeIndent(4) |
| struct_union_check += 'return true;\n' |
| if cur_struct.protect_value: |
| struct_union_check += '#endif // %s\n' % cur_struct.protect_string |
| if enum_tuple.protect_value: |
| struct_union_check += '#endif // %s\n' % enum_tuple.protect_string |
| struct_union_check += self.writeIndent(3) |
| struct_union_check += 'default:\n' |
| struct_union_check += self.writeIndent(4) |
| struct_union_check += 'return false;\n' |
| struct_union_check += self.writeIndent(2) |
| struct_union_check += '}\n' |
| struct_union_check += ' } catch(...) {\n' |
| struct_union_check += ' }\n' |
| struct_union_check += ' return false;\n' |
| struct_union_check += '}\n\n' |
| return struct_union_check |
| |
| # Write the C++ Api Dump function for every command we know about |
| # self the ApiDumpOutputGenerator object |
| def outputLayerCommands(self): |
| cur_extension_name = '' |
| generated_commands = '\n// Automatically generated api_dump layer commands\n' |
| for x in range(0, 2): |
| if x == 0: |
| commands = self.core_commands |
| else: |
| commands = self.ext_commands |
| |
| for cur_cmd in commands: |
| if cur_cmd.ext_name != cur_extension_name: |
| if self.isCoreExtensionName(cur_cmd.ext_name): |
| generated_commands += '\n// ---- Core %s commands\n' % cur_cmd.ext_name[11:].replace( |
| "_", ".") |
| else: |
| generated_commands += '\n// ---- %s extension commands\n' % cur_cmd.ext_name |
| cur_extension_name = cur_cmd.ext_name |
| |
| if cur_cmd.name in DONT_GEN_IN_LAYER or cur_cmd.name in MANUALLY_DEFINED_IN_LAYER: |
| continue |
| |
| # We fill in the GetInstanceProcAddr manually at the end |
| if cur_cmd.name == 'xrGetInstanceProcAddr': |
| continue |
| |
| is_create = False |
| is_destroy = False |
| has_return = False |
| |
| if ('xrCreate' in cur_cmd.name or 'xrConnect' in cur_cmd.name) and cur_cmd.params[-1].is_handle: |
| is_create = True |
| has_return = True |
| elif ('xrDestroy' in cur_cmd.name or 'xrDisconnect' in cur_cmd.name) and cur_cmd.params[-1].is_handle: |
| is_destroy = True |
| has_return = True |
| elif cur_cmd.return_type is not None: |
| has_return = True |
| |
| base_name = cur_cmd.name[2:] |
| |
| if cur_cmd.protect_value: |
| generated_commands += '#if %s\n' % cur_cmd.protect_string |
| |
| prototype = cur_cmd.cdecl.replace(" xr", " ApiDumpLayerXr") |
| prototype = prototype.replace("XRAPI_ATTR ", "") |
| prototype = prototype.replace(" XRAPI_CALL ", " ") |
| prototype = prototype.replace(";", " {\n") |
| generated_commands += prototype |
| |
| if has_return: |
| return_prefix = ' ' |
| return_prefix += cur_cmd.return_type.text |
| return_prefix += ' result' |
| if cur_cmd.return_type.text == 'XrResult': |
| return_prefix += ' = XR_SUCCESS;\n' |
| else: |
| return_prefix += ';\n' |
| generated_commands += return_prefix |
| |
| generated_commands += ' try {\n' |
| generated_commands += ' // Generate output for this command\n' |
| generated_commands += ' std::vector<std::tuple<std::string, std::string, std::string>> contents;\n' |
| |
| # Next, we have to call down to the next implementation of this command in the call chain. |
| # Before we can do that, we have to figure out what the dispatch table is |
| if cur_cmd.params[0].is_handle: |
| handle_param = cur_cmd.params[0] |
| base_handle_name = undecorate(handle_param.type) |
| first_handle_name = self.getFirstHandleName(handle_param) |
| generated_commands += ' std::unique_lock<std::mutex> mlock(g_%s_dispatch_mutex);\n' % base_handle_name |
| generated_commands += ' auto map_iter = g_%s_dispatch_map.find(%s);\n' % (base_handle_name, first_handle_name) |
| generated_commands += ' mlock.unlock();\n\n' |
| generated_commands += ' if (map_iter == g_%s_dispatch_map.end()) return XR_ERROR_VALIDATION_FAILURE;\n' % base_handle_name |
| generated_commands += ' XrGeneratedDispatchTable *gen_dispatch_table = map_iter->second;\n' |
| else: |
| generated_commands += self.printCodeGenErrorMessage( |
| 'Command %s does not have an OpenXR Object handle as the first parameter.' % cur_cmd.name) |
| |
| # Print out a tuple for the header |
| if has_return: |
| generated_commands += ' contents.push_back(std::make_tuple("%s", "%s", ""));\n' % ( |
| cur_cmd.return_type.text, cur_cmd.name) |
| else: |
| generated_commands += ' contents.push_back(std::make_tuple("void", "%s", ""));\n' % cur_cmd.name |
| # Print out information for each parameter |
| for param in cur_cmd.params: |
| can_expand = False |
| # TODO handle array of handles here? |
| if ((self.isStruct(param.type) or self.isUnion(param.type)) and |
| (param.is_const or param.pointer_count == 0)): |
| can_expand = True |
| generated_commands += self.writeParamMember( |
| param, False, can_expand, 2) |
| |
| # Now record the information |
| generated_commands += ' ApiDumpLayerRecordContent(contents);\n\n' |
| |
| # Call down, looking for the returned result if required. |
| generated_commands += ' ' |
| if has_return: |
| generated_commands += 'result = ' |
| generated_commands += 'gen_dispatch_table->%s(' % base_name |
| |
| count = 0 |
| for param in cur_cmd.params: |
| if count > 0: |
| generated_commands += ', ' |
| generated_commands += param.name |
| count = count + 1 |
| generated_commands += ');\n' |
| |
| # If this is a create command, we have to create an entry in the appropriate |
| # unordered_map pointing to the correct dispatch table for the newly created |
| # object. Likewise, if it's a delete command, we have to remove the entry |
| # for the dispatch table from the unordered_map |
| second_base_handle_name = '' |
| if cur_cmd.params[-1].is_handle and (is_create or is_destroy): |
| second_base_handle_name = undecorate(cur_cmd.params[-1].type) |
| if is_create: |
| generated_commands += ' if (XR_SUCCESS == result && nullptr != %s) {\n' % cur_cmd.params[-1].name |
| generated_commands += ' auto exists = g_%s_dispatch_map.find(*%s);\n' % ( |
| second_base_handle_name, cur_cmd.params[-1].name) |
| generated_commands += ' if (exists == g_%s_dispatch_map.end()) {\n' % second_base_handle_name |
| generated_commands += ' std::unique_lock<std::mutex> lock(g_%s_dispatch_mutex);\n' % second_base_handle_name |
| generated_commands += ' g_%s_dispatch_map[*%s] = gen_dispatch_table;\n' % ( |
| second_base_handle_name, cur_cmd.params[-1].name) |
| generated_commands += ' }\n' |
| generated_commands += ' }\n' |
| elif is_destroy: |
| generated_commands += ' auto exists = g_%s_dispatch_map.find(%s);\n' % ( |
| second_base_handle_name, cur_cmd.params[-1].name) |
| generated_commands += ' if (exists != g_%s_dispatch_map.end()) {\n' % second_base_handle_name |
| generated_commands += ' std::unique_lock<std::mutex> lock(g_%s_dispatch_mutex);\n' % second_base_handle_name |
| generated_commands += ' g_%s_dispatch_map.erase(%s);\n' % ( |
| second_base_handle_name, cur_cmd.params[-1].name) |
| generated_commands += ' }\n' |
| |
| # Catch any exceptions that may have occurred. If any occurred between any of the |
| # valid mutex lock/unlock statements, perform the unlock now. |
| generated_commands += ' } catch (...) {\n' |
| if has_return: |
| generated_commands += ' return XR_ERROR_VALIDATION_FAILURE;\n' |
| generated_commands += ' }\n' |
| |
| if has_return: |
| generated_commands += ' return result;\n' |
| |
| generated_commands += '}\n\n' |
| if cur_cmd.protect_value: |
| generated_commands += '#endif // %s\n' % cur_cmd.protect_string |
| |
| # Output the xrGetInstanceProcAddr command for the API Dump layer. |
| generated_commands += '\n// Layer\'s xrGetInstanceProcAddr\n' |
| generated_commands += 'XrResult ApiDumpLayerXrGetInstanceProcAddr(\n' |
| generated_commands += ' XrInstance instance,\n' |
| generated_commands += ' const char* name,\n' |
| generated_commands += ' PFN_xrVoidFunction* function) {\n' |
| generated_commands += ' try {\n' |
| generated_commands += ' std::string func_name = name;\n\n' |
| generated_commands += ' // Generate output for this command\n' |
| generated_commands += ' std::vector<std::tuple<std::string, std::string, std::string>> contents;\n' |
| generated_commands += ' contents.push_back(std::make_tuple("XrResult", "xrGetInstanceProcAddr", ""));\n' |
| generated_commands += ' contents.push_back(std::make_tuple("XrInstance", "instance", HandleToHexString(instance)));\n' |
| generated_commands += ' contents.push_back(std::make_tuple("const char*", "name", name));\n' |
| generated_commands += ' contents.push_back(std::make_tuple("PFN_xrVoidFunction*", "function", PointerToHexString(reinterpret_cast<const void*>(function))));\n' |
| generated_commands += ' ApiDumpLayerRecordContent(contents);\n' |
| |
| count = 0 |
| for x in range(0, 2): |
| if x == 0: |
| commands = self.core_commands |
| else: |
| commands = self.ext_commands |
| |
| for cur_cmd in commands: |
| if cur_cmd.ext_name != cur_extension_name: |
| if self.isCoreExtensionName(cur_cmd.ext_name): |
| generated_commands += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:].replace( |
| "_", ".") |
| else: |
| generated_commands += '\n // ---- %s extension commands\n' % cur_cmd.ext_name |
| cur_extension_name = cur_cmd.ext_name |
| |
| if cur_cmd.name in DONT_GEN_IN_LAYER: |
| continue |
| |
| has_return = False |
| if cur_cmd.return_type is not None: |
| has_return = True |
| |
| # Replace 'xr' in proto name with an API Dump-specific name to avoid collisions.s |
| layer_command_name = cur_cmd.name.replace( |
| "xr", "ApiDumpLayerXr") |
| |
| if cur_cmd.protect_value: |
| generated_commands += '#if %s\n' % cur_cmd.protect_string |
| |
| if count == 0: |
| generated_commands += ' if (func_name == "%s") {\n' % cur_cmd.name |
| else: |
| generated_commands += ' } else if (func_name == "%s") {\n' % cur_cmd.name |
| count = count + 1 |
| |
| generated_commands += ' *function = reinterpret_cast<PFN_xrVoidFunction>(%s);\n' % layer_command_name |
| if cur_cmd.protect_value: |
| generated_commands += '#endif // %s\n' % cur_cmd.protect_string |
| |
| generated_commands += ' }\n' |
| generated_commands += ' // If we setup the function, just return\n' |
| generated_commands += ' if (*function != nullptr) {\n' |
| generated_commands += ' return XR_SUCCESS;\n' |
| generated_commands += ' }\n\n' |
| generated_commands += ' // We have not found it, so pass it down to the next layer/runtime\n' |
| generated_commands += ' std::unique_lock<std::mutex> mlock(g_instance_dispatch_mutex);\n' |
| generated_commands += ' auto map_iter = g_instance_dispatch_map.find(instance);\n' |
| generated_commands += ' mlock.unlock();\n\n' |
| generated_commands += ' if (map_iter == g_instance_dispatch_map.end()) {\n' |
| generated_commands += ' return XR_ERROR_HANDLE_INVALID;\n' |
| generated_commands += ' }\n\n' |
| generated_commands += ' XrGeneratedDispatchTable *gen_dispatch_table = map_iter->second;\n' |
| generated_commands += ' if (nullptr == gen_dispatch_table) {\n' |
| generated_commands += ' return XR_ERROR_HANDLE_INVALID;\n' |
| generated_commands += ' }\n\n' |
| generated_commands += ' return gen_dispatch_table->GetInstanceProcAddr(instance, name, function);\n' |
| generated_commands += ' } catch (...) {\n' |
| generated_commands += ' return XR_ERROR_VALIDATION_FAILURE;\n' |
| generated_commands += ' }\n' |
| generated_commands += '}\n' |
| return generated_commands |