Vulkan Logo

1. Preamble

Copyright 2014-2025 The Khronos Group Inc.

This Specification is protected by copyright laws and contains material proprietary to Khronos. Except as described by these terms, it or any components may not be reproduced, republished, distributed, transmitted, displayed, broadcast or otherwise exploited in any manner without the express prior written permission of Khronos.

Khronos grants a conditional copyright license to use and reproduce the unmodified Specification for any purpose, without fee or royalty, EXCEPT no licenses to any patent, trademark or other intellectual property rights are granted under these terms.

Khronos makes no, and expressly disclaims any, representations or warranties, express or implied, regarding this Specification, including, without limitation: merchantability, fitness for a particular purpose, non-infringement of any intellectual property, correctness, accuracy, completeness, timeliness, and reliability. Under no circumstances will Khronos, or any of its Promoters, Contributors or Members, or their respective partners, officers, directors, employees, agents or representatives be liable for any damages, whether direct, indirect, special or consequential damages for lost revenues, lost profits, or otherwise, arising from or in connection with these materials.

This document contains extensions which are not ratified by Khronos, and as such is not a ratified Specification, though it contains text from (and is a superset of) the ratified Specification.

The ratified version of the Vulkan Specification can be found at https://registry.khronos.org/vulkan/specs/latest-ratified/pdf/vkspec.pdf (core with all ratified extensions).

This Specification contains substantially unmodified functionality from, and is a successor to, Khronos specifications including OpenGL, OpenGL ES and OpenCL.

The Khronos Intellectual Property Rights Policy defines the terms 'Scope', 'Compliant Portion', and 'Necessary Patent Claims'.

Some parts of this Specification are purely informative and so are EXCLUDED the Scope of this Specification. The Document Conventions section of the Introduction defines how these parts of the Specification are identified.

Where this Specification uses technical terminology, defined in the Glossary or otherwise, that refer to enabling technologies that are not expressly set forth in this Specification, those enabling technologies are EXCLUDED from the Scope of this Specification. For clarity, enabling technologies not disclosed with particularity in this Specification (e.g. semiconductor manufacturing technology, hardware architecture, processor architecture or microarchitecture, memory architecture, compiler technology, object oriented technology, basic operating system technology, compression technology, algorithms, and so on) are NOT to be considered expressly set forth; only those application program interfaces and data structures disclosed with particularity are included in the Scope of this Specification.

For purposes of the Khronos Intellectual Property Rights Policy as it relates to the definition of Necessary Patent Claims, all recommended or optional features, behaviors and functionality set forth in this Specification, if implemented, are considered to be included as Compliant Portions.

Where this Specification identifies specific sections of external references, only those specifically identified sections define normative functionality. The Khronos Intellectual Property Rights Policy excludes external references to materials and associated enabling technology not created by Khronos from the Scope of this Specification, and any licenses that may be required to implement such referenced materials and associated technologies must be obtained separately and may involve royalty payments.

Khronos and Vulkan are registered trademarks, and SPIR-V is a trademark of The Khronos Group Inc. OpenCL is a trademark of Apple Inc., used under license by Khronos. OpenGL is a registered trademark and the OpenGL ES logo is a trademark of Hewlett Packard Enterprise, used under license by Khronos. ASTC is a trademark of ARM Holdings PLC. All other product names, trademarks, and/or company names are used solely for identification and belong to their respective owners.

2. Introduction

This document, referred to as the “Vulkan Specification” or just the “Specification” hereafter, describes the Vulkan Application Programming Interface (API). Vulkan is a C99 API designed for explicit control of low-level graphics and compute functionality.

The canonical version of the Specification is available in the official Vulkan Registry (https://registry.khronos.org/vulkan/). The source files used to generate the Vulkan specification are stored in the Vulkan Documentation Repository (https://github.com/KhronosGroup/Vulkan-Docs).

The source repository additionally has a public issue tracker and allows the submission of pull requests that improve the specification.

2.1. Document Conventions

The Vulkan specification is intended for use by both implementors of the API and application developers seeking to make use of the API, forming a contract between these parties. Specification text may address either party; typically the intended audience can be inferred from context, though some sections are defined to address only one of these parties. (For example, Valid Usage sections only address application developers). Any requirements, prohibitions, recommendations or options defined in specification text are imposed only on the audience of that text.

2.1.1. Normative Requirements

The Vulkan Specification uses a combination of normative terminology and normative descriptions to express the requirements that it imposes on applications and implementations. An application which complies with all normative requirements imposed on applications is said to make valid use of the API; failing to comply with such requirements results in undefined behavior, as discussed in the Valid Usage section below. In the context of this document, an implementation which complies with all normative requirements imposed on implementations is said to be conformant.

Note

The Khronos Group imposes additional requirements on implementors who wish to make public statements describing their Vulkan implementations as conformant. These include signing the Vulkan Adopter’s Agreement, paying the associated fee, and making a successful conformance test submission to the Khronos Conformance Process. For details see the Khronos Trademark Guidelines (https://www.khronos.org/legal/khronos-trademark-guidelines).

Normative Terminology

Within this specification, the key words must, required, should, may, and optional are to be interpreted as described in RFC 2119 - Key words for use in RFCs to Indicate Requirement Levels (https://www.ietf.org/rfc/rfc2119.txt). The additional key word optionally is an alternate form of optional, for use where grammatically appropriate. These key words are highlighted in the specification to indicate that they are being used in a specific technical sense.

The additional key words can and cannot are to be interpreted as describing the capabilities of an application, as follows:

can

This word means that the application is able to perform the action described.

cannot

This word means that the API and/or the execution environment provide no mechanism through which the application can express or accomplish the action described.

These key words are never used in text addressing implementors.

Note

There is an important distinction between cannot and must not, as used in this Specification. Cannot refers to something the API provides no way for the application to express or accomplish. Must not describes something that the application is able to express, but that is not valid use of the API, and will have undefined and potentially unrecoverable consequences.

Normative Descriptions

In the Vulkan Specification, the normative term must is primarily used to describe application behavior, and in particular to constrain what inputs or commands issued by the application to the implementation are considered valid.

To constrain implementation behavior, the specification sometimes uses must, but more often simply describes the behavior of the implementation in response to specified commands and inputs. Unless explicitly stated otherwise, such references to implementation behavior describe the behavior of conformant implementations, and express normative requirements which an implementation must satisfy in order to conform to the specification. For example, if the specification says “Under specified condition, the error code VK_ERROR_FEATURE_NOT_PRESENT is returned”, that behavior is a requirement of the specification, and an implementation which does not return that error code under that condition is not conformant.

When the normative terms may, should, or optional are used to describe implementation behavior, they define alternative or optional behaviors which a conformant implementation may or may not exhibit. Such statements are also normative. For example, if the specification says "Under specified condition, the implementation should return A but may instead return B", then an implementation that returns either A or B under that condition is conformant (assuming it does not violate other normative requirements), while an implementation that returns anything else is not.

2.1.2. Normative References

References to external documents are considered normative references if the Specification uses normative terminology or normative descriptions to refer to them or their requirements, either as a whole or in part.

The following documents are referenced by normative sections of the specification:

IEEE. August, 2008. IEEE Standard for Floating-Point Arithmetic. IEEE Std 754-2008. https://dx.doi.org/10.1109/IEEESTD.2008.4610935 .

Andrew Garrard. Khronos Data Format Specification, version 1.3. https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html .

John Kessenich. SPIR-V Extended Instructions for GLSL, Version 1.00 (February 10, 2016). https://registry.khronos.org/spir-v/ .

John Kessenich, Boaz Ouriel, and Raun Krisch. SPIR-V Specification, Version 1.5, Revision 3, Unified (April 24, 2020). https://registry.khronos.org/spir-v/ .

ITU-T. H.264 Advanced Video Coding for Generic Audiovisual Services (August, 2021). https://www.itu.int/rec/T-REC-H.264-202108-I/ .

ITU-T. H.265 High Efficiency Video Coding (August, 2021). https://www.itu.int/rec/T-REC-H.265-202108-S/ .

Google. VP9 Bitstream & Decoding Process Specification (February 22, 2017). https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.7-20170222-draft.pdf .

Alliance for Open Media. AV1 Bitstream & Decoding Process Specification (January 8, 2019). https://aomediacodec.github.io/av1-spec/av1-spec.pdf .

Jon Leech. The Khronos Vulkan API Registry (February 26, 2023). https://registry.khronos.org/vulkan/specs/latest/registry.html .

Jon Leech and Tobias Hector. Vulkan Documentation and Extensions: Procedures and Conventions (February 26, 2023). https://registry.khronos.org/vulkan/specs/latest/styleguide.html .

Architecture of the Vulkan Loader Interfaces (October, 2021). https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderInterfaceArchitecture.md .

2.1.3. Informative Language

Some language in the specification is purely informative, intended to provide background information or make suggestions to implementors or developers. Such language does not impose normative requirements on implementations or applications.

All NOTEs are implicitly informative.

If an entire chapter, section, or appendix contains only informative language, its title will be suffixed with “(Informative)”. Unless so noted in the title, all chapters, sections, and appendices in this document are normative.

2.1.4. Technical Terminology

The Vulkan Specification makes use of common engineering and graphics terms such as Pipeline, Shader, and Host to identify and describe Vulkan API constructs and their attributes, states, and behaviors. The Glossary defines the basic meanings of these terms in the context of the Specification. The Specification text provides fuller definitions of the terms and may elaborate, extend, or clarify the Glossary definitions. When a term defined in the Glossary is used in normative language within the Specification, the definitions within the Specification govern and supersede any meanings the terms may have in other technical contexts (i.e. outside the Specification).

2.1.5. Ratification

Ratification of a Vulkan core version or extension is a status conferred by vote of the Khronos Board of Promoters, bringing that core version or extension under the umbrella of the Khronos IP Rights Policy.

All Vulkan core versions and KHR extensions (including provisional specifications) are ratified, as are some multi-vendor EXT extensions. Ratification status of extensions is described in the Layers & Extensions (Informative) appendix.

Note

Ratification status is primarily of interest to IHVs developing GPU hardware and Vulkan implementations. For developers, ratification does not necessarily mean that an extension is “better”, has a more stable API, or is more widely supported than alternative ways of achieving that functionality.

Interactions between ratified and non-ratified extensions are not themselves ratified.

3. 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.

3.1. 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.

Note

Since a variety of data types and structures in Vulkan may be accessible by both host and physical device operations, the implementation should be able to access such data efficiently in both paths in order to facilitate writing portable and performant applications.

3.2. 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.

Note

A single device may report multiple similar queue families rather than, or as well as, reporting multiple members of one or more of those families. This indicates that while members of those families have similar capabilities, they are not directly compatible with one another.

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.

3.2.1. Queue Operation

Vulkan queues provide an interface to the execution engines of a device. Commands for these execution engines are recorded into command buffers ahead of execution time, and then submitted to a queue for execution. Once submitted to a queue, command buffers will begin and complete execution without further application intervention, though the order of this execution is dependent on a number of implicit and explicit ordering constraints.

Work is submitted to queues using queue submission commands that typically take the form vkQueue* (e.g. vkQueueSubmit , vkQueueBindSparse ), and can take a list of semaphores upon which to wait before work begins and a list of semaphores to signal once work has completed. The work itself, as well as signaling and waiting on the semaphores are all queue operations. Queue submission commands return control to the application once queue operations have been submitted - they do not wait for completion.

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.

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.

Note

Implementations have significant freedom to overlap execution of work submitted to a queue, and this is common due to deep pipelining and parallelism in Vulkan devices.

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.

Note

In the absence of explicit synchronization or implicit ordering guarantees, action commands may overlap execution or execute out of order, potentially leading to data races. However, such reordering does not affect the current state observed by any action command. Each action command uses the state in effect at the point where the command occurs in the command buffer, regardless of when it is executed.

3.3. 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.

3.3.1. 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.

3.3.2. 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.

Note

The scope of external handles and their associated resources may vary according to their type, but they can generally be shared across process and API boundaries.

3.4. 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.

Note

These requirements and recommendations are intended to allow implementors to take advantage of platform-specific conventions for SDKs, ABIs, library versioning mechanisms, etc. while still minimizing the code changes necessary to port applications or libraries between platforms. Platform vendors, or providers of the de facto standard Vulkan shared library for a platform, are encouraged to document what symbols the shared library provides and how it will be versioned when new symbols are added.

Applications should only rely on shared library symbols for commands in the minimum core version required by the application. vkGetInstanceProcAddr and vkGetDeviceProcAddr should be used to obtain function pointers for commands in core versions beyond the application’s minimum required version.

3.5. 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:

VkBool32 represents boolean True and False values, since C does not have a sufficiently portable built-in boolean type:

// Provided by VK_VERSION_1_0
typedef uint32_t VkBool32;

VK_TRUE represents a boolean True (unsigned integer 1) value, and VK_FALSE a boolean False (unsigned integer 0) value.

All values returned from a Vulkan implementation in a VkBool32 will be either VK_TRUE or VK_FALSE.

Applications must not pass any other values than VK_TRUE or VK_FALSE into a Vulkan implementation where a VkBool32 is expected.

VK_TRUE is a constant representing a VkBool32 True value.

#define VK_TRUE                           1U

VK_FALSE is a constant representing a VkBool32 False value.

#define VK_FALSE                          0U

VkDeviceSize represents device memory size and offset values:

// Provided by VK_VERSION_1_0
typedef uint64_t VkDeviceSize;

VkDeviceAddress represents device buffer address values:

// Provided by VK_VERSION_1_0
typedef uint64_t VkDeviceAddress;
Valid Usage
  • VUID-VkDeviceAddress-size-11364
    A valid VkDeviceAddress must be equal to the sum of an address retrieved from a VkBuffer via vkGetBufferDeviceAddress, and any offset in the range [0, size), where size is the value of VkBufferCreateInfo::size used to create that VkBuffer

  • VUID-VkDeviceAddress-None-10894
    If a VkDeviceAddress was retrieved from a non-sparse buffer, that buffer must be bound completely and contiguously to a single VkDeviceMemory object

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.

3.5.1. 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.

3.5.2. 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*Count where * 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.

Note

In practice, this means that applications will typically call such query commands twice:

  • 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 *Count where * 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.

Note

Applications need to separately track the value they provided as the input element count member for such arrays and compare those with the returned element counts in order to determine whether the actually returned element count is smaller than the size of the return array. Another side effect of this is that it is impossible for the application to determine if the number of retrievable elements has increased beyond the provided input element count so using return arrays in output structures should be limited to invariant array results. In practice, this means that applications will typically call such query commands multiple times:

  • 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.

3.5.3. 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_t variable, conventionally named as p*Size where * 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.

Note

If 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.

Note

Some binary queries do not behave consistently with this pattern for historical reasons, primarily that the 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.

3.6. 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.

Note

Memory barriers are particularly relevant for hosts based on the ARM CPU architecture, which is more weakly ordered than many developers are accustomed to from x86/x64 programming. Fortunately, most higher-level synchronization primitives (like the pthread library) perform memory barriers as a part of mutual exclusion, so mutexing Vulkan objects via these primitives will have the desired effect.

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.

Externally Synchronized Parameters and Members