Finished basic rewrite of queue handling logic

Premise is to let the easy creation of a compute or transfer queue.
If the user supplies a custom list they want to use, defer to that.
Else, enable the basic requirements and be on your merry way.
This commit is contained in:
Charles Giessen 2020-02-10 18:01:58 -07:00
parent 3e7e283e3f
commit 2b711f30d5
3 changed files with 189 additions and 76 deletions

View File

@ -75,8 +75,18 @@ int device_initialization (Init& init) {
} }
int get_queues (Init& init, RenderData& data) { int get_queues (Init& init, RenderData& data) {
data.graphics_queue = vkb::get_graphics_queue (init.device).value (); auto gq = vkb::get_graphics_queue (init.device);
data.present_queue = vkb::get_present_queue (init.device).value (); if (!gq.has_value ()) {
std::cout << "failed to get graphics queue\n";
return -1;
}
data.graphics_queue = gq.value ();
auto pq = vkb::get_present_queue (init.device);
if (!pq.has_value ()) {
std::cout << "failed to get present queue\n";
return -1;
}
data.present_queue = pq.value ();
return 0; return 0;
} }

View File

@ -126,6 +126,27 @@ bool check_layers_supported (std::vector<const char*> layer_names) {
return all_found; return all_found;
} }
bool check_extensions_supported (std::vector<const char*> extension_names) {
auto available_extensions =
detail::get_vector<VkExtensionProperties> (vkEnumerateInstanceExtensionProperties, nullptr);
if (!available_extensions.has_value ()) {
return false; // maybe report error?
}
bool all_found = true;
for (const auto& extension_name : extension_names) {
bool found = false;
for (const auto& layer_properties : available_extensions.value ()) {
if (strcmp (extension_name, layer_properties.extensionName) == 0) {
found = true;
break;
}
}
if (!found) all_found = false;
}
return all_found;
}
template <typename T> template <typename T>
void setup_pNext_chain (T& structure, std::vector<VkBaseOutStructure*>& structs) { void setup_pNext_chain (T& structure, std::vector<VkBaseOutStructure*>& structs) {
structure.pNext = nullptr; structure.pNext = nullptr;
@ -180,6 +201,11 @@ detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build
extensions.push_back ("VK_KHR_metal_surface"); extensions.push_back ("VK_KHR_metal_surface");
#endif #endif
} }
bool all_extensions_supported = detail::check_extensions_supported (extensions);
if (!all_extensions_supported) {
return detail::Error<InstanceError>{ InstanceError::requested_extensions_not_present };
}
std::vector<const char*> layers; std::vector<const char*> layers;
for (auto& layer : info.layers) for (auto& layer : info.layers)
layers.push_back (layer.c_str ()); layers.push_back (layer.c_str ());
@ -499,33 +525,46 @@ int get_present_queue_index (VkPhysicalDevice const phys_device,
return -1; return -1;
} }
PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkPhysicalDevice phys_device) { PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_device_details (
Suitable suitable = Suitable::yes; VkPhysicalDevice phys_device) {
PhysicalDeviceSelector::PhysicalDeviceDesc desc{};
desc.phys_device = phys_device;
auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties> ( auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties> (
vkGetPhysicalDeviceQueueFamilyProperties, phys_device); vkGetPhysicalDeviceQueueFamilyProperties, phys_device);
bool dedicated_compute = get_distinct_compute_queue_index (queue_families); desc.queue_families = 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); vkGetPhysicalDeviceProperties (phys_device, &desc.device_properties);
vkGetPhysicalDeviceFeatures (phys_device, &desc.device_features);
vkGetPhysicalDeviceMemoryProperties (phys_device, &desc.mem_properties);
return desc;
}
PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (PhysicalDeviceDesc pd) {
Suitable suitable = Suitable::yes;
bool dedicated_compute = get_distinct_compute_queue_index (pd.queue_families);
bool dedicated_transfer = get_distinct_transfer_queue_index (pd.queue_families);
bool present_queue = get_present_queue_index (pd.phys_device, system_info.surface, pd.queue_families);
if (criteria.require_dedicated_compute_queue && !dedicated_compute) suitable = Suitable::no; if (criteria.require_dedicated_compute_queue && !dedicated_compute) suitable = Suitable::no;
if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) suitable = Suitable::no; if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) suitable = Suitable::no;
if (criteria.require_present && !present_queue) suitable = Suitable::no; if (criteria.require_present && !present_queue) 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 (pd.phys_device, criteria.required_extensions);
if (required_extensions_supported.size () != criteria.required_extensions.size ()) if (required_extensions_supported.size () != criteria.required_extensions.size ())
suitable = Suitable::no; suitable = Suitable::no;
auto desired_extensions_supported = auto desired_extensions_supported =
detail::check_device_extension_support (phys_device, criteria.desired_extensions); detail::check_device_extension_support (pd.phys_device, criteria.desired_extensions);
if (desired_extensions_supported.size () != criteria.desired_extensions.size ()) if (desired_extensions_supported.size () != criteria.desired_extensions.size ())
suitable = Suitable::partial; suitable = Suitable::partial;
bool swapChainAdequate = false; bool swapChainAdequate = false;
if (!info.headless) { if (!system_info.headless) {
auto swapChainSupport_ret = detail::query_surface_support_details (phys_device, info.surface); auto swapChainSupport_ret =
detail::query_surface_support_details (pd.phys_device, system_info.surface);
if (swapChainSupport_ret.has_value ()) { if (swapChainSupport_ret.has_value ()) {
auto swapchain_support = swapChainSupport_ret.value (); auto swapchain_support = swapChainSupport_ret.value ();
swapChainAdequate = swapChainAdequate =
@ -534,17 +573,33 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkP
} }
if (criteria.require_present && !swapChainAdequate) suitable = Suitable::no; if (criteria.require_present && !swapChainAdequate) suitable = Suitable::no;
VkPhysicalDeviceMemoryProperties mem_properties; if ((criteria.preferred_type == PreferredDeviceType::discrete &&
vkGetPhysicalDeviceMemoryProperties (phys_device, &mem_properties); pd.device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ||
(criteria.preferred_type == PreferredDeviceType::integrated &&
pd.device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) ||
(criteria.preferred_type == PreferredDeviceType::virtual_gpu &&
pd.device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)) {
if (criteria.allow_fallback)
suitable = Suitable::partial;
else
suitable = Suitable::no;
}
if (criteria.required_version < pd.device_properties.apiVersion) suitable = Suitable::no;
if (criteria.desired_version < pd.device_properties.apiVersion) suitable = Suitable::partial;
bool required_features_supported =
detail::supports_features (pd.device_features, criteria.required_features);
if (!required_features_supported) suitable = Suitable::no;
bool has_required_memory = false; bool has_required_memory = false;
bool has_preferred_memory = false; bool has_preferred_memory = false;
for (int i = 0; i < mem_properties.memoryHeapCount; i++) { for (int i = 0; i < pd.mem_properties.memoryHeapCount; i++) {
if (mem_properties.memoryHeaps[i].flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { if (pd.mem_properties.memoryHeaps[i].flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
if (mem_properties.memoryHeaps[i].size > criteria.required_mem_size) { if (pd.mem_properties.memoryHeaps[i].size > criteria.required_mem_size) {
has_required_memory = true; has_required_memory = true;
} }
if (mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) { if (pd.mem_properties.memoryHeaps[i].size > criteria.desired_mem_size) {
has_preferred_memory = true; has_preferred_memory = true;
} }
} }
@ -552,40 +607,18 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkP
if (!has_required_memory) suitable = Suitable::no; if (!has_required_memory) suitable = Suitable::no;
if (!has_preferred_memory) suitable = Suitable::partial; if (!has_preferred_memory) suitable = Suitable::partial;
VkPhysicalDeviceProperties device_properties;
vkGetPhysicalDeviceProperties (phys_device, &device_properties);
if ((criteria.preferred_type == PreferredDeviceType::discrete &&
device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ||
(criteria.preferred_type == PreferredDeviceType::integrated &&
device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) ||
(criteria.preferred_type == PreferredDeviceType::virtual_gpu &&
device_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)) {
if (criteria.allow_fallback)
suitable = Suitable::partial;
else
suitable = Suitable::no;
}
if (criteria.required_version < device_properties.apiVersion) suitable = Suitable::no;
if (criteria.desired_version < device_properties.apiVersion) suitable = Suitable::partial;
VkPhysicalDeviceFeatures supported_features{};
vkGetPhysicalDeviceFeatures (phys_device, &supported_features);
bool required_features_supported =
detail::supports_features (supported_features, criteria.required_features);
if (!required_features_supported) suitable = Suitable::no;
return suitable; return suitable;
} }
PhysicalDeviceSelector::PhysicalDeviceSelector (Instance const& instance) { PhysicalDeviceSelector::PhysicalDeviceSelector (Instance const& instance) {
info.instance = instance.instance; system_info.instance = instance.instance;
info.headless = instance.headless; system_info.headless = instance.headless;
criteria.require_present = !instance.headless; criteria.require_present = !instance.headless;
} }
detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> PhysicalDeviceSelector::select () { detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> PhysicalDeviceSelector::select () {
auto physical_devices = detail::get_vector<VkPhysicalDevice> (vkEnumeratePhysicalDevices, info.instance); auto physical_devices =
detail::get_vector<VkPhysicalDevice> (vkEnumeratePhysicalDevices, system_info.instance);
if (!physical_devices.has_value ()) { if (!physical_devices.has_value ()) {
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::failed_enumerate_physical_devices, return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::failed_enumerate_physical_devices,
physical_devices.error () }; physical_devices.error () };
@ -594,42 +627,50 @@ detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> PhysicalDev
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_physical_devices_found }; return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_physical_devices_found };
} }
PhysicalDevice physical_device; std::vector<PhysicalDeviceDesc> phys_device_descriptions;
for (auto& phys_device : physical_devices.value ()) {
phys_device_descriptions.push_back (populate_device_details (phys_device));
}
PhysicalDeviceDesc selected_device{};
if (criteria.use_first_gpu_unconditionally) { if (criteria.use_first_gpu_unconditionally) {
physical_device.phys_device = physical_devices.value ().at (0); selected_device = phys_device_descriptions.at (0);
} else { } else {
for (const auto& device : physical_devices.value ()) { for (const auto& device : phys_device_descriptions) {
auto suitable = is_device_suitable (device); auto suitable = is_device_suitable (device);
if (suitable == Suitable::yes) { if (suitable == Suitable::yes) {
physical_device.phys_device = device; selected_device = device;
break; break;
} else if (suitable == Suitable::partial) { } else if (suitable == Suitable::partial) {
physical_device.phys_device = device; selected_device = device;
} }
} }
} }
if (physical_device.phys_device == VK_NULL_HANDLE) { if (selected_device.phys_device == VK_NULL_HANDLE) {
return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_suitable_device }; return detail::Error<PhysicalDeviceError>{ PhysicalDeviceError::no_suitable_device };
} }
PhysicalDevice out_device{};
out_device.phys_device = selected_device.phys_device;
out_device.surface = system_info.surface;
out_device.features = criteria.required_features;
out_device.queue_families = selected_device.queue_families;
physical_device.surface = info.surface; out_device.extensions_to_enable.insert (out_device.extensions_to_enable.end (),
physical_device.features = criteria.required_features;
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 ());
auto desired_extensions_supported = auto desired_extensions_supported =
detail::check_device_extension_support (physical_device.phys_device, criteria.desired_extensions); detail::check_device_extension_support (out_device.phys_device, criteria.desired_extensions);
physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (), out_device.extensions_to_enable.insert (out_device.extensions_to_enable.end (),
desired_extensions_supported.begin (), desired_extensions_supported.begin (),
desired_extensions_supported.end ()); desired_extensions_supported.end ());
return physical_device; return out_device;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface (VkSurfaceKHR surface) { PhysicalDeviceSelector& PhysicalDeviceSelector::set_surface (VkSurfaceKHR surface) {
info.surface = surface; system_info.surface = surface;
info.headless = false; system_info.headless = false;
return *this; return *this;
} }
PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type (PreferredDeviceType type) { PhysicalDeviceSelector& PhysicalDeviceSelector::prefer_gpu_device_type (PreferredDeviceType type) {
@ -706,21 +747,41 @@ void destroy_device (Device device) { vkDestroyDevice (device.device, nullptr);
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;
info.queue_families = phys_device.queue_families;
} }
detail::Expected<Device, detail::Error<DeviceError>> DeviceBuilder::build () { detail::Expected<Device, detail::Error<DeviceError>> DeviceBuilder::build () {
auto queue_families = detail::get_vector_noerror<VkQueueFamilyProperties> ( std::vector<CustomQueueDescription> queue_descriptions;
vkGetPhysicalDeviceQueueFamilyProperties, info.physical_device.phys_device); queue_descriptions.insert (
queue_descriptions.end (), info.queue_descriptions.begin (), info.queue_descriptions.end ());
if (queue_descriptions.size () == 0) {
int graphics = get_graphics_queue_index (info.queue_families);
if (graphics >= 0) {
queue_descriptions.push_back ({ static_cast<uint32_t> (graphics), 1, { 1.0f } });
}
if (info.request_compute_queue) {
int compute = get_distinct_compute_queue_index (info.queue_families);
if (compute >= 0) {
queue_descriptions.push_back ({ static_cast<uint32_t> (compute), 1, { 1.0f } });
}
}
if (info.request_transfer_queue) {
int transfer = get_distinct_transfer_queue_index (info.queue_families);
if (transfer >= 0) {
queue_descriptions.push_back ({ static_cast<uint32_t> (transfer), 1, { 1.0f } });
}
}
}
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
float priority = 1.0f; for (auto& desc : queue_descriptions) {
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> (i); queue_create_info.queueFamilyIndex = desc.index;
queue_create_info.queueCount = 1; queue_create_info.queueCount = desc.count;
queue_create_info.pQueuePriorities = &priority; queue_create_info.pQueuePriorities = desc.priorities.data ();
queueCreateInfos.push_back (queue_create_info); queueCreateInfos.push_back (queue_create_info);
} }
@ -748,6 +809,7 @@ detail::Expected<Device, detail::Error<DeviceError>> DeviceBuilder::build () {
} }
device.physical_device = info.physical_device; device.physical_device = info.physical_device;
device.surface = info.physical_device.surface; device.surface = info.physical_device.surface;
device.queue_families = info.queue_families;
return device; return device;
} }
@ -756,6 +818,20 @@ template <typename T> DeviceBuilder& DeviceBuilder::add_pNext (T* structure) {
return *this; return *this;
} }
DeviceBuilder& DeviceBuilder::request_dedicated_compute_queue (bool compute) {
info.request_compute_queue = compute;
return *this;
}
DeviceBuilder& DeviceBuilder::request_dedicated_transfer_queue (bool transfer) {
info.request_transfer_queue = transfer;
return *this;
}
DeviceBuilder& DeviceBuilder::custom_queue_setup (std::vector<CustomQueueDescription> queue_descriptions) {
info.queue_descriptions = queue_descriptions;
return *this;
}
// ---- Getting Queues ---- // // ---- Getting Queues ---- //
detail::Expected<uint32_t, detail::Error<QueueError>> get_present_queue_index (Device const& device) { detail::Expected<uint32_t, detail::Error<QueueError>> get_present_queue_index (Device const& device) {

View File

@ -124,6 +124,8 @@ struct Instance {
void destroy_instance (Instance instance); // release instance resources void destroy_instance (Instance instance); // release instance resources
// TODO utility function for users to check if layer/extension is supported
class InstanceBuilder { class InstanceBuilder {
public: public:
detail::Expected<Instance, detail::Error<InstanceError>> build (); // use builder pattern detail::Expected<Instance, detail::Error<InstanceError>> build (); // use builder pattern
@ -138,9 +140,14 @@ class InstanceBuilder {
InstanceBuilder& add_layer (std::string app_name); InstanceBuilder& add_layer (std::string app_name);
InstanceBuilder& add_extension (std::string app_name); InstanceBuilder& add_extension (std::string app_name);
bool check_and_add_layer (std::string app_name);
bool check_and_add_extension (std::string app_name);
InstanceBuilder& setup_validation_layers (bool enable_validation = true); InstanceBuilder& setup_validation_layers (bool enable_validation = true);
InstanceBuilder& set_headless (bool headless = false); InstanceBuilder& set_headless (bool headless = false);
bool check_and_setup_validation_layers (bool enable_validation = true);
InstanceBuilder& set_default_debug_messenger (); InstanceBuilder& set_default_debug_messenger ();
InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback); InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback);
InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity);
@ -222,6 +229,7 @@ struct PhysicalDevice {
private: private:
VkPhysicalDeviceFeatures features{}; VkPhysicalDeviceFeatures features{};
std::vector<std::string> extensions_to_enable; std::vector<std::string> extensions_to_enable;
std::vector<VkQueueFamilyProperties> queue_families;
friend class PhysicalDeviceSelector; friend class PhysicalDeviceSelector;
friend class DeviceBuilder; friend class DeviceBuilder;
}; };
@ -259,11 +267,21 @@ struct PhysicalDeviceSelector {
PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true); PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true);
private: private:
struct PhysicalDeviceInfo { struct SystemInfo {
VkInstance instance = VK_NULL_HANDLE; VkInstance instance = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE;
bool headless = false; bool headless = false;
} info; } system_info;
struct PhysicalDeviceDesc {
VkPhysicalDevice phys_device = VK_NULL_HANDLE;
std::vector<VkQueueFamilyProperties> queue_families;
VkPhysicalDeviceFeatures device_features;
VkPhysicalDeviceProperties device_properties;
VkPhysicalDeviceMemoryProperties mem_properties;
};
PhysicalDeviceDesc populate_device_details (VkPhysicalDevice phys_device);
struct SelectionCriteria { struct SelectionCriteria {
PreferredDeviceType preferred_type = PreferredDeviceType::discrete; PreferredDeviceType preferred_type = PreferredDeviceType::discrete;
@ -287,13 +305,9 @@ struct PhysicalDeviceSelector {
enum class Suitable { yes, partial, no }; enum class Suitable { yes, partial, no };
Suitable is_device_suitable (VkPhysicalDevice phys_device); Suitable is_device_suitable (PhysicalDeviceDesc phys_device);
}; };
// ---- Queue Selection ---- //
enum class QueueType : uint8_t { primary, compute, transfer };
// ---- Device ---- // // ---- Device ---- //
enum class DeviceError { enum class DeviceError {
failed_create_device, failed_create_device,
@ -308,6 +322,12 @@ struct Device {
void destroy_device (Device device); void destroy_device (Device device);
struct CustomQueueDescription {
uint32_t index;
uint32_t count;
std::vector<float> priorities;
};
class DeviceBuilder { class DeviceBuilder {
public: public:
DeviceBuilder (PhysicalDevice device); DeviceBuilder (PhysicalDevice device);
@ -315,12 +335,22 @@ class DeviceBuilder {
template <typename T> DeviceBuilder& add_pNext (T* structure); template <typename T> DeviceBuilder& add_pNext (T* structure);
DeviceBuilder& request_dedicated_compute_queue (bool compute = true);
DeviceBuilder& request_dedicated_transfer_queue (bool transfer = true);
/* For advanced users */
DeviceBuilder& custom_queue_setup (std::vector<CustomQueueDescription> queue_descriptions);
private: private:
struct DeviceInfo { struct DeviceInfo {
VkDeviceCreateFlags flags = 0; VkDeviceCreateFlags flags = 0;
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;
std::vector<VkQueueFamilyProperties> queue_families;
std::vector<CustomQueueDescription> queue_descriptions;
bool request_compute_queue = true;
bool request_transfer_queue = true;
} info; } info;
}; };
@ -381,9 +411,6 @@ class SwapchainBuilder {
detail::Expected<Swapchain, detail::Error<SwapchainError>> build (); detail::Expected<Swapchain, detail::Error<SwapchainError>> build ();
detail::Expected<Swapchain, detail::Error<SwapchainError>> recreate (Swapchain const& swapchain); detail::Expected<Swapchain, detail::Error<SwapchainError>> recreate (Swapchain const& swapchain);
// SwapchainBuilder& set_desired_image_count (uint32_t count);
// SwapchainBuilder& set_maximum_image_count (uint32_t count);
SwapchainBuilder& set_desired_format (VkSurfaceFormatKHR format); SwapchainBuilder& set_desired_format (VkSurfaceFormatKHR format);
SwapchainBuilder& add_fallback_format (VkSurfaceFormatKHR format); SwapchainBuilder& add_fallback_format (VkSurfaceFormatKHR format);
SwapchainBuilder& use_default_format_selection (); SwapchainBuilder& use_default_format_selection ();