@@ -242,12 +242,7 @@ static XrResult ApiLayerInterface::LoadApiLayers(
242242==== The LoaderInstance Class ====
243243
244244The primary OpenXR object is the `XrInstance`, and from that most other data
245- is either queried or created. In many ways, the `XrInstance` object is a
246- loader object that manages all the other underlying OpenXR objects/handles.
247- Because of this, a large amount of data needs to be associated with it by
248- the loader.
249- In order to do this, the loader maps an internal `LoaderInstance` object
250- pointer to the returned `XrInstance` value.
245+ is either queried or created.
251246
252247A `LoaderInstance` is created during the OpenXR `xrCreateInstance` call, and
253248destroyed during the `xrDestroyInstance` call.
@@ -258,10 +253,19 @@ During `xrCreateInstance` the loader code calls
258253[source,c++]
259254----
260255static XrResult LoaderInstance::CreateInstance(
261- std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces,
262- const XrInstanceCreateInfo* info,
263- XrInstance* instance);
264- ----
256+ PFN_xrGetInstanceProcAddr get_instance_proc_addr_term,
257+ PFN_xrCreateInstance create_instance_term,
258+ PFN_xrCreateApiLayerInstance create_api_layer_instance_term,
259+ std::vector<std::unique_ptr<ApiLayerInterface>> layer_interfaces,
260+ const XrInstanceCreateInfo* createInfo,
261+ std::unique_ptr<LoaderInstance>* loader_instance);
262+ ----
263+ * pname:get_instance_proc_addr_term is the function pointer to the
264+ terminator for xrGetInstanceProcAddr.
265+ * pname:create_instance_term is the function pointer to the
266+ terminator for xrCreateInstance.
267+ * pname:create_api_layer_instance_term is the function pointer to the
268+ terminator for xrCreateApiLayerInstance.
265269 * pname:api_layer_interfaces is a vector that contains a unique_ptr to all
266270 `ApiLayerInterface` objects that are valid and enabled. All of these
267271 pointers will be moved to the `LoaderInstance` on successful completion
@@ -279,8 +283,8 @@ work:
279283 `xrGetInstanceProcAddr` that passes through all enabled API layers and the
280284 runtime.
281285* Create the instance using the generated `xrCreateInstance` call chain.
282- * Create a parallel `LoaderInstance* ` associated with the returned
283- `XrInstance` using a C++ "unordered_map"
286+ * Create a parallel `LoaderInstance` associated with the returned
287+ `XrInstance`.
284288* Generate a top-level dispatch table containing all the supported commands.
285289** This table is built by using the generated `xrGetInstanceProcAddr`
286290 call chain
@@ -564,82 +568,11 @@ they can be referenced in the `xr_loader_generated.cpp` source file which
564568is used for `xrGetInstanceProcAddr` as well as setting up the loader
565569dispatch table.
566570
567- Also included is a utility function prototype that will initialize a
568- `LoaderInstance` dispatch table:
569-
570- [[LoaderGenInitInstanceDispatchTable]]
571- [source,c++]
572- ----
573- void LoaderGenInitInstanceDispatchTable(
574- XrInstance runtime_instance,
575- std::unique_ptr<XrGeneratedDispatchTable>& table);
576- ----
577- * pname:runtime_instance is the `XrInstance` returned by the runtime
578- on the related call to fname:xrCreateInstance and that should be
579- used to initialize pname:table.
580- * pname:table is the dispatch table to initialize.
581-
582- Additionally, this file contains extern prototypes for instances of the
583- `HandleLoaderMap<>` class template, containing mainly an `unordered_map`
584- and a `mutex`, that are used for storing the created OpenXR Object handles
585- and relating them to their parent `XrInstance`. There should be one
586- `HandleLoaderMap<>` instance for each OpenXR Object Handle type. See the
587- <<functional-flow, Functional Flow>> section for more information about
588- the usage of these.
589-
590- This file also contains the prototype of a function that is used to
591- clean up the OpenXR Object unordered_maps entries related to a
592- `LoaderInstance` that is being deleted:
593-
594- [[LoaderCleanUpMapsForInstance]]
595- [source,c++]
596- ----
597- void LoaderCleanUpMapsForInstance(
598- class LoaderInstance *instance);
599- ----
600- * pname:loader_instance is a pointer to the `LoaderInstance`
601- that is going away and needs all references removed from all the
602- global state.
603-
604571==== xr_loader_generated.cpp ====
605572
606573The `xr_loader_generated.cpp` source file contains the implementation
607574of all generated OpenXR trampoline functions.
608575
609- For example, it contains the implementations of
610- flink:LoaderGenInitInstanceDispatchTable and
611- flink:LoaderCleanUpMapsForInstance.
612-
613- It also includes a function automatically generated for the `ApiLayerInterface`
614- class that will update a dispatch table for that class,
615- `ApiLayerInterface`::fname:GenUpdateInstanceDispatchTable. The function
616- calls the API layer's version of `xrGetInstanceProcAddr` to populate the various
617- entries in a `XrGeneratedDispatchTable`.
618-
619- [source,c++]
620- ----
621- void ApiLayerInterface::GenUpdateInstanceDispatchTable(
622- XrInstance instance,
623- std::unique_ptr<XrGeneratedDispatchTable>& table);
624- ----
625- * pname:instance is the instance returned by the next component in the call-chain
626- during fname:xrCreateInstance and is used with the next component's
627- fname:xrGetInstanceProcAddr call.
628- * pname:table is a reference to the table to update with the
629- `ApiLayerInterface` 's API layer version of the fname:xrGetInstanceProcAddr.
630-
631- Because this is an API layer, it will only populate the entry of a dispatch table if
632- the API layer's `xrGetInstanceProcAddr` returns non-`NULL` for that command. This is
633- because the loader calls this command to generate the top-level dispatch table
634- when setting up the <<openxr-call-chains,call chain>>. The loader starts
635- by creating a dispatch table pointing to the runtime commands. It then
636- reverse increments through all enabled API layers, calling `ApiLayerInterface`::
637- fname:GenUpdateInstanceDispatchTable for each API layer, which replaces only
638- the commands that the specific API layer implements. This results in a
639- dispatch table whose elements point to only the first implementation of each
640- OpenXR command.
641-
642-
643576[[manually-implemented-code]]
644577=== Manually Implemented Code ===
645578
@@ -675,174 +608,14 @@ implemented in the loader.
675608 chain back to the standard `xrCreateInstance` path.
676609|====
677610
678-
679611[[functional-flow]]
680612=== Functional Flow ===
681613
682- The OpenXR loader makes all its decisions using OpenXR objects.
683- The loader's main goal when dealing with OpenXR objects is to find the
684- parent `LoaderInstance` for a given object.
685- This parent `LoaderInstance` stores the dispatch table to use when calling any
686- OpenXR command, so without the instance, the loader has no knowledge of where
687- to send a command.
688- This is because an application may create more than one `XrInstance`, with
689- some of the instances having API layers enabled and some without.
690- Without knowing which parent instance to use, the loader could end up calling
691- an incorrect sequence of commands.
692-
693-
694- ==== Correlating an Object to Its Parent XrInstance ====
695-
696- The loader automatic generated code tracks all OpenXR Object Handle types
697- using an `HandleLoaderMap<>` containing an
698- https://en.cppreference.com/w/cpp/container/unordered_map[unordered_map]
699- and a mutex.
700- Each type has it's own `HandleLoaderMap<>` associated with it.
701- This `HandleLoaderMap<>` correlates an object against its parent `LoaderInstance` pointer.
702-
703- [example]
704- .HandleLoaderMap Correlating XrSession to LoaderInstance*
705- ====
706- [source,c++]
707- ----
708- HandleLoaderMap<XrSession> g_session_map;
709- ----
710- ====
711-
712- Automatic code generation is used by capturing every `xrCreate` call,
713- determining the parent object type and then determining the created object
714- type.
715- When triggered, the call first proceeds normally down the call chain.
716- However, upon returning, an entry is created in this `HandleLoaderMap`.
717- When a handle is received, the loader first looks up the parent
718- `LoaderInstance` pointer using the `HandleLoaderMap` associated with the handle
719- type.
720- Once we have the parent object's `LoaderInstance*`, we create an entry using
721- this value in our new object type's `HandleLoaderMap`.
722-
723- [example]
724- .HandleLoaderMap Creation examples
725- ====
726-
727- Here's an example of how the automatically generated code looks for both
728- accessing and creating an object which receives the `LoaderInstance*` as its
729- parent.
730- To determine the `LoaderInstance*` associated with the incoming `XrInstance`,
731- you can see the lookup using the `g_instance_map` HandleLoaderMap.
732- Then, a new value associates the recently created `XrSession` with the
733- same `LoaderInstance*`.
734-
735- [source,c++]
736- ----
737- XRAPI_ATTR XrResult XRAPI_CALL xrCreateSession(
738- XrInstance instance,
739- const XrSessionCreateInfo* createInfo,
740- XrSession* session) XRLOADER_ABI_TRY {
741-
742- LoaderInstance *loader_instance = g_instance_map.Get(instance);
743- if (nullptr == loader_instance) {
744- LoaderLogger::LogValidationErrorMessage(
745- "VUID-xrCreateSession-instance-parameter", "xrCreateSession",
746- "instance is not a valid XrInstance",
747- {XrSdkLogObjectInfo{instance, XR_OBJECT_TYPE_INSTANCE}});
748- return XR_ERROR_HANDLE_INVALID;
749- }
750- const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table =
751- loader_instance->DispatchTable();
752- XrResult result = XR_SUCCESS;
753- result = dispatch_table->CreateSession(instance, createInfo, session);
754- if (XR_SUCCESS == result && nullptr != session) {
755- XrResult insert_result =
756- g_session_map.Insert(*session, *loader_instance);
757- if (XR_FAILED(insert_result)) {
758- LoaderLogger::LogErrorMessage("xrCreateSession",
759- "Failed inserting new session into "
760- "map: may be null or not unique");
761- }
762- }
763- return result;
764- }
765- XRLOADER_ABI_CATCH_FALLBACK
766- ----
767- ====
768-
769- Of course, all this is cleaned up when the appropriate `xrDestroy` command
770- is called.
771-
772- [example]
773- ====
774- [source,c++]
775- ----
776- XRAPI_ATTR XrResult XRAPI_CALL xrDestroySession(
777- XrSession session)
778- XRLOADER_ABI_TRY {
779- LoaderInstance *loader_instance = g_session_map.Get(session);
780- // Destroy the mapping entry for this item if it was valid.
781- if (nullptr != loader_instance) {
782- g_session_map.Erase(session);
783- }
784- if (nullptr == loader_instance) {
785- LoaderLogger::LogValidationErrorMessage(
786- "VUID-xrDestroySession-session-parameter", "xrDestroySession",
787- "session is not a valid XrSession",
788- {XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
789- return XR_ERROR_HANDLE_INVALID;
790- }
791- const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table =
792- loader_instance->DispatchTable();
793- XrResult result = XR_SUCCESS;
794- result = dispatch_table->DestroySession(session);
795- return result;
796- }
797- XRLOADER_ABI_CATCH_FALLBACK
798- ----
799- ====
800-
801- If a call to `xrDestroyInstance` is made before any objects related to the
802- `XrInstance` have been destroyed, they are still properly cleaned up by the
803- loader.
804- This is because the loader's `xrDestroyInstance` command calls
805- flink:LoaderCleanUpMapsForInstance to clean up all unordered_maps entries
806- associated with the instance before it's destroyed.
807-
808-
809- ==== Protecting the Unordered_Map Content ====
810-
811- In order to properly protect the contents of the unordered_map in a
812- multithreading scenario, we use a std::mutex per unordered_map to
813- control when the loader writes to, or reads from, the unordered_map.
814-
815- In general, use of these mutexes is handled automatically by the
816- `HandleLoaderMap<>` class template.
817-
818- ==== Potential Problems ====
819-
820- The OpenXR loader could run into issues, even with this design, under
821- certain scenarios:
822-
823- .Loader Problem Scenario 1
824-
825- If an application creates its own OpenXR command dispatch table, but still
826- also uses OpenXR commands exported directly from the loader, we could see an
827- issue. The issue arises if the application calls `xrCreate` for an object
828- using the dispatch table it generated. This would cause the `xrCreate`
829- command to bypass the loader's trampoline call (which is where the
830- unordered_map behavior exists). In this case, the loader would not create an
831- entry in the unordered_map. The problem becomes obvious if the user calls a
832- loader export which uses that command. Now, the loader has no way of finding
833- the parent instance. The loader could make a logical guess, but it may choose
834- the wrong parent instance if more than one has been created.
835-
836- [WARNING]
837- .TODO (i/761)
838- ====
839- * What do we do here?
840- ** I suggest we disallow mixing and matching using loader exports with
841- application generated dispatch tables.
842- ** At least where commands could take a different path for `xrCreate`
843- and usage.
844- ====
845-
614+ The loader supports a single XrInstance at a time in order to avoid
615+ tracingk handle values and their relationship to the `LoaderInstance`. Every
616+ XR function call is assumed to be for the single XrInstance that has been
617+ created. This enables the loader to work with future extensions and handle types
618+ without change.
846619
847620[[platform-specific-behavior]]
848621=== Platform-Specific Behavior ===
0 commit comments