mirror of
https://gitlab.freedesktop.org/mesa/vulkan-wsi-layer.git
synced 2026-05-05 13:28:00 +02:00
Add WSI integration guide
Adds a guide on implementing a WSI backend and extra documentation in a few functions in swapchain_base. Signed-off-by: Iason Paraskevopoulos <iason.paraskevopoulos@arm.com> Change-Id: Ibdc702a05605989940966aae168331268c1ce2ba
This commit is contained in:
parent
dd1f3f24cc
commit
626e40ba96
3 changed files with 262 additions and 0 deletions
|
|
@ -141,6 +141,11 @@ We are open for contributions.
|
|||
Contributors are expected to abide by the
|
||||
[freedesktop.org code of conduct](https://www.freedesktop.org/wiki/CodeOfConduct/).
|
||||
|
||||
### Implement a new WSI backend
|
||||
|
||||
Instructions on how to implement a WSI backend can be found in the
|
||||
[README](wsi/README.md) in the wsi folder.
|
||||
|
||||
## Khronos® Conformance
|
||||
|
||||
This software is based on a published Khronos® Specification and is expected to
|
||||
|
|
|
|||
241
wsi/README.md
Normal file
241
wsi/README.md
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
# 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::optional`
|
||||
* `util::fd_owner`
|
||||
* `util::ring_buffer`
|
||||
* `util::unordered_map`
|
||||
* `util::unordered_set`
|
||||
|
||||
And macros:
|
||||
* `TRY`
|
||||
* ` WSI_LOG_*`
|
||||
|
|
@ -163,6 +163,10 @@ public:
|
|||
/**
|
||||
* @brief Creates a VkImage handle.
|
||||
*
|
||||
* It is used to bind images to memory from the swapchain. It is called if a
|
||||
* VkImageSwapchainCreateInfoKHR struct has been provided in the vkCreateImage
|
||||
* function.
|
||||
*
|
||||
* @param image_create_info Data to be used to create the image.
|
||||
* @param[out] image Handle to the image.
|
||||
*
|
||||
|
|
@ -175,6 +179,10 @@ public:
|
|||
/**
|
||||
* @brief Bind image to a swapchain
|
||||
*
|
||||
* It is used to bind images to memory from the swapchain. It is called if a
|
||||
* VkBindImageMemorySwapchainInfoKHR struct has been provided in the vkBindImageMemory2
|
||||
* function.
|
||||
*
|
||||
* @param device is the logical device that owns the images and memory.
|
||||
* @param bind_image_mem_info details the image we want to bind.
|
||||
* @param bind_sc_info describes the swapchain memory to bind to.
|
||||
|
|
@ -382,6 +390,8 @@ protected:
|
|||
/**
|
||||
* @brief Method to present and image
|
||||
*
|
||||
* It sends the next image for presentation to the presentation engine.
|
||||
*
|
||||
* @param pending_index Index of the pending image to be presented.
|
||||
*
|
||||
*/
|
||||
|
|
@ -399,6 +409,8 @@ protected:
|
|||
/**
|
||||
* @brief Method to release a swapchain image
|
||||
*
|
||||
* Releases resources stored in the data member of a swapchain_image.
|
||||
*
|
||||
* @param image Handle to the image about to be released.
|
||||
*/
|
||||
virtual void destroy_image(swapchain_image &image){};
|
||||
|
|
@ -406,6 +418,10 @@ protected:
|
|||
/**
|
||||
* @brief Hook for any actions to free up a buffer for acquire
|
||||
*
|
||||
* If specific actions are required by the windowing system to query whether a buffer
|
||||
* is still used by it, this function should be implemented by the WSI backend's
|
||||
* swapchain implementation.
|
||||
*
|
||||
* @param[in,out] timeout time to wait, in nanoseconds. 0 doesn't block,
|
||||
* UINT64_MAX waits indefinately. The timeout should
|
||||
* be updated if a sleep is required - this can
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue