Custom Layers: Part II
(Custom Layers: Part I -- Extending Custom Layers)
Authors
Jay Ravi, GPU Software Development Engineer, Intel Corporation
Pamela Harrison, Software Technical Consulting Engineer
Introduction:
In the previous blog post, Custom Layers: Part I -- Extending Intel® GPA Framework with Custom Layers, we saw how to write a basic custom layer. We wrote a hook/override for a single call (Swapchain present call). But what if you are writing a complex feature and you would like to generate hooks for all graphics API calls? It would be painstaking to look up documentation for each call signature and write a matching override function in the layers. This is where a layer generator is useful.
Overview:
A layer generator constitutes a set of API metadata files, python scripts and templated source files working with the Intel® GPA Framework CMake utility functions to produce generated C++ sources for our layer project. To clarify, ‘templated’ source files don’t refer to C++ templates but a file with variable placeholders/loops/conditional statements recognized by a templating engine traditionally used in web development to generate html pages. For our generator, we use the Jinja2 templating engine.
Writing our own generated layer:
In this contrived example, we will use the generator to create a simple logging layer which prints just names of the graphics API functions inside the hooks. We will go over project files of interest in this section.
First, we need to create a JSON text file called vulkan-custom.json or directx-custom.json. This is a prerequisite for generated layers. Depending on which API your layer is hooking, either or both API’s, JSON files are required. The files should be in the same folder as the layer sources and should have the following content:
vulkan-custom.json |
directx-custom.json |
|
|
Second, to be able to generate hook functions automatically we need metadata of the graphics API’s classes, functions and parameters. The metadata are available in JSON files for each graphics API that are shipped as part of the distribution. The metadata contains names of the function calls, parameters’ names and types, function return types, etc. Here is an excerpt from vulkan.json:
"functions": [
{
"name": "vkAcquireFullScreenExclusiveModeEXT",
"params": [
{
"InterfacePointer": true,
"Pointer": true,
"name": "device",
"type": "VkDevice"
},
{
"InterfacePointer": true,
"Pointer": true,
"name": "swapchain",
"type": "VkSwapchainKHR"
}
],
"returnValue": {
"Enum": true,
"type": "VkResult"
}
}
]
Third, templated C++ sources written in Jinja2 templating language. Templates contains a mix of data from JSON and Jinja function helpers to process. Additionally custom JSON files can be used to provide special handling for certain functions. Here is an excerpt from a template file. There is a lot going on here but the Jinja comments ({#..#}) explain each line. Additionally, the template legend gives a quick overview of the template syntax. We encourage you to check the Jinja 2 documentation for more information on templates.
{# Loop over function definitions and create layer overrides for each of them#}
{% for function in data.functions %}
{{function.returnValue.type}} {{data.callingConvention}} {{function.name | cStyleMethodName}}({{function |fullParameterString(true)}})
{
{# Use GPA log function to print function name #}
GPA_LOG_INFO("%s", "{{function.name}}");
{# Generate this statement ‘retValue =‘ if this function is not a void type #}
{% if not function.returnValue.Void %} {{function.returnValue.type}} retValue = {% endif %}
{#Call corresponding function in the dispatch table to forward call in the layer chain.#}
sDispatchTable.{{data.name}}.{{function.name | cStyleMethodName}}({{function | parameterNameString(true)}});
{# Generate return state if this is not a void function#}
{% if not function.returnValue.Void %}return retValue; {% endif %}
}
{% endfor %}
Template Legend: data – root dictionary object of JSON file in memory. name – API name currently being processed e.g., Vulkan. Comes from *-custom.json file. functions – array of function definitions in json var|jinja_filter - variables can be modified by Jinja filters using a pipe syntax. Filters are helper python functions that can further process and return processed data. |
Jinja Constructs: {{…}} – Print expressions {%…%} – Statements such as loops/conditionals {#…#} – Jinja comments. Not displayed in the final source file. |
Data for this template is populated from JSON files we discussed before. Here is an example function override generated by the above template.
HRESULT WINAPI D3D12CreateDevice(IUnknown * pAdapter, D3D_FEATURE_LEVEL MinimumFeatureLevel, const IID & riid, void ** ppDevice)
{
GPA_LOG_INFO("%s", "D3D12CreateDevice");
HRESULT retValue =
sDispatchTable.DirectX.D3D12CreateDevice(pAdapter, MinimumFeatureLevel, riid, ppDevice);
return retValue;
}
And here is another function with void return type.
void WINAPI Unmap(ID3D12Resource* self, UINT Subresource, const D3D12_RANGE * pWrittenRange)
{
GPA_LOG_INFO("%s", "ID3D12Resource::Unmap");
sDispatchTable.DirectX.ID3D12ResourceTable.Unmap(self, Subresource, pWrittenRange);
}
To bring all these files together we need the CMake function below, gpa_framework_add_layer(). It takes template files as arguments, which invokes the python generators. The inputs being the JSON files and templates and outputs are source files generated per supported API (Vulkan*, DirectX*). These generated source files are automatically added to the layer target.
gpa_framework_add_layer(${TARGET}
SOURCES
hello-generated-layer.cpp
FULL_TEMPLATES
templates/hello-generated.h.j2 # template for headers
templates/hello-generated.cpp.j2 # template for sources
templates/hello-generated-method-list.inl.j2
)
Summary
Layer generator is a vital tool for developing a lot of the well know prebuilt Intel® GPA Framework layers such as capture layer, logging layer, state tracking layer and many more. It is the quickest way to generate overrides for all graphics API calls. Users can build debugging tools using this generator to inspect every API call in a manner that they choose or create a new layer that uses a custom logging library for each call.
Resources
Download Intel® Graphics Performance Analyzers
Samples — Intel® GPA Framework documentation
Template Designer Documentation — Jinja Documentation (3.1.x) (palletsprojects.com)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.