class new in Git master
#include <Magnum/Vk/Device.h>
Device Device.
Wraps a VkDevice and stores device-specific Vulkan function pointers. A device provides an abstraction over a physical GPU or a CPU with Vulkan capabilities.
Device creation
With an Instance ready, a device has to be picked first. Commonly it's done by calling pickDevice() and letting the library choose. This selection is affected by the --magnum-device
command-line option, giving the end users an ability to pick a particular device, choose a discrete or integrated GPU or even a software implementation. If the application needs something specific, you can use enumerateDevices() instead, pick a device from the list manually, provide the users with a list to choose from etc.
The picked device is then passed to DeviceCreateInfo. At the very least you'll also need to set up queues, as every Vulkan device needs at least one. That's done by creating an empty Queue instance and then referencing it from DeviceCreateInfo::
#include <Magnum/Vk/DeviceCreateInfo.h> … Vk::Queue queue{NoCreate}; Vk::Device device{instance, Vk::DeviceCreateInfo{Vk::pickDevice(instance)} .addQueues(Vk::QueueFlag::Graphics, {0.0f}, {queue}) };
In the above snippet, we requested a graphics queue via a convenience API. The information about available queues and other device properties is stored in a DeviceProperties that got returned from pickDevice() and DeviceCreateInfo called DeviceProperties::
Same as with Instance, the above won't enable any additional extensions except for what the engine itself needs or what's supplied on the command line. Use DeviceCreateInfo::
Vk::Device device{instance, Vk::DeviceCreateInfo{…} … .addEnabledExtensions< // predefined extensions Vk::Extensions::EXT::index_type_uint8, Vk::Extensions::KHR::device_group>() .addEnabledExtensions({"VK_NV_mesh_shader"_s}) // can be plain strings too };
In addition to extensions, you'll be usually enabling features as well. These are all exposed in a giant DeviceFeatures enum and you can simply OR them together. Internally, those get translated to VkPhysicalDeviceFeatures2 and related structures, features that are not exposed in the enum can be enabled by adding a corresponding structure to the pNext
chain. As with extensions, the set of enabled features can be later checked with enabledFeatures().
Vk::Device device{instance, Vk::DeviceCreateInfo{…} … .setEnabledFeatures( Vk::DeviceFeature::IndexTypeUnsignedByte| Vk::DeviceFeature::SamplerAnisotropy| Vk::DeviceFeature::GeometryShader| …) };
However, usually you'll be checking for extension and feature availability first, which is doable through DeviceProperties::
Vk::DeviceProperties properties = Vk::pickDevice(instance); Vk::ExtensionProperties extensions = properties.enumerateExtensionProperties(); Vk::DeviceCreateInfo info{properties}; if(extensions.isSupported<Vk::Extensions::EXT::index_type_uint8>()) info.addEnabledExtensions<Vk::Extensions::EXT::index_type_uint8>(); if(extensions.isSupported("VK_NV_mesh_shader"_s)) info.addEnabledExtensions({"VK_NV_mesh_shader"_s}); … info.setEnabledFeatures(properties.features() & // mask away unsupported ones (Vk::DeviceFeature::IndexTypeUnsignedByte| Vk::DeviceFeature::SamplerAnisotropy| Vk::DeviceFeature::GeometryShader| …));
With both Instance and Device created, you can proceed to setting up a CommandPool and a Pipeline, which will then need a ShaderSet and a PipelineLayout. For rasterization pipelines, you'll additionally need a MeshLayout and a RenderPass.
Vulkan portability subset
To simplify porting to platforms with the Portability Subset, Magnum implicitly enables the KHR_
For portability-related DeviceFeatures, on conformant Vulkan implementations (which don't advertise KHR_
A workflow that supports both conformant and Portability Subset devices with a single code path is outlined in the following snippet — on device creation you request features that you want (which is a no-op on conformant implementations), and at runtime you query those features in appropriate cases (which will be always true
on conformant implementations). As with other features, all APIs that require a particular Portability Subset feature are marked as such and also listed among others at Functionality requiring a specific Vulkan feature.
Vk::DeviceProperties properties = …; Vk::Device device{instance, Vk::DeviceCreateInfo{properties} /* enable triangle fans only if actually supported */ .setEnabledFeatures(properties.features() & Vk::DeviceFeature::TriangleFans) … }; … if(device.enabledFeatures() & Vk::DeviceFeature::TriangleFans) { // draw a triangle fan mesh } else { // indexed draw fallback }
Command-line options
The Device inherits a subset of the Instance command-line options, in particular the following. If the Instance didn't get argc
/ argv
passed, only the environment variables are used.
--magnum-disable-workarounds LIST
— Vulkan driver workarounds to disable (see Driver workarounds for detailed info) (environment:MAGNUM_DISABLE_WORKAROUNDS
)--magnum-disable-extensions LIST
— Vulkan instance or device extensions to disable, meaning DeviceCreateInfo::addEnabledExtensions() will skip them (environment: MAGNUM_DISABLE_EXTENSIONS
)--magnum-enable-extensions LIST
— Vulkan device extensions to enable in addition to DeviceCreateInfo defaults and what the application requests (environment:MAGNUM_ENABLE_EXTENSIONS
)--magnum-vulkan-version X.Y
— force Device Vulkan version instead of using what the device reports as supported, affecting what entrypoints and extensions get used (environment:MAGNUM_VULKAN_VERSION
)--magnum-log default|quiet|verbose
— console logging (environment:MAGNUM_LOG
) (default:default
)--magnum-device ID|integrated|discrete|virtual|cpu
— device ID or kind to pick in pickDevice(); if a device is selected through enumerateDevices() or any other way, this option has no effect (environment:MAGNUM_DEVICE
)
Interaction with raw Vulkan code
In addition to the common properties explained in Common interfaces for interaction with raw Vulkan code, the Device contains device-level Vulkan function pointers, accessible through operator->():
Vk::Device device{…}; // ... device->ResetQueryPoolEXT(device, …);
These functions are by default not accessible globally (and neither there is a global "current instance"), which is done in order to avoid multiple independent instances affecting each other. Sometimes it is however desirable to have global function pointers — for example when a 3rd party code needs to operate on the same instance, or when writing quick prototype code — and then it's possible to populate those using populateGlobalFunctionPointers(). Compared to the above, the same custom code would then look like this:
#include <MagnumExternal/Vulkan/flextVkGlobal.h> … Vk::Device device{…}; device.populateGlobalFunctionPointers(); … vkResetQueryPoolEXT(device, …);
Similarly you can use Instance::
Disabled move and delayed device creation
Due to the way Queue instances are populated on device creation, and for safety reasons as all device-dependent objects internally have to keep a pointer to the originating Device to access Vulkan function pointers, the Device class is not movable. This leads to a difference compared to other Vulkan object wrappers, where you can use the NoCreate tag to construct an empty instance (for example as a class member) and do a delayed creation by moving a new instance over the empty one. Here you have to use the create() function instead:
class MyApplication { public: explicit MyApplication(…); private: Vk::Device _device{NoCreate}; }; MyApplication::MyApplication(…) { // decide on extensions, features, ... _device.create(instance, Vk::DeviceCreateInfo{…} … ); }
Similar case is with wrap() — instead of being static
, you have to call it on a NoCreate'd instance. The Instance class behaves equivalently.
Constructors, destructors, conversion operators
- Device(Instance& instance, const DeviceCreateInfo& info) explicit
- Constructor.
- Device(Instance& instance, DeviceCreateInfo&& info) explicit
- Construct, reusing already populated device properties.
- Device(NoCreateT) explicit
- Construct without creating the device.
- Device(const Device&) deleted
- Copying is not allowed.
- Device(Device&& other) deleted
- Moving is not allowed.
- ~Device()
- Destructor.
- operator VkDevice()
Public functions
-
void wrap(Instance& instance,
VkPhysicalDevice physicalDevice,
VkDevice handle,
Version version,
const Containers::
StringIterable& enabledExtensions, const DeviceFeatures& enabledFeatures, HandleFlags flags = {}) - Wrap existing Vulkan handle.
- auto operator=(const Device&) -> Device& deleted
- Copying is not allowed.
- auto operator=(Device&&) -> Device& deleted
- Moving is not allowed.
- auto handle() -> VkDevice
- Underlying VkDevice handle.
- auto handleFlags() const -> HandleFlags
- Handle flags.
- void create(Instance& instance, const DeviceCreateInfo& info)
- Create a device.
- void create(Instance& instance, DeviceCreateInfo&& info)
- Create a device, reusing already populated device properties.
- auto tryCreate(Instance& instance, const DeviceCreateInfo& info) -> Result
- Try to create a device.
- auto tryCreate(Instance& instance, DeviceCreateInfo&& info) -> Result
- Try to create a device, reusing already populated device properties.
- auto properties() -> DeviceProperties&
- Device properties.
- auto version() const -> Version
- Version supported by the device.
- auto isVersionSupported(Version version) const -> bool
- Whether given version is supported on the device.
-
template<class E>auto isExtensionEnabled() const -> bool
- Whether given extension is enabled.
- auto isExtensionEnabled(const Extension& extension) const -> bool
- auto enabledFeatures() const -> const DeviceFeatures&
- Features enabled on the device.
- auto operator*() const -> const FlextVkDevice&
- Device-specific Vulkan function pointers.
- auto operator->() const -> const FlextVkDevice*
- auto release() -> VkDevice
- Release the underlying Vulkan device.
- void populateGlobalFunctionPointers()
- Populate global device-level function pointers to be used with third-party code.
Function documentation
Magnum:: Vk:: Device:: Device(Instance& instance,
const DeviceCreateInfo& info) explicit
Constructor.
Equivalent to calling Device(NoCreateT) followed by create(Instance&, const DeviceCreateInfo&).
Magnum:: Vk:: Device:: Device(Instance& instance,
DeviceCreateInfo&& info) explicit
Construct, reusing already populated device properties.
Equivalent to calling Device(NoCreateT) followed by create(Instance&, DeviceCreateInfo&&).
Magnum:: Vk:: Device:: Device(NoCreateT) explicit
Construct without creating the device.
Use create() or tryCreate() to create the device.
Magnum:: Vk:: Device:: Device(Device&& other) deleted
Moving is not allowed.
See Disabled move and delayed device creation for more information.
Magnum:: Vk:: Device:: ~Device()
Destructor.
Destroys associated VkDevice handle, unless the instance was created using wrap() without HandleFlag::
Magnum:: Vk:: Device:: operator VkDevice()
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
void Magnum:: Vk:: Device:: wrap(Instance& instance,
VkPhysicalDevice physicalDevice,
VkDevice handle,
Version version,
const Containers:: StringIterable& enabledExtensions,
const DeviceFeatures& enabledFeatures,
HandleFlags flags = {})
Wrap existing Vulkan handle.
Parameters | |
---|---|
instance | Vulkan instance the device is created on |
physicalDevice | Physical device the VkVkDevice was created from. Used to populate properties(). |
handle | The VkDevice handle |
version | Vulkan version that's assumed to be used on the device |
enabledExtensions | Extensions that are assumed to be enabled on the device |
enabledFeatures | Features that are assumed to be enabled on the device |
flags | Handle flags |
The handle
is expected to be originating from instance
and physicalDevice
. The version
, enabledExtensions
and enabledFeatures
parameters populate internal info about supported version, enabled extensions and enabled features and will be reflected in isVersionSupported(), isExtensionEnabled() and enabledFeatures(), among other things. If enabledExtensions
/ enabledFeatures
is empty, the device will behave as if no extensions / no features were enabled.
Note that this function retrieves all device-specific Vulkan function pointers, which is a relatively costly operation. It's thus not recommended to call this function repeatedly for creating short-lived device instances, even though it's technically correct.
Unlike a device created using the constructor or create(), the Vulkan device is by default not deleted on destruction. Use flags
for different behavior.
Device& Magnum:: Vk:: Device:: operator=(Device&&) deleted
Moving is not allowed.
See Disabled move and delayed device creation for more information.
void Magnum:: Vk:: Device:: create(Instance& instance,
const DeviceCreateInfo& info)
Create a device.
Parameters | |
---|---|
instance | Vulkan instance to create the device on |
info | Device creation info |
Meant to be called on a NoCreate'd instance. After creating the device populates device-level function pointers and runtime information about enabled extensions and features based on info
, and finally requests device queues added via DeviceCreateInfo::
If device creation fails, a message is printed to error output and the application exits — if you need a different behavior, use tryCreate() instead.
void Magnum:: Vk:: Device:: create(Instance& instance,
DeviceCreateInfo&& info)
Create a device, reusing already populated device properties.
Compared to create(Instance&, const DeviceCreateInfo&), it can take ownership of the DeviceProperties added to info
earlier via DeviceCreateInfo::
With that, the properties() getter and any APIs relying on it can reuse what was possibly already queried without having to repeat the potentially complex queries second time.
Result Magnum:: Vk:: Device:: tryCreate(Instance& instance,
const DeviceCreateInfo& info)
Try to create a device.
Unlike create(Instance&, const DeviceCreateInfo&), instead of exiting on error, prints a message to error output and returns a corresponding result value. On success returns Result::
Result Magnum:: Vk:: Device:: tryCreate(Instance& instance,
DeviceCreateInfo&& info)
Try to create a device, reusing already populated device properties.
Unlike create(Instance&, DeviceCreateInfo&&), instead of exiting on error, prints a message to error output and returns a corresponding result value. On success returns Result::
DeviceProperties& Magnum:: Vk:: Device:: properties()
Device properties.
If a r-value DeviceProperties instance was propagated to DeviceCreateInfo and then to Device, it's reused here. Otherwise the contents are populated on first use.
Version Magnum:: Vk:: Device:: version() const
Version supported by the device.
Unless overridden using --magnum-vulkan-version
on the command line, corresponds to DeviceProperties::
bool Magnum:: Vk:: Device:: isVersionSupported(Version version) const
Whether given version is supported on the device.
Compares version
against version().
template<class E>
bool Magnum:: Vk:: Device:: isExtensionEnabled() const
Whether given extension is enabled.
Accepts device extensions from the Extensions namespace, listed also in the Vulkan support tables. Search complexity is . Example usage:
if(device.isExtensionEnabled<Vk::Extensions::EXT::index_type_uint8>()) { // keep mesh indices 8bit } else { // convert them to 16bit }
Note that this returns true
only if given extension is supported by the driver and it was enabled via DeviceCreateInfo::
bool Magnum:: Vk:: Device:: isExtensionEnabled(const Extension& extension) const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
const DeviceFeatures& Magnum:: Vk:: Device:: enabledFeatures() const
Features enabled on the device.
const FlextVkDevice& Magnum:: Vk:: Device:: operator*() const
Device-specific Vulkan function pointers.
Function pointers are implicitly stored per-device, use populateGlobalFunctionPointers() to populate the global vk*
functions.
const FlextVkDevice* Magnum:: Vk:: Device:: operator->() const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
VkDevice Magnum:: Vk:: Device:: release()
Release the underlying Vulkan device.
Releases ownership of the Vulkan device and returns its handle so vkDestroyDevice() is not called on destruction. The internal state is then equivalent to moved-from state.
void Magnum:: Vk:: Device:: populateGlobalFunctionPointers()
Populate global device-level function pointers to be used with third-party code.
Populates device-level global function pointers so third-party code is able to call global device-level vk*
functions. See Interaction with raw Vulkan code for more information.