| /* |
| * Copyright (c) 2015-2025 The Khronos Group Inc. |
| * Copyright (c) 2015-2025 Valve Corporation |
| * Copyright (c) 2015-2025 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "error_monitor.h" |
| #include "test_framework.h" |
| #include "feature_requirements.h" |
| #include "binding.h" |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) |
| #include <android/log.h> |
| #include <android_native_app_glue.h> |
| #endif |
| |
| #include <vector> |
| |
| struct InstanceExtensions; |
| struct DeviceExtensions; |
| |
| static constexpr uint64_t kWaitTimeout{10000000000}; // 10 seconds in ns |
| static constexpr VkDeviceSize kZeroDeviceSize{0}; |
| |
| // Common (and simple) enough to make global |
| static constexpr VkMemoryPropertyFlags kHostVisibleMemProps = |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| |
| struct SurfaceContext { |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| HWND m_win32Window{}; |
| #endif |
| #if defined(VK_USE_PLATFORM_XLIB_KHR) |
| Display *m_surface_dpy{}; |
| Window m_surface_window{}; |
| #endif |
| #if defined(VK_USE_PLATFORM_XCB_KHR) |
| xcb_connection_t *m_surface_xcb_conn{}; |
| #endif |
| #if defined(VK_USE_PLATFORM_WAYLAND_KHR) |
| WaylandContext m_wayland_context{}; |
| #endif |
| #if defined(VK_USE_PLATFORM_METAL_EXT) |
| void *caMetalLayer{}; |
| #endif |
| |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| static bool CanResize() { return true; } |
| void Resize(uint32_t width, uint32_t height); |
| #else |
| static bool CanResize() { return false; } |
| void Resize(uint32_t, uint32_t) {} |
| #endif |
| void Destroy(); |
| ~SurfaceContext() { Destroy(); } |
| }; |
| |
| struct SurfaceInformation { |
| VkSurfaceCapabilitiesKHR surface_capabilities{}; |
| std::vector<VkSurfaceFormatKHR> surface_formats; |
| std::vector<VkPresentModeKHR> surface_present_modes; |
| VkPresentModeKHR surface_non_shared_present_mode{}; |
| VkCompositeAlphaFlagBitsKHR surface_composite_alpha{}; |
| }; |
| |
| class VkRenderFramework : public VkTestFramework { |
| public: |
| VkInstance instance() const { return instance_; } |
| VkDevice device() const { return m_device->handle(); } |
| vkt::Device *DeviceObj() const { return m_device; } |
| VkPhysicalDevice Gpu() const; |
| VkRenderPass RenderPass() const { return m_renderPass; } |
| VkFramebuffer Framebuffer() const { return m_framebuffer->handle(); } |
| |
| vkt::Queue *DefaultQueue() const { return m_default_queue; } |
| vkt::Queue *SecondQueue() const { return m_second_queue; } |
| |
| ErrorMonitor &Monitor(); |
| const VkPhysicalDeviceProperties &PhysicalDeviceProps() const; |
| |
| bool InstanceLayerSupported(const char *layer_name, uint32_t spec_version = 0, uint32_t impl_version = 0); |
| bool InstanceExtensionSupported(const char *extension_name, uint32_t spec_version = 0); |
| |
| VkInstanceCreateInfo GetInstanceCreateInfo() const; |
| void InitFramework(void *instance_pnext = NULL); |
| void ShutdownFramework(); |
| |
| // Functions to modify the VkRenderFramework surface & swapchain variables |
| void InitSurface(); |
| void InitSwapchainInfo(); |
| void InitSwapchain(VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); |
| void DestroySwapchain(); |
| |
| // Functions to create surfaces and swapchains that *aren't* member variables of VkRenderFramework |
| VkResult CreateSurface(SurfaceContext &surface_context, vkt::Surface &surface, VkInstance custom_instance = VK_NULL_HANDLE); |
| SurfaceInformation GetSwapchainInfo(const VkSurfaceKHR surface); |
| static VkSwapchainCreateInfoKHR GetDefaultSwapchainCreateInfo(VkSurfaceKHR surface, const SurfaceInformation &surface_info, |
| VkImageUsageFlags image_usage); |
| vkt::Swapchain CreateSwapchain(VkSurfaceKHR surface, VkImageUsageFlags image_usage, VkSurfaceTransformFlagBitsKHR pre_transform, |
| VkSwapchainKHR old_swapchain = VK_NULL_HANDLE); |
| |
| // Swapchain capabilities declaration to be used with RETURN_IF_SKIP |
| void SupportMultiSwapchain(); |
| void SupportSurfaceResize(); |
| |
| void SetPresentImageLayout(VkImage image); |
| |
| void InitRenderTarget(); |
| void InitRenderTarget(uint32_t targets); |
| void InitRenderTarget(const VkImageView *dsBinding); |
| void InitRenderTarget(uint32_t targets, const VkImageView *dsBinding); |
| void InitDynamicRenderTarget(VkFormat format = VK_FORMAT_UNDEFINED); |
| VkImageView GetDynamicRenderTarget(uint32_t idx = 0) const; |
| VkRect2D GetRenderTargetArea() const; |
| void DestroyRenderTarget(); |
| |
| // Used for VK_EXT_shader_object |
| void SetDefaultDynamicStatesExclude(const std::vector<VkDynamicState> &exclude = {}, bool tessellation = false, |
| VkCommandBuffer commandBuffer = VK_NULL_HANDLE); |
| void SetDefaultDynamicStatesAll(VkCommandBuffer cmdBuffer); |
| |
| static bool IgnoreDisableChecks(); |
| bool IsPlatformMockICD(); |
| void GetPhysicalDeviceFeatures(VkPhysicalDeviceFeatures *features); |
| void GetPhysicalDeviceProperties(VkPhysicalDeviceProperties *props); |
| VkFormat GetRenderTargetFormat(); |
| // default to CommandPool Reset flag to allow recording multiple command buffers simpler |
| void InitState(VkPhysicalDeviceFeatures *features = nullptr, void *create_device_pnext = nullptr, |
| const VkCommandPoolCreateFlags flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); |
| bool DeviceExtensionSupported(const char *extension_name, uint32_t spec_version = 0) const; |
| bool DeviceExtensionSupported(VkPhysicalDevice, const char *, const char *name, |
| uint32_t spec_version = 0) const { // deprecated |
| return DeviceExtensionSupported(name, spec_version); |
| } |
| |
| // Tracks ext_name to be enabled at device creation time and attempts to enable any required instance extensions. |
| // Does not return anything as will be checked when creating the framework |
| // `ext_name` can refer to a device or instance extension. |
| void AddRequiredExtensions(const char *ext_name); |
| // Ensures at least 1 WSI instance extension is enabled |
| void AddWsiExtensions(const char *ext_name); |
| // Same as AddRequiredExtensions but won't skip test if not found |
| void AddOptionalExtensions(const char *ext_name); |
| // After instance and physical device creation (e.g., after InitFramework), returns if extension was enabled |
| bool IsExtensionsEnabled(const char *ext_name) const; |
| // if requested extensions are not supported, helper function to get string to print out |
| std::string RequiredExtensionsNotSupported() const; |
| |
| // By default, requested extensions that are promoted to the effective API version (and thus are redundant) |
| // are not enabled, but this can be overridden for individual test cases that explicitly test such use cases. |
| void AllowPromotedExtensions() { allow_promoted_extensions_ = true; } |
| |
| // Add a feature required for the test to be executed. The currently targeted API version is used to add the correct struct, so |
| // be sure to call SetTargetApiVersion before. |
| // Only features added with this or the AddOptionalFeature method will be enabled. |
| void AddRequiredFeature(vkt::Feature feature); |
| // Add an optional feature for the test to be executed. The currently targeted API version is used to add the correct struct, so |
| // be sure to call SetTargetApiVersion before |
| // Only features added with this or the AddRequiredFeature method will be enabled. |
| void AddOptionalFeature(vkt::Feature feature); |
| |
| std::vector<uint32_t> GLSLToSPV(VkShaderStageFlagBits stage, const char *code, const spv_target_env env = SPV_ENV_VULKAN_1_0); |
| |
| void SetDesiredFailureMsg(const VkFlags msg_flags, const std::string &msg) { |
| m_errorMonitor->SetDesiredFailureMsg(msg_flags, msg); |
| }; |
| void SetDesiredFailureMsg(const VkFlags msg_flags, const char *const msg_string) { |
| m_errorMonitor->SetDesiredFailureMsg(msg_flags, msg_string); |
| }; |
| void VerifyFound() { m_errorMonitor->VerifyFound(); } |
| |
| protected: |
| APIVersion m_instance_api_version = 0; |
| APIVersion m_target_api_version = 0; |
| APIVersion m_attempted_api_version = 0; |
| |
| VkRenderFramework(); |
| virtual ~VkRenderFramework() = 0; |
| |
| std::vector<VkLayerProperties> available_layers_; // allow caching of available layers |
| std::vector<VkExtensionProperties> available_extensions_; // allow caching of available instance extensions |
| |
| ErrorMonitor monitor_ = ErrorMonitor(m_print_vu); |
| ErrorMonitor *m_errorMonitor = &monitor_; // TODO: Removing this properly is it's own PR. It's a big change. |
| |
| bool allow_promoted_extensions_ = false; |
| |
| VkApplicationInfo app_info_; |
| std::vector<const char *> instance_layers_; |
| std::vector<const char *> m_instance_extension_names; |
| VkInstance instance_; |
| VkPhysicalDevice gpu_ = VK_NULL_HANDLE; |
| VkPhysicalDeviceProperties physDevProps_; |
| // This set of required and optional features is used for the features query. |
| // If any required feature is not available, test will fail. |
| // Then all required features and supported optional features are used for device creation. |
| vkt::FeatureRequirements requested_features_; |
| bool all_queue_count_ = false; |
| |
| uint32_t m_gpu_index; |
| vkt::Device *m_device; |
| vkt::CommandPool m_command_pool; |
| vkt::CommandBuffer m_command_buffer; |
| VkRenderPass m_renderPass = VK_NULL_HANDLE; |
| |
| vkt::Buffer *m_vertex_buffer; |
| |
| // WSI items |
| SurfaceContext m_surface_context{}; |
| vkt::Surface m_surface{}; |
| vkt::Swapchain m_swapchain; |
| VkSurfaceCapabilitiesKHR m_surface_capabilities{}; |
| std::vector<VkSurfaceFormatKHR> m_surface_formats; |
| std::vector<VkPresentModeKHR> m_surface_present_modes; |
| VkPresentModeKHR m_surface_non_shared_present_mode{}; |
| VkCompositeAlphaFlagBitsKHR m_surface_composite_alpha{}; |
| |
| std::vector<VkClearValue> m_renderPassClearValues; |
| VkRenderPassBeginInfo m_renderPassBeginInfo; |
| std::vector<std::unique_ptr<vkt::Image>> m_renderTargets; |
| uint32_t m_width, m_height; |
| VkFormat m_render_target_fmt; |
| VkFormat m_depth_stencil_fmt; |
| VkClearColorValue m_clear_color; |
| vkt::Image *m_depthStencil; |
| // first graphics queue, used must often, don't overwrite, use Device class |
| vkt::Queue *m_default_queue = nullptr; |
| VkQueueFlags m_default_queue_caps = 0; |
| |
| // A queue different from the default one (can be null). |
| // The queue with the most capabilities is selected (graphics > compute > transfer). |
| // Supports transfer capabilities; check m_second_queue_caps for compute/graphics support. |
| vkt::Queue *m_second_queue = nullptr; |
| VkQueueFlags m_second_queue_caps = 0; |
| |
| // A queue different from the default or the second one (can be null). |
| // The queue with the most capabilities is selected (graphics > compute > transfer). |
| // Supports transfer capabilities; check m_third_queue_caps for compute/graphics support. |
| vkt::Queue *m_third_queue = nullptr; |
| VkQueueFlags m_third_queue_caps = 0; |
| |
| vkt::CommandPool m_second_command_pool; // associated with a queue family of the second command queue |
| vkt::CommandBuffer m_second_command_buffer; |
| |
| // Requested extensions to enable at device creation time |
| std::vector<const char *> m_required_extensions; |
| // Optional extensions to try and enable at device creation time |
| std::vector<const char *> m_optional_extensions; |
| // wsi extensions to try and enable |
| std::vector<const char *> m_wsi_extensions; |
| // Device extensions to enable |
| std::vector<const char *> m_device_extension_names; |
| |
| private: |
| // TODO - move to own helper logic |
| vkt::Framebuffer *m_framebuffer; |
| std::vector<vkt::ImageView> m_render_target_views; // color attachments but not depth |
| std::vector<VkImageView> m_framebuffer_attachments; // all attachments, can be consumed directly by the API |
| |
| InstanceExtensions *m_instance_extensions = nullptr; |
| DeviceExtensions *m_device_extensions = nullptr; |
| |
| // Add ext_name, the names of all instance extensions required by ext_name, and return true if ext_name is supported. If the |
| // extension is not supported, no extension names are added for instance creation. `ext_name` can refer to a device or instance |
| // extension. |
| bool AddRequestedInstanceExtensions(const char *ext_name); |
| // Returns true if the instance extension is promoted to core in the target API version requested using SetTargetApiVersion(). |
| bool IsPromotedInstanceExtension(const char *inst_ext_name) const; |
| // Returns true if the instance extension inst_ext_name is enabled. This call is only valid _after_ previous |
| // `AddRequired*Extensions` calls and InitFramework has been called. `inst_ext_name` must be an instance extension name; false |
| // is returned for all device extension names. |
| // This function also returns true if the instance extension is implicitly supported in the target API version |
| // requested using SetTargetApiVersion(). |
| bool CanEnableInstanceExtension(const std::string &inst_ext_name) const; |
| // Add dev_ext_name, then names of _device_ extensions required by dev_ext_name, and return true if dev_ext_name is supported. |
| // If the extension is not supported, no extension names are added for device creation. This function has no effect if |
| // dev_ext_name refers to an instance extension. |
| bool AddRequestedDeviceExtensions(const char *dev_ext_name); |
| // Returns true if the device extension is promoted to core in the API version supported by the device. |
| bool IsPromotedDeviceExtension(const char *dev_ext_name) const; |
| // Returns true if the device extension is enabled. This call is only valid _after_ previous `AddRequired*Extensions` calls and |
| // InitFramework has been called. |
| // `dev_ext_name` must be an instance extension name; false is returned for all instance extension names. |
| // This function also returns true if the device extension is implicitly supported by the API version supported |
| // by the device, as queriable using DeviceValidationVersion(). |
| bool CanEnableDeviceExtension(const std::string &dev_ext_name) const; |
| }; |