File: design.adoc

package info (click to toggle)
openxr-sdk-source 1.0.14~dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,564 kB
  • sloc: python: 16,103; cpp: 12,052; ansic: 8,813; xml: 3,480; sh: 410; makefile: 338; ruby: 247
file content (867 lines) | stat: -rw-r--r-- 32,017 bytes parent folder | download
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
// Copyright (c) 2017-2021, The Khronos Group Inc.
//
// SPDX-License-Identifier: CC-BY-4.0

[[loader-design]]
== Loader Design ==

This section of the document is focused on the internal design of the OpenXR
loader provided in the `src/loader` folder.

The OpenXR loader is composed of several classes.
The overall class diagram looks roughly like the following:

image::images/class_diagram.svg[align="center", title="Loader Class Diagram"]

[[class-roles]]
=== Class Roles ===

==== Manifest File Classes ====

The Desktop OpenXR loader uses JSON manifest files for information about
available <<api-layer-manifest-file-format, API layers>> and
<<runtime-manifest-file-format, runtimes>>.
All the functionality for finding, parsing, and processing the various
manifest file formats are found in the three manifest file classes:
`ManifestFile`, `RuntimeManifestFile`, and `ApiLayerManifestFile`.
All three classes are defined in the `manifest_file` header (.hpp) and
source (.cpp) files.

[[manifestfile]]
.ManifestFile

`ManifestFile` is the base class containing the majority of common code.
It only performs a minimal set of validation and provides accessor
functions.
These functions perform all the work using two primary sources:

* The std::experimental::filesystem pass:[C++] functionality exposed by most
  modern compilers for finding the manifest files.
* The https://github.com/open-source-parsers/jsoncpp[JsonCPP] library for
  processing and validating the manifest files once located.


[[runtimemanifestfile]]
.RuntimeManifestFile

`RuntimeManifestFile` is a derived class (with <<manifestfile,ManifestFile>>
as the parent) which handles specifically finding and parsing any
<<runtime-manifest-file-format, runtime manifest files>>.
It implements a static function fname:FindManifestFiles which is to be used
as a factory method for finding all available runtime manifest files,
creating an instance of `RuntimeManifestFiles` for each, and saving them if
they appear to be valid.
This is the command that any OpenXR command inside the loader should call if
it requires a list of available runtime manifest files.

[source,c++]
----
static XrResult RuntimeManifestFile::FindManifestFiles(
            std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files);
----
  * pname:manifest_files is a vector that will be used to store a unique_ptr
    to each valid runtime manifest file found.

The `RuntimeManifestFile` also provides a utility function for creating an
instance of itself if it determines everything is valid inside the JSON
file.
fname:CreateIfValid not only validates the JSON format, but also verifies
that the specific fields required by the RuntimeManifestFile class are
available.

[source,c++]
----
void RuntimeManifestFile::CreateIfValid(
            std::string filename,
            std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files);
----
  * pname:filename indicates the absolute file name path to the manifest
    file that needs to be loaded and verified.
  * pname:manifest_files is a vector that will be used to store a unique_ptr
    to each valid runtime manifest file found.
    If this call determines the file exists and is valid, it will create an
    instance of `RuntimeManifestFile` and add it to this vector.


[[apilayermanifestfile]]
.ApiLayerManifestFile

Similar to <<runtimemanifestfile,RuntimeManifestFile>>,
`ApiLayerManifestFile` is a derived class (with
<<manifestfile,ManifestFile>> as the parent).
This class handles specifically finding and parsing any
<<api-layer-manifest-file-format, API layer manifest files>>.
It also implements a static function fname:FindManifestFiles which is to be
used as a factory method for finding all available API layer manifest files,
creating an instance of `ApiLayerManifestFiles` for each, and saving them if
they appear to be valid.
This is the command that any OpenXR command inside the loader should call if
it requires a list of available API layer manifest files.

[source,c++]
----
static XrResult ApiLayerManifestFile::FindManifestFiles(
            ManifestFileType type,
            std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
----
  * pname:type indicates the specific type of manifest file being searched
    for.
    In this case, it must: be either `MANIFEST_TYPE_IMPLICIT_API_LAYER` or
    `MANIFEST_TYPE_EXPLICIT_API_LAYER`.
  * pname:manifest_files is a vector that will be used to store a unique_ptr
    to each valid API layer manifest file found.

The `ApiLayerManifestFile` also provides a utility function for creating an
instance of itself if it determines everything is valid inside the JSON
file.
fname:CreateIfValid not only validates the JSON format, but also verifies
that the specific fields required by the ApiLayerManifestFile class are
available.

[source,c++]
----
void ApiLayerManifestFile::CreateIfValid(
            std::string filename,
            std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
----
  * pname:filename indicates the absolute file name path to the manifest
    file that needs to be loaded and verified.
  * pname:manifest_files is a vector that will be used to store a unique_ptr
    to each valid runtime manifest file found.
    If this call determines the file exists and is valid, it will create an
    instance of `ApiLayerManifestFile` and add it to this vector.


==== Library Interface Classes ====

The OpenXR loader is responsible with interfacing with other libraries to
complete its tasks.
Typically, there will be some interface negotiation between these other
libraries and the loader (see <<loader-api-layer-interface-negotiation,
Loader/Layer Interface Negotiation>> or
<<loader-runtime-interface-negotiation, Loader/Runtime Interface
Negotiation>> for more information).
The Interface classes are responsible with using the contents of a
ManifestFile to setup the interface.
Each Interface class must be used with the appropriate ManifestFile type to
properly interface with a specific type of library.


[[runtimeinterface]]
.RuntimeInterface

The `RuntimeInterface` is tasked with working with runtime libraries.
Once the runtime manifest file information is found and parsed by the
<<runtimemanifestfile, RuntimeManifestFile>> class, it is passed along to
this class which will load the appropriate library and perform the required
<<loader-runtime-interface-negotiation, Loader/Runtime Interface
Negotiation>>.

The `RuntimeInterface` implements a static function fname:LoadRuntime which
is to be used as a factory method for loading the current active runtime,
performing the appropriate negotiation, and then creating an instance of
`RuntimeInterface`.
This is the command that any OpenXR command inside the loader should call if
it requires loading the runtime.
The command will only truly load the runtime library if it is not currently
loaded, and then keep a reference count of how many requests to load it have
been made.

[source,c++]
----
static XrResult RuntimeInterface::LoadRuntime();
----

Likewise, the `RuntimeInterface` implements a static function
fname:UnloadRuntime which is to be used to unload the current active
runtime.
This will reduce the reference count until it is 0 and then unload the
active runtime library.

[source,c++]
----
static XrResult RuntimeInterface::UnloadRuntime();
----

Finally, the `RuntimeInterface` implements a static function
fname:GetRuntime which is used to return the single instance of the
`RuntimeInterface` class.

[source,c++]
----
static RuntimeInterface& GetRuntime();
----

During `XrInstance` creation and destruction, the `RuntimeInterface` needs
to work directly with the runtime library.
To do this, the `RuntimeInterface` has two methods that are used by the
OpenXR loader:

[source,c++]
----
XrResult RuntimeInterface::CreateInstance(
        const XrInstanceCreateInfo* info);
----
  * pname:info is a pointer to the `XrInstanceCreateInfo` passed into the
    OpenXR `xrCreateInstance` command that triggered this call.

Once successful completion occurs, the `RuntimeInterface` will:

* Store the runtime's version of the `XrInstance`
* Generate a dispatch table to all known OpenXR commands implemented by the
  runtime.

To destroy a runtime's instance as part of the `xrDestroyInstance` command,
the loader calls fname:DestroyInstance;

[source,c++]
----
XrResult RuntimeInterface::DestroyInstance();
----


[[apilayerinterface]]
.ApiLayerInterface

Similarly, the `ApiLayerInterface` class is tasked with working with API
layer libraries.
Once the API layer manifest file information is found and parsed by the
<<apilayermanifestfile, ApiLayerManifestFile>> class, it is passed along to
this class which will load the appropriate library and perform the required
<<loader-api-layer-interface-negotiation, Loader/Layer Interface
Negotiation>>.

The `ApiLayerInterface` implements a static function fname:LoadApiLayers
which is to be used as a factory method for loading all available API
layers, performing the appropriate negotiation, and then creating an
instance of `ApiLayerInterface` for each.
This is the command that any OpenXR command inside the loader should call if
it requires load one or more API layers:

[source,c++]
----
static XrResult ApiLayerInterface::LoadApiLayers(
        std::vector<std::unique_ptr<ApiLayerManifestFile>>& manifest_files,
        std::vector<std::string> enabled_layers,
        std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces);
----
  * pname:manifest_files is a vector of unique_ptr elements containing the
    loaded API layer manifest information.
    The contents of this vector will be either transferred to a new
    `ApiLayerInterface` object placed in the pname:api_layer_interfaces
    vector, or deleted when the call to this method completes.
  * pname:enabled_layers is a vector of names for all API layers that are
    enabled by the environment or the user.
  * pname:api_layer_interfaces is a vector that will be used to store a
    unique_ptr to a `ApiLayerInterface` object representing each valid API
    layer that is enabled and has completed loading and negotiation.


==== The LoaderInstance Class ====

The primary OpenXR object is the `XrInstance`, and from that most other data
is either queried or created.

A `LoaderInstance` is created during the OpenXR `xrCreateInstance` call, and
destroyed during the `xrDestroyInstance` call.
During `xrCreateInstance` the loader code calls
`LoaderInstance`::pname:CreateInstance factory method:

[[CreateInstance]]
[source,c++]
----
static XrResult LoaderInstance::CreateInstance(
        PFN_xrGetInstanceProcAddr get_instance_proc_addr_term,
        PFN_xrCreateInstance create_instance_term,
        PFN_xrCreateApiLayerInstance create_api_layer_instance_term,
        std::vector<std::unique_ptr<ApiLayerInterface>> layer_interfaces,
        const XrInstanceCreateInfo* createInfo,
        std::unique_ptr<LoaderInstance>* loader_instance);
----
  * pname:get_instance_proc_addr_term is the function pointer to the
    terminator for xrGetInstanceProcAddr.
  * pname:create_instance_term is the function pointer to the terminator for
    xrCreateInstance.
  * pname:create_api_layer_instance_term is the function pointer to the
    terminator for xrCreateApiLayerInstance.
  * pname:api_layer_interfaces is a vector that contains a unique_ptr to all
    `ApiLayerInterface` objects that are valid and enabled.
    All of these pointers will be moved to the `LoaderInstance` on
    successful completion of the `CreateInstance` call.
  * pname:info is a pointer to the `XrInstanceCreateInfo` passed into the
    OpenXR `xrCreateInstance` command that triggered this call.
  * pname:instance contains a returned pointer to the `XrInstance` that will
    be returned upon successful execution and associated with this
    `LoaderInstance` object.

During the fname:CreateInstance call, the loader will perform the following
work:

* Generate the call chain for both `xrCreateInstance` and
  `xrGetInstanceProcAddr` that passes through all enabled API layers and the
  runtime.
* Create the instance using the generated `xrCreateInstance` call chain.
* Create a parallel `LoaderInstance` associated with the returned
  `XrInstance`.
* Generate a top-level dispatch table containing all the supported commands.
** This table is built by using the generated `xrGetInstanceProcAddr` call
   chain

Because the loader knows what runtime need to be called as part of the
create sequence, it inserts a terminator during the `xrCreateInstance`
sequence called `loaderXrTermCreateInstance` after the last API layer in
order to create the runtime instance.


==== Logging Classes ====

.LoaderLogger

The `LoaderLogger` class was created to provide global logging capability to
the OpenXR loader.
It was implemented as a Singleton to reduce the overhead of passing
pointers/references around to the various loader objects.

To get a reference to the `LoaderLogger` singleton, use the
fname:GetInstance method:

[source,c++]
----
static LoaderLogger& LoaderLogger::GetInstance();
----

The `LoaderLogger` works by sending all received messages to various
instances of <<loaderlogrecorder, LoaderLogRecorder> objects.
To add a `LoaderLogRecorder` to the `LoaderLogger`, call
fname:AddLogRecorder:

[source,c++]
----
void LoaderLogger::AddLogRecorder(
        std::unique_ptr<LoaderLogRecorder>& recorder);
----
  * pname:recorder is a unique_ptr to a create `LoaderLogRecorder` or
    derived object.

Once added, general log messages will be passed to each of the
`LoaderLogRecorder` stored in an internal vector.
Any source inside of the loader may trigger a log message by using the
pname:LogMessage command:

[source,c++]
----
bool LoaderLogger::LogMessage(
        XrLoaderLogMessageSeverityFlagBits message_severity,
        XrLoaderLogMessageTypeFlags message_type,
        const std::string& message_id,
        const std::string& command_name
        const std::string& message,
        const std::vector<XrLoaderLogObjectInfo>& objects = {});
----
  * pname:message_severity the severity of the message
  * pname:message_type is type of the message
  * pname:message_id is the message id, typically for loader messages this
    is "OpenXR-Loader"
  * pname:command_name is the name of the OpenXR command associated with the
    message.
    May be an empty string.
  * pname:message is the message.
  * pname:objects a vector of objects that are relevant to this message.
    May be empty.

Because of the complex nature of that method, and the fact that most log
messages can be simplified, the OpenXR loader also supplies the following
static methods for logging:

[source,c++]
----
static bool LogErrorMessage(
        const std::string& command_name,
        const std::string& message,
        const std::vector<XrLoaderLogObjectInfo>& objects = {});
static bool LogWarningMessage(
        const std::string& command_name,
        const std::string& message,
        const std::vector<XrLoaderLogObjectInfo>& objects = {});
static bool LogInfoMessage(
        const std::string& command_name,
        const std::string& message,
        const std::vector<XrLoaderLogObjectInfo>& objects = {});
static bool LogVerboseMessage(
        const std::string& command_name,
        const std::string& message,
        const std::vector<XrLoaderLogObjectInfo>& objects = {});
----
  * pname:command_name the OpenXR command that is related to the message.
    May be an empty string.
  * pname:message the message that needs to be logged
  * pname:objects an optional array of OpenXR object handles that are
    related to the log message.

It's important to note that these static methods also take care of grabbing
the `LoaderLogger`::fname:GetInstance() and making the appropriate call to
`LoaderLogger`::pname:LogMessage().

[example]
.Using the Log Messages
====
Here are a few examples of triggering a log message:

[source,c++]
----
XrResult res = xrCreateInstance(info, instance);
if (XR_SUCCESS != res) {
    std::string error_message = "xrCreateInstance failed with result ";
    error_message += std::to_string(res);
    LoaderLogger::LogErrorMessage("", error_message);
}
----

[source,c++]
----
// After successfully adding all API layers
LoaderLogger::LogInfoMessage("", "Loaded all API layers");
----

In these examples, the message does not correspond to a named command so an
empty string is passed.
====

[[loaderlogrecorder]]
.LoaderLogRecorder

The `LoaderLogRecorder` is a base class that defines the basics used for
recording a log message somewhere.
`LoaderLogRecorder` provides no protections for multithreading logging.
Any required protections should be implemented by the derived class that is
multithread sensitive (i.e. if a logger wrote to a file).

The pure virtual method the base class defines that is used to record log
messages is the fname:LogMessage method.
Each derived class is responsible for defining exactly how log messages are
recorded.

[source,c++]
----
virtual bool LogMessage(
        XrLoaderLogMessageSeverityFlagBits message_severity,
        XrLoaderLogMessageTypeFlags message_type,
        const XrLoaderLogMessengerCallbackData* callback_data) = 0;
----

Some utility methods that the `LoaderLogRecorder` base class supplies allow
controls over whether or not log messages are actually recorded.
Upon creation, a `LoaderLogRecorder` is set to actively record all messages
that contain the appropriate flags.
However, if we want to pause recording to one or more of the
`LoaderLogRecorders` at some point and then resume recording again later,
the following utilities can be used:

[source,c++]
----
virtual void Pause();
virtual void Resume();
bool IsPaused();
----

.LoaderLogRecorder Derived classes

Currently, there are two private classes derived from `LoaderLogRecorder`,
providing three basic behaviors:

* `OstreamLoaderLogRecorder`
** Outputs to `std::cerr` when created with `MakeStdErrLoaderLogRecorder()`
** Outputs to `std::cout` when created with `MakeStdOutLoaderLogRecorder()`
* `DebugUtilsLogRecorder`
** Created by `MakeDebugUtilsLoaderLogRecorder()`

The recorder created by `MakeStdErrLoaderLogRecorder()` handles recording
all error messages that occur in the loader out to `std::cerr`.
This logger is always enabled and is intended to always provide error
messages for easier issue diagnosis.

The recorder created by `MakeStdOutLoaderLogRecorder()` records messages out
to `std::cout`.
This logger is enabled when the <<loader-debugging, XR_LOADER_DEBUG>>
environment variable is defined.

The recorder created by `MakeDebugUtilsLoaderLogRecorder()` triggers an
`XR_EXT_debug_utils` callback every time a log message occurs.
Two steps are required before the loader enables this class:

 1. The `XR_EXT_debug_utils` must: be enabled during fname:xrCreateInstance
    call.
 2. The application must: create a `XrDebugUtilsMessengerEXT` by
  * Supplying an sname:XrDebugUtilsMessengerCreateInfoEXT structure to the
    sname:XrInstanceCreateInfo::pname:next chain during
    fname:xrCreateInstance, or
  * Calling fname:xrCreateDebugUtilsMessengerEXT.

[[automatically-generated-code]]
=== Automatically Generated Code ===

In order to allow the OpenXR loader to be as flexible as possible, we
generate a large portion of the code automatically using the xr.xml registry
file.
The generation process is triggered during the build.
This is done inside the `CMakelists.txt` files in both the `src` and
`src/loader` folders, using the macro `run_xml_generate_dependency`.
This macro (defined in the `src/CMakelists.txt` file, triggers python and
generates the source.
The generation scripts are based on the functionality originally defined in
the of the `specification/scripts` folder, but here they've been extended to
generate loader source code.

The loader automatic code generation scripts are found in the `src/scripts`
folder.
The main script of interest for OpenXR loader code generation is
`automatic_source_generation.py` which generates 4 files during the build
process:

.Automatically Generated Loader Files
[width="70%",options="header",cols="50,50"]
|====
| Location | Filename
.2+| <build>/src folder
    l| xr_generated_dispatch_table.h
    l| xr_generated_dispatch_table.c
.2+| <build>/src/loader folder
    l| xr_loader_generated.hpp
    l| xr_loader_generated.cpp
|====

==== xr_generated_dispatch_table.h ====

This C-style header contains the definition of the
sname:XrGeneratedDispatchTable structure.
This structure can be used to store function pointers for any OpenXR
commands defined in the xr.xml at the time the loader was built.
It includes slots for both core and extension function pointers.
Currently, the loader uses this structure as well as the provided API
Layers.

A partial listing from the generated table follows:

[[XrGeneratedDispatchTable]]
[source,c++]
----
// Generated dispatch table
struct XrGeneratedDispatchTable {

    // ---- Core 1.0 commands
    PFN_xrGetInstanceProcAddr GetInstanceProcAddr;
    PFN_xrEnumerateApiLayerProperties EnumerateApiLayerProperties;
    PFN_xrEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties;
    PFN_xrCreateInstance CreateInstance;
    PFN_xrDestroyInstance DestroyInstance;
    ...
};
----

You'll notice that the `xr` prefix was dropped on the name of the elements
within the structure to simplify naming as well as avoid any potential
compilation conflicts.

The `xr_generated_dispatch_table.h` header also includes a utility function
that can be used to populate a dispatch table once it has been created:

[[GeneratedXrPopulateDispatchTable,GeneratedXrPopulateDispatchTable]]
[source,c++]
----
void GeneratedXrPopulateDispatchTable(
        struct XrGeneratedDispatchTable *table,
        XrInstance instance,
        PFN_xrGetInstanceProcAddr get_inst_proc_addr);
----
  * pname:table is a pointer to the slink:XrGeneratedDispatchTable to
    populate.
  * pname:instance is the instance required by pname:get_inst_proc_addr.
    *NOTE*: This may have a value of `XR_NULL_HANDLE`, but many of the
    commands may be `NULL` if this is used.
  * pname:get_inst_proc_addr is a pointer to the `xrGetInstanceProcAddr`
    command to use to populate the table.
    If you're calling into the OpenXR loader, this would be the standard
    `xrGetInstanceProcAddr` call.
    However, if you were calling this from an API layer, you would want to
    use the next level's (API layer or runtime) implementation of
    `xrGetInstanceProcAddr`.


==== xr_generated_dispatch_table.c ====

This file is paired with the above `xr_generated_dispatch_table.h` header
and only implements the flink:GeneratedXrPopulateDispatchTable function used
to populate the elements of a dispatch table.


==== xr_loader_generated.hpp ====

`xr_loader_generated.hpp` contains prototypes for all the manually defined
instance command trampoline and terminator functions.
This is done so that they can be referenced in the `xr_loader_generated.cpp`
source file which is used for `xrGetInstanceProcAddr` as well as setting up
the loader dispatch table.

==== xr_loader_generated.cpp ====

The `xr_loader_generated.cpp` source file contains the implementation of all
generated OpenXR trampoline functions.

[[manually-implemented-code]]
=== Manually Implemented Code ===

Some OpenXR command terminator and trampoline functions need to be manually
implemented in the loader.

.Manually Implemented OpenXR Commands
[width="90%",options="header",cols="<.^,^.^,<.^"]
|====
| Command | Terminator/Trampoline | Reason
| xrEnumerateApiLayerProperties
  | Both (although terminator should never get called)
    | Loader needs to find and parse the various API layer manifest files.
| xrEnumerateInstanceExtensionProperties
  | Both
    | Loader needs to find and parse the various API layer manifest files.
      Also needs to call into runtime and query extensions supported by it.
| xrCreateInstance
  | Both
    | Loader needs to do all API layer and runtime discovery and processing as
      well as storing the results.  The storage is done inside a
      `LoaderInstance` class object, which is created during this call.
| xrDestroyInstance
  | Both
    | Loader needs to call down to all API layers destroying the instance, and
      then clean up its internal storage (i.e. the `LoaderInstance` class
      that was created earlier).
| xrCreateApiLayerInstance
  | Terminator
    | Loader uses this to capture the
      <<api-layer-create-instance-process,`xrCreateApiLayerInstance`>> chain used
      to create API layer instances.  This terminator will then re-direct the
      chain back to the standard `xrCreateInstance` path.
|====

[[functional-flow]]
=== Functional Flow ===

The loader supports a single XrInstance at a time in order to avoid tracking
handle values and their relationship to the `LoaderInstance`.
Every XR function call is assumed to be for the single XrInstance that has
been created.
This enables the loader to work with future extensions and handle types
without change.

[[platform-specific-behavior]]
=== Platform-Specific Behavior ===

The OpenXR loader design is intended to be flexible on supported on a
variety of platforms.
However, the loader on certain platforms will require behavior not necessary
in other environments.
This section describes the common platform-specific behavior expected in the
OpenXR loader.


[[library-handling]]
==== Library Handling ====

The loader works with libraries and runtimes which are exposed as either a
static or dynamic external library files.
Each operating system provides their own utilities for interfacing with
these files, which the loader abstracts.
Most loader platform code can be found in the following source file:

[source,c++]
----
src/loader/loader_platform.hpp
----

The OpenXR loader uses a general handle define for all platform library
functions.
This handle is identified as `LoaderPlatformLibraryHandle` and is used to
interact with all the platform-specific library functions.


.Opening A Platform-Specific Library File

To open a platform-specific library file, and therefore retrieve the
platform-specific `LoaderPlatformLibraryHandle` relative to that file, the
loader calls the fname:LoaderPlatformLibraryOpen function which has the
following prototype:

[[LoaderPlatformLibraryOpen]]
[source,c++]
----
LoaderPlatformLibraryHandle LoaderPlatformLibraryOpen(
    const std::string &path);
----
  * pname:path must: be a constant string containing the absolute path to
    the library file that needs to be opened.

If the function succeeds, the returned value will be non-NULL.
If a failure occurs during this call, the returned value will be NULL.
In the case of failure, the loader can call the
fname:LoaderPlatformLibraryOpenError function:

[[LoaderPlatformLibraryOpenError]]
[source,c++]
----
const char *LoaderPlatformLibraryOpenError(
    const std::string &path);
----
  * pname:path must: be a constant string containing the absolute path to
    the library file that the loader previously attempted to load using
    flink:LoaderPlatformLibraryOpen.

The returned C-style character string contains any available
platform-specific error code that may have occurred.


.Closing A Platform-Specific Library File

When the loader is done using the platform library, it calls
fname:LoaderPlatformLibraryClose to release it.

[[LoaderPlatformLibraryClose]]
[source,c++]
----
void LoaderPlatformLibraryClose(
    LoaderPlatformLibraryHandle library)
----
  * pname:library must: be valid `LoaderPlatformLibraryHandle` opened using
    flink:LoaderPlatformLibraryOpen


.Querying Content In a Platform-Specific Library File

Once a library is opened, the loader will query for the important functions
exported by a library using the fname:LoaderPlatformLibraryGetProcAddr
function:

[[LoaderPlatformLibraryGetProcAddr]]
[source,c++]
----
void *LoaderPlatformLibraryGetProcAddr
    LoaderPlatformLibraryHandle library,
    const std::string &name)
----
  * pname:library must: be valid `LoaderPlatformLibraryHandle` opened using
    flink:LoaderPlatformLibraryOpen, but not yet closed using
    flink:LoaderPlatformLibraryClose.
  * pname:name must: contain the name of the library supplied function who's
    function pointer is desired.


If the function succeeds, the returned value will be a valid function
pointer address.
If the function fails, it will return NULL.
A NULL return value could imply that the function simply isn't exported by
the library, or that an error occurred during the platform call.
To determine what might have happened, the loader will call the
fname:LoaderPlatformLibraryGetProcAddrError function:

[[LoaderPlatformLibraryGetProcAddrError]]
[source,c++]
----
const char *LoaderPlatformLibraryGetProcAddrError(
    const std::string &path);
----
  * pname:path must: be a constant string containing the name of the
    entry-point that was attempted to be queried during the previous
    flink:LoaderPlatformLibraryGetProcAddr call.

The returned C-style character string contains any available
platform-specific error code that may have occurred.


[[environment-variable-usage]]
==== Environment Variable Usage ====

Several environment variables are used in the OpenXR loader, especially on
the Desktop (Windows/Linux).
However, accessing environment variables is different based on each
operating system, so we have added global interface functions to use for
accessing environment variables.
These are defined in `src/common/platform_utils.hpp`

**NOTE:** This is outside of the loader source to allow other items in the
source folder to use these utilities.


.Reading a Standard Environment Variable

To read an environment variable, the loader calls fname:PlatformUtilsGetEnv:

[[PlatformUtilsGetEnv]]
[source,c++]
----
std::string PlatformUtilsGetEnv(
    const char *name);
----
  * pname:name must: be a non-NULL NULL-terminated C-style string indicating
    the name of the environment variable to get the value of.

If the environment variable identified by pname:name exists on the system,
the C-style NULL-terminated string will be returned.
If the environment variable can not be found, an empty string is returned.

If you want to distinguish between an empty value and a variable not set at
all, if the underlying platform distinguishes these cases, use
fname:PlatformUtilsGetEnvSet:

[[PlatformUtilsGetEnvSet]]
[source,c++]
----
bool PlatformUtilsGetEnvSet(
    const char *name);
----

It returns true if the environment variable is set.

.Reading a Secure Environment Variable

Access to certain environment variables needs to be done in a way that
maintains operational security of the program.
To read a secure environment variable, the loader calls
fname:PlatformUtilsGetSecureEnv:

[[PlatformUtilsGetSecureEnv]]
[source,c++]
----
char *PlatformUtilsGetSecureEnv(
    const char *name);
----
  * pname:name must: be a non-NULL NULL-terminated C-style string indicating
    the name of the environment variable to get the value of.

If the platform supports secure environment variable reading, the
appropriate method will be used.
Otherwise, it will fall back to the standard flink:PlatformUtilsGetEnv call.

[[active-runtime-file-management]]
==== Active Runtime File Management ====

.Querying the Active Runtime File name

Since the runtime file name can vary based on the supporting system, the
command fname:PlatformGetGlobalRuntimeFileName provides a quick mechanism
for querying the name of the file.

[NOTE]
.Note
****
Not all platforms implement this call.
****

[[PlatformGetGlobalRuntimeFileName]]
[source,c++]
----
bool PlatformGetGlobalRuntimeFileName(
    uint16_t major_version,
    std::string &file_name);
----
  * pname:major_version is the major API version for the OpenXR you are
    querying the active runtime file name for.
  * pname:file_name is the returned name of the runtime file.
    This is only valid if the command returns `true`.