| 1 | /**************************************************************************/ |
| 2 | /* vulkan_context.h */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #ifndef VULKAN_CONTEXT_H |
| 32 | #define VULKAN_CONTEXT_H |
| 33 | |
| 34 | #include "core/error/error_list.h" |
| 35 | #include "core/os/mutex.h" |
| 36 | #include "core/string/ustring.h" |
| 37 | #include "core/templates/hash_map.h" |
| 38 | #include "core/templates/rb_map.h" |
| 39 | #include "core/templates/rid_owner.h" |
| 40 | #include "servers/display_server.h" |
| 41 | #include "servers/rendering/rendering_device.h" |
| 42 | |
| 43 | #ifdef USE_VOLK |
| 44 | #include <volk.h> |
| 45 | #else |
| 46 | #include <vulkan/vulkan.h> |
| 47 | #endif |
| 48 | |
| 49 | #include "vulkan_hooks.h" |
| 50 | |
| 51 | class VulkanContext { |
| 52 | public: |
| 53 | struct SubgroupCapabilities { |
| 54 | uint32_t size; |
| 55 | VkShaderStageFlags supportedStages; |
| 56 | VkSubgroupFeatureFlags supportedOperations; |
| 57 | VkBool32 quadOperationsInAllStages; |
| 58 | |
| 59 | uint32_t supported_stages_flags_rd() const; |
| 60 | String supported_stages_desc() const; |
| 61 | uint32_t supported_operations_flags_rd() const; |
| 62 | String supported_operations_desc() const; |
| 63 | }; |
| 64 | |
| 65 | struct MultiviewCapabilities { |
| 66 | bool is_supported; |
| 67 | bool geometry_shader_is_supported; |
| 68 | bool tessellation_shader_is_supported; |
| 69 | uint32_t max_view_count; |
| 70 | uint32_t max_instance_count; |
| 71 | }; |
| 72 | |
| 73 | struct VRSCapabilities { |
| 74 | bool pipeline_vrs_supported; // We can specify our fragment rate on a pipeline level. |
| 75 | bool primitive_vrs_supported; // We can specify our fragment rate on each drawcall. |
| 76 | bool attachment_vrs_supported; // We can provide a density map attachment on our framebuffer. |
| 77 | |
| 78 | Size2i min_texel_size; |
| 79 | Size2i max_texel_size; |
| 80 | |
| 81 | Size2i texel_size; // The texel size we'll use |
| 82 | }; |
| 83 | |
| 84 | struct ShaderCapabilities { |
| 85 | bool shader_float16_is_supported; |
| 86 | bool shader_int8_is_supported; |
| 87 | }; |
| 88 | |
| 89 | struct StorageBufferCapabilities { |
| 90 | bool storage_buffer_16_bit_access_is_supported; |
| 91 | bool uniform_and_storage_buffer_16_bit_access_is_supported; |
| 92 | bool storage_push_constant_16_is_supported; |
| 93 | bool storage_input_output_16; |
| 94 | }; |
| 95 | |
| 96 | private: |
| 97 | enum { |
| 98 | MAX_EXTENSIONS = 128, |
| 99 | MAX_LAYERS = 64, |
| 100 | FRAME_LAG = 2 |
| 101 | }; |
| 102 | |
| 103 | static VulkanHooks *vulkan_hooks; |
| 104 | VkInstance inst = VK_NULL_HANDLE; |
| 105 | VkPhysicalDevice gpu = VK_NULL_HANDLE; |
| 106 | VkPhysicalDeviceProperties gpu_props; |
| 107 | uint32_t queue_family_count = 0; |
| 108 | VkQueueFamilyProperties *queue_props = nullptr; |
| 109 | VkDevice device = VK_NULL_HANDLE; |
| 110 | bool device_initialized = false; |
| 111 | bool inst_initialized = false; |
| 112 | |
| 113 | uint32_t instance_api_version = VK_API_VERSION_1_0; |
| 114 | SubgroupCapabilities subgroup_capabilities; |
| 115 | MultiviewCapabilities multiview_capabilities; |
| 116 | VRSCapabilities vrs_capabilities; |
| 117 | ShaderCapabilities shader_capabilities; |
| 118 | StorageBufferCapabilities storage_buffer_capabilities; |
| 119 | |
| 120 | String device_vendor; |
| 121 | String device_name; |
| 122 | VkPhysicalDeviceType device_type; |
| 123 | String pipeline_cache_id; |
| 124 | uint32_t device_api_version = 0; |
| 125 | |
| 126 | bool buffers_prepared = false; |
| 127 | |
| 128 | // Present queue. |
| 129 | bool queues_initialized = false; |
| 130 | uint32_t graphics_queue_family_index = UINT32_MAX; |
| 131 | uint32_t present_queue_family_index = UINT32_MAX; |
| 132 | bool separate_present_queue = false; |
| 133 | VkQueue graphics_queue = VK_NULL_HANDLE; |
| 134 | VkQueue present_queue = VK_NULL_HANDLE; |
| 135 | VkColorSpaceKHR color_space; |
| 136 | VkFormat format; |
| 137 | VkSemaphore draw_complete_semaphores[FRAME_LAG]; |
| 138 | VkSemaphore image_ownership_semaphores[FRAME_LAG]; |
| 139 | int frame_index = 0; |
| 140 | VkFence fences[FRAME_LAG]; |
| 141 | VkPhysicalDeviceMemoryProperties memory_properties; |
| 142 | VkPhysicalDeviceFeatures physical_device_features; |
| 143 | |
| 144 | typedef struct { |
| 145 | VkImage image; |
| 146 | VkCommandBuffer graphics_to_present_cmd; |
| 147 | VkImageView view; |
| 148 | VkFramebuffer framebuffer; |
| 149 | } SwapchainImageResources; |
| 150 | |
| 151 | struct Window { |
| 152 | VkSurfaceKHR surface = VK_NULL_HANDLE; |
| 153 | VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
| 154 | SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE; |
| 155 | VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; |
| 156 | VkSemaphore image_acquired_semaphores[FRAME_LAG]; |
| 157 | bool semaphore_acquired = false; |
| 158 | uint32_t current_buffer = 0; |
| 159 | int width = 0; |
| 160 | int height = 0; |
| 161 | DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; |
| 162 | VkCommandPool present_cmd_pool = VK_NULL_HANDLE; // For separate present queue. |
| 163 | VkRenderPass render_pass = VK_NULL_HANDLE; |
| 164 | }; |
| 165 | |
| 166 | struct LocalDevice { |
| 167 | bool waiting = false; |
| 168 | VkDevice device = VK_NULL_HANDLE; |
| 169 | VkQueue queue = VK_NULL_HANDLE; |
| 170 | }; |
| 171 | |
| 172 | RID_Owner<LocalDevice, true> local_device_owner; |
| 173 | |
| 174 | HashMap<DisplayServer::WindowID, Window> windows; |
| 175 | uint32_t swapchainImageCount = 0; |
| 176 | |
| 177 | // Commands. |
| 178 | |
| 179 | bool prepared = false; |
| 180 | |
| 181 | Vector<VkCommandBuffer> command_buffer_queue; |
| 182 | int command_buffer_count = 1; |
| 183 | |
| 184 | // Extensions. |
| 185 | static bool instance_extensions_initialized; |
| 186 | static HashMap<CharString, bool> requested_instance_extensions; |
| 187 | HashSet<CharString> enabled_instance_extension_names; |
| 188 | |
| 189 | static bool device_extensions_initialized; |
| 190 | static HashMap<CharString, bool> requested_device_extensions; |
| 191 | HashSet<CharString> enabled_device_extension_names; |
| 192 | bool VK_KHR_incremental_present_enabled = true; |
| 193 | bool VK_GOOGLE_display_timing_enabled = true; |
| 194 | |
| 195 | PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT = nullptr; |
| 196 | PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT = nullptr; |
| 197 | PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT = nullptr; |
| 198 | PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT = nullptr; |
| 199 | PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT = nullptr; |
| 200 | PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT = nullptr; |
| 201 | PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT = nullptr; |
| 202 | PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT = nullptr; |
| 203 | PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr; |
| 204 | PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr; |
| 205 | PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR = nullptr; |
| 206 | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; |
| 207 | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR = nullptr; |
| 208 | PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR = nullptr; |
| 209 | PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR = nullptr; |
| 210 | PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR = nullptr; |
| 211 | PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR = nullptr; |
| 212 | PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR = nullptr; |
| 213 | PFN_vkQueuePresentKHR fpQueuePresentKHR = nullptr; |
| 214 | PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE = nullptr; |
| 215 | PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE = nullptr; |
| 216 | PFN_vkCreateRenderPass2KHR fpCreateRenderPass2KHR = nullptr; |
| 217 | |
| 218 | VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; |
| 219 | VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE; |
| 220 | |
| 221 | Error _obtain_vulkan_version(); |
| 222 | Error _initialize_instance_extensions(); |
| 223 | Error _initialize_device_extensions(); |
| 224 | Error _check_capabilities(); |
| 225 | |
| 226 | VkBool32 _check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers); |
| 227 | static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback( |
| 228 | VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, |
| 229 | VkDebugUtilsMessageTypeFlagsEXT messageType, |
| 230 | const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, |
| 231 | void *pUserData); |
| 232 | |
| 233 | static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback( |
| 234 | VkDebugReportFlagsEXT flags, |
| 235 | VkDebugReportObjectTypeEXT objectType, |
| 236 | uint64_t object, |
| 237 | size_t location, |
| 238 | int32_t messageCode, |
| 239 | const char *pLayerPrefix, |
| 240 | const char *pMessage, |
| 241 | void *pUserData); |
| 242 | |
| 243 | Error _create_instance(); |
| 244 | |
| 245 | Error _create_physical_device(VkSurfaceKHR p_surface); |
| 246 | |
| 247 | Error _initialize_queues(VkSurfaceKHR p_surface); |
| 248 | |
| 249 | Error _create_device(); |
| 250 | |
| 251 | Error _clean_up_swap_chain(Window *window); |
| 252 | |
| 253 | Error _update_swap_chain(Window *window); |
| 254 | |
| 255 | Error _create_swap_chain(); |
| 256 | Error _create_semaphores(); |
| 257 | |
| 258 | Vector<VkAttachmentReference> _convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs); |
| 259 | |
| 260 | protected: |
| 261 | virtual const char *_get_platform_surface_extension() const = 0; |
| 262 | |
| 263 | virtual Error _window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height); |
| 264 | |
| 265 | virtual bool _use_validation_layers(); |
| 266 | |
| 267 | Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); |
| 268 | |
| 269 | virtual VkExtent2D _compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const; |
| 270 | |
| 271 | public: |
| 272 | // Extension calls. |
| 273 | bool supports_renderpass2() const { return is_device_extension_enabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); } |
| 274 | VkResult vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass); |
| 275 | |
| 276 | uint32_t get_vulkan_major() const { return VK_API_VERSION_MAJOR(device_api_version); }; |
| 277 | uint32_t get_vulkan_minor() const { return VK_API_VERSION_MINOR(device_api_version); }; |
| 278 | const SubgroupCapabilities &get_subgroup_capabilities() const { return subgroup_capabilities; }; |
| 279 | const MultiviewCapabilities &get_multiview_capabilities() const { return multiview_capabilities; }; |
| 280 | const VRSCapabilities &get_vrs_capabilities() const { return vrs_capabilities; }; |
| 281 | const ShaderCapabilities &get_shader_capabilities() const { return shader_capabilities; }; |
| 282 | const StorageBufferCapabilities &get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; |
| 283 | const VkPhysicalDeviceFeatures &get_physical_device_features() const { return physical_device_features; }; |
| 284 | |
| 285 | VkDevice get_device(); |
| 286 | VkPhysicalDevice get_physical_device(); |
| 287 | VkInstance get_instance() { return inst; } |
| 288 | int get_swapchain_image_count() const; |
| 289 | VkQueue get_graphics_queue() const; |
| 290 | uint32_t get_graphics_queue_family_index() const; |
| 291 | |
| 292 | static void set_vulkan_hooks(VulkanHooks *p_vulkan_hooks) { vulkan_hooks = p_vulkan_hooks; }; |
| 293 | |
| 294 | static void register_requested_instance_extension(const CharString &extension_name, bool p_required); |
| 295 | bool is_instance_extension_enabled(const CharString &extension_name) const { |
| 296 | return enabled_instance_extension_names.has(extension_name); |
| 297 | } |
| 298 | |
| 299 | static void register_requested_device_extension(const CharString &extension_name, bool p_required); |
| 300 | bool is_device_extension_enabled(const CharString &extension_name) const { |
| 301 | return enabled_device_extension_names.has(extension_name); |
| 302 | } |
| 303 | |
| 304 | void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); |
| 305 | int window_get_width(DisplayServer::WindowID p_window = 0); |
| 306 | int window_get_height(DisplayServer::WindowID p_window = 0); |
| 307 | bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0); |
| 308 | void window_destroy(DisplayServer::WindowID p_window_id); |
| 309 | VkFramebuffer window_get_framebuffer(DisplayServer::WindowID p_window = 0); |
| 310 | VkRenderPass window_get_render_pass(DisplayServer::WindowID p_window = 0); |
| 311 | |
| 312 | RID local_device_create(); |
| 313 | VkDevice local_device_get_vk_device(RID p_local_device); |
| 314 | void local_device_push_command_buffers(RID p_local_device, const VkCommandBuffer *p_buffers, int p_count); |
| 315 | void local_device_sync(RID p_local_device); |
| 316 | void local_device_free(RID p_local_device); |
| 317 | |
| 318 | VkFormat get_screen_format() const; |
| 319 | VkPhysicalDeviceLimits get_device_limits() const; |
| 320 | |
| 321 | void set_setup_buffer(VkCommandBuffer p_command_buffer); |
| 322 | void append_command_buffer(VkCommandBuffer p_command_buffer); |
| 323 | void resize_notify(); |
| 324 | void flush(bool p_flush_setup = false, bool p_flush_pending = false); |
| 325 | Error prepare_buffers(); |
| 326 | Error swap_buffers(); |
| 327 | Error initialize(); |
| 328 | |
| 329 | void command_begin_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color); |
| 330 | void command_insert_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color); |
| 331 | void command_end_label(VkCommandBuffer p_command_buffer); |
| 332 | void set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name); |
| 333 | |
| 334 | String get_device_vendor_name() const; |
| 335 | String get_device_name() const; |
| 336 | RenderingDevice::DeviceType get_device_type() const; |
| 337 | String get_device_api_version() const; |
| 338 | String get_device_pipeline_cache_uuid() const; |
| 339 | |
| 340 | void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode); |
| 341 | DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const; |
| 342 | |
| 343 | VulkanContext(); |
| 344 | virtual ~VulkanContext(); |
| 345 | }; |
| 346 | |
| 347 | #endif // VULKAN_CONTEXT_H |
| 348 | |