Fundamentals
This chapter introduces fundamental concepts including the Vulkan architecture and execution model, API syntax, queues, pipeline configurations, numeric representation, state and state queries, and the different types of objects and shaders. It provides a framework for interpreting more specific descriptions of commands and behavior in the remainder of the Specification.
Host and Device Environment
The Vulkan Specification assumes and requires: the following properties of the host environment with respect to Vulkan implementations:
- The host must have runtime support for 8-, 16-, 32-, and 64-bit signed and unsigned twos-complement integers, all addressable at the granularity of their size in bytes.
- The host must have runtime support for 32- and 64-bit floating-point types satisfying the range and precision constraints in the Floating-Point Computation section.
- The representation and endianness of these types on the host must match the representation and endianness of the same types on every physical device supported.
Execution Model
This section outlines the execution model of a Vulkan system.
Vulkan exposes one or more devices, each of which exposes one or more queues which may process work asynchronously to one another. The set of queues supported by a device is partitioned into families. Each family supports one or more types of functionality and may contain multiple queues with similar characteristics. Queues within a single family are considered compatible with one another, and work produced for a family of queues can be executed on any queue within that family. This specification defines the following types of functionality that queues may support: graphics, compute, video decode, video encode, protected memory management, sparse memory management, and transfer.
Device memory is explicitly managed by the application. Each device may advertise one or more heaps, representing different areas of memory. Memory heaps are either device-local or host-local, but are always visible to the device. Further detail about memory heaps is exposed via memory types available on that heap. Examples of memory areas that may be available on an implementation include:
- device-local is memory that is physically connected to the device.
- device-local, host visible is device-local memory that is visible to the host.
- host-local, host visible is memory that is local to the host and visible to the device and host.
On other architectures, there may only be a single heap that can be used for any purpose.
Queue Operation
Each device supports a number of queues, which provide an interface for submitting work for execution on the device.
Queue submission commands are used to submit work, along with a set of synchronization primitives used to constrain the order of submitted operations. Queues are intended for asynchronous execution of submitted workloads, and queue submission commands should return as soon as the work has been submitted, without waiting for the work to complete. Once submitted to a queue, work will begin and complete execution without further application intervention.
There are no implicit ordering constraints between queue operations on different queues, or between queues and the host, so these may operate in any order with respect to each other. Explicit ordering constraints between different queues or with the host can be expressed with semaphores and fences.
Many commands for queues are recorded into command buffers first, before the command buffers are then submitted to a queue for execution. Command buffer submissions to a single queue respect submission order and other implicit ordering guarantees, but otherwise may overlap or execute out of order. Other types of batches and queue submissions against a single queue (e.g. sparse memory binding) have no implicit ordering constraints with any other queue submission or batch. Additional explicit ordering constraints between queue submissions and individual batches can be expressed with semaphores and fences.
Before a fence or semaphore is signaled, it is guaranteed that any previously submitted queue operations have completed execution, and that memory writes from those queue operations are available to future queue operations. Waiting on a signaled semaphore or fence guarantees that previous writes that are available are also visible to subsequent commands.
Command buffer boundaries, both between primary command buffers of the same or different batches or submissions as well as between primary and secondary command buffers, do not introduce any additional ordering constraints. In other words, submitting the set of command buffers (which can include executing secondary command buffers) between any semaphore or fence operations execute the recorded commands as if they had all been recorded into a single primary command buffer, except that the current state is reset on each boundary. Explicit ordering constraints can be expressed with explicit synchronization primitives.
There are a few implicit ordering guarantees between commands within a command buffer, but only covering a subset of execution. Additional explicit ordering constraints can be expressed with the various explicit synchronization primitives.
Commands recorded in command buffers can perform actions, set state that
persists across commands, synchronize other commands, or indirectly launch
other commands, with some commands fulfilling several of these roles.
The Command Properties section for each such command lists which of
these roles the command takes:
Action
Action commands perform operations that can update values in memory. E.g. draw commands, dispatch commands.
State
State setting commands update the current state of a command buffer, affecting the operation of future action commands.
Synchronization
Synchronization commands impose ordering constraints on action commands, by introducing explicit execution and memory dependencies.
Indirection
Indirection commands execute other commands which were not directly recorded in the same command buffer.
Object Model
The devices, queues, and other entities in Vulkan are represented by Vulkan objects. At the API level, all objects are referred to by handles. There are two classes of handles: dispatchable and non-dispatchable. Dispatchable handle types are a pointer to an opaque type. This pointer may be used by layers as part of intercepting API commands, and thus each API command takes a dispatchable type as its first parameter. Each object of a dispatchable type must have a unique handle value during its lifetime.
Non-dispatchable handle types are a 64-bit integer type whose meaning is
implementation-dependent.
If the privateData feature is enabled for a
VkDevice, each object of a non-dispatchable type created on that
device must have a handle value that is unique among objects created on
that device, for the duration of the object’s lifetime.
Otherwise, non-dispatchable
handles may encode object information directly in the handle rather than
acting as a reference to an underlying object, and thus may not have unique
handle values.
If handle values are not unique, then destroying one such handle must not
cause identical handles of other types to become invalid, and must not
cause identical handles of the same type to become invalid if that handle
value has been created more times than it has been destroyed.
All objects created or allocated from a VkDevice (i.e. with a
VkDevice as the first parameter) are private to that device, and must
not be used on other devices.
Object Lifetime
Objects are created or allocated by vkCreate* and vkAllocate*
commands, respectively.
Once an object is created or allocated, its structure is considered to
be immutable, though the content of certain object types is still free to
change.
When an object is passed to another command, it may be accessed by the
implementation, which may include both read and write access unless
explicitly stated otherwise.
Objects are destroyed or freed by vkDestroy* and vkFree*
commands, respectively.
Objects that are allocated (rather than created) take resources from an existing pool object or memory heap, and when freed return resources to that pool or heap. While object creation and destruction are generally expected to be low-frequency occurrences during runtime, allocating and freeing objects can occur at high frequency. Pool objects help accommodate improved performance of the allocations and frees.
Applications are responsible for managing the lifetimes of Vulkan objects and memory passed into the Vulkan API. The access semantics of different functions in the API follow a typical pattern as laid out below, with any exceptions listed with the commands or objects that have them.
Application-owned memory and Vulkan objects may be accessed at any time
during the execution of a command they are passed to.
Vulkan objects that device addresses are retrieved from may be accessed by
the implementation any time that memory backing the device address is
accessed.
Device addresses and
Vulkan objects passed in during the creation or allocation of another object
may be accessed by the implementation any time that the created/allocated
object is accessed unless explicitly stated otherwise.
Device addresses and
Vulkan objects passed to a recording command (vkCmd*) may be accessed
at any time during the execution of the command, when the command buffer is
subsequently recorded into another command buffer, during any subsequent
command that is recorded to either the command buffer or one it is recorded
into, or while the command buffer is in the pending state, unless explicitly stated otherwise.
If an application is using deferred host
operations in a command, and that operation is successfully deferred,
objects and memory passed to that command may be accessed at any time until
the deferred operation is complete.
Some additional operations hold references to other objects or
application-owned memory beyond the duration of the command; in which case
the access semantics and lifetime of those references are described by that
command.
When destroying or freeing an object, implementations must not access any memory or other objects that may otherwise be accessed when the object is accessed. Applications can free or destroy objects in any order, except that parent objects must be freed only after all child objects are freed. An object is the parent of another child object if the parent was used as the first object parameter in the creation of the child. Once an object is freed or destroyed it must not be accessed again, either directly or via access through another object. Applications must not free or destroy any object while it is being accessed.
External Object Handles
As defined above, the scope of object handles created or allocated from a
VkDevice is limited to that logical device.
Objects which are not in scope are said to be external.
To bring an external object into scope, an external handle must be exported
from the object in the source scope and imported into the destination scope.
Application Binary Interface
The mechanism by which Vulkan is made available to applications is platform- or implementation- defined. On many platforms the C interface described in this Specification is provided by a shared library. Since shared libraries can be changed independently of the applications that use them, they present particular compatibility challenges, and this Specification places some requirements on them.
Shared library implementations must use the default Application Binary
Interface (ABI) of the standard C compiler for the platform, or provide
customized API headers that cause application code to use the
implementation’s non-default ABI.
An ABI in this context means the size, alignment, and layout of C data
types; the procedure calling convention; and the naming convention for
shared library symbols corresponding to C functions.
Customizing the calling convention for a platform is usually accomplished by
defining calling
convention macros appropriately in vk_platform.h.
On platforms where Vulkan is provided as a shared library, library symbols
beginning with vk and followed by a digit or uppercase letter are
reserved for use by the implementation.
Applications which use Vulkan must not provide definitions of these
symbols.
This allows the Vulkan shared library to be updated with additional symbols
for new API versions or extensions without causing symbol conflicts with
existing applications.
Shared library implementations should provide library symbols for commands in the highest version of this Specification they support, and for Window System Integration extensions relevant to the platform. They may also provide library symbols for commands defined by additional extensions.
Command Syntax and Duration
The Specification describes Vulkan commands as functions or procedures using C99 syntax. Language bindings for other languages such as C++ and JavaScript may allow for stricter parameter passing, or object-oriented interfaces.
Vulkan uses the standard C types for the base type of scalar parameters
(e.g. types from <stdint.h>), with exceptions described below, or
elsewhere in the text when appropriate:
Commands that create Vulkan objects are of the form vkCreate* and take
Vk*CreateInfo structures with the parameters needed to create the
object.
These Vulkan objects are destroyed with commands of the form
vkDestroy*.
The last in-parameter to each command that creates or destroys a Vulkan
object is pAllocator.
The pAllocator parameter can be a non-NULL value, in which case
allocations for the given object are delegated to an application provided
callback.
Refer to the Memory Allocation chapter for further
details.
Commands that allocate Vulkan objects owned by pool objects are of the form
vkAllocate*, and take Vk*AllocateInfo structures.
These Vulkan objects are freed with commands of the form vkFree*.
These objects do not take allocators; if host memory is needed, they will
use the allocator that was specified when their parent pool was created.
Commands are recorded into a command buffer by calling API commands of the
form vkCmd*.
Each such command may have different restrictions on where it can be used:
in a primary and/or secondary command buffer, inside and/or outside a render
pass, and in one or more of the supported queue types.
These restrictions are documented together with the definition of each such
command.
The duration of a Vulkan command refers to the interval between calling the command and its return to the caller.
Lifetime of Retrieved Results
Information is retrieved from the implementation with commands of the form
vkGet* and vkEnumerate*.
Unless otherwise specified for an individual command, the results are invariant; that is, they will remain unchanged when retrieved again by calling the same command with the same parameters, so long as those parameters themselves all remain valid.
Array Results
Some query commands of the form vkGet* and vkEnumerate* enable
retrieving multiple results in the form of a return array.
Such commands typically have two pointer arguments as follows:
- An element count pointer pointing to an integer variable, conventionally
named as
p*Countwhere*is the capitalized singular form of the name of the retrieved values. - A pointer to an array where the result array is retrieved,
conventionally named as
p*where*is the capitalized plural form of the name of the retrieved values.
If such commands are called with the array pointer set to NULL, then the
number of retrievable elements is returned in the variable pointed to by the
element count pointer.
Otherwise, the element count pointer must point to a variable set by the
application to the number of elements in the return array, and on return the
variable is overwritten with the number of elements actually written to the
return array.
If the input element count is less than the number of retrievable array
elements, the query will write only as many elements to the return array as
specified by the element count variable set by the application, and the
command will return VK_INCOMPLETE instead of VK_SUCCESS, to
indicate that not all retrievable array elements were returned.
- First, with the array pointer set to
NULL, to retrieve the number of retrievable elements. - Second, with the array pointer pointing to an application allocated storage for at least as many elements as indicated by the variable pointed to by the element count pointer, to retrieve at most as many of the retrievable elements.
Query commands that return one or more structures, regardless of whether
they return a single or an array of structures with or without a pNext
chain, may also contain arrays within those structures.
Such return arrays are typically defined in the form of two members as
follows:
- An integer value specifying the element count, conventionally named as
*Countwhere*is the singular form of the name of the retrieved values. - A pointer to an array where the result array is retrieved,
conventionally named as
p*where*is the capitalized plural form of the name of the retrieved values.
Analogously to query commands that return multiple results, if the command
is called with the array pointer member of the output structure in question
set to NULL, then the number of retrievable elements is returned in the
element count member of that output structure.
Otherwise, the element count must specify the number of elements in the
return array, and on return the element count member is overwritten with the
number of elements actually written to the return array.
If the input element count is less than the number of retrievable array
elements, the query will write only as many elements to the return array as
specified by the input element count, and the command will return
VK_INCOMPLETE instead of VK_SUCCESS, if the query command has a
VkResult return type, to indicate that not all retrievable array
elements were returned.
- First, with the array pointer member(s) set to
NULL, to retrieve the number(s) of retrievable elements. - Second, with the array pointer(s) pointing to an application allocated storage for at least as many elements as indicated by the element count member(s), to retrieve at most as many of the retrievable elements.
- Then the process may need to be repeated for all other newly introduced return arrays in any nested output structures indirectly specified through the previously retrieved result arrays.
Regardless of the type of query command, any array pointer member of an
output structure must either be NULL, or point to an
application-allocated array.
Query commands must not return a pointer to implementation allocated
storage in any output structure.
Opaque Binary Data Results
Some query commands of the form vkGet* retrieve opaque binary data in
the form of a byte array and have a possible result code of
VK_ERROR_NOT_ENOUGH_SPACE_KHR.
Such commands always have two pointer arguments as follows:
- A binary data size pointer pointing to a
size_tvariable, conventionally named asp*Sizewhere*is the capitalized form of the name of the retrieved binary data. - A pointer to a byte array where the binary data is retrieved,
conventionally named as
p*where*is the capitalized form of the name of the retrieved binary data.
If such commands are called with the binary pointer not set to NULL, the
binary size pointer must point to a variable set by the application to the
allocated size of the binary pointer.
These arguments may also be placed in an extensible structure, in which
case the binary data size argument is not a pointer.
If the input binary size is less than the total retrievable binary size, the
query will not write any data to the location pointed to the binary pointer,
and the command will return VK_ERROR_NOT_ENOUGH_SPACE_KHR instead of
VK_SUCCESS.
If the return code is VK_SUCCESS or
VK_ERROR_NOT_ENOUGH_SPACE_KHR, the total size of the binary data that
can be retrieved is returned in the variable pointed to by the binary size
pointer.
If multiple binaries are being retrieved,
VK_ERROR_NOT_ENOUGH_SPACE_KHR will be returned if any input binary
sizes are less than their respective total retrievable binary sizes.
Unless otherwise specified, this command will determine writing data to each
binary individually based on if their input binary sizes are sufficiently
sized, following the behavior for single binary retrieval.
For all other error codes, the contents of the return structures are undefined.
VK_ERROR_NOT_ENOUGH_SPACE_KHR is returned with a command that
returns multiple binaries, the application can determine which binaries are
undersized by comparing the total binary size that is returned for each
binary against the allocated size that was provided to the command.VK_ERROR_NOT_ENOUGH_SPACE_KHR
error code was not defined until after those queries were written.A NOTE is added to each such query, describing such inconsistent behavior.Threading Behavior
Vulkan is intended to provide scalable performance when used on multiple host threads. All commands support being called concurrently from multiple threads, but certain parameters, or components of parameters are defined to be externally synchronized. This means that the caller must guarantee that no more than one thread is using such a parameter at a given time.
More precisely, Vulkan commands use simple stores to update the state of Vulkan objects. The implementation may not synchronize accesses to memory parameters or object parameters declared as externally synchronized with other accesses. If two commands access the same object or memory and at least one of the commands declares the object to be externally synchronized, then the caller must guarantee not only that the commands do not execute simultaneously, but also that the two commands are separated by an appropriate memory barrier (if needed). Similarly, if a Vulkan command accesses a non-const memory parameter and the application also accesses that memory, or if the application writes to that memory and the command accesses it as a const memory parameter, the application must ensure the accesses are properly synchronized with a memory barrier if needed.
Any object parameters that are not labeled as externally synchronized are either not mutated by the command or are internally synchronized. Additionally, certain objects related to a command’s parameters (e.g. command pools and descriptor pools) may be affected by a command, and must also be externally synchronized. These implicit parameters are documented as described below.
Parameters of commands that are externally synchronized are listed below.
There are also a few instances where a command can take in an application-allocated list whose contents are externally synchronized parameters. In these cases, the caller must guarantee that at most one thread is using a given element within the list at a given time. These parameters are listed below.
In addition, there are some implicit parameters that need to be externally
synchronized.
For example, when a commandBuffer parameter needs to be externally
synchronized, it implies that the commandPool from which that command
buffer was allocated also needs to be externally synchronized.
The implicit parameters and their associated object are listed below.
Valid Usage
Valid usage defines a set of conditions which must be met in order to achieve well-defined runtime behavior in an application. These conditions depend only on Vulkan state, and the parameters or objects whose usage is constrained by the condition.
The core layer assumes applications are using the API correctly. Except as documented elsewhere in the Specification, the behavior of the core layer to an application using the API incorrectly is undefined, and may include program termination. However, implementations must ensure that incorrect usage by an application does not affect the integrity of the operating system, the Vulkan implementation, or other applications in the system using Vulkan. In particular, any guarantees made by an operating system about whether memory from one process can be visible to another process or not must not be violated by a Vulkan implementation for any memory allocation. Vulkan implementations are not required to make additional security or integrity guarantees beyond those provided by the OS unless explicitly directed by the application’s use of a particular feature or extension.
If the protectedMemory feature is
supported, the implementation provides additional guarantees when invalid
usage occurs to prevent values in protected memory from being accessed or
inferred outside of protected operations, as described in
Protected Memory Access Rules.
Some valid usage conditions have dependencies on runtime limits or feature availability. It is possible to validate these conditions against Vulkan’s minimum supported values for these limits and features, or some subset of other known values.
Valid usage conditions do not cover conditions where well-defined behavior (including returning an error code) exists.
Valid usage conditions should apply to the command or structure where complete information about the condition would be known during execution of an application. This is such that a validation layer or linter can be written directly against these statements at the point they are specified.
Valid usage conditions are described in a block labeled Valid Usage
following each command or structure they apply to.
Usage Validation
Vulkan is a layered API. The lowest layer is the core Vulkan layer, as defined by this Specification. The application can use additional layers above the core for debugging, validation, and other purposes.
One of the core principles of Vulkan is that building and submitting command buffers should be highly efficient. Thus error checking and validation of state in the core layer is minimal, although more rigorous validation can be enabled through the use of layers.
Validation of correct API usage is left to validation layers. Applications should be developed with validation layers enabled, to help catch and eliminate errors. Once validated, released applications should not enable validation layers by default.
Implicit Valid Usage
Some valid usage conditions apply to all commands and structures in the API,
unless explicitly denoted otherwise for a specific command or structure.
These conditions are considered implicit, and are described in a block
labeled Valid Usage (Implicit) following each command or structure they
apply to.
Implicit valid usage conditions are described in detail below.
Valid Usage for Object Handles
Any input parameter to a command that is an object handle must be a valid object handle, unless otherwise specified. An object handle is valid if:
- It has been created or allocated by a previous, successful call to the API. Such calls are noted in the Specification.
- It has not been deleted or freed by a previous call to the API. Such calls are noted in the Specification.
- Any objects used by that object, either as part of creation or execution, must also be valid.
The reserved values VK_NULL_HANDLE and NULL can be used in place of
valid non-dispatchable handles and dispatchable handles, respectively, when
explicitly called out in the Specification.
Any command that creates an object successfully must not return these
values.
It is valid to pass these values to vkDestroy* or vkFree*
commands, which will silently ignore these values.
Valid Usage for Pointers
Any parameter that is a pointer must be a valid pointer only if it is explicitly called out by a Valid Usage statement.
A pointer is valid if it points at memory containing values of the
number and type(s) expected by the command, and all fundamental types
accessed through the pointer (e.g. as elements of an array or as members of
a structure) satisfy the alignment requirements of the host processor.
Valid Usage for Strings
Any parameter that is a pointer to char must be a finite sequence of
values terminated by a null character, or if explicitly called out in the
Specification, can be NULL.
Strings specified as UTF-8 encoded must not contain invalid UTF-8 sequences. See String Representation for additional information about strings.
Valid Usage for Enumerated Types
Any parameter of an enumerated type must be a valid enumerant for that type. Use of an enumerant is valid if the following conditions are true:
- The enumerant is defined as part of the enumerated type.
- The enumerant is not a value suffixed with
_MAX_ENUM.- This value exists only to ensure that C
enumtypes are 32 bits in size and must not be used by applications.
- This value exists only to ensure that C
- If the enumerant is used in a function that has a VkInstance as
its first parameter and either:
- it was added by a core version that is supported
(as reported by vkEnumerateInstanceVersion)
and the value of VkApplicationInfo::
apiVersionis greater than or equal to the version that added it; or - it was added by an instance extension that was enabled for the instance.
- it was added by a core version that is supported
(as reported by vkEnumerateInstanceVersion)
and the value of VkApplicationInfo::
- If the enumerant is used in a function that has a VkPhysicalDevice
object as its first parameter and either:
- it was added by a core version that is supported by that device (as
reported by VkPhysicalDeviceProperties::
apiVersion); - it was added by an instance extension that was enabled for the instance; or
- it was added by a device extension that is supported by that device.
- it was added by a core version that is supported by that device (as
reported by VkPhysicalDeviceProperties::
- If the enumerant is used in a function that has any other dispatchable
object as its first parameter and either:
- it was added by a core version that is supported for the device (as
reported by VkPhysicalDeviceProperties::
apiVersion); or - it was added by a device extension that was enabled for the device.
- it was added by a core version that is supported for the device (as
reported by VkPhysicalDeviceProperties::
Additionally, if the maintenance5 feature is
supported, any integer value representable in the range valid for the
defined type is valid when used in a function that has a
VkPhysicalDevice object as its first parameter.
Physical device queries will either return results indicating lack of
support, or ignore unsupported values when used as a bit flag in a
Vk*Flags* parameter.
Any enumerated type returned from a query command or otherwise output from Vulkan to the application must not have a reserved value. Reserved values are values not defined by any extension for that enumerated type.
valid enumerant rule described here does not address such
cases.hidden extensions
known only to driver internals, or layers enabling extensions without
knowledge of the application, without allowing return of values not defined
by any extension.switch
statements with Vulkan API enums.
This is because new extensions can add new values to existing enums.
Using a default: statement within a switch may avoid future compilation
issues.This is particularly true for enums such as VkDriverId, which may have
values added that do not belong to a corresponding new extension.Valid Usage for Flags
Valid Usage for Structure Types
Any parameter that is a structure containing a sType member must have
a value of sType which is a valid VkStructureType value matching
the type of the structure.
Valid Usage for Structure Pointer Chains
Any parameter that is a structure containing a void* pNext member
must have a value of pNext that is either NULL, or is a pointer to
a valid extending structure, containing sType and pNext
members as described in the Vulkan Documentation and
Extensions document in the section Extending Structures.
The set of structures connected by pNext pointers is referred to as a
pNext chain.
Each structure included in the pNext chain must be defined at runtime
by either:
- a core version which is supported
- an extension which is enabled
- a supported device extension in the case of physical-device-level functionality added by the device extension
Each type of extending structure must not appear more than once in a
pNext chain, including any
aliases.
This general rule may be explicitly overridden for specific structures.
Any component of the implementation (the loader, any enabled layers, and
drivers) must skip over, without processing (other than reading the
sType and pNext members) any extending structures in the chain
not defined by core versions or extensions supported by that component.
As a convenience to implementations and layers needing to iterate through a structure pointer chain, the Vulkan API provides two base structures. These structures allow for some type safety, and can be used by Vulkan API functions that operate on generic inputs and outputs.
Valid Usage for Nested Structures
The above conditions also apply recursively to members of structures provided as input to a command, either as a direct argument to the command, or themselves a member of another structure.
Specifics on valid usage of each command are covered in their individual sections.
Valid Usage for Extensions
Instance-level functionality or behavior added by an instance extension to the API must not be used unless that extension is supported by the instance as determined by vkEnumerateInstanceExtensionProperties, and that extension is enabled in VkInstanceCreateInfo.
Physical-device-level functionality or behavior added by an instance extension to the API must not be used unless that extension is supported by the instance as determined by vkEnumerateInstanceExtensionProperties, and that extension is enabled in VkInstanceCreateInfo.
Physical-device-level functionality or behavior added by a device extension to the API must not be used unless the conditions described in Extending Physical Device From Device Extensions are met.
Device-level functionality added by a device extension that is dispatched from a VkDevice, or from a child object of a VkDevice must not be used unless that extension is supported by the device as determined by vkEnumerateDeviceExtensionProperties, and that extension is enabled in VkDeviceCreateInfo.
Valid Usage for Newer Core Versions
Instance-level functionality or behavior added by a new core
version of the API must not be used unless it is supported by the
instance as determined by vkEnumerateInstanceVersion and the specified
version of VkApplicationInfo::apiVersion.
Physical-device-level functionality or behavior added by a new
core version of the API must not be used unless it is supported by the
physical device as determined by
VkPhysicalDeviceProperties::apiVersion and the specified version
of VkApplicationInfo::apiVersion.
Device-level functionality or behavior added by a new core
version of the API must not be used unless it is supported by the device
as determined by VkPhysicalDeviceProperties::apiVersion and the
specified version of VkApplicationInfo::apiVersion.
VkResult Return Codes
Numeric Representation and Computation
Implementations normally perform computations in floating-point, and must
meet the range and precision requirements defined under Floating-Point Computation below.
These requirements only apply to computations performed in Vulkan operations outside of shader execution, such as texture image specification and sampling, and per-fragment operations. Range and precision requirements during shader execution differ, and are specified by the Precision and Operation of SPIR-V Instructions section.
In some cases, the representation and/or precision of operations is implicitly limited by the specified format of vertex or texel data consumed by Vulkan. Specific floating-point formats are described later in this section.
Floating-Point Computation
Most floating-point computation is performed in SPIR-V shader modules. The properties of computation within shaders are constrained as defined by the Precision and Operation of SPIR-V Instructions section.
Some floating-point computation is performed outside of shaders, such as viewport and depth range calculations. For these computations, we do not specify how floating-point numbers are to be represented, or the details of how operations on them are performed, but only place minimal requirements on representation and precision as described in the remainder of this section.
We require simply that numbers' floating-point parts contain enough bits and that their exponent fields are large enough so that individual results of floating-point operations are accurate to about 1 part in 105. The maximum representable magnitude for all floating-point values must be at least 232.
- x × 0 = 0 × x = 0 for any non-infinite and non-NaN x.
- 1 × x = x × 1 = x.
- x + 0 = 0 + x = x.
- 00 = 1.
Occasionally, further requirements will be specified. Most single-precision floating-point formats meet these requirements.
The special values Inf and -Inf encode values with magnitudes
too large to be represented; the special value NaN encodes Not A Number values resulting from undefined arithmetic operations such as
0 / 0.
Implementations may support Inf and NaN in their floating-point
computations.
Any computation which does not support either Inf or NaN, for
which that value is an input or output will yield an undefined value.
Floating-Point Format Conversions
When a value is converted to a defined floating-point representation, finite values falling between two representable finite values are rounded to one or the other. The rounding mode is not defined. Finite values whose magnitude is larger than that of any representable finite value may be rounded either to the closest representable finite value or to the appropriately signed infinity. For unsigned destination formats any negative values are converted to zero. Positive infinity is converted to positive infinity; negative infinity is converted to negative infinity in signed formats and to zero in unsigned formats; and any NaN is converted to a NaN.
16-Bit Floating-Point Numbers
16-bit floating-point numbers are defined in the 16-bit floating-point numbers section of the Khronos Data Format Specification.
Unsigned 11-Bit Floating-Point Numbers
Unsigned 11-bit floating-point numbers are defined in the Unsigned 11-bit floating-point numbers section of the Khronos Data Format
Specification.
Unsigned 10-Bit Floating-Point Numbers
Unsigned 10-bit floating-point numbers are defined in the Unsigned 10-bit floating-point numbers section of the Khronos Data Format
Specification.
8-bit booleans
An 8-bit boolean uses the following representation for true and false:
- Zero to represent
false - Any non-zero value to represent
true
General Requirements
Any representable floating-point value in the appropriate format is legal as input to a Vulkan command that requires floating-point data. The result of providing a value that is not a floating-point number to such a command is unspecified, but must not lead to Vulkan interruption or termination. For example, providing a negative zero (where applicable) or a denormalized number to a Vulkan command must yield deterministic results, while providing a NaN or Inf yields unspecified results.
Some calculations require division. In such cases (including implied divisions performed by vector normalization), division by zero produces an unspecified result but must not lead to Vulkan interruption or termination.
Fixed-Point Data Conversions
When generic vertex attributes and pixel color or depth components are represented as integers, they are often (but not always) considered to be normalized. Normalized integer values are treated specially when being converted to and from floating-point values, and are usually referred to as normalized fixed-point.
In the remainder of this section, b denotes the bit width of the fixed-point integer representation. When the integer is one of the types defined by the API, b is the bit width of that type. When the integer comes from an image containing color or depth component texels, b is the number of bits allocated to that component in its specified image format.
The signed and unsigned fixed-point representations are assumed to be b-bit binary two’s-complement integers and binary unsigned integers, respectively.
Conversion From Normalized Fixed-Point to Floating-Point
Unsigned normalized fixed-point integers represent numbers in the range [0,1]. The conversion from an unsigned normalized fixed-point value c to the corresponding floating-point value f is defined as
Signed normalized fixed-point integers represent numbers in the range [-1,1]. The conversion from a signed normalized fixed-point value c to the corresponding floating-point value f is performed using
Only the range [-2b-1 + 1, 2b-1 - 1] is used to represent signed fixed-point values in the range [-1,1]. For example, if b = 8, then the integer value -127 corresponds to -1.0 and the value 127 corresponds to 1.0. This equation is used everywhere that signed normalized fixed-point values are converted to floating-point.
Note that while zero is exactly expressible in this representation, one value (-128 in the example) is outside the representable range, and implementations must clamp it to -1.0. Where the value is subject to further processing by the implementation, e.g. during texture filtering, values less than -1.0 may be used but the result must be clamped before the value is returned to shaders.
Conversion From Floating-Point to Normalized Fixed-Point
The conversion from a floating-point value f to the corresponding unsigned normalized fixed-point value c is defined by first clamping f to the range [0,1], then computing
- c = convertFloatToUint(f × (2b - 1), b)
where convertFloatToUint(r,b) returns one of the two unsigned binary integer values with exactly b bits which are closest to the floating-point value r. Implementations should round to nearest. If r is equal to an integer, then that integer value must be returned. In particular, if f is equal to 0.0 or 1.0, then c must be assigned 0 or 2b - 1, respectively.
The conversion from a floating-point value f to the corresponding signed normalized fixed-point value c is performed by clamping f to the range [-1,1], then computing
- c = convertFloatToInt(f × (2b-1 - 1), b)
where convertFloatToInt(r,b) returns one of the two signed two’s-complement binary integer values with exactly b bits which are closest to the floating-point value r. Implementations should round to nearest. If r is equal to an integer, then that integer value must be returned. In particular, if f is equal to -1.0, 0.0, or 1.0, then c must be assigned -(2b-1 - 1), 0, or 2b-1 - 1, respectively.
This equation is used everywhere that floating-point values are converted to signed normalized fixed-point.
String Representation
Strings passed into and returned from Vulkan API commands are usually defined to be null-terminated and UTF-8 encoded.
name is a
null-terminated UTF-16 encoded string used in conjunction with Windows
handles.When a UTF-8 string is returned from a Vulkan API query, it is returned in
a fixed-length buffer of C char.
For example, a string returned in
VkPhysicalDeviceProperties::deviceName has maximum length
VK_MAX_PHYSICAL_DEVICE_NAME_SIZE, and a string returned in
VkExtensionProperties::extensionName has maximum length
VK_MAX_EXTENSION_NAME_SIZE.
The string, including its null terminator, will always fit completely
within this buffer.
If the string is shorter than the buffer size, the contents of char in
the buffer following the null terminator are undefined.
When a UTF-8 string is passed into a Vulkan API, such as
VkDeviceCreateInfo::ppEnabledExtensionNames, there is no
explicit limit on the length of that string.
However, the string must contain a valid UTF-8 encoded string and must be
null-terminated.
Common Object Types
Some types of Vulkan objects are used in many different structures and command parameters, and are described here. These types include offsets, extents, and rectangles.
Offsets
Offsets are used to describe a pixel location within an image or framebuffer, as an (x,y) location for two-dimensional images, or an (x,y,z) location for three-dimensional images.
Extents
Extents are used to describe the size of a rectangular region of pixels within an image or framebuffer, as (width,height) for two-dimensional images, or as (width,height,depth) for three-dimensional images.
Rectangles
Host Address Ranges
Device Address Ranges
Many Vulkan commands need additional information about an address range; this may be provided by the following flags.
Structure Types
API Name Aliases
A small number of APIs did not follow the naming
conventions when initially defined.
For consistency, when we discover an API name that violates the naming
conventions, we rename it in the Specification, XML, and header files.
For backwards compatibility, the original (incorrect) name is retained as a
typo alias.
The alias is legacy and should not be used, but will be retained
indefinitely.
VK_STENCIL_FRONT_AND_BACK is an example of a typo alias.
It was initially defined as part of VkStencilFaceFlagBits.
Once the naming inconsistency was noticed, it was renamed to
VK_STENCIL_FACE_FRONT_AND_BACK, and the old name was aliased to the
correct name.