Extending "Helloworld" Simple Layer

The 'Helloworld' Simple Layer is a minimal implementation that demonstrates common basic functionality of an Intel® GPA Framework layer. Not every layer interacts with every API call. For example, a layer may require to intercept only present calls. There is a certain complexity in creating a layer, as it is necessary to account for API-specific behavior and the layer system expects a certain interface. This guide teaches the basics of adding to, or modifying the 'Helloworld' Simple Layer.

Layer Anatomy

Layers are structured as a dynamically linked/shared library.

The core of a layer is a special structure called GPADispatchTable, part of which is explicitly defined in dispatch-table.h. However, the file also includes in-line lists of funtions for every available API. These lists are added when the layer is loaded by your initialization code. Every layer dispatch table is stacked in the loading order to provide complete access to every API call to every layer.

Layers must at least have a function with the signature GPA_LAYER_EXPORT GPADispatchTable* LAYER_ENTRYPOINT(). The layer system looks for the signature while loading the layer. This function should contain all your initialization. Use the function to explicitly define behavior. After the function execution the code in your layer runs only if the correct hook is triggered.

You can also use this function to connect your custom function to the specific API calls.

It is recommended to include layer specific globals and function implementations into a namespace named after the layer.

To document the layer usage, you can use the second entry point with the signature GPA_LAYER_EXPORT TCHAR const* LAYER_HELP_FUNCTION together with the gpa-help utility.

API Call Routing

To add a custom override function, do the following:

  1. In the main file of the layer, before your LAYER_ENTRYPOINT, list the function definitions to be overridden. These definitions are identical to the corresponding API calls, with the addition of a const pointer to the next layer. You should use the inbuilt HAVE_DIRECTX and HAVE_VULKAN together with the preprocessor directive #if defined to ensure platform cross-compatibility. For example, overriding a Metal* API call on a Windows* machine that has only DirectX* causes fatal build issues.

  2. Implement the custom behavior of your function. Create separate files for each API override to avoid the build issues. Include these files into the layer main file using the #if defined directive.

  3. In the LAYER_ENTRYPOINT, connect your custom implementation to the layer dispatch table by assigning a function pointer to your implementation to the appropriate slot in the dispatch table.

  4. Ensure that the LAYER_ENTRYPOINT returns the modified dispatch table. If you extend the test layer, this step is already done.

The created custom function is run when the correct API call is made and your layer is loaded.