Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/native_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ on:
- '*'
paths-ignore:
- 'lglpy/**'
- '**/*.md'
pull_request:
branches:
- main
paths-ignore:
- 'lglpy/**'
- '**/*.md'

jobs:
build-ubuntu-x64-clang:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/python_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ on:
- main
tags:
- '*'
paths-ignore:
- '**/*.md'
pull_request:
branches:
- main
paths-ignore:
- '**/*.md'

jobs:
python-test:
Expand Down
65 changes: 35 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,56 @@
# About

libGPULayers provides tooling to rapidly create new Vulkan layer drivers,
allowing developers to quickly generate new layers that can be used for
ad hoc experiments during development.
libGPULayers provides tooling to create new Vulkan layer drivers, allowing
you to quickly generate new layers suitable for creation of new developer tools
or for ad hoc experiments during development.

In addition, we provide a number of pre-built layers that have been built
using these tools. These layers can be used as standalone tools in their
own right, and some can be used alongside other Arm tools such as Arm
Performance Studio.
using this framework. These layers might be used as standalone tools in their
own right, or might be used alongside other Arm tools such as
[Arm Performance Studio][2].

## What are layer drivers?

Layers drivers provide a standard mechanism to inject diagnostic functionality
Layer drivers provide a standard mechanism to inject diagnostic functionality
between an application and the underlying graphics driver. Layer drivers
intercept the graphics API calls from the application, perform their diagnostic
function, and then make any necessary API calls into the underlying graphics
driver to actually perform the rendering operations. The ability to see, and
change, everything that the native driver sees makes layers an exceptionally
powerful tool for debugging functional and performance issues.
function, and then call into the underlying graphics driver to actually perform
the requested operation. The ability to see, and change, the calls into the
native driver makes layers an exceptionally powerful tool for debugging both
functional and performance issues.

The Vulkan API defines a standard layer driver mechanism. The API uses layers
to implement API parameter validation and error checking, but they are also a
general purpose mechanism for all kinds of developer tooling.
Layer drivers are designed in to the Vulkan API, and they are the mechanism
for common workflows such as error checking using the Vulkan Validation Layer
(VVL), but they are also a general purpose mechanism suitable for all kinds of
developer tooling.

## What is the purpose of this project?

We support many application developers during their development cycle. We
rarely get access to application source code, so layer drivers provide us with
an invaluable mechanism to make modifications to application API usage. The
`GPU Support` layer in this project is a a tool we use during technical support
investigations to quickly triage developers problems.
We help many application developers to investigate issues during their
development cycle. We rarely get access to application source code for these
investigations, and cannot change drivers on production devices. Layer drivers
provide us with an invaluable mechanism to monitor and make modifications to
application API usage without needing to modify the application itself. The
`GPU Support` layer in this project is a tool we use during technical
support investigations to quickly triage problems.

We also use layer drivers as a way to develop new API-aware debug and profiling
capabilities. The performance layers in this repository, such as the
`GPU Timeline` layer, are often early prototypes that we want to share with
developers to test new ideas and gather feedback. Some are designed to be used
as standalone development tools, others can also be used alongside other Arm
tools such as the Arm Streamline profiler in [Arm Performance Studio][2].
`GPU Profile` and `GPU Timeline` layers, are used to profile performance,
or add API-aware annotations to performance captures made using other tooling.

As you can tell, we find layers exceptionally useful. However, creating a new
layer from scratch requires a lot of boilerplate code and is fiddly to get
right. We therefore also wanted to take this opportunity to share our layer
generation tools which make it trivial to create a complete bare-bones layer
that is ready to extend and use.
As you might be able to tell, we find layers exceptionally useful, and we
often want to create ad hoc layers to use for one-off experiments. Creating a
new layer from scratch requires a lot of code and is fiddly to get right, with
obscure errors when it doesn't work, so we wrote a tool to automate layer
creation. This final part of this project is this layer generation tooling,
which you use to quickly create a new layer that is ready to deploy.

## Supported devices

This library is currently tested on devices running Android or Linux, and using
Arm® Immortalis™ and Arm Mali™ GPUs. Contributions adding support for other
platforms is welcome.
platforms are welcome.

# License

Expand All @@ -60,14 +62,17 @@ from this repository you acknowledge that you accept terms specified in the

Common documentation

* [Building a new layer](./docs/building.md)
* [Building a layer](./docs/building.md)
* [Creating a new layer](./docs/creating.md)
* [Running using a layer on Android](./docs/running_android.md)
* [Running using a layer on Linux](./docs/running_linux.md)
* [About layers design notes](./docs/about_layers.md)
* [Frequently asked questions](./docs/faq.md)

Layer documentation

* [Layer: GPU Support](./layer_gpu_support/README_LAYER.md)
* [Layer: GPU Profile](./layer_gpu_support/README_LAYER.md)
* [Layer: GPU Timeline](./layer_gpu_timeline/README_LAYER.md)

# Support
Expand Down
111 changes: 111 additions & 0 deletions docs/about_layers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# About Vulkan layers

This page captures some interesting points to note about Vulkan layers, and is
mostly intended for developers of layers and maintainers of this project.

## Android vs Linux differences

Linux and Windows use the Khronos Vulkan loader, which has been extended over
time to have a richer loader-layer interface to support more use cases. The
current Khronos loader implements the v2 protocol from the
[Loader-Layer Interface][LLI] specification.

Android uses a custom Vulkan loader, which supports the basic v0 protocol with
some Android-specific limitations. This interface is functional, but lacks
some useful capabilities of the Khronos layer such as being able to intercept
pre-instance functions in implicit layers.

The libGPULayers framework has been designed to support both loaders, but
currently only supports functionality that works with the Android loader. There
are some areas that could be improved for Linux.

## Layer lifetime

Layer lifetime is managed by the Vulkan loader, and it is possible for a layer
to get loaded and unloaded multiple times within the lifetime of a single
application process. When a layer is unloaded, any global state is lost so
there is no way to use memory to persist per-process state in a layer as you
cannot guarantee you stay loaded.

On Android, layer libraries are loaded when the loader needs them (for a query
or to create an instance) and will be unloaded when a non-zero `VkInstance`
refcount is decremented and hits zero. They might subsequently be reloaded
again if the application restarts using Vulkan functionality.


## Querying Instance version

It could be useful for a layer to query `vkEnumerateInstanceVersion()` to
determine the maximum possible Vulkan API version supported on a platform,
although note that a device version might be a lower version so you need
to check both.

It is not possible for a layer to hook pre-instance functions on Android, and
only implicit layers are allowed to do it with the Khronos loader, so we do
not support doing this in libGPULayers. Layers must defer checking the
supported API versions until they get a concrete `VkDevice` to query, which
they would have to anyway, because the device version might be different to the
instance version.

Note: querying device version is much easier, because that uses normal
dispatchable Vulkan API functions, not pre-instance functions.

## Querying Instance extensions

Similar to the above, it could be useful for a layer to query the available
instance extensions using `vkEnumerateInstanceExtensionProperties()`. This
function is also a pre-instance function, and has the same limitations on
layer use as `vkEnumerateInstanceVersion()` in the section above, so it's not
supported in libGPULayers.

Because a layer cannot query the supported Vulkan version, or the available
instance extensions, layers that require the implementation beneath them to
support a specific extension simply have to assume that it is available.

This might result in an error on instance creation if the extension is not
supported. One possibility is that `vkCreateInstance()` will return
`VK_ERROR_EXTENSION_NOT_PRESENT`, because the extension is known but not
supported. Alternatively, it might result in undefined behavior, because the
layer passes in an extension structure on the `pNext` chain which is not known
by the version of Vulkan implemented by the loader or the driver.

Note: querying device extensions is much easier, as that uses normal
dispatchable Vulkan API functions, not pre-instance functions.

## Adding new extensions

Layers might expose extensions that the driver does not. Layers advertise their
new extensions by adding the extension strings and versions to the extension
properties list returned by `vkEnumerateInstanceExtensionProperties()` and
`vkEnumerateDeviceExtensionProperties()` when the `pLayerName` parameter is
the current layer's name.

For device extensions, it is also possible to modify the extension list
returned by the driver below by adding our extensions to the list returned
when `pLayerName` is `nullptr`.

The specification requires that implementations do not expose extensions that
conflict with other extensions but, given that a layer has no way to check
what other layers might be exposing, we just assume that our list is safe to
expose.

## Hiding device extensions

Layers might hide device extensions exposed by the layers below by modifying
the list returned by `vkEnumerateDeviceExtensionProperties()` when calling
down the stack, removing entries that the layer wants to hide before
returning it to the caller.

Note: This is only possible for device extensions, because instance extensions
are discovered per component by the loader, not in a layered manner.


## References

* [Loader-Layer Interface][LLI].

- - -

[LLI]: https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs

_Copyright © 2025, Arm Limited and contributors._
Loading