Added a desired_api_version to InstanceBuilder and cleaned up the triangle example error handling.

Fixed several bugs in the PhysicalDeviceSelector code, causing erroneous failures.
This commit is contained in:
Charles Giessen 2020-03-09 15:10:11 -06:00
parent 1160b19a5c
commit 4c91df1c12
4 changed files with 102 additions and 56 deletions

View File

@ -44,7 +44,8 @@ int device_initialization (Init& init) {
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.use_default_debug_messenger ().request_validation_layers ().build ();
if (!instance_ret) {
std::cout << static_cast<uint32_t> (instance_ret.error ().type) << "\n";
std::cout << vkb::to_string (instance_ret.error ().type) << "\n";
return -1;
}
init.instance = instance_ret.value ();
@ -53,14 +54,16 @@ int device_initialization (Init& init) {
vkb::PhysicalDeviceSelector phys_device_selector (init.instance);
auto phys_device_ret = phys_device_selector.set_surface (init.surface).select ();
if (!phys_device_ret) {
std::cout << static_cast<uint32_t> (phys_device_ret.error ().type) << "\n";
std::cout << vkb::to_string (phys_device_ret.error ().type) << "\n";
return -1;
}
vkb::PhysicalDevice physical_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder{ physical_device };
auto device_ret = device_builder.build ();
if (!device_ret) {
std::cout << static_cast<uint32_t> (device_ret.error ().type) << "\n";
std::cout << vkb::to_string (device_ret.error ().type) << "\n";
return -1;
}
init.device = device_ret.value ();
@ -68,7 +71,8 @@ int device_initialization (Init& init) {
auto swap_ret =
swapchain_builder.use_default_format_selection ().use_default_present_mode_selection ().build ();
if (!swap_ret) {
std::cout << static_cast<uint32_t> (swap_ret.error ().type) << "\n";
std::cout << vkb::to_string (swap_ret.error ().type) << "\n";
return -1;
}
init.swapchain = swap_ret.value ();
return 0;
@ -77,14 +81,14 @@ int device_initialization (Init& init) {
int get_queues (Init& init, RenderData& data) {
auto gq = init.device.get_queue (vkb::QueueType::graphics);
if (!gq.has_value ()) {
std::cout << "failed to get graphics queue\n";
std::cout << "failed to get graphics queue: " << vkb::to_string (gq.error ().type) << "\n";
return -1;
}
data.graphics_queue = gq.value ();
auto pq = init.device.get_queue (vkb::QueueType::present);
if (!pq.has_value ()) {
std::cout << "failed to get present queue\n";
std::cout << "failed to get present queue: " << vkb::to_string (gq.error ().type) << "\n";
return -1;
}
data.present_queue = pq.value ();
@ -129,6 +133,7 @@ int create_render_pass (Init& init, RenderData& data) {
render_pass_info.pDependencies = &dependency;
if (vkCreateRenderPass (init.device.device, &render_pass_info, nullptr, &data.render_pass) != VK_SUCCESS) {
std::cout << "failed to create render pass\n";
return -1; // failed to create render pass!
}
return 0;
@ -173,6 +178,7 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
VkShaderModule vert_module = createShaderModule (init, vert_code);
VkShaderModule frag_module = createShaderModule (init, frag_code);
if (vert_module == VK_NULL_HANDLE || frag_module == VK_NULL_HANDLE) {
std::cout << "failed to create shader module\n";
return -1; // failed to create shader modules
}
@ -257,6 +263,7 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
if (vkCreatePipelineLayout (
init.device.device, &pipeline_layout_info, nullptr, &data.pipeline_layout) != VK_SUCCESS) {
std::cout << "failed to create pipeline layout\n";
return -1; // failed to create pipeline layout
}
@ -277,6 +284,7 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
if (vkCreateGraphicsPipelines (
init.device.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &data.graphics_pipeline) != VK_SUCCESS) {
std::cout << "failed to create pipline\n";
return -1; // failed to create graphics pipeline
}
@ -318,6 +326,7 @@ int create_command_pool (Init& init, RenderData& data) {
pool_info.queueFamilyIndex = init.device.get_queue_index (vkb::QueueType::graphics).value ();
if (vkCreateCommandPool (init.device.device, &pool_info, nullptr, &data.command_pool) != VK_SUCCESS) {
std::cout << "failed to create command pool\n";
return -1; // failed to create command pool
}
return 0;
@ -364,6 +373,7 @@ int create_command_buffers (Init& init, RenderData& data) {
vkCmdEndRenderPass (data.command_buffers[i]);
if (vkEndCommandBuffer (data.command_buffers[i]) != VK_SUCCESS) {
std::cout << "failed to record command buffer\n";
return -1; // failed to record command buffer!
}
}
@ -387,6 +397,7 @@ int create_sync_objects (Init& init, RenderData& data) {
if (vkCreateSemaphore (init.device.device, &semaphore_info, nullptr, &data.available_semaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore (init.device.device, &semaphore_info, nullptr, &data.finished_semaphore[i]) != VK_SUCCESS ||
vkCreateFence (init.device.device, &fence_info, nullptr, &data.in_flight_fences[i]) != VK_SUCCESS) {
std::cout << "failed to create sync objects\n";
return -1; // failed to create synchronization objects for a frame
}
}
@ -428,7 +439,8 @@ int draw_frame (Init& init, RenderData& data) {
vkResetFences (init.device.device, 1, &data.in_flight_fences[data.current_frame]);
if (vkQueueSubmit (data.graphics_queue, 1, &submitInfo, data.in_flight_fences[data.current_frame]) != VK_SUCCESS) {
return -1; //"failed to submit draw command buffer
std::cout << "failed to submit draw command buffer\n";
return -1; //"failed to submit draw command buffer
}
VkPresentInfoKHR present_info = {};
@ -481,20 +493,14 @@ int main () {
Init init;
RenderData render_data;
int res = 0;
res = device_initialization (init);
res = get_queues (init, render_data);
res = create_render_pass (init, render_data);
res = create_graphics_pipeline (init, render_data);
res = create_framebuffers (init, render_data);
res = create_command_pool (init, render_data);
res = create_command_buffers (init, render_data);
res = create_sync_objects (init, render_data);
if (res != 0) {
std::cout << "failed to initialize vulkan\n";
return -1;
}
if(0 != device_initialization (init)) return -1;
if(0 != get_queues (init, render_data)) return -1;
if(0 != create_render_pass (init, render_data)) return -1;
if(0 != create_graphics_pipeline (init, render_data)) return -1;
if(0 != create_framebuffers (init, render_data)) return -1;
if(0 != create_command_pool (init, render_data)) return -1;
if(0 != create_command_buffers (init, render_data)) return -1;
if(0 != create_sync_objects (init, render_data)) return -1;
while (!glfwWindowShouldClose (init.window)) {
glfwPollEvents ();

View File

@ -172,8 +172,14 @@ const char* validation_layer_name = "VK_LAYER_KHRONOS_validation";
const char* to_string (InstanceError err) {
switch (err) {
case InstanceError::unavailable_vulkan_version:
return "unavailable_vulkan_version";
case InstanceError::vulkan_unavailable:
return "vulkan_unavailable";
case InstanceError::vulkan_version_unavailable:
return "vulkan_version_unavailable";
case InstanceError::vulkan_version_1_1_unavailable:
return "vulkan_version_1_1_unavailable";
case InstanceError::vulkan_version_1_2_unavailable:
return "vulkan_version_1_2_unavailable";
case InstanceError::failed_create_debug_messenger:
return "failed_create_debug_messenger";
case InstanceError::failed_create_instance:
@ -278,19 +284,38 @@ SystemInfo InstanceBuilder::get_system_info () const { return system; }
detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build () const {
if (VK_VERSION_MINOR (info.api_version) > 0) {
uint32_t api_version = VK_MAKE_VERSION (1, 0, 0);
if (info.required_api_version > VK_MAKE_VERSION (1, 0, 0) || info.desired_api_version > VK_MAKE_VERSION (1, 0, 0)) {
PFN_vkEnumerateInstanceVersion pfn_vkEnumerateInstanceVersion;
detail::get_inst_proc_addr (
pfn_vkEnumerateInstanceVersion, "vkEnumerateInstanceVersion", nullptr, vkGetInstanceProcAddr);
if (pfn_vkEnumerateInstanceVersion == nullptr) {
return detail::Error<InstanceError>{ InstanceError::unavailable_vulkan_version };
} else if (pfn_vkEnumerateInstanceVersion != nullptr) {
uint32_t api_version = 0;
pfn_vkEnumerateInstanceVersion (&api_version);
if (VK_VERSION_MINOR (api_version) < VK_VERSION_MINOR (info.api_version))
return detail::Error<InstanceError>{ InstanceError::unavailable_vulkan_version };
uint32_t queried_api_version = VK_MAKE_VERSION (1, 0, 0);
if (pfn_vkEnumerateInstanceVersion != nullptr) {
VkResult res = pfn_vkEnumerateInstanceVersion (&api_version);
// Should always return VK_SUCCESS
if (res != VK_SUCCESS && info.required_api_version > 0)
return detail::Error<InstanceError>{ InstanceError::vulkan_version_unavailable };
}
if (pfn_vkEnumerateInstanceVersion == nullptr || queried_api_version < info.required_api_version) {
if (VK_VERSION_MINOR (info.required_api_version) == 2)
return detail::Error<InstanceError>{ InstanceError::vulkan_version_1_2_unavailable };
else if (VK_VERSION_MINOR (info.required_api_version))
return detail::Error<InstanceError>{ InstanceError::vulkan_version_1_1_unavailable };
else
return detail::Error<InstanceError>{ InstanceError::vulkan_version_unavailable };
}
if (info.required_api_version > VK_MAKE_VERSION (1, 0, 0)) {
api_version = info.required_api_version;
} else if (info.desired_api_version > VK_MAKE_VERSION (1, 0, 0)) {
if (queried_api_version >= info.desired_api_version)
api_version = info.desired_api_version;
else
api_version = queried_api_version;
}
}
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pNext = nullptr;
@ -298,7 +323,7 @@ detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build
app_info.applicationVersion = info.application_version;
app_info.pEngineName = info.engine_name != nullptr ? info.engine_name : "";
app_info.engineVersion = info.engine_version;
app_info.apiVersion = info.api_version;
app_info.apiVersion = api_version;
std::vector<const char*> extensions;
for (auto& ext : info.extensions)
@ -406,7 +431,7 @@ detail::Expected<Instance, detail::Error<InstanceError>> InstanceBuilder::build
instance.headless = true;
}
instance.allocation_callbacks = info.allocation_callbacks;
instance.instance_version = info.api_version;
instance.instance_version = api_version;
return instance;
}
@ -429,7 +454,11 @@ InstanceBuilder& InstanceBuilder::set_engine_version (uint32_t major, uint32_t m
return *this;
}
InstanceBuilder& InstanceBuilder::require_api_version (uint32_t major, uint32_t minor, uint32_t patch) {
info.api_version = VK_MAKE_VERSION (major, minor, patch);
info.required_api_version = VK_MAKE_VERSION (major, minor, patch);
return *this;
}
InstanceBuilder& InstanceBuilder::desire_api_version (uint32_t major, uint32_t minor, uint32_t patch) {
info.desired_api_version = VK_MAKE_VERSION (major, minor, patch);
return *this;
}
InstanceBuilder& InstanceBuilder::enable_layer (const char* layer_name) {
@ -674,24 +703,27 @@ PhysicalDeviceSelector::PhysicalDeviceDesc PhysicalDeviceSelector::populate_devi
PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (PhysicalDeviceDesc pd) const {
Suitable suitable = Suitable::yes;
if (criteria.required_version > pd.device_properties.apiVersion) return Suitable::no;
if (criteria.desired_version > pd.device_properties.apiVersion) suitable = Suitable::partial;
bool dedicated_compute = detail::get_dedicated_compute_queue_index (pd.queue_families) >= 0;
bool dedicated_transfer = detail::get_dedicated_transfer_queue_index (pd.queue_families) >= 0;
bool separate_compute = detail::get_separate_compute_queue_index (pd.queue_families) >= 0;
bool separate_transfer = detail::get_separate_transfer_queue_index (pd.queue_families) >= 0;
bool present_queue =
detail::get_present_queue_index (pd.phys_device, system_info.surface, pd.queue_families);
detail::get_present_queue_index (pd.phys_device, system_info.surface, pd.queue_families) >= 0;
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_separate_compute_queue && !separate_compute) suitable = Suitable::no;
if (criteria.require_separate_transfer_queue && !separate_transfer) suitable = Suitable::no;
if (criteria.require_present && !present_queue) suitable = Suitable::no;
if (criteria.require_dedicated_compute_queue && !dedicated_compute) return Suitable::no;
if (criteria.require_dedicated_transfer_queue && !dedicated_transfer) return Suitable::no;
if (criteria.require_separate_compute_queue && !separate_compute) return Suitable::no;
if (criteria.require_separate_transfer_queue && !separate_transfer) return Suitable::no;
if (criteria.require_present && !present_queue) return Suitable::no;
auto required_extensions_supported =
detail::check_device_extension_support (pd.phys_device, criteria.required_extensions);
if (required_extensions_supported.size () != criteria.required_extensions.size ())
suitable = Suitable::no;
return Suitable::no;
auto desired_extensions_supported =
detail::check_device_extension_support (pd.phys_device, criteria.desired_extensions);
@ -713,21 +745,18 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (Phy
swapChainAdequate = !formats.value ().empty () && !present_modes.value ().empty ();
}
}
if (criteria.require_present && !swapChainAdequate) suitable = Suitable::no;
if (criteria.require_present && !swapChainAdequate) return Suitable::no;
if (pd.device_properties.deviceType != static_cast<VkPhysicalDeviceType> (criteria.preferred_type)) {
if (criteria.allow_any_type)
suitable = Suitable::partial;
else
suitable = Suitable::no;
return 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;
if (!required_features_supported) return Suitable::no;
bool has_required_memory = false;
bool has_preferred_memory = false;
@ -741,7 +770,7 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (Phy
}
}
}
if (!has_required_memory) suitable = Suitable::no;
if (!has_required_memory) return Suitable::no;
if (!has_preferred_memory) suitable = Suitable::partial;
return suitable;
@ -752,6 +781,7 @@ PhysicalDeviceSelector::PhysicalDeviceSelector (Instance const& instance) {
system_info.headless = instance.headless;
criteria.require_present = !instance.headless;
criteria.required_version = instance.instance_version;
criteria.desired_version = instance.instance_version;
}
detail::Expected<PhysicalDevice, detail::Error<PhysicalDeviceError>> PhysicalDeviceSelector::select () const {

View File

@ -98,7 +98,10 @@ template <typename E, typename U> class Expected {
} // namespace detail
enum class InstanceError {
unavailable_vulkan_version,
vulkan_unavailable,
vulkan_version_unavailable,
vulkan_version_1_1_unavailable,
vulkan_version_1_2_unavailable,
failed_create_instance,
failed_create_debug_messenger,
requested_layers_not_present,
@ -183,8 +186,10 @@ class InstanceBuilder {
InstanceBuilder& set_app_version (uint32_t major, uint32_t minor, uint32_t patch);
// Sets the (major, minor, patch) version of the engine.
InstanceBuilder& set_engine_version (uint32_t major, uint32_t minor, uint32_t patch);
// Require a minimum vulkan instance API version.
// Require a vulkan instance API version. Will fail to create if this version isn't available.
InstanceBuilder& require_api_version (uint32_t major, uint32_t minor, uint32_t patch);
// Prefer a vulkan instance API version. If the desired version isn't available, it will use the highest version available.
InstanceBuilder& desire_api_version (uint32_t major, uint32_t minor, uint32_t patch);
// Adds a layer to be enabled. Will fail to create an instance if the layer isn't available.
InstanceBuilder& enable_layer (const char* layer_name);
@ -234,7 +239,8 @@ class InstanceBuilder {
const char* engine_name = nullptr;
uint32_t application_version = 0;
uint32_t engine_version = 0;
uint32_t api_version = VK_MAKE_VERSION (1, 0, 0);
uint32_t required_api_version = VK_MAKE_VERSION (1, 0, 0);
uint32_t desired_api_version = VK_MAKE_VERSION (1, 0, 0);
// VkInstanceCreateInfo
std::vector<const char*> layers;

View File

@ -90,10 +90,14 @@ int test_instance_headless () {
}
int test_physical_device_selection () {
printf ("\nphysical device selection\n");
printf ("\nphysical device selection\n");
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.use_default_debug_messenger ().build ();
if(!instance_ret.has_value()){
printf("\n%s",vkb::to_string(instance_ret.error().type));
return -1;
}
auto instance = instance_ret.value ();
auto window = create_window_glfw ();
auto surface = create_surface_glfw (instance.instance, window);
@ -119,7 +123,7 @@ int test_device_creation () {
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.use_default_debug_messenger ().build ();
if (!instance_ret.has_value ()) {
printf ("couldn't create instance %i\n", static_cast<uint32_t> (instance_ret.error ().type));
printf ("couldn't create instance %s\n", vkb::to_string (instance_ret.error ().type));
return -1;
}
auto instance = instance_ret.value ();
@ -129,16 +133,16 @@ int test_device_creation () {
vkb::PhysicalDeviceSelector selector (instance);
auto phys_dev_ret = selector.set_surface (surface).select ();
auto phys_dev = phys_dev_ret.value ();
if (!phys_dev_ret.has_value ()) {
printf ("couldn't select device %i\n", static_cast<uint32_t> (phys_dev_ret.error ().type));
printf ("couldn't select device %s\n", vkb::to_string (phys_dev_ret.error ().type));
return -1;
}
auto phys_dev = phys_dev_ret.value ();
vkb::DeviceBuilder device_builder (phys_dev);
auto dev_ret = device_builder.build ();
if (!dev_ret.has_value ()) {
printf ("couldn't create device %i\n", static_cast<uint32_t> (dev_ret.error ().type));
printf ("couldn't create device %s\n", vkb::to_string (dev_ret.error ().type));
return -1;
}
vkb::destroy_device (dev_ret.value ());