Skip to content

Commit 66bea44

Browse files
committed
backend: Support Vulkan
1 parent c16629e commit 66bea44

File tree

7 files changed

+2392
-0
lines changed

7 files changed

+2392
-0
lines changed

build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub const Backend = enum {
44
no_backend,
55
glfw_wgpu,
66
glfw_opengl3,
7+
glfw_vulkan,
78
glfw_dx12,
89
win32_dx12,
910
glfw,
@@ -328,6 +329,19 @@ pub fn build(b: *std.Build) void {
328329
else => {},
329330
}
330331
},
332+
.glfw_vulkan => {
333+
if (b.lazyDependency("zglfw", .{})) |zglfw| {
334+
imgui.addIncludePath(zglfw.path("libs/glfw/include"));
335+
}
336+
337+
imgui.addCSourceFiles(.{
338+
.files = &.{
339+
"libs/imgui/backends/imgui_impl_glfw.cpp",
340+
"libs/imgui/backends/imgui_impl_vulkan.cpp",
341+
},
342+
.flags = &(cflags.* ++ .{ "-DVK_NO_PROTOTYPES", "-DZGUI_DEGAMMA" }),
343+
});
344+
},
331345
.glfw => {
332346
if (b.lazyDependency("zglfw", .{})) |zglfw| {
333347
imgui.addIncludePath(zglfw.path("libs/glfw/include"));

libs/imgui/backends/imgui_impl_vulkan.cpp

Lines changed: 2032 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// dear imgui: Renderer Backend for Vulkan
2+
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
3+
4+
// Implemented features:
5+
// [x] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
6+
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
7+
// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport).
8+
9+
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
10+
// See imgui_impl_vulkan.cpp file for details.
11+
12+
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
13+
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
14+
15+
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
16+
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
17+
// Learn about Dear ImGui:
18+
// - FAQ https://dearimgui.com/faq
19+
// - Getting Started https://dearimgui.com/getting-started
20+
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
21+
// - Introduction, links and more at the top of imgui.cpp
22+
23+
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
24+
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
25+
// You will use those if you want to use this rendering backend in your engine/app.
26+
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
27+
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
28+
// Read comments in imgui_impl_vulkan.h.
29+
30+
#pragma once
31+
#ifndef IMGUI_DISABLE
32+
#include "imgui.h" // IMGUI_IMPL_API
33+
34+
// [Configuration] in order to use a custom Vulkan function loader:
35+
// (1) You'll need to disable default Vulkan function prototypes.
36+
// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag.
37+
// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit:
38+
// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file
39+
// - Or as a compilation flag in your build system
40+
// - Or uncomment here (not recommended because you'd be modifying imgui sources!)
41+
// - Do not simply add it in a .cpp file!
42+
// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function.
43+
// If you have no idea what this is, leave it alone!
44+
//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
45+
46+
// Convenience support for Volk
47+
// (you can also technically use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
48+
//#define IMGUI_IMPL_VULKAN_USE_VOLK
49+
50+
#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES)
51+
#define VK_NO_PROTOTYPES
52+
#endif
53+
#if defined(VK_USE_PLATFORM_WIN32_KHR) && !defined(NOMINMAX)
54+
#define NOMINMAX
55+
#endif
56+
57+
// Vulkan includes
58+
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
59+
#include <volk.h>
60+
#else
61+
#include <vulkan/vulkan.h>
62+
#endif
63+
#if defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)
64+
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
65+
#endif
66+
67+
// Initialization data, for ImGui_ImplVulkan_Init()
68+
// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
69+
// and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor.
70+
// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
71+
// [Please zero-clear before use!]
72+
struct ImGui_ImplVulkan_InitInfo
73+
{
74+
VkInstance Instance;
75+
VkPhysicalDevice PhysicalDevice;
76+
VkDevice Device;
77+
uint32_t QueueFamily;
78+
VkQueue Queue;
79+
VkDescriptorPool DescriptorPool; // See requirements in note above
80+
VkRenderPass RenderPass; // Ignored if using dynamic rendering
81+
uint32_t MinImageCount; // >= 2
82+
uint32_t ImageCount; // >= MinImageCount
83+
VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT
84+
85+
// (Optional)
86+
VkPipelineCache PipelineCache;
87+
uint32_t Subpass;
88+
89+
// (Optional) Dynamic Rendering
90+
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
91+
bool UseDynamicRendering;
92+
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
93+
VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo;
94+
#endif
95+
96+
// (Optional) Allocation, Debugging
97+
const VkAllocationCallbacks* Allocator;
98+
void (*CheckVkResultFn)(VkResult err);
99+
VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory.
100+
};
101+
102+
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
103+
// FIX(zig-gamedev)
104+
extern "C" {
105+
IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
106+
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
107+
IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame();
108+
IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE);
109+
IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture();
110+
IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture();
111+
IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
112+
113+
// Optional: load Vulkan functions with a custom function loader
114+
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
115+
IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
116+
}
117+
118+
// Register a texture (VkDescriptorSet == ImTextureID)
119+
// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
120+
// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
121+
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
122+
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
123+
124+
//-------------------------------------------------------------------------
125+
// Internal / Miscellaneous Vulkan Helpers
126+
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
127+
//-------------------------------------------------------------------------
128+
// You probably do NOT need to use or care about those functions.
129+
// Those functions only exist because:
130+
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
131+
// 2) the multi-viewport / platform window implementation needs them internally.
132+
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
133+
// but it is too much code to duplicate everywhere so we exceptionally expose them.
134+
//
135+
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
136+
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
137+
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
138+
//-------------------------------------------------------------------------
139+
140+
struct ImGui_ImplVulkanH_Frame;
141+
struct ImGui_ImplVulkanH_Window;
142+
143+
// Helpers
144+
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);
145+
IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator);
146+
IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
147+
IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
148+
IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
149+
150+
// Helper structure to hold the data needed by one rendering frame
151+
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
152+
// [Please zero-clear before use!]
153+
struct ImGui_ImplVulkanH_Frame
154+
{
155+
VkCommandPool CommandPool;
156+
VkCommandBuffer CommandBuffer;
157+
VkFence Fence;
158+
VkImage Backbuffer;
159+
VkImageView BackbufferView;
160+
VkFramebuffer Framebuffer;
161+
};
162+
163+
struct ImGui_ImplVulkanH_FrameSemaphores
164+
{
165+
VkSemaphore ImageAcquiredSemaphore;
166+
VkSemaphore RenderCompleteSemaphore;
167+
};
168+
169+
// Helper structure to hold the data needed by one rendering context into one OS window
170+
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
171+
struct ImGui_ImplVulkanH_Window
172+
{
173+
int Width;
174+
int Height;
175+
VkSwapchainKHR Swapchain;
176+
VkSurfaceKHR Surface;
177+
VkSurfaceFormatKHR SurfaceFormat;
178+
VkPresentModeKHR PresentMode;
179+
VkRenderPass RenderPass;
180+
bool UseDynamicRendering;
181+
bool ClearEnable;
182+
VkClearValue ClearValue;
183+
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)
184+
uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count)
185+
uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR
186+
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
187+
ImGui_ImplVulkanH_Frame* Frames;
188+
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
189+
190+
ImGui_ImplVulkanH_Window()
191+
{
192+
memset((void*)this, 0, sizeof(*this));
193+
PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this.
194+
ClearEnable = true;
195+
}
196+
};
197+
198+
#endif // #ifndef IMGUI_DISABLE

src/backend_glfw.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ pub fn initOpenGL(
1919
}
2020
}
2121

22+
pub fn initVulkan(
23+
window: *const anyopaque, // zglfw.Window
24+
) void {
25+
if (!ImGui_ImplGlfw_InitForVulkan(window, true)) {
26+
unreachable;
27+
}
28+
}
29+
2230
pub fn deinit() void {
2331
ImGui_ImplGlfw_Shutdown();
2432
}
@@ -31,5 +39,6 @@ pub fn newFrame() void {
3139
// (they include few custom changes).
3240
extern fn ImGui_ImplGlfw_InitForOther(window: *const anyopaque, install_callbacks: bool) bool;
3341
extern fn ImGui_ImplGlfw_InitForOpenGL(window: *const anyopaque, install_callbacks: bool) bool;
42+
extern fn ImGui_ImplGlfw_InitForVulkan(window: *const anyopaque, install_callbacks: bool) bool;
3443
extern fn ImGui_ImplGlfw_NewFrame() void;
3544
extern fn ImGui_ImplGlfw_Shutdown() void;

src/backend_glfw_vulkan.zig

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const gui = @import("gui.zig");
2+
const backend_glfw = @import("backend_glfw.zig");
3+
const backend_vulkan = @import("backend_vulkan.zig");
4+
5+
pub const VkHandle = backend_vulkan.VkHandle;
6+
pub const VkPipelineRenderingCreateInfo = backend_vulkan.VkPipelineRenderingCreateInfo;
7+
pub const ImGui_ImplVulkan_InitInfo = backend_vulkan.ImGui_ImplVulkan_InitInfo;
8+
9+
pub fn init(init_info: ImGui_ImplVulkan_InitInfo, window: *const anyopaque) void {
10+
backend_glfw.initVulkan(window);
11+
backend_vulkan.init(init_info);
12+
}
13+
14+
pub fn loadFunctions(
15+
loader: fn (function_name: [*:0]const u8, user_data: ?*anyopaque) callconv(.C) ?*anyopaque,
16+
user_data: ?*anyopaque,
17+
) bool {
18+
return backend_vulkan.loadFunctions(loader, user_data);
19+
}
20+
21+
pub fn deinit() void {
22+
backend_vulkan.deinit();
23+
backend_glfw.deinit();
24+
}
25+
26+
pub fn newFrame(
27+
fb_width: u32,
28+
fb_height: u32,
29+
) void {
30+
backend_glfw.newFrame();
31+
backend_vulkan.newFrame();
32+
33+
gui.io.setDisplaySize(@as(f32, @floatFromInt(fb_width)), @as(f32, @floatFromInt(fb_height)));
34+
gui.io.setDisplayFramebufferScale(1.0, 1.0);
35+
36+
gui.newFrame();
37+
}
38+
39+
pub fn render(
40+
command_buffer: VkHandle,
41+
) void {
42+
gui.render();
43+
backend_vulkan.render(command_buffer);
44+
}
45+
46+
pub fn set_min_image_count(min_image_count: u32) void {
47+
backend_vulkan.set_min_image_count(min_image_count);
48+
}

src/backend_vulkan.zig

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const gui = @import("gui.zig");
2+
3+
pub const VkHandle = usize;
4+
5+
pub const VkPipelineRenderingCreateInfo = extern struct {
6+
s_type: u32 = 0,
7+
p_next: ?*const void = null,
8+
view_mask: u32 = 0,
9+
color_attachment_count: u32 = 0,
10+
p_color_attachment_formats: ?[*]const c_int = null, // VkFormat
11+
depth_attachment_format: c_int = 0, // VkFormat
12+
stencil_attachment_format: c_int = 0, // VkFormat
13+
};
14+
15+
pub const ImGui_ImplVulkan_InitInfo = extern struct {
16+
instance: VkHandle, // VkInstance
17+
physical_device: VkHandle, // VkPhysicalDevice
18+
device: VkHandle, // VkDevice
19+
queue_family: u32,
20+
queue: VkHandle, // VkQueue
21+
descriptor_pool: VkHandle, // VkDescriptorPool
22+
render_pass: VkHandle, // VkRenderPass
23+
min_image_count: u32,
24+
image_count: u32,
25+
msaa_samples: u32 = 0, // vkSampleCountFlags
26+
27+
// Optional fields
28+
pipeline_cache: VkHandle = 0, // VkPipelineCache
29+
subpass: u32 = 0,
30+
31+
use_dynamic_rendering: bool = false,
32+
pipeline_rendering_create_info: VkPipelineRenderingCreateInfo = .{},
33+
34+
allocator: ?*const anyopaque = null,
35+
check_vk_result_fn: ?*const fn (err: u32) void = null,
36+
min_allocation_size: u64 = 0,
37+
};
38+
39+
pub fn init(init_info: ImGui_ImplVulkan_InitInfo) void {
40+
var vk_init: ImGui_ImplVulkan_InitInfo = init_info;
41+
if (!ImGui_ImplVulkan_Init(&vk_init)) {
42+
@panic("failed to init Vulkan for ImGui");
43+
}
44+
ImGui_ImplVulkan_CreateFontsTexture();
45+
}
46+
47+
pub fn loadFunctions(
48+
loader: fn (function_name: [*:0]const u8, user_data: ?*anyopaque) callconv(.C) ?*anyopaque,
49+
user_data: ?*anyopaque,
50+
) bool {
51+
return ImGui_ImplVulkan_LoadFunctions(loader, user_data);
52+
}
53+
54+
pub fn deinit() void {
55+
ImGui_ImplVulkan_DestroyFontsTexture();
56+
ImGui_ImplVulkan_Shutdown();
57+
}
58+
59+
pub fn newFrame() void {
60+
ImGui_ImplVulkan_NewFrame();
61+
}
62+
63+
pub fn render(
64+
command_buffer: VkHandle,
65+
) void {
66+
gui.render();
67+
ImGui_ImplVulkan_RenderDrawData(gui.getDrawData(), command_buffer, 0);
68+
}
69+
70+
pub fn set_min_image_count(min_image_count: u32) void {
71+
ImGui_ImplVulkan_SetMinImageCount(min_image_count);
72+
}
73+
74+
// Those functions are defined in 'imgui_impl_vulkan.cpp`
75+
// (they include few custom changes).
76+
extern fn ImGui_ImplVulkan_Init(init_info: *ImGui_ImplVulkan_InitInfo) bool;
77+
extern fn ImGui_ImplVulkan_Shutdown() void;
78+
extern fn ImGui_ImplVulkan_NewFrame() void;
79+
extern fn ImGui_ImplVulkan_RenderDrawData(
80+
draw_data: *const anyopaque, // *ImDrawData
81+
command_buffer: VkHandle, // VkCommandBuffer
82+
pipeline: VkHandle,
83+
) void;
84+
extern fn ImGui_ImplVulkan_CreateFontsTexture() void;
85+
extern fn ImGui_ImplVulkan_DestroyFontsTexture() void;
86+
extern fn ImGui_ImplVulkan_SetMinImageCount(min_image_count: u32) void;
87+
extern fn ImGui_ImplVulkan_LoadFunctions(
88+
loader_func: *const fn (function_name: [*:0]const u8, user_data: ?*anyopaque) callconv(.C) ?*anyopaque,
89+
user_data: ?*anyopaque,
90+
) bool;

src/gui.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub const backend = switch (@import("zgui_options").backend) {
1313
.glfw_wgpu => @import("backend_glfw_wgpu.zig"),
1414
.glfw_opengl3 => @import("backend_glfw_opengl.zig"),
1515
.glfw_dx12 => @import("backend_glfw_dx12.zig"),
16+
.glfw_vulkan => @import("backend_glfw_vulkan.zig"),
1617
.glfw => @import("backend_glfw.zig"),
1718
.win32_dx12 => @import("backend_win32_dx12.zig"),
1819
.sdl2_opengl3 => @import("backend_sdl2_opengl.zig"),

0 commit comments

Comments
 (0)