Initial commit

This commit is contained in:
Mason Remaley 2024-07-29 01:17:25 -07:00
commit 48a86027ba
16 changed files with 80801 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.zig-cache
zig-out

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

55
README.md Normal file
View file

@ -0,0 +1,55 @@
# Dear ImGui Zig
A Zig interface for [Dear ImGui](https://github.com/ocornut/imgui).
Generated at build time from the JSON generated by [dear_bindings](https://github.com/dearimgui/dear_bindings).
## Which version of Dear ImGui is built?
See `build.zig.zon`, at the time of writing it's a recent commit on the docking branch. See Dear ImGui's readme for info on Dear ImGui branches.
## How to use Dear ImGui
Import the `dear_imgui` module provided by this package. It exports Dear ImGui types and functions, converted to standard Zig syntax. You can always check the generated file if you are unsure how the names map, but it should be fairly straightforward.
## How to build Dear ImGui extensions
The static library for Dear ImGui, and supported backends, are provided as artifacts under the same name as the Zig modules. You can link Dear Imgui extensions to these.
## How to use the backends
Right now, this package only exports the Vulkan backend: `dear_imgui_vulkan`.
Contributions are welcome if you'd like to add others. The process should be fairly straightforward.
## How it works
`dear_bindings` generates a C wrapper for Dear ImGui, and a JSON file detailing the declarations.
The results of executing `dear_bindings` are cached in `src/cached` to avoid a Python dependency
(`dear_bindings` is implemented in Python.)
At build time, `generate.zig` is called on the relevant JSON files to generate a Zig module that links to the wrapper generated by `dear_bindings`.
`dear_bindings` does not provide enough data to generate perfect pointer types for all situations, but a reasonable conservative heuristic is used to avoid `[*c]` where possible. This could be improved in the future through whitelists.
## How to change versions
Update `build.zig.zon`, and then if the headers changed, regenerate them into `src/cached` with [dear_bindings](https://github.com/dearimgui/dear_bindings).
Building `cimgui.h` is explained in their README, here'd how to build a backend. You can also see the command format in their `bindings.bat` file.
```sh
python3 dear_bindings.py --backend --imconfig-path ~/Documents/imgui/imconfig.h -o cimgui_internal ~/Documents/imgui/imgui_internal.h
```
If `generator.zig` fails, you may need to add missing values to the enumeration. `std.json` does not provide helpful errors at the time of writing, the easiest way to debug this is to comment out the actual generation code, and all fields in `Header`, adding them back until you figure out which are causing the issue.
## Known differences
* doesn't build `binary_to_compressed_c`
* doesn't build `misc/cpp/imgui_stdlib.cpp`
* freetype mode not supported (but easily could be)
* only the vulkan backend is provided as of now (but others are easy to add)
* the Vulkan backend assumes `IMGUI_IMPL_VULKAN_NO_PROTOTYPES`
* the Vulkan backend assumes dynamic rendering is available (>= `VK_VERSION_1_3` or `VK_KHR_dynamic_rendering`)
* we could add support for this conditional, but it seems unlikely to get much use

109
build.zig Normal file
View file

@ -0,0 +1,109 @@
const std = @import("std");
const flags: []const []const u8 = &.{
"-fno-exceptions",
"-fno-rtti",
"-fno-threadsafe-statics",
};
pub fn build(b: *std.Build) void {
// Standard options
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const optimize_external = switch (optimize) {
.Debug => .ReleaseSafe,
else => optimize,
};
// Get the upstream code
const upstream = b.dependency("dear-imgui", .{});
// Compile Dear ImGui as a static library
const dear_imgui_lib = b.addStaticLibrary(.{
.name = "dear_imgui",
.target = target,
.optimize = optimize_external,
});
dear_imgui_lib.addIncludePath(upstream.path(""));
dear_imgui_lib.installHeadersDirectory(upstream.path(""), "", .{});
dear_imgui_lib.linkLibC();
dear_imgui_lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"imgui.cpp",
"imgui_demo.cpp",
"imgui_draw.cpp",
"imgui_tables.cpp",
"imgui_widgets.cpp",
},
.flags = flags,
});
dear_imgui_lib.addCSourceFiles(.{
.root = b.path("src/cached"),
.files = &.{"cimgui.cpp"},
.flags = flags,
});
b.installArtifact(dear_imgui_lib);
// Compile the Vulkan backend as a static library
const dear_imgui_vulkan_lib = b.addStaticLibrary(.{
.name = "dear_imgui_vulkan",
.target = target,
.optimize = optimize_external,
});
dear_imgui_vulkan_lib.linkLibrary(dear_imgui_lib);
dear_imgui_vulkan_lib.addCSourceFile(.{ .file = upstream.path("backends/imgui_impl_vulkan.cpp"), .flags = flags });
dear_imgui_vulkan_lib.addCSourceFile(.{ .file = b.path("src/cached/cimgui_impl_vulkan.cpp"), .flags = flags });
dear_imgui_vulkan_lib.addIncludePath(upstream.path(""));
dear_imgui_vulkan_lib.addIncludePath(upstream.path("backends"));
const vulkan_headers = b.dependency("Vulkan-Headers", .{});
dear_imgui_vulkan_lib.addIncludePath(vulkan_headers.path("include"));
dear_imgui_vulkan_lib.defineCMacro("IMGUI_IMPL_VULKAN_NO_PROTOTYPES", "1"); // Assumed in generator
dear_imgui_vulkan_lib.installHeadersDirectory(upstream.path("backends"), "", .{});
dear_imgui_vulkan_lib.installHeadersDirectory(vulkan_headers.path("include"), "", .{});
b.installArtifact(dear_imgui_vulkan_lib);
// Compile the generator
const generate_exe = b.addExecutable(.{
.name = "generate",
.root_source_file = b.path("src/generate.zig"),
.target = target,
.optimize = optimize,
});
const generate_cmd = b.addRunArtifact(generate_exe);
generate_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
generate_cmd.addArgs(args);
}
const generate_step = b.step("generate", "Generate Zig bindings for Dear ImGui. This is done automatically as part of the build process, but is exposed as an option for debugging purposes.");
generate_step.dependOn(&generate_cmd.step);
// Generate Zig bindings for Dear ImGui
const generate_dear_imgui = b.addRunArtifact(generate_exe);
generate_dear_imgui.addFileArg(b.path("src/cached/cimgui.json"));
const dear_imgui_zig = generate_dear_imgui.addOutputFileArg("dear_imgui.zig");
generate_dear_imgui.addFileArg(b.path("src/templates/cimgui_prefix.zig.template"));
generate_dear_imgui.addFileArg(b.path("src/templates/cimgui_postfix.zig.template"));
const dear_imgui_zig_module = b.addModule("dear_imgui", .{
.root_source_file = dear_imgui_zig,
.target = target,
.optimize = optimize,
});
dear_imgui_zig_module.linkLibrary(dear_imgui_lib);
// Generate Zig bindings for the Vulkan backend
const generate_vulkan = b.addRunArtifact(generate_exe);
generate_vulkan.addFileArg(b.path("src/cached/cimgui_impl_vulkan.json"));
const dear_imgui_vulkan_zig = generate_vulkan.addOutputFileArg("dear_imgui_impl_vulkan.zig");
generate_vulkan.addFileArg(b.path("src/templates/impl_vulkan_prefix.zig.template"));
generate_vulkan.addFileArg(b.path("src/templates/impl_vulkan_postfix.zig.template"));
const dear_imgui_vulkan_zig_module = b.addModule("dear_imgui_vulkan", .{
.root_source_file = dear_imgui_vulkan_zig,
.target = target,
.optimize = optimize,
});
dear_imgui_vulkan_zig_module.linkLibrary(dear_imgui_vulkan_lib);
}

22
build.zig.zon Normal file
View file

@ -0,0 +1,22 @@
.{
.name = "dear_imgui_zig",
.version = "0.0.0",
.dependencies = .{
.@"dear-imgui" = .{
.url = "https://github.com/ocornut/imgui/archive/refs/tags/v1.90.9-docking.tar.gz",
.hash = "1220854ebf6bca15874cac634f0c9e39145cc0d13c1cf74822b6ed3073e906a4f8d5",
},
.@"Vulkan-Headers" = .{
.url = "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.3.290.tar.gz",
.hash = "12200beeb3d546548d62a5ad1ac4153e28f78bdeb3617a07aa7a8e584b51cba2daa1",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
"LICENSE",
"README.md",
},
}

3714
src/cached/cimgui.cpp Normal file

File diff suppressed because it is too large Load diff

3861
src/cached/cimgui.h Normal file

File diff suppressed because it is too large Load diff

69600
src/cached/cimgui.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,107 @@
// THIS FILE HAS BEEN AUTO-GENERATED BY THE 'DEAR BINDINGS' GENERATOR.
// **DO NOT EDIT DIRECTLY**
// https://github.com/dearimgui/dear_bindings
#include "imgui.h"
#include "imgui_impl_vulkan.h"
#include <stdio.h>
// Wrap this in a namespace to keep it separate from the C++ API
namespace cimgui
{
#include "cimgui_impl_vulkan.h"
}
// By-value struct conversions
// Function stubs
#ifndef IMGUI_DISABLE
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_Init(cimgui::ImGui_ImplVulkan_InitInfo* info)
{
return ::ImGui_ImplVulkan_Init(reinterpret_cast<::ImGui_ImplVulkan_InitInfo*>(info));
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_Shutdown(void)
{
::ImGui_ImplVulkan_Shutdown();
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_NewFrame(void)
{
::ImGui_ImplVulkan_NewFrame();
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_RenderDrawData(cimgui::ImDrawData* draw_data, VkCommandBuffer command_buffer)
{
::ImGui_ImplVulkan_RenderDrawData(reinterpret_cast<::ImDrawData*>(draw_data), command_buffer);
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_RenderDrawDataEx(cimgui::ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
{
::ImGui_ImplVulkan_RenderDrawData(reinterpret_cast<::ImDrawData*>(draw_data), command_buffer, pipeline);
}
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_CreateFontsTexture(void)
{
return ::ImGui_ImplVulkan_CreateFontsTexture();
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_DestroyFontsTexture(void)
{
::ImGui_ImplVulkan_DestroyFontsTexture();
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count)
{
::ImGui_ImplVulkan_SetMinImageCount(min_image_count);
}
CIMGUI_IMPL_API VkDescriptorSet cimgui::cImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout)
{
return ::ImGui_ImplVulkan_AddTexture(sampler, image_view, image_layout);
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
{
::ImGui_ImplVulkan_RemoveTexture(descriptor_set);
}
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data))
{
return ::ImGui_ImplVulkan_LoadFunctions(loader_func);
}
CIMGUI_IMPL_API bool cimgui::cImGui_ImplVulkan_LoadFunctionsEx(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data), void* user_data)
{
return ::ImGui_ImplVulkan_LoadFunctions(loader_func, user_data);
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, cimgui::ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count)
{
::ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, reinterpret_cast<::ImGui_ImplVulkanH_Window*>(wd), queue_family, allocator, w, h, min_image_count);
}
CIMGUI_IMPL_API void cimgui::cImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, cimgui::ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator)
{
::ImGui_ImplVulkanH_DestroyWindow(instance, device, reinterpret_cast<::ImGui_ImplVulkanH_Window*>(wd), allocator);
}
CIMGUI_IMPL_API VkSurfaceFormatKHR cimgui::cImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space)
{
return ::ImGui_ImplVulkanH_SelectSurfaceFormat(physical_device, surface, request_formats, request_formats_count, request_color_space);
}
CIMGUI_IMPL_API VkPresentModeKHR cimgui::cImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count)
{
return ::ImGui_ImplVulkanH_SelectPresentMode(physical_device, surface, request_modes, request_modes_count);
}
CIMGUI_IMPL_API int cimgui::cImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode)
{
return ::ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(present_mode);
}
#endif // #ifndef IMGUI_DISABLE

View file

@ -0,0 +1,198 @@
// THIS FILE HAS BEEN AUTO-GENERATED BY THE 'DEAR BINDINGS' GENERATOR.
// **DO NOT EDIT DIRECTLY**
// https://github.com/dearimgui/dear_bindings
// dear imgui: Renderer Backend for Vulkan
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [x] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport).
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
// See imgui_impl_vulkan.cpp file for details.
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// 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.
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef IMGUI_DISABLE
#include "cimgui.h"
// [Configuration] in order to use a custom Vulkan function loader:
// (1) You'll need to disable default Vulkan function prototypes.
// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag.
// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit:
// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file
// - Or as a compilation flag in your build system
// - Or uncomment here (not recommended because you'd be modifying imgui sources!)
// - Do not simply add it in a .cpp file!
// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function.
// If you have no idea what this is, leave it alone!
//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
// Convenience support for Volk
// (you can also technically use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
//#define IMGUI_IMPL_VULKAN_USE_VOLK
#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)
#define VK_NO_PROTOTYPES
#endif // #if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)
#if defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)
#define NOMINMAX
#endif // #if defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)
// Vulkan includes
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
#include <volk.h>
#else
#include <vulkan/vulkan.h>
#endif // #ifdef IMGUI_IMPL_VULKAN_USE_VOLK
#if defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
#endif // #if defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)
// 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.
// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
// [Please zero-clear before use!]
typedef struct ImGui_ImplVulkan_InitInfo_t
{
VkInstance Instance;
VkPhysicalDevice PhysicalDevice;
VkDevice Device;
uint32_t QueueFamily;
VkQueue Queue;
VkDescriptorPool DescriptorPool; // See requirements in note above
VkRenderPass RenderPass; // Ignored if using dynamic rendering
uint32_t MinImageCount; // >= 2
uint32_t ImageCount; // >= MinImageCount
VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT
// (Optional)
VkPipelineCache PipelineCache;
uint32_t Subpass;
// (Optional) Dynamic Rendering
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
bool UseDynamicRendering;
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo;
#endif // #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
// (Optional) Allocation, Debugging
const VkAllocationCallbacks* Allocator;
void (*CheckVkResultFn)(VkResult err);
VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory.
} ImGui_ImplVulkan_InitInfo;
typedef struct ImDrawData_t ImDrawData;
// Called by user code
CIMGUI_IMPL_API bool cImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
CIMGUI_IMPL_API void cImGui_ImplVulkan_Shutdown(void);
CIMGUI_IMPL_API void cImGui_ImplVulkan_NewFrame(void);
CIMGUI_IMPL_API void cImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); // Implied pipeline = VK_NULL_HANDLE
CIMGUI_IMPL_API void cImGui_ImplVulkan_RenderDrawDataEx(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline /* = VK_NULL_HANDLE */);
CIMGUI_IMPL_API bool cImGui_ImplVulkan_CreateFontsTexture(void);
CIMGUI_IMPL_API void cImGui_ImplVulkan_DestroyFontsTexture(void);
CIMGUI_IMPL_API void cImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
// Register a texture (VkDescriptorSet == ImTextureID)
// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
CIMGUI_IMPL_API VkDescriptorSet cImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
CIMGUI_IMPL_API void cImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
// Optional: load Vulkan functions with a custom function loader
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
CIMGUI_IMPL_API bool cImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data)); // Implied user_data = nullptr
CIMGUI_IMPL_API bool cImGui_ImplVulkan_LoadFunctionsEx(PFN_vkVoidFunction (*loader_func)(const char* function_name, void* user_data), void* user_data /* = nullptr */);
//-------------------------------------------------------------------------
// Internal / Miscellaneous Vulkan Helpers
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
//-------------------------------------------------------------------------
// You probably do NOT need to use or care about those functions.
// Those functions only exist because:
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
// 2) the multi-viewport / platform window implementation needs them internally.
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
// but it is too much code to duplicate everywhere so we exceptionally expose them.
//
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
//-------------------------------------------------------------------------
typedef struct ImGui_ImplVulkanH_Frame_t ImGui_ImplVulkanH_Frame;
typedef struct ImGui_ImplVulkanH_Window_t ImGui_ImplVulkanH_Window;
// Helpers
CIMGUI_IMPL_API void cImGui_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);
CIMGUI_IMPL_API void cImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator);
CIMGUI_IMPL_API VkSurfaceFormatKHR cImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
CIMGUI_IMPL_API VkPresentModeKHR cImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
CIMGUI_IMPL_API int cImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
// 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.)
// [Please zero-clear before use!]
typedef struct ImGui_ImplVulkanH_Frame_t
{
VkCommandPool CommandPool;
VkCommandBuffer CommandBuffer;
VkFence Fence;
VkImage Backbuffer;
VkImageView BackbufferView;
VkFramebuffer Framebuffer;
} ImGui_ImplVulkanH_Frame;
typedef struct ImGui_ImplVulkanH_FrameSemaphores_t
{
VkSemaphore ImageAcquiredSemaphore;
VkSemaphore RenderCompleteSemaphore;
} ImGui_ImplVulkanH_FrameSemaphores;
// Helper structure to hold the data needed by one rendering context into one OS window
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
typedef struct ImGui_ImplVulkanH_Window_t
{
int Width;
int Height;
VkSwapchainKHR Swapchain;
VkSurfaceKHR Surface;
VkSurfaceFormatKHR SurfaceFormat;
VkPresentModeKHR PresentMode;
VkRenderPass RenderPass;
bool UseDynamicRendering;
bool ClearEnable;
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)
uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
ImGui_ImplVulkanH_Frame* Frames;
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
} ImGui_ImplVulkanH_Window;
#endif// #ifndef IMGUI_DISABLE
#ifdef __cplusplus
} // End of extern "C" block
#endif

File diff suppressed because it is too large Load diff

971
src/generate.zig Normal file
View file

@ -0,0 +1,971 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const Declarations = std.StringArrayHashMap(struct { is_opaque: bool });
const max_size = 5000000;
// The header type we'll parse from JSON. Fields are only included as needed.
// practice, etc.
const Header = struct {
defines: []Define,
typedefs: []Typedef,
enums: []Enum,
structs: []const Struct,
functions: []Function,
const Define = struct {
name: []const u8,
content: ?[]const u8 = null,
is_internal: bool,
conditionals: []const Conditional = &.{},
};
const Typedef = struct {
name: []const u8,
type: Type,
conditionals: []const Conditional = &.{},
};
const Enum = struct {
name: []const u8,
storage_type: StorageType = .{ .declaration = .int },
is_flags_enum: bool,
elements: []const Element,
is_internal: bool,
conditionals: []const Conditional = &.{},
const Element = struct {
name: []const u8,
value: i64,
is_count: bool,
is_internal: bool,
conditionals: []const Conditional = &.{},
};
const StorageType = struct {
declaration: enum { int, ImU8 },
};
};
const Struct = struct {
name: []const u8,
is_anonymous: bool,
kind: enum { @"struct", @"union" },
forward_declaration: bool,
fields: []const Field,
conditionals: []const Conditional = &.{},
const Field = struct {
name: []const u8,
is_anonymous: bool,
type: Type,
width: ?usize = null,
default_value: ?std.json.Value = null,
conditionals: []const Conditional = &.{},
};
};
const Function = struct {
original_class: ?[]const u8 = null,
name: []const u8,
arguments: []const Argument = &.{},
return_type: Type,
conditionals: []const Conditional = &.{},
const Argument = struct {
type: ?Type = null,
is_varargs: bool,
is_instance_pointer: bool,
default_value: ?[]const u8 = null,
};
};
// `dear_bindings` doesn't parse these for us, it just forwards the c preprocessor strings.
// This is a whitelist of the current values so that we can react appropriately as new values
// are added.
const Conditional = struct {
condition: enum { ifdef, ifndef, @"if", ifnot },
expression: enum {
IMGUI_DISABLE_OBSOLETE_FUNCTIONS,
IMGUI_DISABLE_OBSOLETE_KEYIO,
IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT,
IMGUI_USE_WCHAR32,
ImTextureID,
ImDrawIdx,
ImDrawCallback,
CIMGUI_API,
CIMGUI_IMPL_API,
@"defined(_MSC_VER)&&!defined(__clang__)&&!defined(__INTEL_COMPILER)&&!defined(IMGUI_DEBUG_PARANOID)",
@"defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_OBSOLETE_KEYIO)",
IMGUI_DEFINE_MATH_OPERATORS,
IM_COL32_R_SHIFT,
IMGUI_USE_BGRA_PACKED_COLOR,
IM_DRAWLIST_TEX_LINES_WIDTH_MAX,
@"defined(IMGUI_DISABLE_METRICS_WINDOW)&&!defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_DEBUG_TOOLS)",
@"defined(IMGUI_HAS_IMSTR)",
IMGUI_HAS_IMSTR,
@"defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)",
@"defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)",
@"defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)",
IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING,
},
};
const Type = struct {
type_details: ?Details = null,
description: Description,
const Details = struct {
flavour: enum { function_pointer },
arguments: []const struct {
type: Type,
is_array: bool,
is_varargs: bool,
},
return_type: *Type,
};
const Description = struct {
kind: enum { Type, Function, Array, Pointer, Builtin, User },
storage_classes: []const enum { @"const" } = &.{},
inner_type: ?*Description = null,
bounds: ?[]const u8 = null, // Literal or variable
builtin_type: ?Builtin = null,
name: ?[]const u8 = null,
const Builtin = enum {
void,
char,
unsigned_char,
short,
unsigned_short,
int,
unsigned_int,
long,
unsigned_long,
long_long,
unsigned_long_long,
float,
double,
long_double,
bool,
};
};
};
};
pub fn main() !void {
// Allocator and command line args
var gpa = std.heap.GeneralPurposeAllocator(.{
.thread_safe = false,
}){};
defer std.debug.assert(gpa.deinit() == .ok);
const allocator = gpa.allocator();
var args = try std.process.argsWithAllocator(allocator);
std.debug.assert(args.skip());
const in_path = args.next().?;
const out_path = args.next().?;
const prefix_path = args.next();
const postfix_path = args.next();
std.debug.assert(args.next() == null);
const out = try std.fs.cwd().createFile(out_path, .{});
defer out.close();
var buf = std.io.bufferedWriter(out.writer());
const writer = buf.writer();
// Write the prefix
if (prefix_path) |p| {
const prefix_source = try std.fs.cwd().readFileAlloc(allocator, p, max_size);
defer allocator.free(prefix_source);
try writer.writeAll(prefix_source);
try writer.writeAll("\n// End of prefix\n\n");
}
// Write the source
{
const source = try std.fs.cwd().readFileAlloc(allocator, in_path, max_size);
defer allocator.free(source);
const header = try std.json.parseFromSlice(Header, allocator, source, .{
.ignore_unknown_fields = true,
});
defer header.deinit();
// We need the list of declarations up front.
var declarations = try getDeclarations(allocator, &header.value);
defer declarations.deinit();
// Write all defines as private constants.
try writeDefines(writer, &header.value);
// Write all typedefs as private constants.
try writeTypedefs(writer, &header.value, &declarations);
// Write all cimgui functions as private extern functions.
try writeExternFunctions(writer, &header.value, &declarations);
// Alias cimgui free functions under Zig friendly names.
try writeFreeFunctions(writer, &header.value);
// Get a list of cimgui methods. These were already written as externs, and can be aliased
// when we write their respective types.
var methods = try Methods.get(allocator, &header.value);
defer methods.deinit();
// Write cimgui enums as Zig enums.
try writeEnums(allocator, writer, &header.value);
// Write cimgui structs as Zig structs and unions.
try writeStructs(writer, &header.value, &declarations, &methods);
// Write helpers used by the other generated code.
try writeHelpers(writer);
}
// Write the postfix
if (postfix_path) |p| {
const postfix_source = try std.fs.cwd().readFileAlloc(allocator, p, max_size);
defer allocator.free(postfix_source);
try writer.writeAll("\n// Start of postfix\n\n");
try writer.writeAll(postfix_source);
}
// Flush and exit
try buf.flush();
}
fn getDeclarations(allocator: Allocator, header: *const Header) !Declarations {
var declarations = Declarations.init(allocator);
errdefer declarations.deinit();
for (header.structs) |ty| {
if (skip(ty.conditionals)) continue;
// Check if we're a forward decl, or a packed struct.
//
// We *could* write out the Zig code to pack them by passing the widths in when writing
// the type (and overwriting number types with the actual width, asserting otherwise.)
//
// However, we'd need to decide the correct backing type for the packed struct to make
// it compatible with C. I'm also not 100% what the guarantees are for packed struct
// layout in C.
var is_opaque = false;
if (ty.forward_declaration) {
is_opaque = true;
} else if (ty.kind == .@"struct") {
for (ty.fields) |field| if (field.width != null) {
is_opaque = true;
break;
};
}
const trimmed = std.mem.trimRight(u8, ty.name, "_");
try declarations.put(trimmed, .{ .is_opaque = is_opaque });
}
for (header.enums) |e| {
if (e.is_internal) continue;
if (skip(e.conditionals)) continue;
const trimmed = std.mem.trimRight(u8, e.name, "_");
try declarations.put(trimmed, .{ .is_opaque = false });
}
return declarations;
}
fn writeDefines(writer: anytype, header: *const Header) !void {
for (header.defines) |define| {
if (define.is_internal) continue;
if (skip(define.conditionals)) continue;
if (define.content) |content| {
try writer.print("const {s} = {s};\n", .{ define.name, content });
}
}
}
fn writeTypedefs(writer: anytype, header: *const Header, declarations: *const Declarations) !void {
for (header.typedefs) |typedef| {
// Skip typedefs skipped by the preprocessor
if (skip(typedef.conditionals)) continue;
// Skip duplicate declarations (e.g. naming enums as ints in C)
if (declarations.contains(typedef.name)) continue;
// Write the typedef
try writer.writeAll("const ");
try writeTypeName(writer, typedef.name);
try writer.writeAll(" = ");
try writeType(writer, typedef.type, declarations, .{});
try writer.writeAll(";\n");
}
}
fn writeExternFunctions(
writer: anytype,
header: *const Header,
declarations: *const Declarations,
) !void {
for (header.functions) |function| {
if (skip(function.conditionals)) continue;
if (argsContainsVaList(function.arguments)) continue;
try writer.print("extern fn {s}(", .{function.name});
for (function.arguments) |argument| {
if (argument.type) |ty| {
std.debug.assert(!argument.is_varargs);
try writeType(writer, ty, declarations, .{
.is_instance_pointer = argument.is_instance_pointer,
.is_argument = true,
.default_null = if (argument.default_value) |d| std.mem.eql(u8, d, "NULL") else false,
});
} else {
std.debug.assert(argument.is_varargs);
try writer.writeAll("...");
}
try writer.writeAll(", ");
}
try writer.writeAll(") callconv(.C) ");
try writeType(writer, function.return_type, declarations, .{ .is_result = true });
try writer.writeAll(";\n");
}
}
fn argsContainsVaList(arguments: []const Header.Function.Argument) bool {
for (arguments) |argument| {
if (argument.type) |ty| {
if (ty.description.name) |name| {
if (std.mem.eql(u8, name, "va_list")) return true;
}
}
}
return false;
}
fn writeFreeFunctions(writer: anytype, header: *const Header) !void {
for (header.functions) |function| {
if (skip(function.conditionals)) continue;
if (function.original_class != null) continue;
if (argsContainsVaList(function.arguments)) continue;
try writer.writeAll("pub const ");
try writeFunctionName(writer, function.name);
try writer.print(" = {s};\n", .{function.name});
}
}
const Methods = struct {
types: std.StringArrayHashMap(std.ArrayList([]const u8)),
fn get(allocator: Allocator, header: *const Header) !Methods {
// Initialize an empty method list for each type
var types = std.StringArrayHashMap(std.ArrayList([]const u8)).init(allocator);
errdefer types.deinit();
errdefer for (types.values()) |methods| {
methods.deinit();
};
for (header.structs) |ty| {
const methods = std.ArrayList([]const u8).init(allocator);
errdefer methods.deinit();
try types.put(ty.name, methods);
}
// Fill in the method lists
for (header.functions) |function| {
if (skip(function.conditionals)) continue;
if (argsContainsVaList(function.arguments)) continue;
if (function.original_class) |class| {
const methods = types.getPtr(class).?;
try methods.append(function.name);
}
}
return .{ .types = types };
}
fn deinit(self: *Methods) void {
for (self.types.values()) |methods| {
methods.deinit();
}
self.types.deinit();
self.* = undefined;
}
};
fn writeEnums(allocator: Allocator, writer: anytype, header: *const Header) !void {
for (header.enums) |e| {
if (e.is_internal) continue;
if (skip(e.conditionals)) continue;
try writer.writeAll("pub const ");
try writeTypeName(writer, e.name);
try writer.writeAll(" = ");
if (e.is_flags_enum) {
try writeFlagsEnum(writer, e);
} else {
try writeNormalEnum(allocator, writer, e);
}
}
}
fn writeFlagsEnum(writer: anytype, e: Header.Enum) !void {
const backing, const backing_bits = switch (e.storage_type.declaration) {
.int => .{"c_int", @typeInfo(c_int).Int.bits},
.ImU8 => .{"u8", 8},
};
try writer.print("packed struct({s}) {{\n", .{backing});
var current_offset: usize = 0;
var padding_i: usize = 0;
for (e.elements) |element| {
// Skip internal elements, and elements discarded by preprocessor
if (skip(element.conditionals)) continue;
if (element.is_internal) continue;
if (element.value == 0) continue;
// Calculate the offset of this element
const offset = std.math.log2(@as(usize, @intCast(element.value)));
// Sometimes at the end of flag sets, elements are defined that are combinations of
// existing flags. Skip these--the only reasonable representation would be constants, but
// they're hard to compose in Zig so it's not worth it.
if (offset < current_offset) {
continue;
}
// Add padding to get this element to the correct offset
const padding = offset - current_offset;
if (padding > 0) {
try writer.print(" __padding{}: u{} = 0,\n", .{ padding_i, padding });
padding_i += 1;
current_offset = offset;
}
// Write the element
try writer.writeAll(" ");
try writeElementName(writer, e.name, element.name);
try writer.writeAll(": bool = false,\n");
current_offset += 1;
}
// Add padding at end to make total type the correct size
const padding = backing_bits - current_offset;
if (padding > 0) {
try writer.print(" __padding{}: u{} = 0,\n", .{ padding_i, padding });
}
try writer.writeAll("};\n");
}
fn writeNormalEnum(allocator: Allocator, writer: anytype, e: Header.Enum) !void {
var values = std.AutoArrayHashMap(i64, void).init(allocator);
defer values.deinit();
try writer.writeAll("enum(");
switch (e.storage_type.declaration) {
.int => try writer.writeAll("c_int"),
.ImU8 => try writer.writeAll("u8"),
}
try writer.writeAll(") {\n");
// Write elements
for (e.elements) |element| {
// Skip internal and count
if (element.is_internal) continue;
if (element.is_count) continue;
if (skip(element.conditionals)) continue;
// We skip duplicate values, these are sometimes present e.g. in the keys enum which
// contains the mods enum and therefore two "none" options that are identical.
if (values.contains(element.value)) continue;
try values.put(element.value, {});
// Write the element
try writer.writeAll(" ");
try writeElementName(writer, e.name, element.name);
try writer.print(" = {},\n", .{element.value});
}
// Write constants since they're used internally, but keep them private
for (e.elements) |element| {
if (element.is_count) {
try writer.writeAll(" const ");
try writeElementName(writer, e.name, element.name);
try writer.print(" = {};\n", .{element.value});
}
}
try writer.writeAll("};\n");
}
fn writeStructs(
writer: anytype,
header: *const Header,
declarations: *const Declarations,
methods: *const Methods,
) !void {
for (header.structs) |ty| {
// Skip structs skipped by the preprocessor. We don't skip structs marked as internal,
// because many of these appear to be generally useful (it may be set incorrectly in the
// JSON?)
if (skip(ty.conditionals)) continue;
// Write the struct
try writer.writeAll("pub const ");
try writeTypeName(writer, ty.name);
try writer.writeAll(" = ");
// If we're opaque, don't try to fill out the struct fields
if (declarations.get(ty.name).?.is_opaque) {
try writer.writeAll("opaque {};\n");
continue;
}
// Declare the struct or union
switch (ty.kind) {
.@"struct" => try writer.writeAll("extern struct {\n"),
.@"union" => try writer.writeAll("extern union {\n"),
}
// Fill in the fields
for (ty.fields) |field| {
// Skip fields skipped by the preprocessor.
if (skip(field.conditionals)) continue;
// Not yet used, but when it is we want to start using it. Safe to disable this assert
// if you're just trying to get things working with a different version.
if (field.default_value != null) @panic("unimplemented");
// Write the field.
try writer.writeAll(" ");
if (field.is_anonymous) {
try writer.writeAll("data");
} else {
try writeFieldName(writer, field.name);
}
try writer.writeAll(": ");
try writeType(writer, field.type, declarations, .{});
try writer.writeAll(",\n");
}
// Alias all relevant methods into the type.
const method_names = methods.types.getPtr(ty.name).?;
for (method_names.items) |name| {
try writer.writeAll(" pub const ");
try writeFunctionName(writer, name[ty.name.len + 1..]);
try writer.print(" = {s};\n", .{name});
}
try writer.writeAll("};\n");
}
}
fn writeHelpers(writer: anytype) !void {
try writer.writeAll(
\\fn toUsize(v: anytype) usize {
\\ if (@typeInfo(@TypeOf(v)) == .Enum) return @intFromEnum(v);
\\ return @intCast(v);
\\}
\\
);
}
// Type hints aren't required, but they help us figure out the best pointer types to use.
const WriteTypeHints = packed struct {
is_instance_pointer: bool = false,
is_argument: bool = false,
default_null: bool = false,
is_result: bool = false,
};
// Write a cimgui type as a Zig type.
fn writeType(
writer: anytype,
ty: Header.Type,
declarations: *const Declarations,
hints: WriteTypeHints,
) @TypeOf(writer).Error!void {
// Handle function pointers which are stored separately.
if (ty.type_details) |details| switch (details.flavour) {
.function_pointer => return writeFunctionPointer(writer, details, declarations),
};
// Handle all other types
switch (ty.description.kind) {
.Builtin => return writeBuiltinType(writer, ty.description.builtin_type.?),
.Array => return writeArrayType(writer, ty.description, declarations),
.Pointer => return writePointerType(writer, ty, declarations, hints),
.User => try writeTypeName(writer, ty.description.name.?),
.Type, .Function => @panic("unimplemented"),
}
}
fn writeFunctionPointer(
writer: anytype,
details: Header.Type.Details,
declarations: *const Declarations,
) !void {
try writer.writeAll("*const fn(");
for (details.arguments) |argument| {
try writeType(writer, argument.type, declarations, .{});
try writer.writeAll(", ");
}
try writer.writeAll(") callconv(.C) ");
try writeType(writer, details.return_type.*, declarations, .{});
}
fn writeBuiltinType(writer: anytype, builtin_type: Header.Type.Description.Builtin) !void {
switch (builtin_type) {
.void => try writer.writeAll("void"),
.char, .unsigned_char => try writer.writeAll("u8"),
.short => try writer.writeAll("c_short"),
.unsigned_short => try writer.writeAll("c_ushort"),
.int => try writer.writeAll("c_int"),
.unsigned_int => try writer.writeAll("c_uint"),
.long => try writer.writeAll("c_long"),
.unsigned_long => try writer.writeAll("c_ulong"),
.long_long => try writer.writeAll("c_longlong"),
.unsigned_long_long => try writer.writeAll("c_ulonglong"),
.float => try writer.writeAll("f32"),
.double => try writer.writeAll("f64"),
.long_double => try writer.writeAll("c_longdouble"),
.bool => try writer.writeAll("bool"),
}
}
fn writeArrayType(
writer: anytype,
description: Header.Type.Description,
declarations: *const Declarations,
) !void {
if (description.bounds) |bounds| {
try writeArrayBounds(writer, bounds);
} else {
try writer.writeAll("[*]");
}
try writeType(
writer,
.{ .description = description.inner_type.?.* },
declarations,
.{},
);
}
// Array bounds sometimes include expressions, so we need to tokenize them and convert the
// expressions to Zig syntax.
fn writeArrayBounds(writer: anytype, bounds: []const u8) !void {
try writer.writeByte('[');
var token_start: usize = 0;
while (token_start < bounds.len) {
// Calculate token start
if (bounds[token_start] == ' ') {
token_start += 1;
continue;
}
// Special handling for one character tokens
switch (bounds[token_start]) {
'(' => {
const token = bounds[token_start .. token_start + 1];
token_start += 1;
try writer.writeAll(token);
continue;
},
'+', '/' => {
const token = bounds[token_start .. token_start + 1];
token_start += 1;
try writer.print(" {s} ", .{token});
continue;
},
else => {},
}
// Calculate token end
var token_end = token_start + 1;
while (token_end < bounds.len) : (token_end += 1) {
switch (bounds[token_end]) {
' ', '+', ')', '/' => break,
else => {},
}
}
const token = bounds[token_start..token_end];
token_start = token_end;
// As of the time of writing, tokens here are either numbers or enum values.
if (std.mem.indexOfScalar(u8, token, '_')) |underscore| {
// If it's all uppercase, it's a define. Otherwise it's an enum.
const is_define = for (token) |c| {
switch (c) {
'a'...'z' => break false,
else => {},
}
} else true;
if (is_define) {
try writer.writeAll(token);
} else {
try writer.writeAll("toUsize(");
const type_name = token[0..underscore];
try writeTypeName(writer, type_name);
try writer.writeByte('.');
try writeElementName(writer, type_name, token);
try writer.writeAll(")");
}
} else {
// Otherwise, just write the value as is.
try writer.writeAll(token);
}
}
try writer.writeByte(']');
}
fn writePointerType(
writer: anytype,
ty: Header.Type,
declarations: *const Declarations,
hints: WriteTypeHints,
) !void {
// Check if we're a pointer to an opaque type
var is_opaque = false;
if (ty.description.inner_type.?.name) |name| {
if (declarations.get(name)) |decl| {
is_opaque = decl.is_opaque;
}
}
// Check if we're a pointer to void
const is_void = ty.description.inner_type.?.builtin_type == .void;
// Check if we're a string
const is_string = b: {
if (ty.description.inner_type.?.builtin_type != .char) break :b false;
for (ty.description.inner_type.?.*.storage_classes) |storage_class| switch (storage_class) {
.@"const" => break :b true,
};
break :b false;
};
// Use the hints to decide what kind of pointer we are
if (is_string) {
// We currently write all strings as c pointers since some are null terminated, and some are
// not.
try writer.writeAll("[*c]");
} else if (hints.is_instance_pointer) {
// We assume all instance pointers do *not* allow null.
std.debug.assert(!hints.default_null);
try writer.writeByte('*');
} else if (hints.is_argument) {
// Arguments are assumed to be single value pointers, because many value pointers when
// passed as arguments get marked as arrays and handled separately from pointers.
if (hints.default_null) {
// If the default value is set to null, we must be nullable. Otherwise we conservatively
// assume null is not allowed, which tends to be correct in practice more often than
// not.
try writer.writeByte('?');
}
try writer.writeByte('*');
} else if (hints.is_result) {
// Results are assumed to be single value pointers, since otherwise, we'd have no way of
// knowing the length. We conservatively assume these are nullable since we have no way of
// knowing. Ideally dear bindings would mark this eventually (there is a field for it but
// it is unused), if it becomes annoying we can always add a whitelist.
try writer.writeAll("?*");
} else {
// If we've reached this case, we're a struct field, and we don't know whether it's many
// value or nullable. We fall back to a c pointer so that the user can decide how to
// interpret it with less friction, unless it's an opaque type in which case that's not
// allowed so we assume it's a nullable single value pointer.
if (is_opaque or is_void) {
try writer.writeAll("?*");
} else {
try writer.writeAll("[*c]");
}
}
// Write any storage classes
for (ty.description.inner_type.?.*.storage_classes) |storage_class| switch (storage_class) {
.@"const" => try writer.writeAll("const "),
};
// Write the actual type
if (is_void) {
// Treat pointers to c void as pointers to anyopaque
try writer.writeAll("anyopaque");
} else {
try writeType(
writer,
.{ .description = ty.description.inner_type.?.* },
declarations,
.{},
);
}
}
// Returns true if we should skip due to a conditional
fn skip(conditionals: []const Header.Conditional) bool {
for (conditionals) |conditional| {
const defined = switch (conditional.expression) {
.IMGUI_DISABLE_OBSOLETE_FUNCTIONS,
.IMGUI_DISABLE_OBSOLETE_KEYIO,
.CIMGUI_API,
.CIMGUI_IMPL_API,
.@"defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)&&!defined(VK_NO_PROTOTYPES)",
.@"defined(VK_USE_PLATFORM_WIN32_KHR)&&!defined(NOMINMAX)",
.IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING,
.@"defined(VK_VERSION_1_3)|| defined(VK_KHR_dynamic_rendering)",
=> true,
.IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT,
.IMGUI_USE_WCHAR32,
.ImTextureID,
.ImDrawIdx,
.ImDrawCallback,
.@"defined(_MSC_VER)&&!defined(__clang__)&&!defined(__INTEL_COMPILER)&&!defined(IMGUI_DEBUG_PARANOID)",
.@"defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_OBSOLETE_KEYIO)",
.IMGUI_DEFINE_MATH_OPERATORS,
.IM_COL32_R_SHIFT,
.IMGUI_USE_BGRA_PACKED_COLOR,
.IM_DRAWLIST_TEX_LINES_WIDTH_MAX,
.@"defined(IMGUI_DISABLE_METRICS_WINDOW)&&!defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)&&!defined(IMGUI_DISABLE_DEBUG_TOOLS)",
.@"defined(IMGUI_HAS_IMSTR)",
.IMGUI_HAS_IMSTR,
=> false,
};
switch (conditional.condition) {
.ifdef, .@"if" => if (!defined) return true,
.ifndef, .ifnot => if (defined) return true,
}
}
return false;
}
// Converts a cimgui type name to a Zig type name
fn writeTypeName(writer: anytype, raw: []const u8) !void {
// These are considered user types
if (std.mem.eql(u8, raw, "size_t")) {
try writer.writeAll("usize");
return;
}
if (std.mem.eql(u8, raw, "uint32_t")) {
try writer.writeAll("u32");
return;
}
// We skip all declarations that contain `va_list`, so this shouldn't trigger.
if (std.mem.eql(u8, raw, "va_list")) unreachable;
// Remove prefixes
var name = raw;
{
// Imgui prefixes
{
const prefixes: []const []const u8 = &.{ "ImGui", "Im" };
for (prefixes) |prefix| {
if (std.mem.startsWith(u8, name, prefix)) {
name = name[prefix.len..];
break;
}
}
}
// Backend prefixes
{
const prefixes: []const []const u8 = &.{ "_ImplVulkanH", "_ImplVulkan" };
for (prefixes) |prefix| {
if (std.mem.startsWith(u8, name, prefix)) {
name = name[prefix.len..];
break;
}
}
}
}
for (name) |c| switch (c) {
'_' => {},
else => try writer.writeByte(c),
};
}
// Convert a cimgui field name to a Zig field name
fn writeFieldName(writer: anytype, name: []const u8) !void {
var escape = false;
for (name, 0..) |c, i| {
switch (c) {
'0'...'9' => {
if (i == 0) {
escape = true;
try writer.writeAll("@\"");
} else switch (name[i - 1]) {
'0'...'9' => {},
else => try writer.writeByte('_'),
}
try writer.writeByte(c);
},
'a'...'z' => try writer.writeByte(c),
'A'...'Z' => {
if (i > 0) switch (name[i - 1]) {
'A'...'Z', '_' => {},
else => try writer.writeByte('_'),
};
try writer.writeByte(c + 32);
},
'_' => if (i != name.len - 1) try writer.writeByte('_'),
else => std.debug.panic("unexpected char in name: {c}", .{c}),
}
}
if (escape) try writer.writeAll("\"");
}
// Convert a cimgui element name to a Zig element name
fn writeElementName(writer: anytype, type_name: []const u8, raw: []const u8) !void {
var name = if (std.mem.startsWith(u8, raw, type_name)) raw[type_name.len..] else raw;
name = if (std.mem.startsWith(u8, name, "ImGui")) name["ImGui".len..] else name;
name = if (name[0] == '_') name[1..] else name;
try writeFieldName(writer, name);
}
// Write a cimgui function name as a Zig function name
fn writeFunctionName(writer: anytype, raw: []const u8) !void {
var name = raw;
// Imgui prefixes
{
const prefixes: []const []const u8 = &.{ "cImGui_ImplVulkan", "ImGui", "Im" };
for (prefixes) |prefix| {
if (std.mem.startsWith(u8, name, prefix)) {
name = name[prefix.len..];
break;
}
}
}
if (name[0] == '_') name = name[1..];
// Lowercase the first set of contiguous capital letters
var i: usize = 0;
while (i < name.len and name[i] >= 'A' and name[i] <= 'Z') : (i += 1) {
try writer.writeByte(name[i] + 32);
}
// The rest of the string is usually already camelcase, write it as is unless we encounter an
// underscore in which case we should skip it an uppercase the next letter.
if (i < name.len) {
var uppercase_next = false;
for (name[i..]) |c| {
// If we're an underscore, skip it and uppercase the next letter
if (c == '_') {
uppercase_next = true;
continue;
}
// Write the next character, adjusting the case as necessary
switch (c) {
'a'...'z' => try writer.writeByte(if (uppercase_next) c - 32 else c),
else => try writer.writeByte(c),
}
uppercase_next = false;
}
}
}

View file

@ -0,0 +1,8 @@
// It's unclear to me where the generator gets this define (maybe it's directly from the original
// cpp header?), so we generate it manually.
pub fn col32(r: u32, g: u32, b: u32, a: u32) u32 {
return a << IM_COL32_A_SHIFT |
b << IM_COL32_B_SHIFT |
g << IM_COL32_G_SHIFT |
r << IM_COL32_R_SHIFT;
}

View file

View file

@ -0,0 +1,2 @@
};
}

View file

@ -0,0 +1,68 @@
const Options = struct {
PFNvkVoidFunction: type,
VkAllocationCallbacks: type,
VkClearValue: type,
VkColorSpaceKHR: type,
VkCommandBuffer: type,
VkCommandPool: type,
VkDescriptorPool: type,
VkDescriptorSet: type,
VkDevice: type,
VkDeviceSize: type,
VkFence: type,
VkFramebuffer: type,
VkImage: type,
VkImageLayout: type,
VkImageView: type,
VkInstance: type,
VkPhysicalDevice: type,
VkPipeline: type,
VkPipelineCache: type,
VkPipelineRenderingCreateInfoKHR: type,
VkPresentModeKHR: type,
VkQueue: type,
VkRenderPass: type,
VkResult: type,
VkSampleCountFlagBits: type,
VkSampler: type,
VkSemaphore: type,
VkSurfaceFormatKHR: type,
VkSurfaceKHR: type,
VkSwapchainKHR: type,
VkFormat: type,
};
pub fn get(options: Options) type {
const VkFormat = options.VkFormat;
const PFNvkVoidFunction = options.PFNvkVoidFunction;
const VkAllocationCallbacks = options.VkAllocationCallbacks;
const VkClearValue = options.VkClearValue;
const VkColorSpaceKHR = options.VkColorSpaceKHR;
const VkCommandBuffer = options.VkCommandBuffer;
const VkCommandPool = options.VkCommandPool;
const VkDescriptorPool = options.VkDescriptorPool;
const VkDescriptorSet = options.VkDescriptorSet;
const VkDevice = options.VkDevice;
const VkDeviceSize = options.VkDeviceSize;
const VkFence = options.VkFence;
const VkFramebuffer = options.VkFramebuffer;
const VkImage = options.VkImage;
const VkImageLayout = options.VkImageLayout;
const VkImageView = options.VkImageView;
const VkInstance = options.VkInstance;
const VkPhysicalDevice = options.VkPhysicalDevice;
const VkPipeline = options.VkInstance;
const VkPipelineCache = options.VkPipelineCache;
const VkPipelineRenderingCreateInfoKHR = options.VkPipelineRenderingCreateInfoKHR;
const VkPresentModeKHR = options.VkPresentModeKHR;
const VkQueue = options.VkQueue;
const VkRenderPass = options.VkRenderPass;
const VkResult = options.VkResult;
const VkSampleCountFlagBits = options.VkSampleCountFlagBits;
const VkSampler = options.VkSampler;
const VkSemaphore = options.VkSemaphore;
const VkSurfaceFormatKHR = options.VkSurfaceFormatKHR;
const VkSurfaceKHR = options.VkSurfaceKHR;
const VkSwapchainKHR = options.VkSwapchainKHR;
return struct {