| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #ifndef SRC_GRAPHICS_TESTS_COMMON_VULKAN_CONTEXT_H_ |
| #define SRC_GRAPHICS_TESTS_COMMON_VULKAN_CONTEXT_H_ |
| |
| #include <memory> |
| |
| #include <gtest/gtest.h> |
| |
| #include <vulkan/vulkan.hpp> |
| |
| // |
| // VulkanContext is a convenience class for handling boilerplate vulkan setup code. |
| // It creates / encapsulates vulkan: |
| // - instance |
| // - physical device |
| // - queue family |
| // - device |
| // - queue |
| // |
| // VulkanContext leverages hpp to have smart pointer semantics for simplified vulkan |
| // resource allocation and free. |
| // |
| // There are 2 canonical usage modalities expected for VulkanContext: |
| // |
| // (1) the simplest mode is to pair VulkanContext with its nested Builder class |
| // to selectively modify the required vulkan "CreateInfo" structs during |
| // construction. E.g. to create an std::unique_ptr<VulkanContext> setting the instance |
| // CreateInfo and the queue flag bits: |
| // |
| // auto ctx = VulkanContext::Builder{}.set_instance_info(info).set_queue_flag_bits(bits).Unique(); |
| // |
| // (2) the second construction mode is for more sophisticated construction where more fine |
| // grained control is required during construction. There are three primary piecewise construction |
| // phases that must be done in order: |
| // |
| // - InitInstance() |
| // - InitQueueFamily() |
| // - InitDevice() |
| // |
| // For example, the device CreateInfo structure may need to be customized |
| // (e.g. to specify protected memory) before calling InitDevice(), and those modifications require |
| // access to the physical device chosen in the pair of calls to InitInstance() and |
| // InitQueueFamily(). |
| // |
| class VulkanContext { |
| public: |
| class Builder; |
| class ContextWithUserData; |
| static constexpr int kInvalidQueueFamily = -1; |
| static constexpr vk::QueueFlags kDefaultQueueFlags = vk::QueueFlagBits::eGraphics; |
| static vk::DebugUtilsMessengerCreateInfoEXT default_debug_info_s_; |
| static ContextWithUserData default_debug_callback_user_data_s_; |
| |
| // All struct arguments are shallow copied. |
| VulkanContext(const vk::InstanceCreateInfo &instance_info, uint32_t physical_device_index, |
| const vk::DeviceCreateInfo &device_info, |
| const vk::DeviceQueueCreateInfo &queue_info, |
| const vk::QueueFlags &queue_flags = vk::QueueFlagBits::eGraphics, |
| const vk::DebugUtilsMessengerCreateInfoEXT &debug_info = default_debug_info_s_, |
| ContextWithUserData debug_user_data = default_debug_callback_user_data_s_, |
| vk::Optional<const vk::AllocationCallbacks> allocator = nullptr, |
| bool validation_layers_enabled = true, bool validation_layers_ignored_ = false); |
| |
| explicit VulkanContext(uint32_t physical_device_index, |
| const vk::QueueFlags &queue_flags = vk::QueueFlagBits::eGraphics, |
| vk::Optional<const vk::AllocationCallbacks> allocator = nullptr); |
| |
| bool Init(); |
| bool InitInstance(); |
| bool InitQueueFamily(); |
| bool InitDevice(); |
| |
| // All struct arguments to modifiers are shallow copied. |
| bool set_instance_info(const vk::InstanceCreateInfo &v); |
| bool set_device_info(const vk::DeviceCreateInfo &v); |
| bool set_queue_info(const vk::DeviceQueueCreateInfo &v); |
| bool set_queue_flags(const vk::QueueFlags &v); |
| void set_validation_layers_enabled(bool v) { validation_layers_enabled_ = v; } |
| // Set to true to ignore validation errors and allow the test to pass even with errors. |
| void set_validation_errors_ignored(bool v) { validation_errors_ignored_ = v; } |
| void set_validation_allowed_errors(const std::vector<vk::Result> &v); |
| void set_debug_utils_messenger(const vk::DebugUtilsMessengerCreateInfoEXT &debug_info, |
| const ContextWithUserData &user_data); |
| |
| const vk::InstanceCreateInfo &instance_info() const { return instance_info_; } |
| const vk::DeviceCreateInfo &device_info() const { return device_info_; } |
| const vk::DeviceQueueCreateInfo &queue_info() const { return queue_info_; } |
| |
| const vk::UniqueInstance &instance() const; |
| const vk::PhysicalDevice &physical_device() const; |
| const vk::UniqueDevice &device() const; |
| const vk::Queue &queue() const; |
| int queue_family_index() const; |
| const vk::QueueFlags &queue_flag_bits() const { return queue_flags_; } |
| bool validation_errors_ignored() const { return validation_errors_ignored_; } |
| const vk::DispatchLoaderDynamic &loader() const { return loader_; } |
| |
| // Package up the vulkan context and the user data for the debug callback together. |
| // |user_data_| declared such that VulkanContext will own the |user_data_| so we don't |
| // accidentally end up with a dangling (void *). |
| class ContextWithUserData { |
| public: |
| ContextWithUserData() : user_data_(nullptr) {} |
| explicit ContextWithUserData(std::shared_ptr<void> &user_data) |
| : user_data_(std::move(user_data)) {} |
| const VulkanContext *context() const { return context_; } |
| std::shared_ptr<void> user_data() { return user_data_; } |
| |
| private: |
| // VulkanContext (only) should set the |context_| member. |
| friend class VulkanContext; |
| |
| VulkanContext *context_ = nullptr; |
| std::shared_ptr<void> user_data_; |
| }; |
| |
| private: |
| FRIEND_TEST(VkContext, Unique); |
| FRIEND_TEST(VkContext, ImplicitDebugUtilsMessenger); |
| |
| // Is a debug utils messenger installed in the |instance_info_| structure chain. |
| bool DebugUtilsMessengerInstalled() const; |
| |
| bool initialized_ = false; |
| bool instance_initialized_ = false; |
| bool queue_family_initialized_ = false; |
| bool device_initialized_ = false; |
| |
| // These ivars are listed in order of their use in initialization. |
| vk::UniqueInstance instance_; |
| vk::InstanceCreateInfo instance_info_; |
| |
| vk::PhysicalDevice physical_device_; |
| uint32_t physical_device_index_; |
| |
| float queue_priority_ = 0.0f; |
| int queue_family_index_; |
| vk::DeviceQueueCreateInfo queue_info_; |
| |
| vk::DeviceCreateInfo device_info_; |
| vk::UniqueDevice device_; |
| |
| ContextWithUserData debug_callback_user_data_; |
| vk::DebugUtilsMessengerCreateInfoEXT debug_info_; |
| |
| vk::Queue queue_; |
| vk::QueueFlags queue_flags_; |
| |
| vk::Optional<const vk::AllocationCallbacks> allocator_; |
| |
| // The data in |layers_| and |extensions_| may be referenced by |instance_info_|. |
| std::vector<const char *> layers_; |
| std::vector<const char *> extensions_; |
| |
| // By default validation layers should be enabled. A test may want to disable them if it's testing |
| // completely invalid behavior that could cause the layers to crash, or if it's a benchmark. |
| bool validation_layers_enabled_ = true; |
| |
| // By default validation errors should fail the test. |
| bool validation_errors_ignored_ = false; |
| |
| vk::DispatchLoaderDynamic loader_; |
| vk::UniqueHandle<vk::DebugUtilsMessengerEXT, vk::DispatchLoaderDynamic> messenger_; |
| }; |
| |
| class VulkanContext::Builder { |
| public: |
| Builder(); |
| Builder(const Builder &) = delete; |
| |
| std::unique_ptr<VulkanContext> Unique() const; |
| |
| // Convenience methods for retrieving default device and queue info |
| // structs for read/modify/write amendments to these info structs during |
| // construction. The device info struct |device_info_| captures the address |
| // of the queue info ivar |queue_info_| as part of its definition. |
| vk::DeviceCreateInfo DeviceInfo() const; |
| vk::DeviceQueueCreateInfo QueueInfo() const; |
| |
| Builder &set_allocator(vk::Optional<const vk::AllocationCallbacks> allocator); |
| |
| // |
| // The mutators below shallow-copy the *CreateInfo structs because of the |
| // chaining nature of these structs (i.e. the pNext member). |
| // |
| // The caller of these methods must preserve memory backing the *info |
| // members through any calls to Unique() or Shared() which rely upon |
| // this information for instantiation. |
| // |
| // Typical construction example: |
| // auto ctx = VulkanContext::Builder{}.(optional set* calls).Unique(); |
| // |
| Builder &set_instance_info(const vk::InstanceCreateInfo &v); |
| Builder &set_physical_device_index(uint32_t v); |
| Builder &set_queue_info(const vk::DeviceQueueCreateInfo &v); |
| Builder &set_device_info(const vk::DeviceCreateInfo &v); |
| Builder &set_queue_flags(const vk::QueueFlags &v); |
| Builder &set_validation_layers_enabled(bool v); |
| Builder &set_validation_errors_ignored(bool v); |
| Builder &set_debug_utils_messenger(const vk::DebugUtilsMessengerCreateInfoEXT &v0, |
| const ContextWithUserData &v1); |
| |
| private: |
| vk::InstanceCreateInfo instance_info_; |
| uint32_t physical_device_index_; |
| float queue_priority_; |
| vk::DeviceQueueCreateInfo queue_info_; |
| vk::DeviceCreateInfo device_info_; |
| vk::QueueFlags queue_flags_; |
| bool validation_layers_enabled_ = true; |
| bool validation_errors_ignored_ = false; |
| vk::Optional<const vk::AllocationCallbacks> allocator_; |
| vk::DebugUtilsMessengerCreateInfoEXT debug_info_; |
| ContextWithUserData debug_callback_user_data_; |
| }; |
| |
| #endif // SRC_GRAPHICS_TESTS_COMMON_VULKAN_CONTEXT_H_ |