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.
30static 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
52XrResult 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
122XrResult 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
189XrResult 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
378ApiLayerInterface::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
388ApiLayerInterface::~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
395bool 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