Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Vulkan Cookbook

You're reading from   Vulkan Cookbook Work through recipes to unlock the full potential of the next generation graphics API—Vulkan

Arrow left icon
Product type Paperback
Published in Apr 2017
Publisher Packt
ISBN-13 9781786468154
Length 700 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Pawel Lapinski Pawel Lapinski
Author Profile Icon Pawel Lapinski
Pawel Lapinski
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Instance and Devices FREE CHAPTER 2. Image Presentation 3. Command Buffers and Synchronization 4. Resources and Memory 5. Descriptor Sets 6. Render Passes and Framebuffers 7. Shaders 8. Graphics and Compute Pipelines 9. Command Recording and Drawing 10. Helper Recipes 11. Lighting 12. Advanced Rendering Techniques

Creating a logical device with geometry shaders, graphics, and compute queues

In Vulkan, when we create various objects, we need to prepare many different structures that describe the creation process itself, but they may also require other objects to be created.

A logical device is no different: We need to enumerate physical devices, check their properties and supported queue families, and prepare a VkDeviceCreateInfo structure that requires much more information.

To organize these operations, we will present a sample recipe that creates a logical device from one of the available physical devices that support geometry shaders, and both graphics and compute queues.

How to do it...

  1. Prepare a variable of type VkDevice named logical_device.
  2. Create two variables of type VkQueue, one named graphics_queue and one named compute_queue.
  1. Create a variable of type std::vector<VkPhysicalDevice> named physical_devices.
  2. Get the list of all physical devices available on a given platform and store it in the physical_devices vector (refer to the Enumerating available physical devices recipe).
  3. For each physical device from the physical_devices vector:
    1. Create a variable of type VkPhysicalDeviceFeatures named device_features.
    2. Acquire the list of features supported by a given physical device and store it in the device_features variable.
    3. Check whether the geometryShader member of the device_features variable is equal to VK_TRUE (is not 0). If it is, reset all the other members of the device_features variable (set their values to zero); if it is not, start again with another physical device.
    4. Create two variables of type uint32_t named graphics_queue_family_index and compute_queue_family_index.
    5. Acquire indices of queue families that support graphics and compute operations, and store them in the graphics_queue_family_index and compute_queue_family_index variables, respectively (refer to the Selecting index of a queue family with desired capabilities recipe). If any of these operations is not supported, search for another physical device.
    6. Create a variable of type std::vector with elements of type QueueInfo (refer to Creating a logical device recipe). Name this variable requested_queues.
    7. Store the graphics_queue_family_index variable and one-element vector of floats with a 1.0f value in the requested_queues variable. If a value of the compute_queue_family_index variable is different than the value of the graphics_queue_family_index variable, add another element to the requested_queues vector, with the compute_queue_family_index variable and a one-element vector of floats with 1.0f value.
    8. Create a logical device using the physical_device, requested_queues, device_features and logical_device variables (refer to the Creating a logical device recipe). If this operation failed, repeat the preceding operations with another physical device.
    1. If the logical device was successfully created, load the device-level functions (refer to the Loading device-level functions recipe). Get the handle of the queue from the graphics_queue_family_index family and store it in the graphics_queue variable. Get the queue from the compute_queue_family_index family and store it in the compute_queue variable.

How it works...

To start the process of creating a logical device, we need to acquire the handles of all physical devices available on a given computer:

std::vector<VkPhysicalDevice> physical_devices; 
EnumerateAvailablePhysicalDevices( instance, physical_devices );

Next we need to loop through all available physical devices. For each such device, we need to acquire its features. This will give us the information about whether a given physical device supports geometry shaders:

for( auto & physical_device : physical_devices ) { 
  VkPhysicalDeviceFeatures device_features; 
  VkPhysicalDeviceProperties device_properties; 
  GetTheFeaturesAndPropertiesOfAPhysicalDevice( physical_device, device_features, device_properties ); 

  if( !device_features.geometryShader ) { 
    continue; 
  } else { 
    device_features = {}; 
    device_features.geometryShader = VK_TRUE; 
  }

If geometry shaders are supported, we can reset all the other members of a returned list of features. We will provide this list during the logical device creation, but we don't want to enable any other feature. In this example, geometry shaders are the only additional feature we want to use.

Next we need to check if a given physical device exposes queue families that support graphics and compute operations. This may be just one single family or two separate families. We acquire the indices of such queue families:

  uint32_t graphics_queue_family_index; 
  if( !SelectIndexOfQueueFamilyWithDesiredCapabilities( physical_device, VK_QUEUE_GRAPHICS_BIT, graphics_queue_family_index ) ) { 
    continue; 
  } 

  uint32_t compute_queue_family_index; 
  if( !SelectIndexOfQueueFamilyWithDesiredCapabilities( physical_device, VK_QUEUE_COMPUTE_BIT, compute_queue_family_index ) ) { 
    continue; 
  }

Next, we need to prepare a list of queue families, from which we want to request queues. We also need to assign priorities to each queue from each family:

  std::vector<QueueInfo> requested_queues = { { graphics_queue_family_index, { 1.0f } } }; 
  if( graphics_queue_family_index != compute_queue_family_index ) { 
    requested_queues.push_back( { compute_queue_family_index, { 1.0f } } ); 
  }

If graphics and compute queue families have the same index, we request only one queue from one queue family. If they are different, we need to request two queues: One from the graphics family and one from the compute family.

We are ready to create a logical device for which we provide the prepared data. Upon success, we can the load device-level functions and acquire the handles of the requested queues:

  if( !CreateLogicalDevice( physical_device, requested_queues, {}, &device_features, logical_device ) ) { 
    continue; 
  } else { 
    if( !LoadDeviceLevelFunctions( logical_device, {} ) ) { 
      return false; 
    } 
    GetDeviceQueue( logical_device, graphics_queue_family_index, 0, graphics_queue ); 
    GetDeviceQueue( logical_device, compute_queue_family_index, 0, compute_queue ); 
    return true; 
  } 
} 
return false;

See also

The following recipes in this chapter:

  • Enumerating available physical devices
  • Getting features and properties of a physical device
  • Selecting the index of a queue family with the desired capabilities
  • Creating a logical device
  • Loading device-level functions
  • Getting a device queue
  • Destroying a logical device
You have been reading a chapter from
Vulkan Cookbook
Published in: Apr 2017
Publisher: Packt
ISBN-13: 9781786468154
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime