mirror of
https://gitlab.freedesktop.org/mesa/vulkan-wsi-layer.git
synced 2025-12-20 02:10:13 +01:00
240 lines
9.9 KiB
Markdown
240 lines
9.9 KiB
Markdown
# WSI integration guide
|
|
|
|
This integration guide provides information relevant for a developer wishing to
|
|
extend the Vulkan® WSI Layer with support for a new windowing system. In Vulkan®
|
|
a windowing system is abstracted by a WSI platform and for each platform a specific
|
|
extension exists.
|
|
|
|
In the layer a Vulkan® WSI platform is represented by a WSI backend. Adding a
|
|
WSI backend means implementing a WSI extension. Two WSI extensions that are implemented
|
|
in the layer are `VK_KHR_wayland_surface` and `VK_EXT_headless_surface`.
|
|
|
|
## Directory Structure
|
|
|
|
Each WSI backend implementation resides in the `wsi` folder and has its own
|
|
directory. A new folder should be created for a new implementation,
|
|
for example `wsi/new_wsi`.
|
|
|
|
Each WSI backend implements the `surface`, `surface_properties`
|
|
and `swapchain` interfaces. A new WSI backend implementation should be structured
|
|
as follows, where a separate file is used to contain the implementation of each
|
|
interface:
|
|
|
|
```
|
|
wsi/new_wsi
|
|
├── surface.cpp
|
|
├── surface.hpp
|
|
├── surface_properties.cpp
|
|
├── surface_properties.hpp
|
|
├── swapchain.cpp
|
|
└── swapchain.hpp
|
|
```
|
|
|
|
## Build configuration
|
|
|
|
A new build option should be added to the [CMakeLists.txt](../CMakeLists.txt)
|
|
file to enable support for the new WSI backend. This allows compiling the layer
|
|
for platforms that do not have the necessary support for the backend.
|
|
|
|
Furthermore the extension that is implemented by the new backend must be added in
|
|
the `instance_extensions` list in the
|
|
[VkLayer_window_system_integration.json](../layer/VkLayer_window_system_integration.json)
|
|
file. For example, `VK_KHR_wayland_surface` is added to the JSON manifest when Wayland
|
|
support is enabled.
|
|
|
|
## Interfaces implementations
|
|
|
|
### surface_properties
|
|
|
|
This interface contains functions that will be called during the interception of
|
|
various Vulkan® surface related entrypoints. These functions should contain the
|
|
platform specific code required to support the new WSI backend. For example, some of
|
|
the functions that are intercepted are:
|
|
* vkGetPhysicalDeviceSurfaceCapabilitiesKHR
|
|
* vkGetPhysicalDeviceSurfaceCapabilitiesKHR2
|
|
* vkGetPhysicalDeviceSurfaceFormatsKHR
|
|
* vkGetPhysicalDeviceSurfaceFormatsKHR2
|
|
* vkGetPhysicalDeviceSurfacePresentModesKHR
|
|
|
|
A new WSI backend should implement the functions the interface consists of, which
|
|
are described in the [surface_properties.hpp](surface_properties.hpp) file.
|
|
|
|
Each WSI extension defines surface/platform specific entrypoints, which are only used by
|
|
each individual surface. These entrypoints are exposed to the application through
|
|
the `surface_properties::get_proc_addr` function, which returns a function pointer
|
|
to the implementation of the requested entrypoint. For example, if the extension for
|
|
a new WSI backend defines the entrypoint `vkCreateNewXyzSurface`, then `get_proc_addr`
|
|
should look like this.
|
|
|
|
```c++
|
|
VWL_VKAPI_CALL(VkResult) CreateNewXyzSurface(...) VWL_API_POST
|
|
{
|
|
...
|
|
}
|
|
|
|
PFN_vkVoidFunction surface_properties::get_proc_addr(const char *name)
|
|
{
|
|
if (strcmp(name, "vkCreateNewXyzSurface") == 0)
|
|
{
|
|
return reinterpret_cast<PFN_vkVoidFunction>(CreateNewXyzSurface);
|
|
}
|
|
...
|
|
}
|
|
```
|
|
|
|
> **_NOTE:_** The VWL_* macros are defined in [macros.hpp](../util/macros.hpp) and
|
|
are used to mark function signatures.
|
|
|
|
> **_NOTE:_** The extensions and their entrypoints are partially implemented by
|
|
the Vulkan® loader so an appropriate loader should be used.
|
|
|
|
|
|
If any of the extension's specific entrypoints needs to call the next implementation
|
|
in the chain (e.g. call the ICD's entrypoint implementation), depending on
|
|
whether it is an instance or device entrypoint either the
|
|
`INSTANCE_ENTRYPOINTS_LIST` or `DEVICE_ENTRYPOINTS_LIST` must be extended with
|
|
the name of the entrypoint. For example, for the `vkCreateNewXyzSurface` the instance
|
|
list should be extended.
|
|
|
|
```diff
|
|
diff --git a/layer/private_data.hpp b/layer/private_data.hpp
|
|
--- a/layer/private_data.hpp
|
|
+++ b/layer/private_data.hpp
|
|
@@ -73,6 +73,9 @@ namespace layer
|
|
OPTIONAL(CreateHeadlessSurfaceEXT) \
|
|
/* VK_KHR_wayland_surface */ \
|
|
OPTIONAL(CreateWaylandSurfaceKHR) \
|
|
+ /* VK_KHR_new_wsi */ \
|
|
+ OPTIONAL(CreateNewXyzSurface) \
|
|
/* VK_KHR_get_surface_capabilities2 */ \
|
|
OPTIONAL(GetPhysicalDeviceSurfaceCapabilities2KHR) \
|
|
OPTIONAL(GetPhysicalDeviceSurfaceFormats2KHR) \
|
|
```
|
|
|
|
Furthermore, these lists must be extended when the implementation needs to make use of
|
|
entrypoints defined by other extensions. For example, if a WSI implementation
|
|
needs to import a fence payload from a POSIX file descriptor the `vkImportFenceFdKHR`
|
|
function can be used, which is provided by the `VK_KHR_external_fence_fd`
|
|
extension. In order for the entrypoint to become visible to the implementation,
|
|
`OPTIONAL(ImportFenceFdKHR)` must be added to the `DEVICE_ENTRYPOINTS_LIST`
|
|
(also an ICD that implements the extension must be used).
|
|
|
|
When a new entry is added to either of these lists the `disp` member variable of
|
|
the singleton `instance_private_data` or `device_private_data` object is extended with a
|
|
function pointer that has the same name as the one that was added in the list.
|
|
The entrypoint can be called by getting the `instance_private_data`/`device_private_data`
|
|
object and then using the function pointer in the `disp` attribute.
|
|
For example, a WSI implementation that calls down the chain for the
|
|
`vkCreateNewXyzSurface` function, should do the following.
|
|
|
|
```c++
|
|
auto &instance_data = layer::instance_private_data::get(instance);
|
|
VkResult res = instance_data.disp.CreateNewXyzSurface(...);
|
|
```
|
|
|
|
In order for the new `surface_properties` implementation to be picked up by the
|
|
common layer code the following changes must be applied to the
|
|
[wsi_factory.cpp](wsi_factory.cpp) file.
|
|
1. Conditionally include the header of the WSI specific `surface_properties` implementation.
|
|
1. Extend the `supported_wsi_extensions` with the new WSI.
|
|
1. Add a new case with the new WSI platform in `get_surface_properties`.
|
|
|
|
```diff
|
|
diff --git a/wsi/wsi_factory.cpp b/wsi/wsi_factory.cpp
|
|
--- a/wsi/wsi_factory.cpp
|
|
+++ b/wsi/wsi_factory.cpp
|
|
@@ -46,6 +46,10 @@
|
|
#include "wayland/surface_properties.hpp"
|
|
#endif
|
|
|
|
+#if BUILD_NEW_WSI
|
|
+#include "new_wsi/surface_properties.hpp"
|
|
+#endif /* BUILD_NEW_WSI */
|
|
+
|
|
namespace wsi
|
|
{
|
|
|
|
@@ -60,6 +64,9 @@ static struct wsi_extension
|
|
#if BUILD_WSI_WAYLAND
|
|
{ { VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_SPEC_VERSION }, VK_ICD_WSI_PLATFORM_WAYLAND },
|
|
#endif
|
|
+#if BUILD_NEW_WSI
|
|
+ { { VK_KHR_NEW_WSI_EXTENSION_NAME, VK_KHR_NEW_WSI_SPEC_VERSION }, VK_ICD_WSI_PLATFORM_NEW_WSI },
|
|
+#endif /* BUILD_NEW_WSI */
|
|
};
|
|
|
|
static surface_properties *get_surface_properties(VkIcdWsiPlatform platform)
|
|
@@ -74,6 +81,10 @@ static surface_properties *get_surface_properties(VkIcdWsiPlatform platform)
|
|
case VK_ICD_WSI_PLATFORM_WAYLAND:
|
|
return &wayland::surface_properties::get_instance();
|
|
#endif
|
|
+#if BUILD_NEW_WSI
|
|
+ case VK_ICD_WSI_PLATFORM_NEW_WSI:
|
|
+ return &new_wsi::surface_properties::get_instance();
|
|
+#endif /* BUILD_NEW_WSI */
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
```
|
|
|
|
### surface
|
|
|
|
The surface interface represents a VkSurface. Each WSI backend's implementation
|
|
of this interface represents the platform specific surface. The `wsi::surface`
|
|
objects are associated with the corresponding `VkSurface` objects in
|
|
[`instance_private_data`](../layer/private_data.hpp) and they should be created
|
|
and linked to the `instance_data` during the specific `VkSurface` creation entrypoint.
|
|
|
|
A new WSI backend should implement the functions the interface consists of, which
|
|
are described in [surface.hpp](surface.hpp).
|
|
|
|
### swapchain
|
|
|
|
The common swapchain functionality is implemented in the `swapchain_base` class.
|
|
A new WSI backend should implement the virtual functions defined in
|
|
[swapchain_base.hpp](swapchain_base.hpp).
|
|
|
|
The base swapchain implementation has support for the FIFO presentation mode, which
|
|
makes use of the presentation thread. Also, it gives the option to disable the
|
|
thread in order to support other present modes like Mailbox. All of the WSI
|
|
implementations can use the FIFO mode without any modifications. If a WSI
|
|
implementation wants to leverage the Mailbox present mode extra synchronization
|
|
with the presentation engine may be needed.
|
|
|
|
The use of the presentation thread is enabled in the `init_platform` function
|
|
by setting the `use_presentation_thread` flag.
|
|
|
|
In the layer the swapchain images are represented by the `swapchain_image` struct.
|
|
This struct has a member variable which is called `data` and is of `void *` type.
|
|
This member variable is used to store the unique data that are needed by the images in
|
|
each WSI implementation.
|
|
|
|
Before submitting an image to the presentation engine the swapchain must wait
|
|
for the rendering operations on this image to finish. The synchronization primitives
|
|
used for this waiting operation by the WSI implementations are abstracted by the
|
|
classes defined in the [synchronization.hpp](synchronization.hpp) file. Currently only
|
|
Vulkan® fences and fences exportable to Sync FD are supported. The specific behaviour
|
|
that depends on the type of the synchronization primitive a backend uses is implemented
|
|
in the `image_set_present_payload` and `image_wait_present` functions.
|
|
|
|
## Helpers
|
|
|
|
Helper objects and utilities can be found in [util](../util). These helpers can
|
|
be used to aid development. For example, alternatives to the standard containers
|
|
(`std::vector`, `std::unordered_map`, ...) are provided to make it easier for the
|
|
Vulkan® WSI Layer code to conform to the allocation requirements of Vulkan®. Also,
|
|
logging macros have been defined there for logging messages with different priority.
|
|
A non exhaustive list follows with the useful helper objects and functions
|
|
implemented there:
|
|
* `util::vector`
|
|
* `util::allocator`
|
|
* `util::allocator::make_unique`
|
|
* `util::fd_owner`
|
|
* `util::ring_buffer`
|
|
* `util::unordered_map`
|
|
* `util::unordered_set`
|
|
|
|
And macros:
|
|
* `TRY`
|
|
* ` WSI_LOG_*`
|