diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 9558d0af5e16..9de0228ef345 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -949,9 +949,9 @@ static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport) SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); } - Uint32 sdl_flags = 0; + SDL_WindowFlags sdl_flags = 0; sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0); - sdl_flags |= SDL_GetWindowFlags(bd->Window); + sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_ALLOW_HIGHDPI; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0; diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index cb54fa17ad7e..d4e970eb5461 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -58,7 +58,7 @@ // 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. // 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. // 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). -// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. +// 2020-11-11: Vulkan: Added support for specifying which Subpass to reference during VkPipeline creation. // 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). // 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. // 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. @@ -100,6 +100,7 @@ // Forward Declarations struct ImGui_ImplVulkan_FrameRenderBuffers; struct ImGui_ImplVulkan_WindowRenderBuffers; +struct ImGui_ImplVulkan_RenderPassKey; bool ImGui_ImplVulkan_CreateDeviceObjects(); void ImGui_ImplVulkan_DestroyDeviceObjects(); void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); @@ -107,8 +108,11 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, uint32_t queue_family); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkan_DestroyAllViewportsRenderPasses(); +void ImGui_ImplVulkanH_GetOrCreateViewportsRenderPass(ImGui_ImplVulkanH_Window* wd, VkDevice device, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_CreateWindowFramebuffers(ImGui_ImplVulkanH_Window* wd, VkDevice device, const VkAllocationCallbacks* allocator); // Vulkan prototypes for use with custom loaders // (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h @@ -233,10 +237,42 @@ struct ImGui_ImplVulkan_ViewportData bool WindowOwned; bool SwapChainNeedRebuild; // Flag when viewport swapchain resized in the middle of processing a frame - ImGui_ImplVulkan_ViewportData() { WindowOwned = SwapChainNeedRebuild = false; memset(&RenderBuffers, 0, sizeof(RenderBuffers)); } + ImGui_ImplVulkan_ViewportData() + { + memset(this, 0, sizeof(*this)); + } ~ImGui_ImplVulkan_ViewportData() { } }; +inline bool operator!=(VkSurfaceFormatKHR const& a, VkSurfaceFormatKHR const& b) +{ + return a.format != b.format || a.colorSpace != b.colorSpace; +} + +inline bool operator==(VkSurfaceFormatKHR const& a, VkSurfaceFormatKHR const& b) +{ + return a.format == b.format && a.colorSpace == b.colorSpace; +} + +struct ImGui_ImplVulkan_RenderPassKey +{ + bool Clear = {}; + VkFormat Format = {}; + + ImGui_ImplVulkan_RenderPassKey() = default; + + ImGui_ImplVulkan_RenderPassKey(ImGui_ImplVulkanH_Window* wd) + { + Clear = wd->RenderPassClear; + Format = wd->SurfaceFormat.format; + } + + bool operator==(ImGui_ImplVulkan_RenderPassKey const& o) const + { + return Clear == o.Clear && Format == o.Format; + } +}; + // Vulkan data struct ImGui_ImplVulkan_Data { @@ -244,11 +280,10 @@ struct ImGui_ImplVulkan_Data VkDeviceSize BufferMemoryAlignment; VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; - VkPipelineLayout PipelineLayout; - VkPipeline Pipeline; // pipeline for main render pass (created by app) - VkPipeline PipelineForViewports; // pipeline for secondary viewports (created by backend) - VkShaderModule ShaderModuleVert; - VkShaderModule ShaderModuleFrag; + VkPipelineLayout PipelineLayout; // Common for all ImGui_ImplVulkan pipelines + VkPipeline Pipeline; // Pipeline for main render pass (created by app) + VkShaderModule ShaderModuleVert; // Common for all ImGui_ImplVulkan pipelines + VkShaderModule ShaderModuleFrag; // Common for all ImGui_ImplVulkan pipelines // Font data VkSampler FontSampler; @@ -262,10 +297,26 @@ struct ImGui_ImplVulkan_Data // Render buffers for main window ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; + // Viewports specific data + struct CachedRenderPass + { + ImGui_ImplVulkan_RenderPassKey Key; + VkRenderPass RenderPass; + }; + // A Map would be more efficient, but there will be only a handful of entries, so it should be good enough for now. + ImVector CachedRenderPassesForViewports; + VkPipelineCache PipelineCacheForViewports; + + VkPresentModeKHR DesiredViewportsPresentMode; + VkSurfaceFormatKHR DesiredViewportsSurfaceFormat; + ImGui_ImplVulkan_ColorCorrectionMethod ViewportsColorCorrectionMethod; // Only if DesiredSurfaceFormat is used + ImGui_ImplVulkan_ColorCorrectionParameters ViewportsColorCorrectionParams; // Only if ColorCorrectionMethod is used + ImGui_ImplVulkan_Data() { memset((void*)this, 0, sizeof(*this)); BufferMemoryAlignment = 256; + DesiredViewportsPresentMode = (VkPresentModeKHR)~0; } }; @@ -342,44 +393,165 @@ static uint32_t __glsl_shader_vert_spv[] = }; // backends/vulkan/glsl_shader.frag, compiled with: -// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag -/* -#version 450 core -layout(location = 0) out vec4 fColor; -layout(set=0, binding=0) uniform sampler2D sTexture; -layout(location = 0) in struct { vec4 Color; vec2 UV; } In; -void main() +// # glslangValidator -V -x -DUSE_SPEC_CONSTANT_PARAMS=1 -o glsl_shader.frag.u32 glsl_shader.frag +static uint32_t __glsl_shader_frag_static_spv[] = { - fColor = In.Color * texture(sTexture, In.UV.st); -} -*/ -static uint32_t __glsl_shader_frag_spv[] = + 0x07230203,0x00010000,0x0008000b,0x00000053,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000039,0x0000003d,0x00030010, + 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00090005,0x0000000b,0x6c707041,0x6c6f4379,0x6f43726f,0x63657272,0x6e6f6974, + 0x34667628,0x0000003b,0x00030005,0x0000000a,0x00637273,0x00030005,0x0000000d,0x00736572, + 0x00080005,0x00000010,0x6f6c6f63,0x6f635f72,0x63657272,0x6e6f6974,0x74656d5f,0x00646f68, + 0x00080005,0x00000019,0x6f6c6f63,0x6f635f72,0x63657272,0x6e6f6974,0x7261705f,0x00326d61, + 0x00050005,0x00000019,0x6f707865,0x65727573,0x00000000,0x00080005,0x0000001d,0x6f6c6f63, + 0x6f635f72,0x63657272,0x6e6f6974,0x7261705f,0x00316d61,0x00040005,0x0000001d,0x6d6d6167, + 0x00000061,0x00080005,0x00000032,0x6f6c6f63,0x6f635f72,0x63657272,0x6e6f6974,0x7261705f, + 0x00336d61,0x00050005,0x00000032,0x68706c61,0x61675f61,0x00616d6d,0x00040005,0x00000039, + 0x6c6f4366,0x0000726f,0x00030005,0x0000003b,0x00000000,0x00050006,0x0000003b,0x00000000, + 0x6f6c6f43,0x00000072,0x00040006,0x0000003b,0x00000001,0x00005655,0x00030005,0x0000003d, + 0x00006e49,0x00050005,0x00000045,0x78655473,0x65727574,0x00000000,0x00040005,0x0000004f, + 0x61726170,0x0000006d,0x00080005,0x00000052,0x6f6c6f63,0x6f635f72,0x63657272,0x6e6f6974, + 0x7261705f,0x00346d61,0x00040047,0x00000010,0x00000001,0x00000000,0x00040047,0x00000019, + 0x00000001,0x00000002,0x00040047,0x0000001d,0x00000001,0x00000001,0x00040047,0x00000032, + 0x00000001,0x00000003,0x00040047,0x00000039,0x0000001e,0x00000000,0x00040047,0x0000003d, + 0x0000001e,0x00000000,0x00040047,0x00000045,0x00000022,0x00000000,0x00040047,0x00000045, + 0x00000021,0x00000000,0x00040047,0x00000052,0x00000001,0x00000004,0x00020013,0x00000002, + 0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007, + 0x00000006,0x00000004,0x00040020,0x00000008,0x00000007,0x00000007,0x00040021,0x00000009, + 0x00000007,0x00000008,0x00040015,0x0000000f,0x00000020,0x00000001,0x00040032,0x0000000f, + 0x00000010,0x00000000,0x0004002b,0x0000000f,0x00000011,0x00000001,0x00020014,0x00000012, + 0x00060034,0x00000012,0x00000013,0x000000aa,0x00000010,0x00000011,0x0004002b,0x0000000f, + 0x00000014,0x00000002,0x00060034,0x00000012,0x00000015,0x000000aa,0x00000010,0x00000014, + 0x00060034,0x00000012,0x00000016,0x000000a6,0x00000013,0x00000015,0x00040032,0x00000006, + 0x00000019,0x3f800000,0x00040017,0x0000001a,0x00000006,0x00000003,0x00040032,0x00000006, + 0x0000001d,0x3f800000,0x00060033,0x0000001a,0x0000001e,0x0000001d,0x0000001d,0x0000001d, + 0x00040015,0x00000021,0x00000020,0x00000000,0x0004002b,0x00000021,0x00000022,0x00000000, + 0x00040020,0x00000023,0x00000007,0x00000006,0x0004002b,0x00000021,0x00000026,0x00000001, + 0x0004002b,0x00000021,0x00000029,0x00000002,0x00060034,0x00000012,0x0000002c,0x000000aa, + 0x00000010,0x00000014,0x0004002b,0x00000021,0x0000002f,0x00000003,0x00040032,0x00000006, + 0x00000032,0x3f800000,0x00040020,0x00000038,0x00000003,0x00000007,0x0004003b,0x00000038, + 0x00000039,0x00000003,0x00040017,0x0000003a,0x00000006,0x00000002,0x0004001e,0x0000003b, + 0x00000007,0x0000003a,0x00040020,0x0000003c,0x00000001,0x0000003b,0x0004003b,0x0000003c, + 0x0000003d,0x00000001,0x0004002b,0x0000000f,0x0000003e,0x00000000,0x00040020,0x0000003f, + 0x00000001,0x00000007,0x00090019,0x00000042,0x00000006,0x00000001,0x00000000,0x00000000, + 0x00000000,0x00000001,0x00000000,0x0003001b,0x00000043,0x00000042,0x00040020,0x00000044, + 0x00000000,0x00000043,0x0004003b,0x00000044,0x00000045,0x00000000,0x00040020,0x00000047, + 0x00000001,0x0000003a,0x00060034,0x00000012,0x0000004c,0x000000ab,0x00000010,0x0000003e, + 0x00040032,0x00000006,0x00000052,0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000, + 0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000008,0x0000004f,0x00000007,0x00050041, + 0x0000003f,0x00000040,0x0000003d,0x0000003e,0x0004003d,0x00000007,0x00000041,0x00000040, + 0x0004003d,0x00000043,0x00000046,0x00000045,0x00050041,0x00000047,0x00000048,0x0000003d, + 0x00000011,0x0004003d,0x0000003a,0x00000049,0x00000048,0x00050057,0x00000007,0x0000004a, + 0x00000046,0x00000049,0x00050085,0x00000007,0x0000004b,0x00000041,0x0000004a,0x0003003e, + 0x00000039,0x0000004b,0x000300f7,0x0000004e,0x00000000,0x000400fa,0x0000004c,0x0000004d, + 0x0000004e,0x000200f8,0x0000004d,0x0004003d,0x00000007,0x00000050,0x00000039,0x0003003e, + 0x0000004f,0x00000050,0x00050039,0x00000007,0x00000051,0x0000000b,0x0000004f,0x0003003e, + 0x00000039,0x00000051,0x000200f9,0x0000004e,0x000200f8,0x0000004e,0x000100fd,0x00010038, + 0x00050036,0x00000007,0x0000000b,0x00000000,0x00000009,0x00030037,0x00000008,0x0000000a, + 0x000200f8,0x0000000c,0x0004003b,0x00000008,0x0000000d,0x00000007,0x0004003d,0x00000007, + 0x0000000e,0x0000000a,0x0003003e,0x0000000d,0x0000000e,0x000300f7,0x00000018,0x00000000, + 0x000400fa,0x00000016,0x00000017,0x00000018,0x000200f8,0x00000017,0x0004003d,0x00000007, + 0x0000001b,0x0000000a,0x0008004f,0x0000001a,0x0000001c,0x0000001b,0x0000001b,0x00000000, + 0x00000001,0x00000002,0x0007000c,0x0000001a,0x0000001f,0x00000001,0x0000001a,0x0000001c, + 0x0000001e,0x0005008e,0x0000001a,0x00000020,0x0000001f,0x00000019,0x00050041,0x00000023, + 0x00000024,0x0000000d,0x00000022,0x00050051,0x00000006,0x00000025,0x00000020,0x00000000, + 0x0003003e,0x00000024,0x00000025,0x00050041,0x00000023,0x00000027,0x0000000d,0x00000026, + 0x00050051,0x00000006,0x00000028,0x00000020,0x00000001,0x0003003e,0x00000027,0x00000028, + 0x00050041,0x00000023,0x0000002a,0x0000000d,0x00000029,0x00050051,0x00000006,0x0000002b, + 0x00000020,0x00000002,0x0003003e,0x0000002a,0x0000002b,0x000300f7,0x0000002e,0x00000000, + 0x000400fa,0x0000002c,0x0000002d,0x0000002e,0x000200f8,0x0000002d,0x00050041,0x00000023, + 0x00000030,0x0000000a,0x0000002f,0x0004003d,0x00000006,0x00000031,0x00000030,0x0007000c, + 0x00000006,0x00000033,0x00000001,0x0000001a,0x00000031,0x00000032,0x00050041,0x00000023, + 0x00000034,0x0000000d,0x0000002f,0x0003003e,0x00000034,0x00000033,0x000200f9,0x0000002e, + 0x000200f8,0x0000002e,0x000200f9,0x00000018,0x000200f8,0x00000018,0x0004003d,0x00000007, + 0x00000035,0x0000000d,0x000200fe,0x00000035,0x00010038 + +}; + +// # glslangValidator -V -x -DUSE_SPEC_CONSTANT_PARAMS=0 -o glsl_shader.frag.u32 glsl_shader.frag +static uint32_t __glsl_shader_frag_dynamic_spv[] = { - 0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x0000005f,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000047,0x0000004b,0x00030010, 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, - 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000, - 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001, - 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,0x78655473,0x65727574, - 0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e, - 0x00000000,0x00040047,0x00000016,0x00000022,0x00000000,0x00040047,0x00000016,0x00000021, - 0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, - 0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003, - 0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006, - 0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001, - 0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020, - 0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001, - 0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000, - 0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,0x00000015,0x00000000, - 0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,0x0000000e,0x00000018, - 0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004, - 0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d, - 0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017, - 0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a, - 0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085, - 0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x000100fd, - 0x00010038 + 0x00000000,0x00090005,0x0000000b,0x6c707041,0x6c6f4379,0x6f43726f,0x63657272,0x6e6f6974, + 0x34667628,0x0000003b,0x00030005,0x0000000a,0x00637273,0x00030005,0x0000000d,0x00736572, + 0x00080005,0x00000010,0x6f6c6f63,0x6f635f72,0x63657272,0x6e6f6974,0x74656d5f,0x00646f68, + 0x00040005,0x0000001a,0x6d6d6167,0x00000061,0x00060005,0x0000001b,0x73755075,0x6e6f4368, + 0x6e617473,0x00000074,0x00090006,0x0000001b,0x00000000,0x6f6c6f63,0x6f635f72,0x63657272, + 0x6e6f6974,0x7261705f,0x00316d61,0x00090006,0x0000001b,0x00000001,0x6f6c6f63,0x6f635f72, + 0x63657272,0x6e6f6974,0x7261705f,0x00326d61,0x00090006,0x0000001b,0x00000002,0x6f6c6f63, + 0x6f635f72,0x63657272,0x6e6f6974,0x7261705f,0x00336d61,0x00090006,0x0000001b,0x00000003, + 0x6f6c6f63,0x6f635f72,0x63657272,0x6e6f6974,0x7261705f,0x00346d61,0x00030005,0x0000001d, + 0x00000000,0x00050005,0x00000022,0x6f707865,0x65727573,0x00000000,0x00050005,0x0000003a, + 0x68706c61,0x61675f61,0x00616d6d,0x00040005,0x00000047,0x6c6f4366,0x0000726f,0x00030005, + 0x00000049,0x00000000,0x00050006,0x00000049,0x00000000,0x6f6c6f43,0x00000072,0x00040006, + 0x00000049,0x00000001,0x00005655,0x00030005,0x0000004b,0x00006e49,0x00050005,0x00000052, + 0x78655473,0x65727574,0x00000000,0x00040005,0x0000005c,0x61726170,0x0000006d,0x00040047, + 0x00000010,0x00000001,0x00000000,0x00050048,0x0000001b,0x00000000,0x00000023,0x00000010, + 0x00050048,0x0000001b,0x00000001,0x00000023,0x00000014,0x00050048,0x0000001b,0x00000002, + 0x00000023,0x00000018,0x00050048,0x0000001b,0x00000003,0x00000023,0x0000001c,0x00030047, + 0x0000001b,0x00000002,0x00040047,0x00000047,0x0000001e,0x00000000,0x00040047,0x0000004b, + 0x0000001e,0x00000000,0x00040047,0x00000052,0x00000022,0x00000000,0x00040047,0x00000052, + 0x00000021,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, + 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008, + 0x00000007,0x00000007,0x00040021,0x00000009,0x00000007,0x00000008,0x00040015,0x0000000f, + 0x00000020,0x00000001,0x00040032,0x0000000f,0x00000010,0x00000000,0x0004002b,0x0000000f, + 0x00000011,0x00000001,0x00020014,0x00000012,0x00060034,0x00000012,0x00000013,0x000000aa, + 0x00000010,0x00000011,0x0004002b,0x0000000f,0x00000014,0x00000002,0x00060034,0x00000012, + 0x00000015,0x000000aa,0x00000010,0x00000014,0x00060034,0x00000012,0x00000016,0x000000a6, + 0x00000013,0x00000015,0x00040020,0x00000019,0x00000007,0x00000006,0x0006001e,0x0000001b, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00040020,0x0000001c,0x00000009,0x0000001b, + 0x0004003b,0x0000001c,0x0000001d,0x00000009,0x0004002b,0x0000000f,0x0000001e,0x00000000, + 0x00040020,0x0000001f,0x00000009,0x00000006,0x00040017,0x00000026,0x00000006,0x00000003, + 0x00040015,0x0000002d,0x00000020,0x00000000,0x0004002b,0x0000002d,0x0000002e,0x00000000, + 0x0004002b,0x0000002d,0x00000031,0x00000001,0x0004002b,0x0000002d,0x00000034,0x00000002, + 0x00060034,0x00000012,0x00000037,0x000000aa,0x00000010,0x00000014,0x0004002b,0x0000002d, + 0x0000003d,0x00000003,0x00040020,0x00000046,0x00000003,0x00000007,0x0004003b,0x00000046, + 0x00000047,0x00000003,0x00040017,0x00000048,0x00000006,0x00000002,0x0004001e,0x00000049, + 0x00000007,0x00000048,0x00040020,0x0000004a,0x00000001,0x00000049,0x0004003b,0x0000004a, + 0x0000004b,0x00000001,0x00040020,0x0000004c,0x00000001,0x00000007,0x00090019,0x0000004f, + 0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b, + 0x00000050,0x0000004f,0x00040020,0x00000051,0x00000000,0x00000050,0x0004003b,0x00000051, + 0x00000052,0x00000000,0x00040020,0x00000054,0x00000001,0x00000048,0x00060034,0x00000012, + 0x00000059,0x000000ab,0x00000010,0x0000001e,0x00050036,0x00000002,0x00000004,0x00000000, + 0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000008,0x0000005c,0x00000007,0x00050041, + 0x0000004c,0x0000004d,0x0000004b,0x0000001e,0x0004003d,0x00000007,0x0000004e,0x0000004d, + 0x0004003d,0x00000050,0x00000053,0x00000052,0x00050041,0x00000054,0x00000055,0x0000004b, + 0x00000011,0x0004003d,0x00000048,0x00000056,0x00000055,0x00050057,0x00000007,0x00000057, + 0x00000053,0x00000056,0x00050085,0x00000007,0x00000058,0x0000004e,0x00000057,0x0003003e, + 0x00000047,0x00000058,0x000300f7,0x0000005b,0x00000000,0x000400fa,0x00000059,0x0000005a, + 0x0000005b,0x000200f8,0x0000005a,0x0004003d,0x00000007,0x0000005d,0x00000047,0x0003003e, + 0x0000005c,0x0000005d,0x00050039,0x00000007,0x0000005e,0x0000000b,0x0000005c,0x0003003e, + 0x00000047,0x0000005e,0x000200f9,0x0000005b,0x000200f8,0x0000005b,0x000100fd,0x00010038, + 0x00050036,0x00000007,0x0000000b,0x00000000,0x00000009,0x00030037,0x00000008,0x0000000a, + 0x000200f8,0x0000000c,0x0004003b,0x00000008,0x0000000d,0x00000007,0x0004003b,0x00000019, + 0x0000001a,0x00000007,0x0004003b,0x00000019,0x00000022,0x00000007,0x0004003b,0x00000019, + 0x0000003a,0x00000007,0x0004003d,0x00000007,0x0000000e,0x0000000a,0x0003003e,0x0000000d, + 0x0000000e,0x000300f7,0x00000018,0x00000000,0x000400fa,0x00000016,0x00000017,0x00000018, + 0x000200f8,0x00000017,0x00050041,0x0000001f,0x00000020,0x0000001d,0x0000001e,0x0004003d, + 0x00000006,0x00000021,0x00000020,0x0003003e,0x0000001a,0x00000021,0x00050041,0x0000001f, + 0x00000023,0x0000001d,0x00000011,0x0004003d,0x00000006,0x00000024,0x00000023,0x0003003e, + 0x00000022,0x00000024,0x0004003d,0x00000006,0x00000025,0x00000022,0x0004003d,0x00000007, + 0x00000027,0x0000000a,0x0008004f,0x00000026,0x00000028,0x00000027,0x00000027,0x00000000, + 0x00000001,0x00000002,0x0004003d,0x00000006,0x00000029,0x0000001a,0x00060050,0x00000026, + 0x0000002a,0x00000029,0x00000029,0x00000029,0x0007000c,0x00000026,0x0000002b,0x00000001, + 0x0000001a,0x00000028,0x0000002a,0x0005008e,0x00000026,0x0000002c,0x0000002b,0x00000025, + 0x00050041,0x00000019,0x0000002f,0x0000000d,0x0000002e,0x00050051,0x00000006,0x00000030, + 0x0000002c,0x00000000,0x0003003e,0x0000002f,0x00000030,0x00050041,0x00000019,0x00000032, + 0x0000000d,0x00000031,0x00050051,0x00000006,0x00000033,0x0000002c,0x00000001,0x0003003e, + 0x00000032,0x00000033,0x00050041,0x00000019,0x00000035,0x0000000d,0x00000034,0x00050051, + 0x00000006,0x00000036,0x0000002c,0x00000002,0x0003003e,0x00000035,0x00000036,0x000300f7, + 0x00000039,0x00000000,0x000400fa,0x00000037,0x00000038,0x00000039,0x000200f8,0x00000038, + 0x00050041,0x0000001f,0x0000003b,0x0000001d,0x00000014,0x0004003d,0x00000006,0x0000003c, + 0x0000003b,0x0003003e,0x0000003a,0x0000003c,0x00050041,0x00000019,0x0000003e,0x0000000a, + 0x0000003d,0x0004003d,0x00000006,0x0000003f,0x0000003e,0x0004003d,0x00000006,0x00000040, + 0x0000003a,0x0007000c,0x00000006,0x00000041,0x00000001,0x0000001a,0x0000003f,0x00000040, + 0x00050041,0x00000019,0x00000042,0x0000000d,0x0000003d,0x0003003e,0x00000042,0x00000041, + 0x000200f9,0x00000039,0x000200f8,0x00000039,0x000200f9,0x00000018,0x000200f8,0x00000018, + 0x0004003d,0x00000007,0x00000043,0x0000000d,0x000200fe,0x00000043,0x00010038 }; //----------------------------------------------------------------------------- @@ -456,7 +628,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory buffer_size = buffer_size_aligned; } -static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height) +static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height, const ImGui_ImplVulkan_ColorCorrectionParameters* color_correction_params = nullptr) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); @@ -498,10 +670,15 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } + + if(color_correction_params) + { + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 4 * sizeof(float), sizeof(ImGui_ImplVulkan_ColorCorrectionParameters), color_correction_params); + } } // Render function -void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) +void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline, const ImGui_ImplVulkan_ColorCorrectionParameters* color_correction_params) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); @@ -513,6 +690,12 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (pipeline == VK_NULL_HANDLE) pipeline = bd->Pipeline; + if(!color_correction_params) + color_correction_params = &v->ColorCorrectionParams; + if(v->UseStaticColorCorrectionsParams) + { + color_correction_params = nullptr; + } // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData; @@ -568,7 +751,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm } // Setup desired Vulkan state - ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height, color_correction_params); // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports @@ -589,7 +772,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height, color_correction_params); else pcmd->UserCallback(cmd_list, pcmd); } @@ -875,27 +1058,86 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca { VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - frag_info.codeSize = sizeof(__glsl_shader_frag_spv); - frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; + const ImGui_ImplVulkan_InitInfo * v = &bd->VulkanInitInfo; + if(v->UseStaticColorCorrectionsParams) + { + frag_info.codeSize = sizeof(__glsl_shader_frag_static_spv); + frag_info.pCode = (uint32_t*)__glsl_shader_frag_static_spv; + } + else + { + frag_info.codeSize = sizeof(__glsl_shader_frag_dynamic_spv); + frag_info.pCode = (uint32_t*)__glsl_shader_frag_dynamic_spv; + } + VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); check_vk_result(err); } } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) +struct ImGui_ImplVulkan_PipelineCreateInfo +{ + VkDevice Device = VK_NULL_HANDLE; + const VkAllocationCallbacks * Allocator = nullptr; + VkPipelineCache PipelineCache = VK_NULL_HANDLE; + VkRenderPass RenderPass = VK_NULL_HANDLE; + uint32_t Subpass = 0; + VkSampleCountFlagBits MSAASamples = {}; + const ImGui_ImplVulkan_PipelineRenderingInfo * pRenderingInfo = nullptr; + ImGui_ImplVulkan_ColorCorrectionMethod ColorCorrectionMethod = {}; + const ImGui_ImplVulkan_ColorCorrectionParameters * ColorCorrectionParams = nullptr; +}; + +static VkPipeline ImGui_ImplVulkan_CreatePipeline(ImGui_ImplVulkan_PipelineCreateInfo const& pci) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_CreateShaderModules(device, allocator); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?"); + ImGui_ImplVulkan_CreateShaderModules(pci.Device, pci.Allocator); VkPipelineShaderStageCreateInfo stage[2] = {}; + stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stage[0].module = bd->ShaderModuleVert; stage[0].pName = "main"; + stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stage[1].module = bd->ShaderModuleFrag; stage[1].pName = "main"; + VkSpecializationInfo frag_specialization_info = {}; + VkSpecializationMapEntry frag_spec_constants[1 + 4] = {}; + struct SpecConstantData + { + uint32_t method; + ImGui_ImplVulkan_ColorCorrectionParameters params; + }; + SpecConstantData frag_spec_constant_data = {}; + frag_spec_constant_data.method = (uint32_t)pci.ColorCorrectionMethod; + frag_spec_constants[0].constantID = 0; + frag_spec_constants[0].offset = offsetof(SpecConstantData, method); + frag_spec_constants[0].size = sizeof(uint32_t); + if(pci.ColorCorrectionParams) + { + frag_specialization_info.mapEntryCount = 3; + frag_specialization_info.dataSize = sizeof(SpecConstantData); + frag_spec_constant_data.params = *pci.ColorCorrectionParams; + for (unsigned int i = 0; i < 4; ++i) + { + frag_spec_constants[1 + i].constantID = 1 + i; + frag_spec_constants[1 + i].offset = offsetof(SpecConstantData, params) + i * sizeof(float); + frag_spec_constants[1 + i].size = sizeof(float); + } + } + else + { + frag_specialization_info.mapEntryCount = 1; + frag_specialization_info.dataSize = sizeof(uint32_t); + } + frag_specialization_info.pMapEntries = frag_spec_constants; + frag_specialization_info.pData = &frag_spec_constant_data; + stage[1].pSpecializationInfo = &frag_specialization_info; + VkVertexInputBindingDescription binding_desc[1] = {}; binding_desc[0].stride = sizeof(ImDrawVert); @@ -940,7 +1182,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC VkPipelineMultisampleStateCreateInfo ms_info = {}; ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + ms_info.rasterizationSamples = (pci.MSAASamples != 0) ? pci.MSAASamples : VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState color_attachment[1] = {}; color_attachment[0].blendEnable = VK_TRUE; @@ -980,21 +1222,24 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC info.pColorBlendState = &blend_info; info.pDynamicState = &dynamic_state; info.layout = bd->PipelineLayout; - info.renderPass = renderPass; - info.subpass = subpass; + info.renderPass = pci.RenderPass; + info.subpass = pci.Subpass; #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING if (bd->VulkanInitInfo.UseDynamicRendering) { - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be NULL"); - info.pNext = &bd->VulkanInitInfo.PipelineRenderingCreateInfo; info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. + IM_ASSERT(!!pci.pRenderingInfo && "Dynamic Rendering requires a PipelineRenderingCreateInfo"); + IM_ASSERT(pci.pRenderingInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo::sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); + IM_ASSERT(pci.pRenderingInfo->pNext == nullptr && "PipelineRenderingCreateInfo::pNext must be NULL"); + info.pNext = pci.pRenderingInfo; } #endif - VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + VkPipeline result; + VkResult err = vkCreateGraphicsPipelines(pci.Device, pci.PipelineCache, 1, &info, pci.Allocator, &result); check_vk_result(err); + return result; } bool ImGui_ImplVulkan_CreateDeviceObjects() @@ -1038,26 +1283,85 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() if (!bd->PipelineLayout) { // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix - VkPushConstantRange push_constants[1] = {}; + VkPushConstantRange push_constants[2] = {}; push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].offset = sizeof(float) * 0; push_constants[0].size = sizeof(float) * 4; + uint32_t push_constants_count = 1; + if(!v->UseStaticColorCorrectionsParams) + { + push_constants[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + push_constants[1].offset = 4 * sizeof(float); + push_constants[1].size = sizeof(ImGui_ImplVulkan_ColorCorrectionParameters); + ++push_constants_count; + } VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; VkPipelineLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.setLayoutCount = 1; layout_info.pSetLayouts = set_layout; - layout_info.pushConstantRangeCount = 1; + layout_info.pushConstantRangeCount = push_constants_count; layout_info.pPushConstantRanges = push_constants; err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout); check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, &bd->Pipeline, v->Subpass); - return true; } +void ImGui_ImplVulkan_ReCreateMainPipeline(ImGui_ImplVulkan_MainPipelineCreateInfo const& info) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (bd->Pipeline) + { + vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); + bd->Pipeline = VK_NULL_HANDLE; + } + v->RenderPass = info.RenderPass; + v->MSAASamples = info.MSAASamples; + v->Subpass = info.Subpass; + + v->ColorCorrectionMethod = info.ColorCorrectionMethod; + if(v->UseStaticColorCorrectionsParams && info.ColorCorrectionParams) + { + v->ColorCorrectionParams = *info.ColorCorrectionParams; + } + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (info.pDynamicRendering) + { + v->PipelineRenderingCreateInfo = *info.pDynamicRendering; + } +#else + IM_ASSERT(info.pDynamicRendering == nullptr); +#endif + + ImGui_ImplVulkan_PipelineCreateInfo pci; + pci.Device = v->Device; + pci.Allocator = v->Allocator; + pci.PipelineCache = v->PipelineCache; + pci.RenderPass = v->RenderPass; + pci.Subpass = v->Subpass; + pci.MSAASamples = v->MSAASamples; + pci.pRenderingInfo = info.pDynamicRendering; + + pci.ColorCorrectionMethod = v->ColorCorrectionMethod; + if (v->UseStaticColorCorrectionsParams) + { + pci.ColorCorrectionParams = &v->ColorCorrectionParams; + } + + bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(pci); +} + +void ImGui_ImplVulkan_SetMainColorCorrectionParams(const ImGui_ImplVulkan_ColorCorrectionParameters& params) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + v->ColorCorrectionParams = params; +} + void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); @@ -1073,7 +1377,7 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } - if (bd->PipelineForViewports) { vkDestroyPipeline(v->Device, bd->PipelineForViewports, v->Allocator); bd->PipelineForViewports = VK_NULL_HANDLE; } + ImGui_ImplVulkan_DestroyAllViewportsRenderPasses(); } bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) @@ -1140,10 +1444,11 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE); IM_ASSERT(info->MinImageCount >= 2); IM_ASSERT(info->ImageCount >= info->MinImageCount); - if (info->UseDynamicRendering == false) - IM_ASSERT(info->RenderPass != VK_NULL_HANDLE); + //if (info->UseDynamicRendering == false) + // IM_ASSERT(info->RenderPass != VK_NULL_HANDLE); - bd->VulkanInitInfo = *info; + ImGui_ImplVulkan_InitInfo * v = &bd->VulkanInitInfo; + *v = *info; ImGui_ImplVulkan_CreateDeviceObjects(); @@ -1152,7 +1457,45 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) main_viewport->RendererUserData = IM_NEW(ImGui_ImplVulkan_ViewportData)(); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { ImGui_ImplVulkan_InitPlatformInterface(); + //ImGui_ImplVulkan_SecondaryViewportInfo vinfo = {}; + ////vinfo.ResetToDefaultIfUnsupported = true; + //ImGui_ImplVulkan_RequestSecondaryViewportsChanges(vinfo); + } + + { + bool create_pipeline = false; + const ImGui_ImplVulkan_PipelineRenderingInfo * p_dynamic_rendering = nullptr; + if (v->RenderPass) + { + create_pipeline = true; + } + else + { + #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (v->UseDynamicRendering && v->PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR) + { + p_dynamic_rendering = &v->PipelineRenderingCreateInfo; + create_pipeline = true; + } + #endif + } + if (create_pipeline) + { + ImGui_ImplVulkan_MainPipelineCreateInfo mp_info = {}; + mp_info.RenderPass = v->RenderPass; + mp_info.Subpass = v->Subpass; + mp_info.MSAASamples = info->MSAASamples; + mp_info.pDynamicRendering = p_dynamic_rendering; + mp_info.ColorCorrectionMethod = v->ColorCorrectionMethod; + if(v->UseStaticColorCorrectionsParams) + { + mp_info.ColorCorrectionParams = &v->ColorCorrectionParams; + } + ImGui_ImplVulkan_ReCreateMainPipeline(mp_info); + } + } return true; } @@ -1261,7 +1604,7 @@ void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulka void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator) { - for (uint32_t n = 0; n < buffers->Count; n++) + for (uint32_t n = 0; n < buffers->Count; n++) ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); IM_FREE(buffers->FrameRenderBuffers); buffers->FrameRenderBuffers = nullptr; @@ -1269,6 +1612,302 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk buffers->Count = 0; } +VkSurfaceFormatKHR ImGui_ImplVulkan_GetViewportOptimalSurfaceFormat(const ImGui_ImplVulkanH_Window* wd) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkSurfaceFormatKHR surface_format = wd->SurfaceFormat; + if ((wd->SurfaceFormat.format == VK_FORMAT_UNDEFINED) || (wd->SurfaceFormat != bd->DesiredViewportsSurfaceFormat)) + { + const VkFormat candidate_formats[] = { bd->DesiredViewportsSurfaceFormat.format, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + size_t first_candidate_format = 0; + VkColorSpaceKHR candidate_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + if (candidate_formats[0] == VK_FORMAT_UNDEFINED) + { + first_candidate_format = 1; + } + else + { + candidate_color_space = bd->DesiredViewportsSurfaceFormat.colorSpace; + } + surface_format = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, wd->Surface, candidate_formats + first_candidate_format, static_cast(IM_ARRAYSIZE(candidate_formats) - first_candidate_format), candidate_color_space); + } + return surface_format; +} + +VkPresentModeKHR ImGui_ImplVulkan_GetViewportOptimalPresentMode(const ImGui_ImplVulkanH_Window* wd) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkPresentModeKHR present_mode = wd->PresentMode; + if ((wd->PresentMode == (VkPresentModeKHR)~0) || (wd->PresentMode != bd->DesiredViewportsPresentMode)) + { + const VkPresentModeKHR present_modes[] = { bd->DesiredViewportsPresentMode, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; + size_t first_present_mode = 0; + if (bd->DesiredViewportsPresentMode == (VkPresentModeKHR)~0) + { + first_present_mode = 1; + } + present_mode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, present_modes + first_present_mode, static_cast(IM_ARRAYSIZE(present_modes) - first_present_mode)); + } + return present_mode; +} + +void ImGui_ImplVulkan_RequestSecondaryViewportsChanges(ImGui_ImplVulkan_SecondaryViewportInfo const& info) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + const VkDevice device = v->Device; + + bool new_format = false; + bool new_present_mode = false; + bool new_color_correction_method = false; + bool new_color_correction_params = false; + + const VkSurfaceFormatKHR old_common_format = bd->DesiredViewportsSurfaceFormat; + if (info.SurfaceFormat.format != VK_FORMAT_UNDEFINED) + { + bd->DesiredViewportsSurfaceFormat = info.SurfaceFormat; + new_format = true; + } + + if (info.PresentMode) + { + bd->DesiredViewportsPresentMode = *info.PresentMode; + new_present_mode = true; + } + + const ImGui_ImplVulkan_ColorCorrectionMethod old_common_color_correction_method = bd->ViewportsColorCorrectionMethod; + if (bd->ViewportsColorCorrectionMethod != info.ColorCorrectionMethod) + { + bd->ViewportsColorCorrectionMethod = info.ColorCorrectionMethod; + new_color_correction_method = true; + } + + if (info.ColorCorrectionParams) + { + bd->ViewportsColorCorrectionParams = *info.ColorCorrectionParams; + if (v->UseStaticColorCorrectionsParams) + { + new_color_correction_params = true; + } + } + + const bool change_any = new_format || new_present_mode || new_color_correction_method || new_color_correction_params; + if (change_any) + { + bool synched = false; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 0; i < platform_io.Viewports.size(); ++i) + { + ImGuiViewport * viewport = platform_io.Viewports[i]; + ImGui_ImplVulkan_ViewportData * vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + if (vd->WindowOwned) + { + ImGui_ImplVulkanH_Window * wd = &vd->Window; + + const ImGui_ImplVulkan_ColorCorrectionMethod old_vp_color_correction_method = (wd->SurfaceFormat == old_common_format) ? old_common_color_correction_method : ImGui_ImplVulkan_ColorCorrection_None; + + bool changed_format = false; + if (new_format || new_present_mode) + { + bool recreate_swapchain = false; + VkSurfaceFormatKHR format = wd->SurfaceFormat; + if (new_format) + { + format = ImGui_ImplVulkan_GetViewportOptimalSurfaceFormat(wd); + changed_format = (format != wd->SurfaceFormat); + recreate_swapchain |= changed_format; + } + VkPresentModeKHR present_mode = wd->PresentMode; + if (new_present_mode) + { + present_mode = ImGui_ImplVulkan_GetViewportOptimalPresentMode(wd); + recreate_swapchain |= (present_mode != wd->PresentMode); + wd->PresentMode = present_mode; + } + + if (changed_format) + { + wd->SurfaceFormat = format; + ImGui_ImplVulkan_RenderPassKey key = wd; + ImGui_ImplVulkanH_GetOrCreateViewportsRenderPass(wd, device, v->Allocator); + } + + if (recreate_swapchain) + { + if (!synched) {vkDeviceWaitIdle(device); synched = true;} + ImGui_ImplVulkanH_CreateWindowSwapChain(v->PhysicalDevice, device, wd, v->Allocator, wd->Width, wd->Height, v->MinImageCount, v->QueueFamily); + vd->SwapChainNeedRebuild = false; + ImGui_ImplVulkanH_CreateWindowFramebuffers(wd, device, v->Allocator); + } + } + + const ImGui_ImplVulkan_ColorCorrectionMethod new_vp_color_correction_method = (wd->SurfaceFormat == bd->DesiredViewportsSurfaceFormat) ? bd->ViewportsColorCorrectionMethod : ImGui_ImplVulkan_ColorCorrection_None; + const bool recreate_pipeline = (new_vp_color_correction_method != old_vp_color_correction_method) || (changed_format) || ((new_vp_color_correction_method != ImGui_ImplVulkan_ColorCorrection_None) && new_color_correction_params); + if (recreate_pipeline) + { + if (!synched) { vkDeviceWaitIdle(device); synched = true; } + ImGui_ImplVulkanH_CreateViewportPipeline(wd, device, v->Allocator); + } + } + } + } +} + +void ImGui_ImplVulkanH_GetOrCreateViewportsRenderPass(ImGui_ImplVulkanH_Window* wd, VkDevice device, const VkAllocationCallbacks* allocator) +{ + if (wd->UseDynamicRendering) + { + wd->RenderPass = VK_NULL_HANDLE; + return; + } + if (wd->OwnsRenderPass) + { + vkDestroyRenderPass(device, wd->RenderPass, allocator); + wd->RenderPass = VK_NULL_HANDLE; + } + ImGui_ImplVulkan_RenderPassKey key = wd; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + VkRenderPass _res = VK_NULL_HANDLE; + VkRenderPass * res = &_res; + if (bd) + { + int found = -1; + for (int i = 0; i < bd->CachedRenderPassesForViewports.size(); ++i) + { + ImGui_ImplVulkan_Data::CachedRenderPass & c = bd->CachedRenderPassesForViewports[i]; + if (c.Key == key) + { + found = i; + break; + } + } + + if (found == -1) + { + ImGui_ImplVulkan_Data::CachedRenderPass entry; + entry.Key = key; + entry.RenderPass = VK_NULL_HANDLE; + bd->CachedRenderPassesForViewports.push_back(entry); + found = bd->CachedRenderPassesForViewports.size() - 1; + } + res = &bd->CachedRenderPassesForViewports[found].RenderPass; + } + wd->OwnsRenderPass = !bd; + + if(*res == VK_NULL_HANDLE) + { + VkAttachmentDescription attachment = {}; + attachment.format = key.Format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = key.Clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + info.dependencyCount = 1; + info.pDependencies = &dependency; + VkResult err = vkCreateRenderPass(device, &info, allocator, res); + check_vk_result(err); + } + wd->RenderPass = *res; +} + +void ImGui_ImplVulkanH_CreateViewportPipeline(ImGui_ImplVulkanH_Window* wd, VkDevice device, const VkAllocationCallbacks* allocator) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + + VkPipeline & target = wd->Pipeline; + VkRenderPass & render_pass = wd->RenderPass; + VkPipelineCache cache = VK_NULL_HANDLE; + + if (target) + { + vkDestroyPipeline(device, target, allocator); + target = VK_NULL_HANDLE; + } + + if (!wd->UseDynamicRendering && render_pass == VK_NULL_HANDLE) + { + ImGui_ImplVulkanH_GetOrCreateViewportsRenderPass(wd, device, allocator); + } + + if (bd) + { + cache = bd->PipelineCacheForViewports; + } + + ImGui_ImplVulkan_PipelineCreateInfo pci = {}; + pci.Device = device; + pci.Allocator = allocator; + pci.PipelineCache = cache; + pci.RenderPass = render_pass; + pci.Subpass = 0; + pci.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + pci.pRenderingInfo = nullptr; + pci.ColorCorrectionMethod = bd ? bd->ViewportsColorCorrectionMethod : ImGui_ImplVulkan_ColorCorrection_None; + if (bd && wd->UseStaticColorCorrectionParams) + { + pci.ColorCorrectionParams = &bd->ViewportsColorCorrectionParams; + } + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR rendering_info = {}; + if (wd->UseDynamicRendering) + { + rendering_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + rendering_info.pNext = nullptr; + rendering_info.viewMask = 0; + rendering_info.colorAttachmentCount = 1; + rendering_info.pColorAttachmentFormats = &wd->SurfaceFormat.format; + rendering_info.depthAttachmentFormat = VK_FORMAT_UNDEFINED; + rendering_info.stencilAttachmentFormat = VK_FORMAT_UNDEFINED; + pci.RenderPass = VK_NULL_HANDLE; + pci.pRenderingInfo = &rendering_info; + } +#endif + + target = ImGui_ImplVulkan_CreatePipeline(pci); +} + +void ImGui_ImplVulkan_DestroyAllViewportsRenderPasses() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + for (int i = 0; i < bd->CachedRenderPassesForViewports.size(); ++i) + { + if (bd->CachedRenderPassesForViewports[i].RenderPass) + { + vkDestroyRenderPass(v->Device, bd->CachedRenderPassesForViewports[i].RenderPass, v->Allocator); + bd->CachedRenderPassesForViewports[i].RenderPass = VK_NULL_HANDLE; + } + } + bd->CachedRenderPassesForViewports.clear(); +} + + //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers // (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) @@ -1415,28 +2054,46 @@ int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_m return 1; } +void ImGui_ImplVulkanH_CreateWindowFramebuffers(ImGui_ImplVulkanH_Window* wd, VkDevice device, const VkAllocationCallbacks* allocator) +{ + if (!wd->UseDynamicRendering) + { + IM_ASSERT(!!(wd->RenderPass)); + VkImageView attachment[1]; + VkFramebufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.renderPass = wd->RenderPass; + info.attachmentCount = 1; + info.pAttachments = attachment; + info.width = wd->Width; + info.height = wd->Height; + info.layers = 1; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + if (fd->Framebuffer) + { + vkDestroyFramebuffer(device, fd->Framebuffer, allocator); + fd->Framebuffer = VK_NULL_HANDLE; + } + attachment[0] = fd->BackbufferView; + VkResult err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer); + check_vk_result(err); + } + } +} + // Also destroy old swap chain and in-flight frames data, if any. -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, uint32_t queue_family) { VkResult err; - VkSwapchainKHR old_swapchain = wd->Swapchain; + const VkSwapchainKHR old_swapchain = wd->Swapchain; wd->Swapchain = VK_NULL_HANDLE; err = vkDeviceWaitIdle(device); check_vk_result(err); - // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one. - // Destroy old Framebuffer - for (uint32_t i = 0; i < wd->ImageCount; i++) - ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); - for (uint32_t i = 0; i < wd->SemaphoreCount; i++) - ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; - wd->ImageCount = 0; - if (wd->RenderPass) - vkDestroyRenderPass(device, wd->RenderPass, allocator); + const uint32_t old_image_count = wd->ImageCount; + const uint32_t old_semaphore_count = wd->SemaphoreCount; // If min image count was not specified, request different count of images dependent on selected present mode if (min_image_count == 0) @@ -1486,58 +2143,36 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); check_vk_result(err); - IM_ASSERT(wd->Frames == nullptr && wd->FrameSemaphores == nullptr); - wd->SemaphoreCount = wd->ImageCount + 1; - wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); - wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount); - memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); - memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount); - for (uint32_t i = 0; i < wd->ImageCount; i++) - wd->Frames[i].Backbuffer = backbuffers[i]; - } - if (old_swapchain) - vkDestroySwapchainKHR(device, old_swapchain, allocator); + // Only recreate the frames if necessary + if (wd->ImageCount != old_image_count) + { + if (old_image_count) + { + for (uint32_t i = 0; i < old_image_count; i++) + ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); + for (uint32_t i = 0; i < old_semaphore_count; i++) + ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + IM_FREE(wd->Frames); + IM_FREE(wd->FrameSemaphores); + wd->Frames = nullptr; + wd->FrameSemaphores = nullptr; + } + else + { + IM_ASSERT(wd->Frames == nullptr); + IM_ASSERT(wd->FrameSemaphores == nullptr); + } - // Create the Render Pass - if (wd->UseDynamicRendering == false) - { - VkAttachmentDescription attachment = {}; - attachment.format = wd->SurfaceFormat.format; - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_attachment = {}; - color_attachment.attachment = 0; - color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment; - VkSubpassDependency dependency = {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - VkRenderPassCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - info.attachmentCount = 1; - info.pAttachments = &attachment; - info.subpassCount = 1; - info.pSubpasses = &subpass; - info.dependencyCount = 1; - info.pDependencies = &dependency; - err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); - check_vk_result(err); + wd->SemaphoreCount = wd->ImageCount + 1; + wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); + wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount); + memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); + memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount); - // We do not create a pipeline by default as this is also used by examples' main.cpp, - // but secondary viewport in multi-viewport mode may want to create one with: - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, v->Subpass); + ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); + } + for (uint32_t i = 0; i < wd->ImageCount; i++) + wd->Frames[i].Backbuffer = backbuffers[i]; } // Create The Image Views @@ -1555,42 +2190,38 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V for (uint32_t i = 0; i < wd->ImageCount; i++) { ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + if (fd->BackbufferView) + { + if (fd->Framebuffer) + { + vkDestroyFramebuffer(device, fd->Framebuffer, allocator); + fd->Framebuffer = VK_NULL_HANDLE; + } + vkDestroyImageView(device, fd->BackbufferView, allocator); + fd->BackbufferView = VK_NULL_HANDLE; + } info.image = fd->Backbuffer; err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView); check_vk_result(err); } } - // Create Framebuffer - if (wd->UseDynamicRendering == false) - { - VkImageView attachment[1]; - VkFramebufferCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - info.renderPass = wd->RenderPass; - info.attachmentCount = 1; - info.pAttachments = attachment; - info.width = wd->Width; - info.height = wd->Height; - info.layers = 1; - for (uint32_t i = 0; i < wd->ImageCount; i++) - { - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; - attachment[0] = fd->BackbufferView; - err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer); - check_vk_result(err); - } - } + if (old_swapchain) + vkDestroySwapchainKHR(device, old_swapchain, allocator); } // Create or resize window -void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) +void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count, bool create_pipeline) { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); (void)instance; - ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, g_VulkanInitInfo.Subpass); - ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); + ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count, queue_family); + ImGui_ImplVulkanH_GetOrCreateViewportsRenderPass(wd, device, allocator); + ImGui_ImplVulkanH_CreateWindowFramebuffers(wd, device, allocator); + if (create_pipeline) + { + ImGui_ImplVulkanH_CreateViewportPipeline(wd, device, allocator); + } } void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) @@ -1602,11 +2233,20 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + if (wd->Pipeline) + { + vkDestroyPipeline(device, wd->Pipeline, allocator); + wd->Pipeline = VK_NULL_HANDLE; + } + if (wd->OwnsRenderPass && wd->RenderPass) + { + vkDestroyRenderPass(device, wd->RenderPass, allocator); + wd->RenderPass = nullptr; + } IM_FREE(wd->Frames); IM_FREE(wd->FrameSemaphores); wd->Frames = nullptr; wd->FrameSemaphores = nullptr; - vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); @@ -1622,8 +2262,13 @@ void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd fd->CommandBuffer = VK_NULL_HANDLE; fd->CommandPool = VK_NULL_HANDLE; + if (fd->Framebuffer) + { + vkDestroyFramebuffer(device, fd->Framebuffer, allocator); + fd->Framebuffer = VK_NULL_HANDLE; + } vkDestroyImageView(device, fd->BackbufferView, allocator); - vkDestroyFramebuffer(device, fd->Framebuffer, allocator); + fd->BackbufferView = VK_NULL_HANDLE; } void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator) @@ -1660,6 +2305,10 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) VkResult err = (VkResult)platform_io.Platform_CreateVkSurface(viewport, (ImU64)v->Instance, (const void*)v->Allocator, (ImU64*)&wd->Surface); check_vk_result(err); + wd->UseDynamicRendering = v->UseDynamicRendering; + wd->RenderPassClear = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; + wd->UseStaticColorCorrectionParams = v->UseStaticColorCorrectionsParams; + // Check for WSI support VkBool32 res; vkGetPhysicalDeviceSurfaceSupportKHR(v->PhysicalDevice, v->QueueFamily, wd->Surface, &res); @@ -1669,34 +2318,17 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) return; } - // Select Surface Format - ImVector requestSurfaceImageFormats; -#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - for (uint32_t n = 0; n < v->PipelineRenderingCreateInfo.colorAttachmentCount; n++) - requestSurfaceImageFormats.push_back(v->PipelineRenderingCreateInfo.pColorAttachmentFormats[n]); -#endif - const VkFormat defaultFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - for (VkFormat format : defaultFormats) - requestSurfaceImageFormats.push_back(format); - - const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, wd->Surface, requestSurfaceImageFormats.Data, (size_t)requestSurfaceImageFormats.Size, requestSurfaceColorSpace); - + wd->SurfaceFormat = ImGui_ImplVulkan_GetViewportOptimalSurfaceFormat(wd); + // Select Present Mode // FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1) - VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + wd->PresentMode = ImGui_ImplVulkan_GetViewportOptimalPresentMode(wd); //printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode); - // Create SwapChain, RenderPass, Framebuffer, etc. - wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; - wd->UseDynamicRendering = v->UseDynamicRendering; + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); vd->WindowOwned = true; - - // Create pipeline (shared by all secondary viewports) - if (bd->PipelineForViewports == VK_NULL_HANDLE) - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &bd->PipelineForViewports, 0); + } static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) @@ -1721,7 +2353,6 @@ static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) if (vd == nullptr) // This is nullptr for the main viewport (which is left to the user/app to handle) return; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - vd->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); } @@ -1739,6 +2370,14 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) vd->SwapChainNeedRebuild = false; } + { + const bool clear_flag = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; + if (clear_flag != wd->RenderPassClear) + { + printf("[vulkan] Viewport clear flag does not match with viewport RenderPass clear\n"); + } + } + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; { @@ -1774,7 +2413,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); } #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - if (v->UseDynamicRendering) + if (wd->UseDynamicRendering) { // Transition swapchain image to a layout suitable for drawing. VkImageMemoryBarrier barrier = {}; @@ -1793,7 +2432,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) attachmentInfo.imageView = fd->BackbufferView; attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE; - attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentInfo.loadOp = wd->RenderPassClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachmentInfo.clearValue = wd->ClearValue; @@ -1823,7 +2462,9 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) } } - ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, bd->PipelineForViewports); + const ImGui_ImplVulkan_ColorCorrectionParameters * color_correction_params = &bd->ViewportsColorCorrectionParams; + + ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, wd->Pipeline, color_correction_params); { #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING @@ -1922,6 +2563,8 @@ void ImGui_ImplVulkan_InitPlatformInterface() void ImGui_ImplVulkan_ShutdownPlatformInterface() { ImGui::DestroyPlatformWindows(); + + ImGui_ImplVulkan_DestroyAllViewportsRenderPasses(); } //----------------------------------------------------------------------------- diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 22df982cf847..a30ffe92be3c 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -64,6 +64,38 @@ #define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #endif +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +typedef VkPipelineRenderingCreateInfoKHR ImGui_ImplVulkan_PipelineRenderingInfo; +#else +typedef void ImGui_ImplVulkan_PipelineRenderingInfo; +#endif + +enum ImGui_ImplVulkan_ColorCorrectionMethod : uint32_t +{ + ImGui_ImplVulkan_ColorCorrection_None = 0, // Pass Through, no color correction + ImGui_ImplVulkan_ColorCorrection_Gamma = 1, // RGB gamma correction + ImGui_ImplVulkan_ColorCorrection_GammaAlpha = 2, // RGB Gamma correction + Alpha Gamma correction (with separate gamma factor) +}; +struct ImGui_ImplVulkan_ColorCorrectionParameters +{ + // Gamma, GammaAlpha: gamma exponent + float param1; + // Gamma, GammaAlpha: exposure multiplier + float param2; + // GammaAlpha: alpha channel gamma + float param3; + float param4; + + static inline ImGui_ImplVulkan_ColorCorrectionParameters MakeGamma(float gamma, float exposure = 1.0f, float alpha_gamma = 1.0f) + { + ImGui_ImplVulkan_ColorCorrectionParameters res = {}; + res.param1 = gamma; + res.param2 = exposure; + res.param3 = alpha_gamma; + return res; + } +}; + // Initialization data, for ImGui_ImplVulkan_Init() // - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, // and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor. @@ -81,11 +113,13 @@ struct ImGui_ImplVulkan_InitInfo uint32_t MinImageCount; // >= 2 uint32_t ImageCount; // >= MinImageCount VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT - + ImGui_ImplVulkan_ColorCorrectionMethod ColorCorrectionMethod; + ImGui_ImplVulkan_ColorCorrectionParameters ColorCorrectionParams; // (Optional) VkPipelineCache PipelineCache; uint32_t Subpass; + bool UseStaticColorCorrectionsParams; // (Optional) Dynamic Rendering // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3. bool UseDynamicRendering; @@ -100,10 +134,21 @@ struct ImGui_ImplVulkan_InitInfo }; // Follow "Getting Started" link and check examples/ folder to learn about using backends! -IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); +IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); // The main pipeline will be created if possible (RenderPass xor (UseDynamicRendering && PipelineRenderingCreateInfo->sType is correct)) +struct ImGui_ImplVulkan_MainPipelineCreateInfo +{ + VkRenderPass RenderPass = VK_NULL_HANDLE; + uint32_t Subpass = 0; + VkSampleCountFlagBits MSAASamples = {}; + const ImGui_ImplVulkan_PipelineRenderingInfo * pDynamicRendering = nullptr; + ImGui_ImplVulkan_ColorCorrectionMethod ColorCorrectionMethod = {}; + const ImGui_ImplVulkan_ColorCorrectionParameters * ColorCorrectionParams = nullptr; +}; +IMGUI_IMPL_API void ImGui_ImplVulkan_ReCreateMainPipeline(ImGui_ImplVulkan_MainPipelineCreateInfo const& info); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetMainColorCorrectionParams(const ImGui_ImplVulkan_ColorCorrectionParameters& params); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE, const ImGui_ImplVulkan_ColorCorrectionParameters * color_correction_params = nullptr); IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(); IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture(); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) @@ -118,6 +163,19 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet de // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); +#define IMGUI_IMPL_VULKAN_SECONDARY_VIEWPORTS_CONTROL 1 + +struct ImGui_ImplVulkan_SecondaryViewportInfo +{ + VkSurfaceFormatKHR SurfaceFormat = {}; + const VkPresentModeKHR * PresentMode = nullptr; // Acts as an optional + ImGui_ImplVulkan_ColorCorrectionMethod ColorCorrectionMethod = {}; // Only applied if SurfaceFormat + const ImGui_ImplVulkan_ColorCorrectionParameters* ColorCorrectionParams = nullptr; +}; + +// Note: If the changes are accepted, they will take effect during the call to ImGui_ImplVulkan_RenderWindow(...) (when rendering secondary viewports) +IMGUI_IMPL_API void ImGui_ImplVulkan_RequestSecondaryViewportsChanges(ImGui_ImplVulkan_SecondaryViewportInfo const& info); + //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers // (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) @@ -138,11 +196,14 @@ struct ImGui_ImplVulkanH_Frame; struct ImGui_ImplVulkanH_Window; // Helpers -IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +// To create the pipeline, ImGui_ImplVulkan_Init(...) must have been called before! +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, bool create_pipeline = true); IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator); IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); +// Create the pipeline of wd +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateViewportPipeline(ImGui_ImplVulkanH_Window* wd, VkDevice device, const VkAllocationCallbacks* allocator); // Helper structure to hold the data needed by one rendering frame // (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) @@ -152,6 +213,7 @@ struct ImGui_ImplVulkanH_Frame VkCommandPool CommandPool; VkCommandBuffer CommandBuffer; VkFence Fence; + VkImage Backbuffer; VkImageView BackbufferView; VkFramebuffer Framebuffer; @@ -173,9 +235,8 @@ struct ImGui_ImplVulkanH_Window VkSurfaceKHR Surface; VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; - VkRenderPass RenderPass; - bool UseDynamicRendering; - bool ClearEnable; + VkRenderPass RenderPass; // Ownedship determined by OwnsRenderPass + VkPipeline Pipeline; // Owned by this VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) @@ -184,11 +245,15 @@ struct ImGui_ImplVulkanH_Window ImGui_ImplVulkanH_Frame* Frames; ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores; + bool RenderPassClear; + bool UseDynamicRendering; + bool UseStaticColorCorrectionParams; + bool OwnsRenderPass; + ImGui_ImplVulkanH_Window() { memset((void*)this, 0, sizeof(*this)); PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. - ClearEnable = true; } }; diff --git a/backends/vulkan/generate_spv.sh b/backends/vulkan/generate_spv.sh index 948ef773d6e1..8ee79ed2fc2b 100755 --- a/backends/vulkan/generate_spv.sh +++ b/backends/vulkan/generate_spv.sh @@ -2,5 +2,6 @@ ## -V: create SPIR-V binary ## -x: save binary output as text-based 32-bit hexadecimal numbers ## -o: output file -glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag +glslangValidator -V -x -DUSE_SPEC_CONSTANT_PARAMS=1 -o glsl_shader_static.frag.u32 glsl_shader.frag +glslangValidator -V -x -DUSE_SPEC_CONSTANT_PARAMS=0 -o glsl_shader_dynamic.frag.u32 glsl_shader.frag glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert diff --git a/backends/vulkan/glsl_shader.frag b/backends/vulkan/glsl_shader.frag index ce7e6f72b25d..2ff1ba665de7 100644 --- a/backends/vulkan/glsl_shader.frag +++ b/backends/vulkan/glsl_shader.frag @@ -1,4 +1,9 @@ #version 450 core + +#ifndef USE_SPEC_CONSTANT_PARAMS +#define USE_SPEC_CONSTANT_PARAMS 0 +#endif + layout(location = 0) out vec4 fColor; layout(set=0, binding=0) uniform sampler2D sTexture; @@ -8,7 +13,43 @@ layout(location = 0) in struct { vec2 UV; } In; +layout(constant_id = 0) const int color_correction_method = 0; +#if USE_SPEC_CONSTANT_PARAMS +layout(constant_id = 1) const float color_correction_param1 = 1.0f; +layout(constant_id = 2) const float color_correction_param2 = 1.0f; +layout(constant_id = 3) const float color_correction_param3 = 1.0f; +layout(constant_id = 4) const float color_correction_param4 = 1.0f; +#else +layout(push_constant) uniform uPushConstant { + layout(offset = 16 + 4 * 0) float color_correction_param1; + layout(offset = 16 + 4 * 1) float color_correction_param2; + layout(offset = 16 + 4 * 2) float color_correction_param3; + layout(offset = 16 + 4 * 3) float color_correction_param4; +}; +#endif + +vec4 ApplyColorCorrection(vec4 src) +{ + vec4 res = src; + if(color_correction_method == 1 || color_correction_method == 2) + { + const float gamma = color_correction_param1; + const float exposure = color_correction_param2; + res.rgb = exposure * pow(src.rgb, gamma.xxx); + if(color_correction_method == 2) + { + const float alpha_gamma = color_correction_param3; + res.a = pow(src.a, alpha_gamma); + } + } + return res; +} + void main() { fColor = In.Color * texture(sTexture, In.UV.st); + if(color_correction_method != 0) + { + fColor = ApplyColorCorrection(fColor); + } } diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 2e63bbceb54c..60b35eb5fb61 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -252,6 +252,7 @@ static void SetupVulkan(ImVector instance_extensions) static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { wd->Surface = surface; + wd->RenderPassClear = true; // Check for WSI support VkBool32 res; @@ -278,7 +279,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, false); } static void CleanupVulkan() @@ -465,6 +466,8 @@ int main(int, char**) init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkanH_CreateViewportPipeline(wd, g_Device, g_Allocator); + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index bc93057f4f1d..142f8f13397f 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -240,6 +240,7 @@ static void SetupVulkan(ImVector instance_extensions) static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { wd->Surface = surface; + wd->RenderPassClear = true; // Check for WSI support VkBool32 res; @@ -266,7 +267,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, false); } static void CleanupVulkan() @@ -465,6 +466,8 @@ int main(int, char**) init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkanH_CreateViewportPipeline(wd, g_Device, g_Allocator); + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp new file mode 100644 index 000000000000..aa5dcec2e4cc --- /dev/null +++ b/examples/example_sdl3_vulkan/main.cpp @@ -0,0 +1,616 @@ +// Dear ImGui: standalone example application for SDL2 + Vulkan + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_vulkan.h" +#include // printf, fprintf +#include // abort +#include +#include + +// Volk headers +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK +#define VOLK_IMPLEMENTATION +#include +#endif + +//#define APP_USE_UNLIMITED_FRAME_RATE +#ifdef _DEBUG +#define APP_USE_VULKAN_DEBUG_REPORT +#endif + +#ifdef SDL_ENABLE_OLD_NAMES +#undef SDL_ENABLE_OLD_NAMES +#endif + +// Data +static VkAllocationCallbacks* g_Allocator = nullptr; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static ImGui_ImplVulkanH_Window g_MainWindowData; +static uint32_t g_MinImageCount = 2; +static bool g_SwapChainRebuild = false; + +static void check_vk_result(VkResult err) +{ + if (err == 0) + return; + fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); + if (err < 0) + abort(); +} + +#ifdef APP_USE_VULKAN_DEBUG_REPORT +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) +{ + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments + fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + return VK_FALSE; +} +#endif // APP_USE_VULKAN_DEBUG_REPORT + +static bool IsExtensionAvailable(const ImVector& properties, const char* extension) +{ + for (const VkExtensionProperties& p : properties) + if (strcmp(p.extensionName, extension) == 0) + return true; + return false; +} + +static VkPhysicalDevice SetupVulkan_SelectPhysicalDevice() +{ + uint32_t gpu_count; + VkResult err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, nullptr); + check_vk_result(err); + IM_ASSERT(gpu_count > 0); + + ImVector gpus; + gpus.resize(gpu_count); + err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus.Data); + check_vk_result(err); + + // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers + // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple + // dedicated GPUs) is out of scope of this sample. + for (VkPhysicalDevice& device : gpus) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(device, &properties); + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return device; + } + + // Use first GPU (Integrated) is a Discrete one is not available. + if (gpu_count > 0) + return gpus[0]; + return VK_NULL_HANDLE; +} + +static void SetupVulkan(ImVector instance_extensions) +{ + VkResult err; +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkInitialize(); +#endif + + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + // Enumerate available extensions + uint32_t properties_count; + ImVector properties; + vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr); + properties.resize(properties_count); + err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.Data); + check_vk_result(err); + + // Enable required extensions + if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) + { + instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } +#endif + + // Enabling validation layers +#ifdef APP_USE_VULKAN_DEBUG_REPORT + const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + instance_extensions.push_back("VK_EXT_debug_report"); +#endif + + // Create Vulkan Instance + create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size; + create_info.ppEnabledExtensionNames = instance_extensions.Data; + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkLoadInstance(g_Instance); +#endif + + // Setup the debug report callback +#ifdef APP_USE_VULKAN_DEBUG_REPORT + auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr); + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = nullptr; + err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); +#endif + } + + // Select Physical Device (GPU) + g_PhysicalDevice = SetupVulkan_SelectPhysicalDevice(); + + // Select graphics queue family + { + uint32_t count; + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, nullptr); + VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); + for (uint32_t i = 0; i < count; i++) + if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + g_QueueFamily = i; + break; + } + free(queues); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); + } + + // Create Logical Device (with 1 queue) + { + ImVector device_extensions; + device_extensions.push_back("VK_KHR_swapchain"); + + // Enumerate physical device extension + uint32_t properties_count; + ImVector properties; + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, nullptr); + properties.resize(properties_count); + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, properties.Data); +#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) + device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + + const float queue_priority[] = { 1.0f }; + VkDeviceQueueCreateInfo queue_info[1] = {}; + queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[0].queueFamilyIndex = g_QueueFamily; + queue_info[0].queueCount = 1; + queue_info[0].pQueuePriorities = queue_priority; + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); + create_info.pQueueCreateInfos = queue_info; + create_info.enabledExtensionCount = (uint32_t)device_extensions.Size; + create_info.ppEnabledExtensionNames = device_extensions.Data; + err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); + check_vk_result(err); + vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); + } + + // Create Descriptor Pool + // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) + // If you wish to load e.g. additional textures you may need to alter pools sizes. + { + VkDescriptorPoolSize pool_sizes[] = + { + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }, + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 1; + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + check_vk_result(err); + } +} + +// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. +// Your real engine/app may not use them. +static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) +{ + wd->Surface = surface; + wd->RenderPassClear = true; + + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Select Surface Format + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + + // Select Present Mode +#ifdef APP_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; +#else + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; +#endif + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); + + // Create SwapChain, RenderPass, Framebuffer, etc. + IM_ASSERT(g_MinImageCount >= 2); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, false); +} + +static void CleanupVulkan() +{ + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); + +#ifdef APP_USE_VULKAN_DEBUG_REPORT + // Remove the debug report callback + auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); + f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); +#endif // APP_USE_VULKAN_DEBUG_REPORT + + vkDestroyDevice(g_Device, g_Allocator); + vkDestroyInstance(g_Instance, g_Allocator); +} + +static void CleanupVulkanWindow() +{ + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); +} + +static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) +{ + VkResult err; + + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + { + g_SwapChainRebuild = true; + return; + } + check_vk_result(err); + + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + { + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking + check_vk_result(err); + + err = vkResetFences(g_Device, 1, &fd->Fence); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = fd->Framebuffer; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = 1; + info.pClearValues = &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + + // Record dear imgui primitives into command buffer + ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &image_acquired_semaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &render_complete_semaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); + check_vk_result(err); + } +} + +static void FramePresent(ImGui_ImplVulkanH_Window* wd) +{ + if (g_SwapChainRebuild) + return; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &render_complete_semaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &wd->FrameIndex; + VkResult err = vkQueuePresentKHR(g_Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + { + g_SwapChainRebuild = true; + return; + } + check_vk_result(err); + wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores +} + +// Main code +int main(int, char**) +{ + // Setup SDL + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: %s\n", SDL_GetError()); + return -1; + } + + // From 2.0.18: Enable native IME. +#ifdef SDL_HINT_IME_SHOW_UI + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); +#endif + + // Create window with Vulkan graphics context + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Vulkan example", 1280, 720, window_flags); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } + + + uint32_t extensions_count = 0; + const char* const* extensions = SDL_Vulkan_GetInstanceExtensions(&extensions_count); + ImVector extensions_vec; + extensions_vec.resize(extensions_count); + for (uint32_t i = 0; i < extensions_count; ++i) + { + extensions_vec[i] = extensions[i]; + } + // std::move + SetupVulkan(static_cast&&>(extensions_vec)); + + // Create Window Surface + VkSurfaceKHR surface; + VkResult err; + if (!SDL_Vulkan_CreateSurface(window, g_Instance, g_Allocator, &surface)) + { + printf("Failed to create Vulkan surface.\n"); + return 1; + } + + // Create Framebuffers + int w, h; + SDL_GetWindowSize(window, &w, &h); + ImGui_ImplVulkanH_Window* wd = &g_MainWindowData; + SetupVulkanWindow(wd, surface, w, h); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForVulkan(window); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = g_Instance; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.RenderPass = wd->RenderPass; + init_info.Subpass = 0; + init_info.MinImageCount = g_MinImageCount; + init_info.ImageCount = wd->ImageCount; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = g_Allocator; + init_info.CheckVkResultFn = check_vk_result; + ImGui_ImplVulkan_Init(&init_info); + + ImGui_ImplVulkanH_CreateViewportPipeline(wd, g_Device, g_Allocator); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + if (event.type == SDL_QUIT) + done = true; + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } + + // Resize swap chain? + int fb_width, fb_height; + SDL_GetWindowSize(window, &fb_width, &fb_height); + if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) + { + ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount); + g_MainWindowData.FrameIndex = 0; + g_SwapChainRebuild = false; + } + + // Start the Dear ImGui frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImDrawData* main_draw_data = ImGui::GetDrawData(); + const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + if (!main_is_minimized) + FrameRender(wd, main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + // Present Main Platform Window + if (!main_is_minimized) + FramePresent(wd); + } + + // Cleanup + err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + + CleanupVulkanWindow(); + CleanupVulkan(); + + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +}