| 1 | // Copyright (c) 2017-2023, The Khronos Group Inc. |
| 2 | // Copyright (c) 2017-2019 Valve Corporation |
| 3 | // Copyright (c) 2017-2019 LunarG, Inc. |
| 4 | // |
| 5 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
| 6 | // |
| 7 | // Initial Author: Mark Young <marky@lunarg.com> |
| 8 | // |
| 9 | |
| 10 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 11 | #define _CRT_SECURE_NO_WARNINGS |
| 12 | #endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 13 | |
| 14 | #include "loader_instance.hpp" |
| 15 | |
| 16 | #include "api_layer_interface.hpp" |
| 17 | #include "hex_and_handles.h" |
| 18 | #include "loader_interfaces.h" |
| 19 | #include "loader_logger.hpp" |
| 20 | #include "runtime_interface.hpp" |
| 21 | #include "xr_generated_dispatch_table.h" |
| 22 | #include "xr_generated_loader.hpp" |
| 23 | |
| 24 | #include <openxr/openxr.h> |
| 25 | |
| 26 | #include <cstring> |
| 27 | #include <memory> |
| 28 | #include <sstream> |
| 29 | #include <string> |
| 30 | #include <utility> |
| 31 | #include <vector> |
| 32 | |
| 33 | namespace { |
| 34 | std::unique_ptr<LoaderInstance>& GetSetCurrentLoaderInstance() { |
| 35 | static std::unique_ptr<LoaderInstance> current_loader_instance; |
| 36 | return current_loader_instance; |
| 37 | } |
| 38 | } // namespace |
| 39 | |
| 40 | namespace ActiveLoaderInstance { |
| 41 | XrResult Set(std::unique_ptr<LoaderInstance> loader_instance, const char* log_function_name) { |
| 42 | if (GetSetCurrentLoaderInstance() != nullptr) { |
| 43 | LoaderLogger::LogErrorMessage(log_function_name, "Active XrInstance handle already exists" ); |
| 44 | return XR_ERROR_LIMIT_REACHED; |
| 45 | } |
| 46 | |
| 47 | GetSetCurrentLoaderInstance() = std::move(loader_instance); |
| 48 | return XR_SUCCESS; |
| 49 | } |
| 50 | |
| 51 | XrResult Get(LoaderInstance** loader_instance, const char* log_function_name) { |
| 52 | *loader_instance = GetSetCurrentLoaderInstance().get(); |
| 53 | if (*loader_instance == nullptr) { |
| 54 | LoaderLogger::LogErrorMessage(log_function_name, "No active XrInstance handle." ); |
| 55 | return XR_ERROR_HANDLE_INVALID; |
| 56 | } |
| 57 | |
| 58 | return XR_SUCCESS; |
| 59 | } |
| 60 | |
| 61 | bool IsAvailable() { return GetSetCurrentLoaderInstance() != nullptr; } |
| 62 | |
| 63 | void Remove() { GetSetCurrentLoaderInstance().release(); } |
| 64 | } // namespace ActiveLoaderInstance |
| 65 | |
| 66 | // Extensions that are supported by the loader, but may not be supported |
| 67 | // the the runtime. |
| 68 | const std::array<XrExtensionProperties, 1>& LoaderInstance::LoaderSpecificExtensions() { |
| 69 | static const std::array<XrExtensionProperties, 1> extensions = {XrExtensionProperties{ |
| 70 | XR_TYPE_EXTENSION_PROPERTIES, nullptr, XR_EXT_DEBUG_UTILS_EXTENSION_NAME, XR_EXT_debug_utils_SPEC_VERSION}}; |
| 71 | return extensions; |
| 72 | } |
| 73 | |
| 74 | namespace { |
| 75 | class InstanceCreateInfoManager { |
| 76 | public: |
| 77 | explicit InstanceCreateInfoManager(const XrInstanceCreateInfo* info) : original_create_info(info), modified_create_info(*info) { |
| 78 | Reset(); |
| 79 | } |
| 80 | |
| 81 | // Reset the "modified" state to match the original state. |
| 82 | void Reset() { |
| 83 | enabled_extensions_cstr.clear(); |
| 84 | enabled_extensions_cstr.reserve(original_create_info->enabledExtensionCount); |
| 85 | |
| 86 | for (uint32_t i = 0; i < original_create_info->enabledExtensionCount; ++i) { |
| 87 | enabled_extensions_cstr.push_back(original_create_info->enabledExtensionNames[i]); |
| 88 | } |
| 89 | Update(); |
| 90 | } |
| 91 | |
| 92 | // Remove extensions named in the parameter and return a pointer to the current state. |
| 93 | const XrInstanceCreateInfo* FilterOutExtensions(const std::vector<const char*>& extensions_to_skip) { |
| 94 | if (enabled_extensions_cstr.empty()) { |
| 95 | return Get(); |
| 96 | } |
| 97 | if (extensions_to_skip.empty()) { |
| 98 | return Get(); |
| 99 | } |
| 100 | for (auto& ext : extensions_to_skip) { |
| 101 | FilterOutExtension(ext); |
| 102 | } |
| 103 | return Update(); |
| 104 | } |
| 105 | // Remove the extension named in the parameter and return a pointer to the current state. |
| 106 | const XrInstanceCreateInfo* FilterOutExtension(const char* extension_to_skip) { |
| 107 | if (enabled_extensions_cstr.empty()) { |
| 108 | return &modified_create_info; |
| 109 | } |
| 110 | auto b = enabled_extensions_cstr.begin(); |
| 111 | auto e = enabled_extensions_cstr.end(); |
| 112 | auto it = std::find_if(b, e, [&](const char* extension) { return strcmp(extension_to_skip, extension) == 0; }); |
| 113 | if (it != e) { |
| 114 | // Just that one element goes away |
| 115 | enabled_extensions_cstr.erase(it); |
| 116 | } |
| 117 | return Update(); |
| 118 | } |
| 119 | |
| 120 | // Get the current modified XrInstanceCreateInfo |
| 121 | const XrInstanceCreateInfo* Get() const { return &modified_create_info; } |
| 122 | |
| 123 | private: |
| 124 | const XrInstanceCreateInfo* Update() { |
| 125 | modified_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions_cstr.size()); |
| 126 | modified_create_info.enabledExtensionNames = enabled_extensions_cstr.empty() ? nullptr : enabled_extensions_cstr.data(); |
| 127 | return &modified_create_info; |
| 128 | } |
| 129 | const XrInstanceCreateInfo* original_create_info; |
| 130 | |
| 131 | XrInstanceCreateInfo modified_create_info; |
| 132 | std::vector<const char*> enabled_extensions_cstr; |
| 133 | }; |
| 134 | } // namespace |
| 135 | |
| 136 | // Factory method |
| 137 | XrResult LoaderInstance::CreateInstance(PFN_xrGetInstanceProcAddr get_instance_proc_addr_term, |
| 138 | PFN_xrCreateInstance create_instance_term, |
| 139 | PFN_xrCreateApiLayerInstance create_api_layer_instance_term, |
| 140 | std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces, |
| 141 | const XrInstanceCreateInfo* info, std::unique_ptr<LoaderInstance>* loader_instance) { |
| 142 | LoaderLogger::LogVerboseMessage("xrCreateInstance" , "Entering LoaderInstance::CreateInstance" ); |
| 143 | |
| 144 | // Check the list of enabled extensions to make sure something supports them, and, if we do, |
| 145 | // add it to the list of enabled extensions |
| 146 | XrResult last_error = XR_SUCCESS; |
| 147 | for (uint32_t ext = 0; ext < info->enabledExtensionCount; ++ext) { |
| 148 | bool found = false; |
| 149 | // First check the runtime |
| 150 | if (RuntimeInterface::GetRuntime().SupportsExtension(info->enabledExtensionNames[ext])) { |
| 151 | found = true; |
| 152 | } |
| 153 | // Next check the loader |
| 154 | if (!found) { |
| 155 | for (auto& loader_extension : LoaderInstance::LoaderSpecificExtensions()) { |
| 156 | if (strcmp(loader_extension.extensionName, info->enabledExtensionNames[ext]) == 0) { |
| 157 | found = true; |
| 158 | break; |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | // Finally, check the enabled layers |
| 163 | if (!found) { |
| 164 | for (auto& layer_interface : api_layer_interfaces) { |
| 165 | if (layer_interface->SupportsExtension(info->enabledExtensionNames[ext])) { |
| 166 | found = true; |
| 167 | break; |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | if (!found) { |
| 172 | std::string msg = "LoaderInstance::CreateInstance, no support found for requested extension: " ; |
| 173 | msg += info->enabledExtensionNames[ext]; |
| 174 | LoaderLogger::LogErrorMessage("xrCreateInstance" , msg); |
| 175 | last_error = XR_ERROR_EXTENSION_NOT_PRESENT; |
| 176 | break; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | // Topmost means "closest to the application" |
| 181 | PFN_xrGetInstanceProcAddr topmost_gipa = get_instance_proc_addr_term; |
| 182 | XrInstance instance{XR_NULL_HANDLE}; |
| 183 | |
| 184 | if (XR_SUCCEEDED(last_error)) { |
| 185 | // Remove the loader-supported-extensions (debug utils), if it's in the list of enabled extensions but not supported by |
| 186 | // the runtime. |
| 187 | InstanceCreateInfoManager create_info_manager{info}; |
| 188 | const XrInstanceCreateInfo* modified_create_info = info; |
| 189 | if (info->enabledExtensionCount > 0) { |
| 190 | std::vector<const char*> extensions_to_skip; |
| 191 | for (const auto& ext : LoaderInstance::LoaderSpecificExtensions()) { |
| 192 | if (!RuntimeInterface::GetRuntime().SupportsExtension(ext.extensionName)) { |
| 193 | extensions_to_skip.emplace_back(ext.extensionName); |
| 194 | } |
| 195 | } |
| 196 | modified_create_info = create_info_manager.FilterOutExtensions(extensions_to_skip); |
| 197 | } |
| 198 | |
| 199 | // Only start the xrCreateApiLayerInstance stack if we have layers. |
| 200 | if (!api_layer_interfaces.empty()) { |
| 201 | // Initialize an array of ApiLayerNextInfo structs |
| 202 | std::unique_ptr<XrApiLayerNextInfo[]> next_info_list(new XrApiLayerNextInfo[api_layer_interfaces.size()]); |
| 203 | size_t ni_index = api_layer_interfaces.size() - 1; |
| 204 | for (size_t i = 0; i <= ni_index; i++) { |
| 205 | next_info_list[i].structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO; |
| 206 | next_info_list[i].structVersion = XR_API_LAYER_NEXT_INFO_STRUCT_VERSION; |
| 207 | next_info_list[i].structSize = sizeof(XrApiLayerNextInfo); |
| 208 | } |
| 209 | |
| 210 | // Go through all layers, and override the instance pointers with the layer version. However, |
| 211 | // go backwards through the layer list so we replace in reverse order so the layers can call their next function |
| 212 | // appropriately. |
| 213 | PFN_xrCreateApiLayerInstance topmost_cali_fp = create_api_layer_instance_term; |
| 214 | XrApiLayerNextInfo* topmost_nextinfo = nullptr; |
| 215 | for (auto layer_interface = api_layer_interfaces.rbegin(); layer_interface != api_layer_interfaces.rend(); |
| 216 | ++layer_interface) { |
| 217 | // Collect current layer's function pointers |
| 218 | PFN_xrGetInstanceProcAddr cur_gipa_fp = (*layer_interface)->GetInstanceProcAddrFuncPointer(); |
| 219 | PFN_xrCreateApiLayerInstance cur_cali_fp = (*layer_interface)->GetCreateApiLayerInstanceFuncPointer(); |
| 220 | |
| 221 | // Fill in layer info and link previous (lower) layer fxn pointers |
| 222 | strncpy(next_info_list[ni_index].layerName, (*layer_interface)->LayerName().c_str(), |
| 223 | XR_MAX_API_LAYER_NAME_SIZE - 1); |
| 224 | next_info_list[ni_index].layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0'; |
| 225 | next_info_list[ni_index].next = topmost_nextinfo; |
| 226 | next_info_list[ni_index].nextGetInstanceProcAddr = topmost_gipa; |
| 227 | next_info_list[ni_index].nextCreateApiLayerInstance = topmost_cali_fp; |
| 228 | |
| 229 | // Update saved pointers for next iteration |
| 230 | topmost_nextinfo = &next_info_list[ni_index]; |
| 231 | topmost_gipa = cur_gipa_fp; |
| 232 | topmost_cali_fp = cur_cali_fp; |
| 233 | ni_index--; |
| 234 | } |
| 235 | |
| 236 | // Populate the ApiLayerCreateInfo struct and pass to topmost CreateApiLayerInstance() |
| 237 | XrApiLayerCreateInfo api_layer_ci = {}; |
| 238 | api_layer_ci.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO; |
| 239 | api_layer_ci.structVersion = XR_API_LAYER_CREATE_INFO_STRUCT_VERSION; |
| 240 | api_layer_ci.structSize = sizeof(XrApiLayerCreateInfo); |
| 241 | api_layer_ci.loaderInstance = nullptr; // Not used. |
| 242 | api_layer_ci.settings_file_location[0] = '\0'; |
| 243 | api_layer_ci.nextInfo = next_info_list.get(); |
| 244 | //! @todo do we filter our create info extension list here? |
| 245 | //! Think that actually each layer might need to filter... |
| 246 | last_error = topmost_cali_fp(modified_create_info, &api_layer_ci, &instance); |
| 247 | |
| 248 | } else { |
| 249 | // The loader's terminator is the topmost CreateInstance if there are no layers. |
| 250 | last_error = create_instance_term(modified_create_info, &instance); |
| 251 | } |
| 252 | |
| 253 | if (XR_FAILED(last_error)) { |
| 254 | LoaderLogger::LogErrorMessage("xrCreateInstance" , "LoaderInstance::CreateInstance chained CreateInstance call failed" ); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | if (XR_SUCCEEDED(last_error)) { |
| 259 | loader_instance->reset(new LoaderInstance(instance, info, topmost_gipa, std::move(api_layer_interfaces))); |
| 260 | |
| 261 | std::ostringstream oss; |
| 262 | oss << "LoaderInstance::CreateInstance succeeded with " ; |
| 263 | oss << (*loader_instance)->LayerInterfaces().size(); |
| 264 | oss << " layers enabled and runtime interface - created instance = " ; |
| 265 | oss << HandleToHexString((*loader_instance)->GetInstanceHandle()); |
| 266 | LoaderLogger::LogInfoMessage("xrCreateInstance" , oss.str()); |
| 267 | } |
| 268 | |
| 269 | return last_error; |
| 270 | } |
| 271 | |
| 272 | XrResult LoaderInstance::GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) { |
| 273 | return _topmost_gipa(_runtime_instance, name, function); |
| 274 | } |
| 275 | |
| 276 | LoaderInstance::LoaderInstance(XrInstance instance, const XrInstanceCreateInfo* create_info, PFN_xrGetInstanceProcAddr topmost_gipa, |
| 277 | std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces) |
| 278 | : _runtime_instance(instance), |
| 279 | _topmost_gipa(topmost_gipa), |
| 280 | _api_layer_interfaces(std::move(api_layer_interfaces)), |
| 281 | _dispatch_table(new XrGeneratedDispatchTable{}) { |
| 282 | for (uint32_t ext = 0; ext < create_info->enabledExtensionCount; ++ext) { |
| 283 | _enabled_extensions.push_back(create_info->enabledExtensionNames[ext]); |
| 284 | } |
| 285 | |
| 286 | GeneratedXrPopulateDispatchTable(_dispatch_table.get(), instance, topmost_gipa); |
| 287 | } |
| 288 | |
| 289 | LoaderInstance::~LoaderInstance() { |
| 290 | std::ostringstream oss; |
| 291 | oss << "Destroying LoaderInstance = " ; |
| 292 | oss << PointerToHexString(this); |
| 293 | LoaderLogger::LogInfoMessage("xrDestroyInstance" , oss.str()); |
| 294 | } |
| 295 | |
| 296 | bool LoaderInstance::ExtensionIsEnabled(const std::string& extension) { |
| 297 | for (std::string& cur_enabled : _enabled_extensions) { |
| 298 | if (cur_enabled == extension) { |
| 299 | return true; |
| 300 | } |
| 301 | } |
| 302 | return false; |
| 303 | } |
| 304 | |