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 | #include "api_layer_interface.hpp" |
11 | |
12 | #include "loader_interfaces.h" |
13 | #include "loader_logger.hpp" |
14 | #include "loader_platform.hpp" |
15 | #include "manifest_file.hpp" |
16 | #include "platform_utils.hpp" |
17 | |
18 | #include <openxr/openxr.h> |
19 | |
20 | #include <cstring> |
21 | #include <memory> |
22 | #include <sstream> |
23 | #include <string> |
24 | #include <utility> |
25 | #include <vector> |
26 | |
27 | #define OPENXR_ENABLE_LAYERS_ENV_VAR "XR_ENABLE_API_LAYERS" |
28 | |
29 | // Add any layers defined in the loader layer environment variable. |
30 | static void AddEnvironmentApiLayers(std::vector<std::string>& enabled_layers) { |
31 | std::string layers = PlatformUtilsGetEnv(OPENXR_ENABLE_LAYERS_ENV_VAR); |
32 | |
33 | std::size_t last_found = 0; |
34 | std::size_t found = layers.find_first_of(PATH_SEPARATOR); |
35 | std::string cur_search; |
36 | |
37 | // Handle any path listings in the string (separated by the appropriate path separator) |
38 | while (found != std::string::npos) { |
39 | cur_search = layers.substr(last_found, found - last_found); |
40 | enabled_layers.push_back(cur_search); |
41 | last_found = found + 1; |
42 | found = layers.find_first_of(PATH_SEPARATOR, last_found); |
43 | } |
44 | |
45 | // If there's something remaining in the string, copy it over |
46 | if (last_found < layers.size()) { |
47 | cur_search = layers.substr(last_found); |
48 | enabled_layers.push_back(cur_search); |
49 | } |
50 | } |
51 | |
52 | XrResult ApiLayerInterface::GetApiLayerProperties(const std::string& openxr_command, uint32_t incoming_count, |
53 | uint32_t* outgoing_count, XrApiLayerProperties* api_layer_properties) { |
54 | std::vector<std::unique_ptr<ApiLayerManifestFile>> manifest_files; |
55 | uint32_t manifest_count = 0; |
56 | // Validate props struct before proceeding |
57 | if (0 < incoming_count && nullptr != api_layer_properties) { |
58 | for (uint32_t i = 0; i < incoming_count; i++) { |
59 | if (XR_TYPE_API_LAYER_PROPERTIES != api_layer_properties[i].type) { |
60 | LoaderLogger::LogErrorMessage(openxr_command, |
61 | "VUID-XrApiLayerProperties-type-type: unknown type in api_layer_properties" ); |
62 | return XR_ERROR_VALIDATION_FAILURE; |
63 | } |
64 | } |
65 | } |
66 | |
67 | // "Independent of elementCapacityInput or elements parameters, elementCountOutput must be a valid pointer, |
68 | // and the function sets elementCountOutput." - 2.11 |
69 | if (nullptr == outgoing_count) { |
70 | return XR_ERROR_VALIDATION_FAILURE; |
71 | } |
72 | |
73 | // Find any implicit layers which we may need to report information for. |
74 | XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files); |
75 | if (XR_SUCCEEDED(result)) { |
76 | // Find any explicit layers which we may need to report information for. |
77 | result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, manifest_files); |
78 | } |
79 | if (XR_FAILED(result)) { |
80 | LoaderLogger::LogErrorMessage(openxr_command, |
81 | "ApiLayerInterface::GetApiLayerProperties - failed searching for API layer manifest files" ); |
82 | return result; |
83 | } |
84 | |
85 | // check for potential overflow before static_cast<uint32_t> |
86 | if (manifest_files.size() >= UINT32_MAX) { |
87 | LoaderLogger::LogErrorMessage(openxr_command, "ApiLayerInterface::GetApiLayerProperties - too many API layers found" ); |
88 | return XR_ERROR_RUNTIME_FAILURE; |
89 | } |
90 | |
91 | manifest_count = static_cast<uint32_t>(manifest_files.size()); |
92 | if (nullptr == outgoing_count) { |
93 | LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties" , |
94 | "VUID-xrEnumerateApiLayerProperties-propertyCountOutput-parameter: null propertyCountOutput" ); |
95 | return XR_ERROR_VALIDATION_FAILURE; |
96 | } |
97 | |
98 | *outgoing_count = manifest_count; |
99 | if (0 == incoming_count) { |
100 | // capacity check only |
101 | return XR_SUCCESS; |
102 | } |
103 | if (nullptr == api_layer_properties) { |
104 | // incoming_count is not 0 BUT the api_layer_properties is NULL |
105 | LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties" , |
106 | "VUID-xrEnumerateApiLayerProperties-properties-parameter: non-zero capacity but null array" ); |
107 | return XR_ERROR_VALIDATION_FAILURE; |
108 | } |
109 | if (incoming_count < manifest_count) { |
110 | LoaderLogger::LogErrorMessage( |
111 | "xrEnumerateInstanceExtensionProperties" , |
112 | "VUID-xrEnumerateApiLayerProperties-propertyCapacityInput-parameter: insufficient space in array" ); |
113 | return XR_ERROR_SIZE_INSUFFICIENT; |
114 | } |
115 | |
116 | for (uint32_t prop = 0; prop < incoming_count && prop < manifest_count; ++prop) { |
117 | manifest_files[prop]->PopulateApiLayerProperties(api_layer_properties[prop]); |
118 | } |
119 | return XR_SUCCESS; |
120 | } |
121 | |
122 | XrResult ApiLayerInterface::GetInstanceExtensionProperties(const std::string& openxr_command, const char* layer_name, |
123 | std::vector<XrExtensionProperties>& extension_properties) { |
124 | std::vector<std::unique_ptr<ApiLayerManifestFile>> manifest_files; |
125 | |
126 | // If a layer name is supplied, only use the information out of that one layer |
127 | if (nullptr != layer_name && 0 != strlen(layer_name)) { |
128 | XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files); |
129 | if (XR_SUCCEEDED(result)) { |
130 | // Find any explicit layers which we may need to report information for. |
131 | result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, manifest_files); |
132 | if (XR_FAILED(result)) { |
133 | LoaderLogger::LogErrorMessage( |
134 | openxr_command, |
135 | "ApiLayerInterface::GetInstanceExtensionProperties - failed searching for API layer manifest files" ); |
136 | return result; |
137 | } |
138 | |
139 | bool found = false; |
140 | size_t num_files = manifest_files.size(); |
141 | for (size_t man_file = 0; man_file < num_files; ++man_file) { |
142 | // If a layer with the provided name exists, get it's instance extension information. |
143 | if (manifest_files[man_file]->LayerName() == layer_name) { |
144 | manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties); |
145 | found = true; |
146 | break; |
147 | } |
148 | } |
149 | |
150 | // If nothing found, report 0 |
151 | if (!found) { |
152 | return XR_ERROR_API_LAYER_NOT_PRESENT; |
153 | } |
154 | } |
155 | // Otherwise, we want to add only implicit API layers and explicit API layers enabled using the environment variables |
156 | } else { |
157 | XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files); |
158 | if (XR_SUCCEEDED(result)) { |
159 | // Find any environmentally enabled explicit layers. If they're present, treat them like implicit layers |
160 | // since we know that they're going to be enabled. |
161 | std::vector<std::string> env_enabled_layers; |
162 | AddEnvironmentApiLayers(env_enabled_layers); |
163 | if (!env_enabled_layers.empty()) { |
164 | std::vector<std::unique_ptr<ApiLayerManifestFile>> exp_layer_man_files = {}; |
165 | result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, exp_layer_man_files); |
166 | if (XR_SUCCEEDED(result)) { |
167 | for (auto& exp_layer_man_file : exp_layer_man_files) { |
168 | for (std::string& enabled_layer : env_enabled_layers) { |
169 | // If this is an enabled layer, transfer it over to the manifest list. |
170 | if (enabled_layer == exp_layer_man_file->LayerName()) { |
171 | manifest_files.push_back(std::move(exp_layer_man_file)); |
172 | break; |
173 | } |
174 | } |
175 | } |
176 | } |
177 | } |
178 | } |
179 | |
180 | // Grab the layer instance extensions information |
181 | size_t num_files = manifest_files.size(); |
182 | for (size_t man_file = 0; man_file < num_files; ++man_file) { |
183 | manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties); |
184 | } |
185 | } |
186 | return XR_SUCCESS; |
187 | } |
188 | |
189 | XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uint32_t enabled_api_layer_count, |
190 | const char* const* enabled_api_layer_names, |
191 | std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces) { |
192 | XrResult last_error = XR_SUCCESS; |
193 | std::unordered_set<std::string> layers_already_found; |
194 | |
195 | bool any_loaded = false; |
196 | std::vector<std::unique_ptr<ApiLayerManifestFile>> enabled_layer_manifest_files_in_init_order = {}; |
197 | |
198 | // Find any implicit layers. |
199 | XrResult result = |
200 | ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, enabled_layer_manifest_files_in_init_order); |
201 | |
202 | for (const auto& enabled_layer_manifest_file : enabled_layer_manifest_files_in_init_order) { |
203 | layers_already_found.insert(enabled_layer_manifest_file->LayerName()); |
204 | } |
205 | |
206 | // Find any explicit layers. |
207 | std::vector<std::unique_ptr<ApiLayerManifestFile>> explicit_layer_manifest_files = {}; |
208 | |
209 | if (XR_SUCCEEDED(result)) { |
210 | result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, explicit_layer_manifest_files); |
211 | } |
212 | |
213 | bool found_all_layers = true; |
214 | |
215 | if (XR_SUCCEEDED(result)) { |
216 | // Put all explicit and then xrCreateInstance enabled layers into a string vector |
217 | |
218 | std::vector<std::string> enabled_explicit_api_layer_names = {}; |
219 | |
220 | AddEnvironmentApiLayers(enabled_explicit_api_layer_names); |
221 | |
222 | if (enabled_api_layer_count > 0) { |
223 | if (nullptr == enabled_api_layer_names) { |
224 | LoaderLogger::LogErrorMessage( |
225 | "xrCreateInstance" , |
226 | "VUID-XrInstanceCreateInfo-enabledApiLayerNames-parameter: enabledApiLayerCount is non-0 but array is NULL" ); |
227 | LoaderLogger::LogErrorMessage( |
228 | "xrCreateInstance" , "VUID-xrCreateInstance-info-parameter: something wrong with XrInstanceCreateInfo contents" ); |
229 | return XR_ERROR_VALIDATION_FAILURE; |
230 | } |
231 | |
232 | std::copy(enabled_api_layer_names, enabled_api_layer_names + enabled_api_layer_count, |
233 | std::back_inserter(enabled_explicit_api_layer_names)); |
234 | } |
235 | |
236 | // add explicit layers to list of layers to enable |
237 | for (const auto& layer_name : enabled_explicit_api_layer_names) { |
238 | bool found_this_layer = false; |
239 | |
240 | for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) { |
241 | bool erased_layer_manifest_file = false; |
242 | |
243 | if (layers_already_found.count(layer_name) > 0) { |
244 | found_this_layer = true; |
245 | } else if (layer_name == (*it)->LayerName()) { |
246 | found_this_layer = true; |
247 | layers_already_found.insert(layer_name); |
248 | enabled_layer_manifest_files_in_init_order.push_back(std::move(*it)); |
249 | it = explicit_layer_manifest_files.erase(it); |
250 | erased_layer_manifest_file = true; |
251 | } |
252 | |
253 | if (!erased_layer_manifest_file) { |
254 | it++; |
255 | } |
256 | } |
257 | |
258 | // If even one of the layers wasn't found, we want to return an error |
259 | if (!found_this_layer) { |
260 | found_all_layers = false; |
261 | std::string error_message = "ApiLayerInterface::LoadApiLayers - failed to find layer " ; |
262 | error_message += layer_name; |
263 | LoaderLogger::LogErrorMessage(openxr_command, error_message); |
264 | } |
265 | } |
266 | } |
267 | |
268 | for (std::unique_ptr<ApiLayerManifestFile>& manifest_file : enabled_layer_manifest_files_in_init_order) { |
269 | LoaderPlatformLibraryHandle layer_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath()); |
270 | if (nullptr == layer_library) { |
271 | if (!any_loaded) { |
272 | last_error = XR_ERROR_FILE_ACCESS_ERROR; |
273 | } |
274 | std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath()); |
275 | std::string warning_message = "ApiLayerInterface::LoadApiLayers skipping layer " ; |
276 | warning_message += manifest_file->LayerName(); |
277 | warning_message += ", failed to load with message \"" ; |
278 | warning_message += library_message; |
279 | warning_message += "\"" ; |
280 | LoaderLogger::LogWarningMessage(openxr_command, warning_message); |
281 | continue; |
282 | } |
283 | |
284 | // Get and settle on an layer interface version (using any provided name if required). |
285 | std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderApiLayerInterface" ); |
286 | auto negotiate = reinterpret_cast<PFN_xrNegotiateLoaderApiLayerInterface>( |
287 | LoaderPlatformLibraryGetProcAddr(layer_library, function_name)); |
288 | |
289 | if (nullptr == negotiate) { |
290 | std::ostringstream oss; |
291 | oss << "ApiLayerInterface::LoadApiLayers skipping layer " << manifest_file->LayerName() |
292 | << " because negotiation function " << function_name << " was not found" ; |
293 | LoaderLogger::LogErrorMessage(openxr_command, oss.str()); |
294 | LoaderPlatformLibraryClose(layer_library); |
295 | last_error = XR_ERROR_API_LAYER_NOT_PRESENT; |
296 | continue; |
297 | } |
298 | |
299 | // Loader info for negotiation |
300 | XrNegotiateLoaderInfo loader_info = {}; |
301 | loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO; |
302 | loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION; |
303 | loader_info.structSize = sizeof(XrNegotiateLoaderInfo); |
304 | loader_info.minInterfaceVersion = 1; |
305 | loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; |
306 | loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0); |
307 | loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version. |
308 | |
309 | // Set up the layer return structure |
310 | XrNegotiateApiLayerRequest api_layer_info = {}; |
311 | api_layer_info.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST; |
312 | api_layer_info.structVersion = XR_API_LAYER_INFO_STRUCT_VERSION; |
313 | api_layer_info.structSize = sizeof(XrNegotiateApiLayerRequest); |
314 | |
315 | XrResult res = negotiate(&loader_info, manifest_file->LayerName().c_str(), &api_layer_info); |
316 | // If we supposedly succeeded, but got a nullptr for getInstanceProcAddr |
317 | // then something still went wrong, so return with an error. |
318 | if (XR_SUCCEEDED(res) && nullptr == api_layer_info.getInstanceProcAddr) { |
319 | std::string warning_message = "ApiLayerInterface::LoadApiLayers skipping layer " ; |
320 | warning_message += manifest_file->LayerName(); |
321 | warning_message += ", negotiation did not return a valid getInstanceProcAddr" ; |
322 | LoaderLogger::LogWarningMessage(openxr_command, warning_message); |
323 | res = XR_ERROR_FILE_CONTENTS_INVALID; |
324 | } |
325 | if (XR_FAILED(res)) { |
326 | if (!any_loaded) { |
327 | last_error = res; |
328 | } |
329 | std::ostringstream oss; |
330 | oss << "ApiLayerInterface::LoadApiLayers skipping layer " << manifest_file->LayerName() |
331 | << " due to failed negotiation with error " << res; |
332 | LoaderLogger::LogWarningMessage(openxr_command, oss.str()); |
333 | LoaderPlatformLibraryClose(layer_library); |
334 | continue; |
335 | } |
336 | |
337 | { |
338 | std::ostringstream oss; |
339 | oss << "ApiLayerInterface::LoadApiLayers succeeded loading layer " << manifest_file->LayerName() |
340 | << " using interface version " << api_layer_info.layerInterfaceVersion << " and OpenXR API version " |
341 | << XR_VERSION_MAJOR(api_layer_info.layerApiVersion) << "." << XR_VERSION_MINOR(api_layer_info.layerApiVersion); |
342 | LoaderLogger::LogInfoMessage(openxr_command, oss.str()); |
343 | } |
344 | |
345 | // Grab the list of extensions this layer supports for easy filtering after the |
346 | // xrCreateInstance call |
347 | std::vector<std::string> supported_extensions; |
348 | std::vector<XrExtensionProperties> extension_properties; |
349 | manifest_file->GetInstanceExtensionProperties(extension_properties); |
350 | supported_extensions.reserve(extension_properties.size()); |
351 | for (XrExtensionProperties& ext_prop : extension_properties) { |
352 | supported_extensions.emplace_back(ext_prop.extensionName); |
353 | } |
354 | |
355 | // Add this API layer to the vector |
356 | api_layer_interfaces.emplace_back(new ApiLayerInterface(manifest_file->LayerName(), layer_library, supported_extensions, |
357 | api_layer_info.getInstanceProcAddr, |
358 | api_layer_info.createApiLayerInstance)); |
359 | |
360 | // If we load one, clear all errors. |
361 | any_loaded = true; |
362 | last_error = XR_SUCCESS; |
363 | } |
364 | |
365 | // Set error here to preserve prior error behavior |
366 | if (!found_all_layers) { |
367 | last_error = XR_ERROR_API_LAYER_NOT_PRESENT; |
368 | } |
369 | |
370 | // If we failed catastrophically for some reason, clean up everything. |
371 | if (XR_FAILED(last_error)) { |
372 | api_layer_interfaces.clear(); |
373 | } |
374 | |
375 | return last_error; |
376 | } |
377 | |
378 | ApiLayerInterface::ApiLayerInterface(const std::string& layer_name, LoaderPlatformLibraryHandle layer_library, |
379 | std::vector<std::string>& supported_extensions, |
380 | PFN_xrGetInstanceProcAddr get_instance_proc_addr, |
381 | PFN_xrCreateApiLayerInstance create_api_layer_instance) |
382 | : _layer_name(layer_name), |
383 | _layer_library(layer_library), |
384 | _get_instance_proc_addr(get_instance_proc_addr), |
385 | _create_api_layer_instance(create_api_layer_instance), |
386 | _supported_extensions(supported_extensions) {} |
387 | |
388 | ApiLayerInterface::~ApiLayerInterface() { |
389 | std::string info_message = "ApiLayerInterface being destroyed for layer " ; |
390 | info_message += _layer_name; |
391 | LoaderLogger::LogInfoMessage("" , info_message); |
392 | LoaderPlatformLibraryClose(_layer_library); |
393 | } |
394 | |
395 | bool ApiLayerInterface::SupportsExtension(const std::string& extension_name) const { |
396 | bool found_prop = false; |
397 | for (const std::string& supported_extension : _supported_extensions) { |
398 | if (supported_extension == extension_name) { |
399 | found_prop = true; |
400 | break; |
401 | } |
402 | } |
403 | return found_prop; |
404 | } |
405 | |