WIP queue rewrite

This commit is contained in:
Charles Giessen 2020-02-07 17:34:05 -07:00
parent 7d2402ba2d
commit 5375504bb3
2 changed files with 98 additions and 209 deletions

View File

@ -460,63 +460,53 @@ bool supports_features (VkPhysicalDeviceFeatures supported, VkPhysicalDeviceFeat
} }
} // namespace detail } // namespace detail
QueueFamilies find_queue_families (VkPhysicalDevice phys_device, VkSurfaceKHR surface) { int get_graphics_queue_index (std::vector<VkQueueFamilyProperties> const& families) {
auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties> ( for (int i = 0; i < families.size (); i++) {
vkGetPhysicalDeviceQueueFamilyProperties, phys_device); if (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) return i;
}
QueueFamilies families; return -1;
int dedicated_compute = -1; }
int dedicated_transfer = -1; int get_distinct_compute_queue_index (std::vector<VkQueueFamilyProperties> const& families) {
for (int i = 0; i < families.size (); i++) {
for (int i = 0; i < queue_families.size (); i++) { if ((families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
auto& queueFlags = queue_families[i].queueFlags; ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0))
if (queueFlags & VK_QUEUE_GRAPHICS_BIT) families.graphics = i; return i;
if (queueFlags & VK_QUEUE_COMPUTE_BIT) families.compute = i; }
if (queueFlags & VK_QUEUE_TRANSFER_BIT) families.transfer = i; return -1;
if (queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) families.sparse = i; }
int get_distinct_transfer_queue_index (std::vector<VkQueueFamilyProperties> const& families) {
// compute that isn't graphics for (int i = 0; i < families.size (); i++) {
if (queueFlags & VK_QUEUE_COMPUTE_BIT && ((queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) if ((families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) &&
dedicated_compute = i; ((families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0))
return true;
// transfer that isn't computer or graphics }
if (queueFlags & VK_QUEUE_TRANSFER_BIT && ((queueFlags & VK_QUEUE_COMPUTE_BIT) == 0) && return -1;
((queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) }
dedicated_transfer = i; int get_present_queue_index (VkPhysicalDevice const phys_device,
VkSurfaceKHR const surface,
std::vector<VkQueueFamilyProperties> const& families) {
for (int i = 0; i < families.size (); i++) {
VkBool32 presentSupport = false; VkBool32 presentSupport = false;
if (surface != VK_NULL_HANDLE) { if (surface != VK_NULL_HANDLE) {
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR (phys_device, i, surface, &presentSupport); VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR (phys_device, i, surface, &presentSupport);
} }
if (presentSupport == true) families.present = i; if (presentSupport == true) return i;
} }
return -1;
if (dedicated_compute != -1) families.compute = dedicated_compute;
if (dedicated_transfer != -1) families.transfer = dedicated_transfer;
// compute and transfer always supported on the graphics family
if (families.compute != -1 && queue_families[families.graphics].queueFlags & VK_QUEUE_COMPUTE_BIT)
families.compute = families.graphics;
if (families.transfer != -1 && queue_families[families.graphics].queueFlags & VK_QUEUE_TRANSFER_BIT)
families.transfer = families.graphics;
families.count_graphics = queue_families[families.graphics].queueCount;
families.count_transfer = queue_families[families.transfer].queueCount;
families.count_compute = queue_families[families.compute].queueCount;
if (families.sparse != -1) families.count_sparse = queue_families[families.sparse].queueCount;
return families;
} }
PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkPhysicalDevice phys_device) { PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkPhysicalDevice phys_device) {
Suitable suitable = Suitable::yes; Suitable suitable = Suitable::yes;
QueueFamilies indices = find_queue_families (phys_device, info.surface); auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties> (
vkGetPhysicalDeviceQueueFamilyProperties, phys_device);
bool dedicated_compute = get_distinct_compute_queue_index (queue_families);
bool dedicated_transfer = get_distinct_transfer_queue_index (queue_families);
bool present_queue = get_present_queue_index (phys_device, info.surface, queue_families);
if (criteria.require_dedicated_compute_queue && indices.graphics != indices.compute) if (criteria.require_dedicated_compute_queue && !dedicated_compute) suitable = Suitable::no;
suitable = Suitable::no; if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) suitable = Suitable::no;
if (criteria.require_dedicated_transfer_queue && indices.graphics != indices.transfer) if (criteria.require_present && !present_queue) suitable = Suitable::no;
suitable = Suitable::no;
if (criteria.require_present && indices.present == -1) suitable = Suitable::no;
auto required_extensions_supported = auto required_extensions_supported =
detail::check_device_extension_support (phys_device, criteria.required_extensions); detail::check_device_extension_support (phys_device, criteria.required_extensions);
@ -580,9 +570,6 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkP
bool required_features_supported = bool required_features_supported =
detail::supports_features (supported_features, criteria.required_features); detail::supports_features (supported_features, criteria.required_features);
if (!required_features_supported) suitable = Suitable::no; if (!required_features_supported) suitable = Suitable::no;
bool desired_features_supported = detail::supports_features (supported_features, criteria.desired_features);
if (!desired_features_supported) suitable = Suitable::partial;
return suitable; return suitable;
} }
@ -620,15 +607,10 @@ detail::Expected<PhysicalDevice, VkResult> PhysicalDeviceSelector::select () {
if (physical_device.phys_device == VK_NULL_HANDLE) { if (physical_device.phys_device == VK_NULL_HANDLE) {
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to find a suitable GPU!" }; return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to find a suitable GPU!" };
} }
// vkGetPhysicalDeviceFeatures (physical_device.phys_device, &physical_device.features);
vkGetPhysicalDeviceProperties (physical_device.phys_device, &physical_device.properties);
vkGetPhysicalDeviceMemoryProperties (physical_device.phys_device, &physical_device.memory_properties);
physical_device.surface = info.surface; physical_device.surface = info.surface;
physical_device.features = criteria.required_features; physical_device.features = criteria.required_features;
physical_device.queue_family_properties = find_queue_families (physical_device.phys_device, info.surface);
physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (), physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (),
criteria.required_extensions.begin (), criteria.required_extensions.begin (),
criteria.required_extensions.end ()); criteria.required_extensions.end ());
@ -703,66 +685,37 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features (VkPhysica
criteria.required_features = features; criteria.required_features = features;
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::set_desired_features (VkPhysicalDeviceFeatures features) {
criteria.desired_features = features;
return *this;
}
PhysicalDeviceSelector& PhysicalDeviceSelector::select_first_device_unconditionally (bool unconditionally) { PhysicalDeviceSelector& PhysicalDeviceSelector::select_first_device_unconditionally (bool unconditionally) {
criteria.use_first_gpu_unconditionally = unconditionally; criteria.use_first_gpu_unconditionally = unconditionally;
return *this; return *this;
} }
// ---- Queue Selection ---- //
// ---- Device ---- // // ---- Device ---- //
void destroy_device (Device device) { vkDestroyDevice (device.device, nullptr); } void destroy_device (Device device) { vkDestroyDevice (device.device, nullptr); }
struct QueueFamily {
int32_t family;
std::vector<float> priorities;
};
DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device) { DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device) {
info.physical_device = phys_device; info.physical_device = phys_device;
info.extensions = phys_device.extensions_to_enable; info.extensions = phys_device.extensions_to_enable;
} }
detail::Expected<Device, VkResult> DeviceBuilder::build () { detail::Expected<Device, VkResult> DeviceBuilder::build () {
auto& queue_properties = info.physical_device.queue_family_properties;
std::vector<QueueFamily> families;
families.push_back ({ queue_properties.graphics, auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties> (
info.graphics_queue_priorities.size () == 0 ? vkGetPhysicalDeviceQueueFamilyProperties, info.physical_device.phys_device);
std::vector<float> (info.use_multiple_queues_per_family ? queue_properties.count_graphics : 1, 1.0f) :
info.graphics_queue_priorities });
if (queue_properties.compute != -1 && queue_properties.compute != queue_properties.graphics) {
families.push_back ({ queue_properties.compute,
info.compute_queue_priorities.size () == 0 ?
std::vector<float> (info.use_multiple_queues_per_family ? queue_properties.count_compute : 1, 1.0f) :
info.compute_queue_priorities });
}
if (queue_properties.transfer != -1 && queue_properties.transfer != queue_properties.graphics &&
queue_properties.transfer != queue_properties.compute)
families.push_back ({ queue_properties.transfer,
info.transfer_queue_priorities.size () == 0 ?
std::vector<float> (info.use_multiple_queues_per_family ? queue_properties.count_transfer : 1, 1.0f) :
info.transfer_queue_priorities });
if (queue_properties.sparse != -1 && queue_properties.sparse != queue_properties.graphics &&
queue_properties.sparse != queue_properties.compute &&
queue_properties.sparse != queue_properties.transfer)
families.push_back ({ queue_properties.sparse,
info.sparse_queue_priorities.size () == 0 ?
std::vector<float> (info.use_multiple_queues_per_family ? queue_properties.count_sparse : 1, 1.0f) :
info.sparse_queue_priorities });
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
for (auto& queue : families) { float priority = 1.0f;
for (int i = 0; i < queue_families.size (); i++) {
VkDeviceQueueCreateInfo queue_create_info = {}; VkDeviceQueueCreateInfo queue_create_info = {};
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.queueFamilyIndex = static_cast<uint32_t> (queue.family); queue_create_info.queueFamilyIndex = static_cast<uint32_t> (i);
queue_create_info.queueCount = queue.priorities.size (); queue_create_info.queueCount = 1;
queue_create_info.pQueuePriorities = queue.priorities.data (); queue_create_info.pQueuePriorities = &priority;
queueCreateInfos.push_back (queue_create_info); queueCreateInfos.push_back (queue_create_info);
} }
@ -797,81 +750,44 @@ template <typename T> DeviceBuilder& DeviceBuilder::add_pNext (T* structure) {
info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure)); info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure));
return *this; return *this;
} }
DeviceBuilder& DeviceBuilder::use_multiple_queues_per_family (bool multi_queue) {
info.use_multiple_queues_per_family = true; // ---- Getting Queues ---- //
return *this;
VkQueue get_queue (VkDevice device, int32_t family) {
VkQueue out_queue;
vkGetDeviceQueue (device, family, 0, &out_queue);
return out_queue;
} }
DeviceBuilder& DeviceBuilder::set_graphics_queue_priorities (std::vector<float> priorities) { detail::Expected<VkQueue, VkResult> get_present_queue (Device const& device) {
if (info.physical_device.queue_family_properties.count_graphics >= priorities.size ()) int present = get_present_queue_index (device.physical_device, device.surface, device.queue_families);
info.graphics_queue_priorities = priorities; if (present >= 0) {
return *this; return get_queue (device.device, present);
}
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "presentation queue is not available" };
} }
DeviceBuilder& DeviceBuilder::set_compute_queue_priorities (std::vector<float> priorities) { detail::Expected<VkQueue, VkResult> get_graphics_queue (Device const& device) {
if (info.physical_device.queue_family_properties.count_compute >= priorities.size ()) int graphics = get_graphics_queue_index (device.physical_device, device.surface, device.queue_families);
info.compute_queue_priorities = priorities; if (graphics >= 0) {
return *this; return get_queue (device.device, graphics);
}
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested queue family is not available" };
} }
DeviceBuilder& DeviceBuilder::set_transfer_queue_priorities (std::vector<float> priorities) { detail::Expected<VkQueue, VkResult> get_compute_queue (Device const& device) {
if (info.physical_device.queue_family_properties.count_transfer >= priorities.size ()) int compute = get_distinct_compute_queue_index (device.physical_device, device.surface, device.queue_families);
info.transfer_queue_priorities = priorities; if (compute >= 0) {
return *this; return get_queue (device.device, compute);
}
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested queue family is not available" };
} }
DeviceBuilder& DeviceBuilder::set_sparse_queue_priorities (std::vector<float> priorities) { detail::Expected<VkQueue, VkResult> get_transfer_queue (Device const& device) {
if (info.physical_device.queue_family_properties.count_sparse >= priorities.size ()) int transfer = get_distinct_transfer_queue_index (device.physical_device, device.surface, device.queue_families);
info.sparse_queue_priorities = priorities; if (transfer >= 0) {
return *this; return get_queue (device.device, transfer);
}
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested queue family is not available" };
} }
// ---- Queue ---- //
uint32_t get_queue_index_present (Device const& device) {
return device.physical_device.queue_family_properties.present;
}
uint32_t get_queue_index_graphics (Device const& device) {
return device.physical_device.queue_family_properties.graphics;
}
uint32_t get_queue_index_compute (Device const& device) {
return device.physical_device.queue_family_properties.compute;
}
uint32_t get_queue_index_transfer (Device const& device) {
return device.physical_device.queue_family_properties.transfer;
}
uint32_t get_queue_index_sparse (Device const& device) {
return device.physical_device.queue_family_properties.sparse;
}
namespace detail {
VkQueue get_queue (Device const& device, uint32_t family, uint32_t index) {
VkQueue queue;
vkGetDeviceQueue (device.device, family, index, &queue);
return queue;
}
} // namespace detail
detail::Expected<VkQueue, VkResult> get_queue_present (Device const& device) {
return detail::get_queue (device, device.physical_device.queue_family_properties.present, 0);
}
detail::Expected<VkQueue, VkResult> get_queue_graphics (Device const& device, uint32_t index) {
if (index >= device.physical_device.queue_family_properties.count_graphics)
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested graphics queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.graphics, index);
}
detail::Expected<VkQueue, VkResult> get_queue_compute (Device const& device, uint32_t index) {
if (index >= device.physical_device.queue_family_properties.count_compute)
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested compute queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.compute, index);
}
detail::Expected<VkQueue, VkResult> get_queue_transfer (Device const& device, uint32_t index) {
if (index >= device.physical_device.queue_family_properties.count_transfer)
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested transfer queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.transfer, index);
}
detail::Expected<VkQueue, VkResult> get_queue_sparse (Device const& device, uint32_t index) {
if (index >= device.physical_device.queue_family_properties.count_sparse)
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested sparse queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.sparse, index);
}
namespace detail { namespace detail {
VkSurfaceFormatKHR find_surface_format (std::vector<VkSurfaceFormatKHR> const& available_formats, VkSurfaceFormatKHR find_surface_format (std::vector<VkSurfaceFormatKHR> const& available_formats,
std::vector<VkSurfaceFormatKHR> const& desired_formats) { std::vector<VkSurfaceFormatKHR> const& desired_formats) {

View File

@ -199,33 +199,19 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback (VkDebugUtilsMessag
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData); void* pUserData);
struct QueueFamilies {
int graphics = -1;
int present = -1;
int transfer = -1;
int compute = -1;
int sparse = -1;
uint32_t count_graphics = 0;
uint32_t count_transfer = 0;
uint32_t count_compute = 0;
uint32_t count_sparse = 0;
};
QueueFamilies find_queue_families (VkPhysicalDevice phys_device, VkSurfaceKHR surface);
// ---- Physical Device ---- // // ---- Physical Device ---- //
class PhysicalDeviceSelector;
class DeviceBuilder;
struct PhysicalDevice { struct PhysicalDevice {
VkPhysicalDevice phys_device = VK_NULL_HANDLE; VkPhysicalDevice phys_device = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE;
private:
VkPhysicalDeviceFeatures features{}; VkPhysicalDeviceFeatures features{};
VkPhysicalDeviceProperties properties{};
VkPhysicalDeviceMemoryProperties memory_properties{};
QueueFamilies queue_family_properties;
std::vector<std::string> extensions_to_enable; std::vector<std::string> extensions_to_enable;
friend class PhysicalDeviceSelector;
friend class DeviceBuilder;
}; };
struct PhysicalDeviceSelector { struct PhysicalDeviceSelector {
@ -257,7 +243,6 @@ struct PhysicalDeviceSelector {
PhysicalDeviceSelector& set_minimum_version (uint32_t major = 1, uint32_t minor = 0); PhysicalDeviceSelector& set_minimum_version (uint32_t major = 1, uint32_t minor = 0);
PhysicalDeviceSelector& set_required_features (VkPhysicalDeviceFeatures features); PhysicalDeviceSelector& set_required_features (VkPhysicalDeviceFeatures features);
PhysicalDeviceSelector& set_desired_features (VkPhysicalDeviceFeatures features);
PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true); PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true);
@ -284,7 +269,6 @@ struct PhysicalDeviceSelector {
uint32_t desired_version = VK_MAKE_VERSION (1, 0, 0); uint32_t desired_version = VK_MAKE_VERSION (1, 0, 0);
VkPhysicalDeviceFeatures required_features{}; VkPhysicalDeviceFeatures required_features{};
VkPhysicalDeviceFeatures desired_features{};
bool use_first_gpu_unconditionally = false; bool use_first_gpu_unconditionally = false;
} criteria; } criteria;
@ -294,12 +278,17 @@ struct PhysicalDeviceSelector {
Suitable is_device_suitable (VkPhysicalDevice phys_device); Suitable is_device_suitable (VkPhysicalDevice phys_device);
}; };
// ---- Queue Selection ---- //
enum class QueueType : uint8_t { primary, compute, transfer };
// ---- Device ---- // // ---- Device ---- //
struct Device { struct Device {
VkDevice device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE;
PhysicalDevice physical_device; PhysicalDevice physical_device;
VkSurfaceKHR surface = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE;
std::vector<VkQueueFamilyProperties> queue_families;
}; };
void destroy_device (Device device); void destroy_device (Device device);
@ -311,43 +300,24 @@ class DeviceBuilder {
template <typename T> DeviceBuilder& add_pNext (T* structure); template <typename T> DeviceBuilder& add_pNext (T* structure);
DeviceBuilder& use_multiple_queues_per_family (bool multi_queue = true);
DeviceBuilder& set_graphics_queue_priorities (std::vector<float> priorities);
DeviceBuilder& set_compute_queue_priorities (std::vector<float> priorities);
DeviceBuilder& set_transfer_queue_priorities (std::vector<float> priorities);
DeviceBuilder& set_sparse_queue_priorities (std::vector<float> priorities);
private: private:
struct DeviceInfo { struct DeviceInfo {
VkDeviceCreateFlags flags; VkDeviceCreateFlags flags;
std::vector<VkBaseOutStructure*> pNext_chain; std::vector<VkBaseOutStructure*> pNext_chain;
PhysicalDevice physical_device; PhysicalDevice physical_device;
std::vector<std::string> extensions; std::vector<std::string> extensions;
bool use_multiple_queues_per_family = false;
std::vector<float> graphics_queue_priorities;
std::vector<float> compute_queue_priorities;
std::vector<float> transfer_queue_priorities;
std::vector<float> sparse_queue_priorities;
} info; } info;
}; };
// ---- Queue ---- // // ---- Getting Queues ---- //
uint32_t get_queue_index_present (Device const& device); detail::Expected<VkQueue, VkResult> get_present_queue (Device const& device);
uint32_t get_queue_index_graphics (Device const& device); detail::Expected<VkQueue, VkResult> get_graphics_queue (Device const& device);
uint32_t get_queue_index_compute (Device const& device); detail::Expected<VkQueue, VkResult> get_compute_queue (Device const& device);
uint32_t get_queue_index_transfer (Device const& device); detail::Expected<VkQueue, VkResult> get_transfer_queue (Device const& device);
uint32_t get_queue_index_sparse (Device const& device);
detail::Expected<VkQueue, VkResult> get_queue_present (Device const& device);
detail::Expected<VkQueue, VkResult> get_queue_graphics (Device const& device, uint32_t index = 0); // ---- Swapchain ---- //
detail::Expected<VkQueue, VkResult> get_queue_compute (Device const& device, uint32_t index = 0);
detail::Expected<VkQueue, VkResult> get_queue_transfer (Device const& device, uint32_t index = 0);
detail::Expected<VkQueue, VkResult> get_queue_sparse (Device const& device, uint32_t index = 0);
struct Swapchain { struct Swapchain {
VkDevice device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE;
@ -358,6 +328,7 @@ struct Swapchain {
}; };
void destroy_swapchain (Swapchain const& swapchain); void destroy_swapchain (Swapchain const& swapchain);
detail::Expected<std::vector<VkImage>, VkResult> get_swapchain_images (Swapchain const& swapchain); detail::Expected<std::vector<VkImage>, VkResult> get_swapchain_images (Swapchain const& swapchain);
detail::Expected<std::vector<VkImageView>, VkResult> get_swapchain_image_views ( detail::Expected<std::vector<VkImageView>, VkResult> get_swapchain_image_views (
Swapchain const& swapchain, std::vector<VkImage> const& images); Swapchain const& swapchain, std::vector<VkImage> const& images);
@ -394,6 +365,8 @@ class SwapchainBuilder {
uint32_t desired_width = 256; uint32_t desired_width = 256;
uint32_t desired_height = 256; uint32_t desired_height = 256;
std::vector<VkBaseOutStructure*> pNext_elements; std::vector<VkBaseOutStructure*> pNext_elements;
uint32_t graphics_queue_index = 0;
uint32_t present_queue_index = 0;
} info; } info;
}; };