vulkan,docs: Document vk_object_base

Acked-by: Iago Toral Quiroga <itoral@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15472>
This commit is contained in:
Jason Ekstrand 2022-03-18 17:31:35 -05:00 committed by Marge Bot
parent 0ca8b95824
commit f06fa8f7e0
4 changed files with 176 additions and 0 deletions

83
docs/vulkan/base-objs.rst Normal file
View File

@ -0,0 +1,83 @@
Base object structs
===================
The Vulkan runtime code provides a set of base object structs which must be
used if you want your driver to take advantage of any of the runtime code.
There are other base structs for various things which are not covered here
but those are optional. The ones covered here are the bare minimum set
which form the core of the Vulkan runtime code:
.. contents::
:local:
vk_object_base
--------------
The root base struct for all Vulkan objects is
:cpp:struct:`vk_object_base`. Every object exposed to the client through
the Vulkan API *must* inherit from :cpp:struct:`vk_object_base` by having a
:cpp:struct:`vk_object_base` or some struct that inherits from
:cpp:struct:`vk_object_base` as the driver struct's first member. Even
though we have `container_of()` and use it liberally, the
:cpp:struct:`vk_object_base` should be the first member as there are a few
places, particularly in the logging framework, where we use void pointers
to avoid casting and this only works if the address of the driver struct is
the same as the address of the :cpp:struct:`vk_object_base`.
The standard pattern for defining a Vulkan object inside a driver looks
something like this:
.. code-block:: c
struct drv_sampler {
struct vk_object_base base;
/* Driver fields */
};
VK_DEFINE_NONDISP_HANDLE_CASTS(drv_sampler, base, VkSampler,
VK_OBJECT_TYPE_SAMPLER);
Then, to the object in a Vulkan entrypoint,
.. code-block:: c
VKAPI_ATTR void VKAPI_CALL drv_DestroySampler(
VkDevice _device,
VkSampler _sampler,
const VkAllocationCallbacks* pAllocator)
{
VK_FROM_HANDLE(drv_device, device, _device);
VK_FROM_HANDLE(drv_sampler, sampler, _sampler);
if (!sampler)
return;
/* Tear down the sampler */
vk_object_free(&device->vk, pAllocator, sampler);
}
The :cpp:any:`VK_DEFINE_NONDISP_HANDLE_CASTS()` macro defines a set of
type-safe cast functions called ``drv_sampler_from_handle()`` and
``drv_sampler_to_handle()`` which cast a :cpp:type:`VkSampler` to and from a
``struct drv_sampler *``. Because compile-time type checking with Vulkan
handle types doesn't always work in C, the ``_from_handle()`` helper uses the
provided :cpp:type:`VkObjectType` to assert at runtime that the provided
handle is the correct type of object. Both cast helpers properly handle
``NULL`` and ``VK_NULL_HANDLE`` as inputs. The :cpp:any:`VK_FROM_HANDLE()`
macro provides a convenient way to declare a ``drv_foo`` pointer and
initialize it from a ``VkFoo`` handle in one smooth motion.
.. doxygenstruct:: vk_object_base
:members:
.. doxygenfunction:: vk_object_base_init
.. doxygenfunction:: vk_object_base_finish
.. doxygendefine:: VK_DEFINE_HANDLE_CASTS
.. doxygendefine:: VK_DEFINE_NONDISP_HANDLE_CASTS
.. doxygendefine:: VK_FROM_HANDLE

View File

@ -9,4 +9,5 @@ hardware-agnostic bits in common code.
.. toctree::
:maxdepth: 2
base-objs
renderpass

View File

@ -86,13 +86,24 @@ enum vk_queue_submit_mode {
VK_QUEUE_SUBMIT_MODE_THREADED_ON_DEMAND,
};
/** Base struct for VkDevice */
struct vk_device {
struct vk_object_base base;
/** Allocator used to create this device
*
* This is used as a fall-back for when a NULL pAllocator is passed into a
* device-level create function such as vkCreateImage().
*/
VkAllocationCallbacks alloc;
/** Pointer to the physical device */
struct vk_physical_device *physical;
/** Table of enabled extensions */
struct vk_device_extension_table enabled_extensions;
/** Device-level dispatch table */
struct vk_device_dispatch_table dispatch_table;
/** Command dispatch table
@ -228,6 +239,12 @@ struct vk_device {
VK_DEVICE_TIMELINE_MODE_NATIVE,
} timeline_mode;
/** Per-device submit mode
*
* This represents the device-wide submit strategy which may be different
* from the per-queue submit mode. See vk_queue.submit.mode for more
* details.
*/
enum vk_queue_submit_mode submit_mode;
#ifdef ANDROID

View File

@ -38,10 +38,23 @@ struct hash_table;
struct vk_device;
/** Base struct for all Vulkan objects */
struct vk_object_base {
VK_LOADER_DATA _loader_data;
/** Type of this object
*
* This is used for runtime type checking when casting to and from Vulkan
* handle types since compile-time type checking doesn't always work.
*/
VkObjectType type;
/** Pointer to the device in which this object exists, if any
*
* This is NULL for instances and physical devices but should point to a
* valid vk_device for almost everything else. (There are a few WSI
* objects that don't inherit from a device.)
*/
struct vk_device *device;
/* True if this object is fully constructed and visible to the client */
@ -54,9 +67,20 @@ struct vk_object_base {
char *object_name;
};
/** Initialize a vk_base_object
*
* @param[in] device The vk_device this object was created from or NULL
* @param[out] base The vk_object_base to initialize
* @param[in] obj_type The VkObjectType of the object being initialized
*/
void vk_object_base_init(struct vk_device *device,
struct vk_object_base *base,
VkObjectType obj_type);
/** Tear down a vk_object_base
*
* @param[out] base The vk_object_base being torn down
*/
void vk_object_base_finish(struct vk_object_base *base);
static inline void
@ -74,6 +98,26 @@ vk_object_base_from_u64_handle(uint64_t handle, VkObjectType obj_type)
return base;
}
/** Define handle cast macros for the given dispatchable handle type
*
* For a given `driver_struct`, this defines `driver_struct_to_handle()` and
* `driver_struct_from_handle()` helpers which provide type-safe (as much as
* possible with Vulkan handle types) casts to and from the `driver_struct`
* type. As an added layer of protection, these casts use the provided
* `VkObjectType` to assert that the object is of the correct type when
* running with a debug build.
*
* @param __driver_type The name of the driver struct; it is assumed this is
* the name of a struct type and `struct` will be
* prepended automatically
*
* @param __base The name of the vk_base_object member
*
* @param __VkType The Vulkan object type such as VkImage
*
* @param __VK_TYPE The VkObjectType corresponding to __VkType, such as
* VK_OBJECT_TYPE_IMAGE
*/
#define VK_DEFINE_HANDLE_CASTS(__driver_type, __base, __VkType, __VK_TYPE) \
static inline struct __driver_type * \
__driver_type ## _from_handle(__VkType _handle) \
@ -93,6 +137,26 @@ vk_object_base_from_u64_handle(uint64_t handle, VkObjectType obj_type)
return (__VkType) _obj; \
}
/** Define handle cast macros for the given non-dispatchable handle type
*
* For a given `driver_struct`, this defines `driver_struct_to_handle()` and
* `driver_struct_from_handle()` helpers which provide type-safe (as much as
* possible with Vulkan handle types) casts to and from the `driver_struct`
* type. As an added layer of protection, these casts use the provided
* `VkObjectType` to assert that the object is of the correct type when
* running with a debug build.
*
* @param __driver_type The name of the driver struct; it is assumed this is
* the name of a struct type and `struct` will be
* prepended automatically
*
* @param __base The name of the vk_base_object member
*
* @param __VkType The Vulkan object type such as VkImage
*
* @param __VK_TYPE The VkObjectType corresponding to __VkType, such as
* VK_OBJECT_TYPE_IMAGE
*/
#define VK_DEFINE_NONDISP_HANDLE_CASTS(__driver_type, __base, __VkType, __VK_TYPE) \
static inline struct __driver_type * \
__driver_type ## _from_handle(__VkType _handle) \
@ -113,6 +177,17 @@ vk_object_base_from_u64_handle(uint64_t handle, VkObjectType obj_type)
return (__VkType)(uintptr_t) _obj; \
}
/** Declares a __driver_type pointer which represents __handle
*
* @param __driver_type The name of the driver struct; it is assumed this is
* the name of a struct type and `struct` will be
* prepended automatically
*
* @param __name The name of the declared pointer
*
* @param __handle The Vulkan object handle with which to initialize
* `__name`
*/
#define VK_FROM_HANDLE(__driver_type, __name, __handle) \
struct __driver_type *__name = __driver_type ## _from_handle(__handle)