1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
|
// Copyright (c) 2017-2021, The Khronos Group Inc.
//
// SPDX-License-Identifier: CC-BY-4.0
[[api-layer-interaction]]
== API Layer Interaction ==
This section details how the loader interacts with API layers.
[[api-layer-call-chains-and-distributed-dispatch]]
=== API Layer Call Chains and Distributed Dispatch ===
Remember, an API layer does not need to intercept all commands, instead, it
can choose to intercept only a subset.
Normally, when an API layer intercepts a given OpenXR command, it will call
down the <<openxr-call-chains,call chain>> as needed.
The loader and all enabled API layers that participate in a call chain
cooperate to ensure the correct sequencing of calls from one entity to the
next.
This group effort for call chain sequencing is hereinafter referred to as
*distributed dispatch*.
If an API layer does not wish to intercept a command, it must forward the
request made to its `xrGetInstanceProcAddr` implementation (provided through
pname:getInstanceProcAddr) down to the next `xrGetInstanceProcAddr`
implementation in the call chain (provided to the API layer through
pname:nextGetInstanceProcAddr).
In distributed dispatch each API layer is responsible for properly calling
the next entity in the call chain.
This means that a dispatch mechanism is required for all OpenXR commands
that an API layer intercepts.
For example, if the enabled API layers intercepted only certain functions,
the call chain would look as follows:
image::images/optional_layer_function_impl.svg[align="center", title="Optional Function Example"]
[NOTE]
.Note
====
In the above example, notice that "API Layer A" fails to pass along the
information for "xrFunction6".
This may not be an error since some commands expect to only work with one
specific API layer (`xrEnumerateInstanceExtensionProperties` when
"layerName" is set).
However, a badly designed API layer could do something similar for other
commands.
====
The loader is responsible for dispatching all core and instance extension
OpenXR functions to the first entity in the call chain.
[[api-layer-discovery]]
=== API Layer Discovery ===
As mentioned in the <<implicit-vs-explicit-api-layers, Implicit vs Explicit
API Layers>> section, API layers can be categorized into two categories:
* Implicit API layers
* Explicit API layers
The main difference between the two is that implicit API layers are
automatically enabled, unless overridden, and explicit API layers must be
manually enabled.
On any system, the loader looks in specific areas for information on the API
layers that it can load.
The process of finding the available API layers on a system is known as "API
Layer Discovery".
During "API Layer Discovery", the loader determines what API layers are
available and then grabs some basic information about each API layer: the
name, the version, and any extensions it supports.
This information is then provided back to the application through the
`xrEnumerateApiLayerProperties` command.
This section specifies the minimal conventions and rules an API layer must
follow, especially with regards to interacting with the loader and other API
layers.
On Windows and Linux systems, JSON formatted manifest files are used to
store API layer information.
In order to find OpenXR API layers, the OpenXR loader will read the JSON
files to identify the names and attributes of API layers and their
extensions.
[[desktop-api-layer-discovery]]
==== Desktop API Layer Discovery ====
On Desktop systems (such as Windows and Linux) JSON formatted manifest files
are used to store OpenXR component information.
The OpenXR loader will search specific areas of the computer for the
corresponding JSON manifest files that are used to identify the names and
attributes of OpenXR API layers.
The use of manifest files allows the loader to avoid actively loading the
shared library files until the application request causes them to be
activated.
The following sections define the standard locations the OpenXR loader
searches when looking for JSON manifest files.
[[windows-manifest-registry-usage]]
===== Windows Manifest Registry Usage =====
On Windows, the OpenXR loader will scan the Windows registry for details on
JSON manifest files in the following locations:
[NOTE]
.Note
====
The following table uses 2 acronyms:
* <HKLM> is used to indicate the top key of "HKEY_LOCAL_MACHINE"
* <HKCU> is used to indicate the top key of "HKEY_CURRENT_USER"
This is done purely to simplify the reading of the table
====
.Typical Windows Manifest File Registry Keys
[options="header",cols="10,10,30,50%"]
|====
| Windows OS Target | Binary Target | Base Registry Key (see note above) | Common Uses
.2+| 32-bit
.2+| 32-bit
l| <HKLM>\SOFTWARE\Khronos\OpenXR
| Manifest files typically installed by some application for the
entire system and all users on that system are found under this
base key.
l| <HKCU>\SOFTWARE\Khronos\OpenXR
| Manifest files typically installed by some application for just
the current user are found under this base key.
.2+| 64-bit
.2+| 64-bit
l| <HKLM>\SOFTWARE\Khronos\OpenXR
| Manifest files for 64-bit OpenXR components are typically
installed by some application for the entire system and all users
on that system and recorded under this base key.
l| <HKCU>\SOFTWARE\Khronos\OpenXR
| Manifest files for 64-bit OpenXR components are typically
installed by some application for just the current user
and recorded under this base key.
.2+| 64-bit
.2+| 32-bit
l| <HKLM>\WOW6432Node\SOFTWARE\Khronos\OpenXR
| Manifest files for 32-bit OpenXR components are typically
installed by some application for the entire system and all users
on that system and recorded under this base key.
l| <HKCU>\WOW6432Node\SOFTWARE\Khronos\OpenXR
| Manifest files for 32-bit OpenXR components are typically
installed by some application for just the current user
and recorded under this base key.
|====
When the OpenXR loader scans each of the appropriate registry keys based on
the Binary Target size, the loader will search under specific sub-keys for
the correct JSON manifest files based on the type of content stored in the
manifest file.
Additionally, the major version of the OpenXR API is used to separate up
each group of elements so that they don't conflict with one another in the
future.
Finally, each API layer type has their own sub-key under which their list of
available layers resides:
.Sub-Key Suffix for Manifest Search
[width="70%",options="header",cols="^.^40%e,^.^60%v"]
|====
| JSON Manifest File Type | Sub-key Suffix Added to Each Valid Key
| Implicit API Layer | ApiLayers\Implicit
| Explicit API Layer | ApiLayers\Explicit
|====
The following example shows possible keys searched for OpenXR 1.x explicit
API layers for a 64-bit loader on a 64-bit system.
[example]
.OpenXR 1.x Explicit Layer Windows' Registry Locations
====
----
HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenXR\1\ApiLayers\Explicit
HKEY_CURRENT_USER\SOFTWARE\Khronos\OpenXR\1\ApiLayers\Explicit
----
====
When searching for implicit API layers, the loader appends
`ApiLayers\Implicit` to each of the keys found.
Similar to the above example, this example shows the possible keys searched
for OpenXR 1.x implicit API layers for the same system as above.
[example]
.OpenXR 1.x Implicit Layer Windows' Registry Locations
====
----
HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenXR\1\ApiLayers\Implicit
HKEY_CURRENT_USER\SOFTWARE\Khronos\OpenXR\1\ApiLayers\Implicit
----
====
Each registry value under the above keys must: be defined in the following
way:
* The name of the value must: be the full path name to a valid manifest JSON
file (including the ".json" suffix)
* The data for value must: be a DWORD
** In order for the loader to attempt to load the binary associated with
this, the value must: be 0.
** To disable this manifest file so that the loader does not attempt to use
it, set the value to a non-zero value.
For each value in these keys which has DWORD data set to 0, the loader opens
the manifest file specified by the name of the value.
The OpenXR loader will then obtain information about the library, including
the name and pathname of the shared library (".dll") file which defines the
actual API layer binary.
For example, let us assume the registry contains the following data:
----
[HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenXR\1\ApiLayers\Explicit\]
"C:\vendor a\layer_a.json"=dword:00000000
"C:\windows\system32\layer_b.json"=dword:00000001
"C:\windows\system32\layer_c.json"=dword:00000000
----
In this case, the loader will step through each entry, and check the value.
If the value is 0, then the loader will attempt to load the API layer
manifest file.
In this case, the loader will open the first and last listings, but not the
middle one.
This is because the value of 1 for layer_b.json disables the layer from
being loaded.
[[linux-manifest-search-paths]]
===== Linux Manifest Search Paths =====
On Linux, the OpenXR loader will scan for JSON manifest files using the
following base folders:
.Typical Linux Manifest File Search Directories
[options="header",cols="20%e,20%v,60%"]
|====
| Environment Variable | Common Locations | Common Uses
| XDG_CONFIG_DIRS
| /etc/xdg
| Defines common paths for config files
| SYSCONFDIR
| /usr/local/etc
| The directory for installing read-only data files that pertain to
a single machine. In this case, it is commonly used to store locally
built API layers.
| EXTRASYSCONFDIR
| /etc
| Location of API layers installed from non-Linux-distribution-provided
packages.
| XDG_DATA_DIRS
| /usr/local/share, /usr/share/
| Location of API layers installed from Linux-distribution-provided
packages.
| XDG_DATA_HOME
| $HOME/.local/share
| Used to define manually enabled API layers per user. $HOME is the
current home directory of the application's user id; this
path will be ignored for suid programs
|====
[NOTE]
.Note
====
The "/usr/local/*" directories can: be configured to be other directories at
build time.
====
When the OpenXR loader scans the directories defined by each of the above
environmental variables, it pulls out each individual path (tokenizing the
value based on the colon (:) separator), and then appends additional
sub-path information on the end of each based on the type of manifest file
being searched for.
The following table shows the sub-path information added onto the end of
each path based on the JSON manifest file type:
.Sub-Folder Suffix for Manifest Search
[width="70%",options="header",cols="^.^40%e,^.^60%v"]
|====
| JSON Manifest File Type | Sub-Folder Suffix Added to Each Path
| Implicit Layer | openxr/<major_version>/api_layers/implicit.d
| Explicit Layer | openxr/<major_version>/api_layers/explicit.d
|====
Where <major_version> is the integer number for the OpenXR API's major
version the API layers are associated with.
The following example shows possible search paths for OpenXR 1.x explicit
API layers (depending on the environmental variables defined on a user's
system).
[example]
.OpenXR 1.x Linux Explicit Layer Search Paths
====
----
/usr/local/etc/openxr/1/api_layers/explicit.d
/usr/local/share/openxr/1/api_layers/explicit.d
/etc/openxr/1/api_layers/explicit.d
/usr/share/openxr/1/api_layers/explicit.d
$HOME/.local/share/openxr/1/api_layers/explicit.d
----
====
When searching for OpenXR 1.x implicit API layers, the loader appends
`openxr/1/api_layers/implicit.d` to each of the paths found.
Similar to the above example, the following shows the search paths for
OpenXR 1.x implicit API layers for the same system as above.
[example]
.OpenXR 1.x Linux Implicit Layer Search Paths
====
----
/usr/local/etc/openxr/1/api_layers/implicit.d
/usr/local/share/openxr/1/api_layers/implicit.d
/etc/openxr/1/api_layers/implicit.d
/usr/share/openxr/1/api_layers/implicit.d
$HOME/.local/share/openxr/1/api_layers/implicit.d
----
====
[[overriding-the-default-api-layer-paths]]
===== Overriding the Default API Layer Paths =====
There may be times that a developer wishes to force the loader to use their
own explicit API layers (or specific explicit API layers).
In order to support this, the desktop loader can be forced to look in
specific paths for explicit API layers with the `XR_API_LAYER_PATH`
environment variable.
Simply set it to a properly delimited list of paths that you want the loader
to search for explicit API layer JSON Manfiest files.
While relative paths may work, it is preferable to use absolute paths when
defining this environment variable to reduce issues.
[NOTE]
.Important
====
If the "XR_API_LAYER_PATH" environmental variable is defined, then the
desktop loader will not look in the standard locations to find explicit API
layers, instead looking only at the paths defined in that environment
variable.
Implicit API layers will always be discovered using the standard paths.
====
[example]
.Setting XR_API_LAYER_PATH Override
====
*Windows*
----
set XR_API_LAYER_PATH=<my_api_layer_path_1>;<my_api_layer_path_2>;<my_api_layer_path_3>
----
*Linux*
----
export XR_API_LAYER_PATH=<my_api_layer_path_1>:<my_api_layer_path_2>:<my_api_layer_path_3>
----
====
[[api-layer-manifest-file-format]]
===== API Layer Manifest File Format =====
On Windows and Linux, the desktop loader uses manifest files to discover API
layers.
The desktop loader doesn't load the API layer libraries (e.g. DLL or .so
files) for each of the enabled API layers except during `xrCreateInstance`
when it sets up the call chain.
This is to reduce the likelihood of loading a malicious API layer into
memory.
Instead, details are read from the manifest file, which are then provided
for applications to determine what API layers should actually be loaded.
The following section discusses the details of the API layer manifest JSON
file format.
The JSON file itself does not have any requirements for naming.
The only requirement is that the filename extension is ".json".
[example]
.API Layer Manifest Examples
====
*Simple Explicit API Layer Manifest File*
[source,json]
----
{
"file_format_version" : "1.0.0",
"api_layer": {
"name": "XR_APILAYER_LUNARG_test",
"library_path": "xrTestLayer.dll"
"api_version" : "1.0",
"implementation_version" : "2",
"description" : "LunarG test API layer"
}
}
----
*More Complex Implicit API Layer Manifest File*
[source,json]
----
{
"file_format_version" : "1.0.0",
"api_layer": {
"name": "XR_APILAYER_LUNARG_test",
"library_path": "xrTestLayer.dll"
"api_version" : "1.0",
"implementation_version" : "2",
"description" : "LunarG test API layer",
"functions": {
"xrNegotiateLoaderApiLayerInterface":
"TestLayer_xrNegotiateLoaderApiLayerInterface"
},
"instance_extensions": [
{
"name": "XR_EXT_instance_extension_example",
"extension_version": "1"
}
],
"enable_environment": "ENABLE_XR_API_LAYER_TEST_1",
"disable_environment": "DISABLE_XR_API_LAYER_TEST_1"
}
}
----
====
.API Layer Manifest JSON Fields
[width="70%",options="header",cols="<.^20%,^.^10%,<.^60%,^.^10%"]
|====
| JSON Node | API Layer Type | Description and Notes | Introspection Query
| "file_format_version"
| *Required* for Implicit / Explicit
| Manifest format major.minor.patch version number. Currently only a
value of 1.0.0 is supported.
| N/A
| "api_layer"
| *Required* for Implicit / Explicit
| The identifier used to group a single API layer's information together.
| xrEnumerateApiLayerProperties
| "name"
| *Required* for Implicit / Explicit
| The string used to uniquely identify this API layer to applications.
| xrEnumerateApiLayerProperties
| "library_path"
| *Required* for Implicit / Explicit
| The "library_path" specifies either a filename, a relative pathname,
or a full pathname to the API layer's shared library file. If
"library_path" specifies a relative pathname, it is relative to the
path of the JSON manifest file (e.g. for cases when an application
provides an API layer that is in the same folder hierarchy as the rest of
the application files). If "library_path" specifies a filename, the
library must live in the system's shared object search path. There
are no rules about the name of the API layer shared library files other
than it should end with the appropriate suffix (".DLL" on Windows,
and ".so" on Linux).
| N/A
| "api_version"
| *Required* for Implicit / Explicit
| The major.minor (but not patch) version number of the OpenXR API that the
shared library file for the library was built against. For example:
1.0.
| xrEnumerateApiLayerProperties
| "implementation_version"
| *Required* for Implicit / Explicit
| The version of the API layer implemented. If the API layer itself has any
major changes, this number should change so the loader and/or
application can identify it properly.
| xrEnumerateApiLayerProperties
| "description"
| *Required* for Implicit / Explicit
| A high-level description of the API layer and it's intended use.
| xrEnumerateApiLayerProperties
| "functions"
| Optional for Implicit / Explicit
| This section can be used to identify a different function name for
the loader to use in place of standard API layer interface functions. The
"functions" node is required if the API layer is using an alternative name
for `xrNegotiateLoaderApiLayerInterface`.
| xrGet*ProcAddr (except for `xrNegotiateLoaderApiLayerInterface`
which must be queried using the OS/platform-specific
GetProcAddress)s.
| "instance_extensions"
| Optional for Implicit / Explicit
| Contains the list of instance extension names supported by this
API layer. One "instance_extensions" node with an array of one or more
elements is required if any instance extensions are supported by a
API layer, otherwise the node is optional. Each element of the array
must have the nodes "name" and "extension_version" which correspond to
`XrExtensionProperties` "extensionName" and "extensionVersion" respectively.
| xrEnumerateInstanceExtensionProperties
| "enable_environment"
| Optional for Implicit
| Indicates an environment variable used to enable the implicit API layer.
If provided in the JSON file, this environment variable (which should vary
with each "version" of the API layer) must be set in the environment or else
the implicit API layer is not loaded. This is for application environments
(e.g. Steam) which want to enable an API layer(s) only for applications that
they launch, and allows for applications run outside of that
environment to not get that implicit API layer(s).
| N/A
| "disable_environment"
| *Required* for Implicit
| Indicates an environment variable used to disable the implicit API layer.
Required to allow users or applications to disable implicit layers that are
not desired or that cause problems for the application. The user/application
can set this environment variable (before calling OpenXR functions) to
"blacklist" the API layer.
This environment variable should vary with each "version" of the
API layer. If both the
"enable_environment" and "disable_environment" variables are set, the
implicit API layer is disabled.
| N/A
|====
[NOTE]
.Note
====
If the same API layer shared library supports multiple, incompatible
versions of text manifest file format versions, it must have separate JSON
files for each (all of which may point to the same shared library).
====
[[api-layer-manifest-file-version-history]]
====== API Layer Manifest File Version History ======
The current highest supported API layer manifest file format supported is
1.0.0.
Information about each version is detailed in the following sub-sections:
_API Layer Manifest File Version 1.0.0_
The initial version of the API layer manifest file specified the basic
format and fields of an API layer JSON file.
The fields of the 1.0.0 file format include:
* "file_format_version"
* "api_layer"
* "name"
* "library_path"
* "api_version"
* "implementation_version"
* "description"
* "functions"
* "instance_extensions"
* "enable_environment"
* "disable_environment"
[[loader-api-layer-interface-negotiation]]
=== Loader/API Layer Interface Negotiation ===
Now that an API layer has been discovered, an application can choose to load
it (or it is loaded by default if it is an implicit API layer).
When the loader attempts to load the API layer, the first thing it does is
attempt to negotiate the version of the loader to API layer interface.
In order to negotiate the loader/API layer interface version, the API layer
must implement the fname:xrNegotiateLoaderApiLayerInterface function (or a
renamed version of this function identified in the manifest file).
[[xrNegotiateLoaderApiLayerInterface,xrNegotiateLoaderApiLayerInterface]]
[source,c++]
----
XrResult xrNegotiateLoaderApiLayerInterface(
const XrNegotiateLoaderInfo *loaderInfo,
const char *layerName,
XrNegotiateApiLayerRequest *apiLayerRequest);
----
* pname:loaderInfo must: be a valid pointer to a constant
slink:XrNegotiateLoaderInfo structure.
* pname:layerName must: be NULL or a valid C-style NULL-terminated string
listing the name of an API layer which the loader is attempting to
negotiate with.
* pname:layerRequest must be a valid pointer to a
slink:XrNegotiateApiLayerRequest structure.
This function should be directly exported by an API layer so that using
"GetProcAddress" on Windows or "dlsym" on Linux, should return a valid
function pointer to it.
If the function succeeds, the API layer must: return `XR_SUCCESS`.
If the function fails, the API layer must: return
`XR_ERROR_INITIALIZATION_FAILED`.
The entire <<loader-api-layer-negotiation-process, negotiation process>> is
defined in more detail below.
The sname:XrNegotiateLoaderInfo struct is defined in the
`src/common/loader_interfaces.h` header.
It is used to pass information about the loader to an API layer during the
negotiation process.
[[XrNegotiateLoaderInfo,XrNegotiateLoaderInfo]]
[source,c++]
----
struct XrNegotiateLoaderInfo {
XrLoaderInterfaceStructs structType;
uint32_t structVersion;
size_t structSize;
uint32_t minInterfaceVersion;
uint32_t maxInterfaceVersion;
XrVersion minApiVersion;
XrVersion maxApiVersion;
};
----
* pname:structType must: be a valid value of
elink:XrLoaderInterfaceStructs.
In this case, it must: specifically be
`XR_LOADER_INTERFACE_STRUCT_LOADER_INFO`.
* pname:structVersion must: be a valid version of the structure.
The `loader_interfaces.h` header uses the value
`XR_LOADER_INFO_STRUCT_VERSION` to describe the current latest version
of this structure.
* pname:structSize must: be the size in bytes of the current version of
the structure (i.e. sizeof(XrNegotiateLoaderInfo)).
* pname:minInterfaceVersion is the minimum
<<api-layer-interface-versions,loader/API layer interface version>>
supported by the loader.
* pname:maxInterfaceVersion is the maximum valid version of the
<<api-layer-interface-versions,loader/API layer interface version>>
supported by the loader, currently defined using
`XR_CURRENT_LOADER_API_LAYER_VERSION`.
* pname:minApiVersion is the minimum supported version of the OpenXR API
by this loader as formatted by `XR_MAKE_VERSION` defined in `openxr.h`.
Patch is ignored.
* pname:maxApiVersion is the maximum supported version of the OpenXR API
by this loader as formatted by `XR_MAKE_VERSION` defined in `openxr.h`.
Patch is ignored.
You'll notice the structures are similar to other OpenXR structures.
The "structType" field, in this case takes a new enum defined just for
internal loader interfacing use.
The ename:XrLoaderInterfaceStructs enumeration is also defined in the
`src/common/loader_interfaces.h` header and currently looks like:
[[XrLoaderInterfaceStructs,XrLoaderInterfaceStructs]]
[source,c++]
----
enum XrLoaderInterfaceStructs {
XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0,
XR_LOADER_INTERFACE_STRUCT_LOADER_INFO,
XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST,
XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST,
XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO,
XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO,
};
----
Just like standard OpenXR structs, the ename:XrLoaderInterfaceStructs enum
defines the set of valid structures and can grow in the future.
The sname:XrNegotiateApiLayerRequest can also be found in the
`src/common/loader_interfaces.h` header.
It is used to pass information about the API layer back to the loader during
the negotiation process.
[[XrNegotiateApiLayerRequest,XrNegotiateApiLayerRequest]]
[source,c++]
----
struct XrNegotiateApiLayerRequest {
XrLoaderInterfaceStructs structType;
uint32_t structVersion;
size_t structSize;
uint32_t layerInterfaceVersion;
XrVersion layerApiVersion;
PFN_xrGetInstanceProcAddr getInstanceProcAddr;
PFN_xrCreateApiLayerInstance createApiLayerInstance;
};
----
* pname:structType must: be a valid value of
elink:XrLoaderInterfaceStructs.
In this case, it must: specifically be
`XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST`.
* pname:structVersion must: be a valid version of the structure.
The `loader_interfaces.h` header uses the value
`XR_API_LAYER_INFO_STRUCT_VERSION` to describe the current latest
version of this structure.
* pname:structSize must: be the size in bytes of the current version of
the structure (i.e. sizeof(XrNegotiateApiLayerRequest)).
* pname:layerInterfaceVersion is the version of the
<<api-layer-interface-versions,loader/API layer interface version>>
being requested by the API layer.
Should not be outside of the bounds of the
sname:XrNegotiateLoaderInfo::`minInterfaceVersion` and
sname:XrNegotiateLoaderInfo::`maxInterfaceVersion` values (inclusive).
* pname:layerApiVersion is the version of the OpenXR API supported by this
API layer as formatted by `XR_MAKE_VERSION` defined in `openxr.h`.
Patch is ignored.
* pname:getInstanceProcAddr is a pointer to the API layer's
`xrGetInstanceProcAddr` command that will be used by the loader to
complete a dispatch table to all valid OpenXR commands supported by the
API layer.
* pname:createLayerInstance is a pointer to the API layer's
`xrCreateApiLayerInstance` command that will be used by the loader when
a `xrCreateInstance` command is triggered and an API layer exists.
This is used because API layers need additional information at
`xrCreateInstance` time.
[[loader-api-layer-negotiation-process]]
==== Loader/API Layer Negotiation Process ====
Once the loader has obtained a valid address to the API layer's
flink:xrNegotiateLoaderApiLayerInterface function, the loader will create a
variable of type slink:XrNegotiateLoaderInfo and initialize it in the
following ways:
1. Set the structure "structType" to
`XR_LOADER_INTERFACE_STRUCT_LOADER_INFO`
2. Set the structure "structVersion" to the current version,
`XR_LOADER_INFO_STRUCT_VERSION`
3. Set the structure "structSize" to the current size of the
`XrNegotiateLoaderInfo` structure
4. Set "minInterfaceVersion" to the minimum
<<api-layer-interface-versions,loader/API layer interface version>> that
the loader supports
5. Set "maxInterfaceVersion" to the current version of the
<<api-layer-interface-versions,loader/API layer interface version>> at
the time of loader compilation
6. Set "minApiVersion" to the minimum version of OpenXR supported by the
loader
7. Set "maxApiVersion" to the maximum version of OpenXR supported by the
loader (the current version at the time of loader compilation).
The loader also creates and initializes a variable of type
slink:XrNegotiateApiLayerRequest to allow the API layer to properly respond
to the request.
The structure will be initialized by the loader in the following way:
1. Set the structure "structType" to
`XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST`
2. Set the structure "structVersion" to the current version,
`XR_API_LAYER_INFO_STRUCT_VERSION`
3. Set the structure "structSize" to the current size of the
`XrNegotiateApiLayerRequest` structure
The loader will leave the remaining fields uninitialized to allow each API
layer to fill in the appropriate information for itself.
The loader will then individually call each API layer's
flink:xrNegotiateLoaderApiLayerInterface function and each API layer then
must: :
* Determine if it can support the loader's request:
** Does the API layer support any <<api-layer-interface-versions, loader/API
layer interface version>> between
pname:loaderInfo->pname:minInterfaceVersion and
pname:loaderInfo->pname:maxInterfaceVersion:
** *AND* does the API layer support any OpenXR API version between
pname:loaderInfo->pname:minApiVersion and
pname:loaderInfo->pname:maxApiVersion:
* If it is able to support the request, it must: return `XR_SUCCESS` and:
** Fill in pname:layerRequest->pname:layerInterfaceVersion with the API
layer interface version it desires to support.
** Fill in pname:layerRequest->pname:layerApiVersion with the API version of
OpenXR it will execute under.
** Fill in pname:layerRequest->pname:getInstanceProcAddr with a valid
function pointer so that the loader can query function pointers to the
remaining OpenXR commands supported by the API layer.
** Fill in pname:layerRequest->pname:createLayerInstance with a valid
function pointer so that the loader can create the instance through the
API layer call chain.
* Otherwise, it must: return `XR_ERROR_INITIALIZATION_FAILED`
[NOTE]
.Note
====
The API layer must: not call to another API layer while inside of the
`xrNegotiateLoaderApiLayerInterface` function.
The loader will handle all API layer negotiations with each API layer
individually.
====
[[api-layer-interface-versions]]
==== API Layer Interface Versions ====
The current API layer interface is at version 1.
The following sections detail the differences between the various versions.
[[api-layer-interface-version-1]]
===== API Layer Interface Version 1 =====
* Defined manifest file version 1.0.0.
* Introduced the concept of negotiation.
** Requires API layers to export `xrNegotiateLoaderApiLayerInterface`
function.
[[api-layer-intercept-requirements]]
=== API Layer Intercept Requirements ===
* API Layers intercept an OpenXR command by defining a C/C++ function with
signature *identical* to the OpenXR API for that command.
* The following commands are required: to be implemented by any API layer:
** `xrGetInstanceProcAddr`
** `xrCreateApiLayerInstance`
* The following commands must: not be implemented by any API layer:
** `xrCreateInstance`
* For any OpenXR command an API layer intercepts which has a non-void return
value, an appropriate value must: be returned by the API layer intercept
command.
* Most commands an API layer intercepts must: call down the chain to the
corresponding OpenXR command in the next entity.
** The common behavior for an API layer is to intercept a call, perform some
behavior, then pass it down to the next entity.
*** If a layer does not call down to the next entity for a given command,
undefined behavior may occur.
This is because the command will not be received by API layers and
runtimes further down the call chain.
*** One command that cannot: call down the chain is:
**** `xrNegotiateLoaderApiLayerInterfaceVersion`
*** Some commands that may: choose to not call down the chain are:
**** `xrGetInstanceProcAddr`
* API layer intercept commands may: insert extra calls to OpenXR commands in
addition to those that are intercepted
** If an API layer inserts new calls, that API layer must: pass along all
new commands to the next entity.
[[api-layer-conventions-and-rules]]
=== API Layer Conventions and Rules ===
An API layer, when inserted into an otherwise compliant OpenXR
implementation, must still result in a compliant OpenXR implementation.
The intention is for API layers to have a well-defined baseline behavior.
Therefore, it must follow some conventions and rules defined below:
* An API layer may: be in a call chain with any number of API layers before
or after it.
* It must: not make invalid calls to, or rely on undefined behaviors of, its
lower API layers.
* If it changes the behavior of a function, it must: ensure the API layers
called prior to itself do not make invalid calls because of the changed
behavior.
** For example, if an API layer chooses to intercept an object creation
function, and then wraps the objects created by lower API layers, it
must: make sure the lower API layers never see the wrapped objects.
*** This means it must protect the lower API layers directly from itself or
indirectly from its upper API layers.
* `xrEnumerateApiLayerProperties` must: return only its own API layer
properties.
* `xrEnumerateInstanceExtensionProperties` must: obey the "layerName"
parameter:
** If "layerName" is the name of this API layer, it must: return the
contents of the instance extensions it supports.
** If "layerName" is NULL and:
*** It is an explicit API layer, it must: not fill in any data.
*** It is an implicit API layer, it must: add it's own instance extension
contents to the list of extensions.
* For any OpenXR command the API layer intercepts, `xrGetInstanceProcAddr`
must: return a pointer to a local entry-point.
** Otherwise it returns the value obtained by calling down the instance call
chain.
[[api-layer-create-instance-process]]
=== API Layer Create Instance Process ===
After <<loader-api-layer-interface-negotiation, interface negotiation>> and
any directed `xrEnumerateInstanceExtensionProperties` calls, the next time
an API layer is invoked is during the loader's `xrCreateInstance` call.
The API layer is only involved if it is in the enabled API layer list (this
includes implicit, environment variable enabled, and application enabled API
layers).
An API layer needs additional information during `xrCreateInstance` calls,
so each API layer must implement the fname:xrCreateApiLayerInstance command,
which is a special API layer command.
[[xrCreateApiLayerInstance,xrCreateApiLayerInstance]]
[source,c++]
----
XrResult xrCreateApiLayerInstance(
const XrInstanceCreateInfo *info,
const struct XrApiLayerCreateInfo *layerInfo,
XrInstance *instance);
----
* pname:info is a pointer to the slink:XrInstanceCreateInfo information
passed into the standard flink:xrCreateInstance command.
* pname:layerInfo is a pointer to an slink:XrApiLayerCreateInfo structure
that contains special information required by a API layer during its
create instance process.
This is generated by the loader.
* pname:instance is a pointer to store the returned instance in, just as
in the standard fname:xrCreateInstance command.
The sname:XrApiLayerCreateInfo structure is defined in
`src/common/loader_interfaces.h` and looks like:
[[XrApiLayerCreateInfo,XrApiLayerCreateInfo]]
[source,c++]
----
struct XrApiLayerCreateInfo {
XrLoaderInterfaceStructs structType;
uint32_t structVersion;
size_t structSize;
XrInstance loaderInstance;
char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE];
XrApiLayerNextInfo *nextInfo;
};
----
* pname:structType is the type of structure, or
XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO in this case.
* pname:structVersion is the version of the structure being supplied by
the loader.
* pname:structSize is the size of the structure supplied by the loader.
* pname:loaderInstance is deprecated and must be ignored.
* pname:settings_file_location is the location of any API layer settings
file that can be used.
This is currently unused.
* pname:nextInfo is a pointer to the slink:XrApiLayerNextInfo structure
which contains information to work with the next API layer in the chain.
The sname:XrApiLayerNextInfo structure is also defined in
`src/common/loader_interfaces.h` and looks like:
[[XrApiLayerNextInfo,XrApiLayerNextInfo]]
[source,c++]
----
struct XrApiLayerNextInfo {
XrLoaderInterfaceStructs structType;
uint32_t structVersion;
size_t structSize;
char layerName[XR_MAX_API_LAYER_NAME_SIZE];
PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr;
PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance;
XrApiLayerNextInfo *next;
};
----
* pname:structType is the type of structure, or
XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO in this case.
* pname:structVersion is the version of the structure being supplied by
the loader.
* pname:structSize is the size of the structure supplied by the loader.
* pname:layerName is the name of the API layer which should be receiving
this information.
The intent is to make sure the API layer chain information is behaving
properly.
* pname:nextGetInstanceProcAddr is a pointer to the next API layer's
`xrGetInstanceProcAddr`.
This can be used to generate a dispatch table to the next commands in
the chain.
* pname:nextCreateLayerInstance is a pointer to the next API layer's
`xrCreateApiLayerInstance` command.
This is to be called after the API layer has done any localized
creation, but before the API layer records any command addresses from
the next API layer using `xrGetInstanceProcAddr`.
* pname:next is a pointer to the sname:XrApiLayerNextInfo for the next API
layer.
If no API layer is after this, it will be NULL.
However, the loader implements a `xrTerminatorCreateLayerInstance`
command to re-direct the call-chain back to the runtime's
`xrCreateInstance` command, so this should be the last command to
receive this information.
During the `xrCreateInstance` call, the following happens:
. The call enters the loader's trampoline function `xrCreateInstance`
. The loader will generate an instance of the slink:XrApiLayerCreateInfo
structure
. The loader will go through each API layer in reverse order (i.e. starting with
the layer closest to the runtime and ending with the API layer closest to
the application):
.. Record the API layer's name, `xrGetInstanceProcAddr` address, and the
`xrCreateApiLayerInstance` address.
.. Build a `XrApiLayerNextInfo` structure for the API layer recording the name and
command addresses.
... If this is the first API layer (the one closest to the runtime) we want it
to enter the loader again when we're done.
So, the loader sets the following:
.... `XrApiLayerNextInfo.nextGetInstanceProcAddr` =
`loaderXrTermGetInstanceProcAddr`
.... `XrApiLayerNextInfo.nextCreateLayerInstance` =
`loaderXrTermCreateLayerInstance`
.... `XrApiLayerNextInfo.next` = NULL
... Otherwise, the loader sets the information to the previous API layer's
information:
.... `XrApiLayerNextInfo.nextGetInstanceProcAddr` =
Previous `XrApiLayerNextInfo.loaderXrTermGetInstanceProcAddr`
.... `XrApiLayerNextInfo.nextCreateLayerInstance` =
Previous `XrApiLayerNextInfo.loaderXrTermCreateLayerInstance`
.... `XrApiLayerNextInfo.next` = address to previous `XrApiLayerNextInfo`
. The loader will then update the `XrApiLayerCreateInfo.nextInfo` to point to the
last created `XrApiLayerNextInfo` since this is the first API layer in the
call-chain.
. The loader calls the first API layer's flink:xrCreateApiLayerInstance command
passing in the pointer to the created slink:XrApiLayerCreateInfo
. The API layer receives the information in its flink:xrCreateApiLayerInstance command.
. The API layer copies the slink:XrApiLayerCreateInfo structure into it's own structure.
. The API layer then updates it's version of the slink:XrApiLayerCreateInfo structure
setting pname:nextInfo to point to the slink:XrApiLayerNextInfo for the
next API layer (i.e. `XrApiLayerCreateInfo->nextInfo =
XrApiLayerCreateInfo->nextInfo->next;`).
. The API layer may validate that it is getting the correct next information by
checking that the pname:layerName matches.
. The API layer then uses the information out of its sname:XrApiLayerNextInfo to call
down the call-chain to the next fname:xrCreateApiLayerInstance, using a
pointer to its slink:XrApiLayerCreateInfo structure instead of the one
that was passed in during its fname:xrCreateApiLayerInstance command.
.. If the call passes, this API layer may choose to setup its own dispatch table to
the next API layer's commands using the returned `XrInstance`, the next
API layer's `xrGetInstanceProcAddr` and the
fname:GeneratedXrPopulateDispatchTable utility command provided in the
generated `xr_generated_dispatch_table.h` header.
. Finally, the API layer should return the result passed in from the next API layer.
|