12 KiB
Background
This repo is based on the amazing work done by danilw in the archived repository danilw/nanovg_vulkan
Goal
For this library to be a Vulkan 1.0 compatible implementation of NanoVG, while using the full featureset of more capable hardware via optional extensions:
-
Ubuntu 24.04.1 LTS. Vulkan = 2857 fps (0.35ms), OpenGL 3.2 = 2632 fps (0.38ms). [1]
-
Windows 11 2024H2. Vulkan = 2273 fps (0.44 ms), OpenGL 3.2 = 2500 fps (0.40 ms). [1]
-
MacOS X. Vulkan = 709 fps (1.41 ms), OpenGL 3.2 = 763 fps (1.31 ms). [2]
-
Optimising pipeline creation and selection via use of supported (and/or enabled) extensions. Specifically:
- vkCmdSetColorBlendEquationEXT
- vkCmdSetPrimitiveTopologyEXT
- vkCmdSetStencilTestEnableEXT
- vkCmdSetStencilOpEXT
- vkCmdSetColorWriteMaskEXT
-
Using one fragment shader instance, based on the
NVG_ANTIALIASflag (specialization constants) -
Using Push constants in the Vertex Shader
-
Bind vertex buffer once per flush;
vkCmdDrawusesfirstVertex -
Using SSBO for a single write of fragment uniforms, indexed via push constants (no per-draw UBO offset dance)
-
One texture descriptor set per call; extra stencil/AA uniforms are only a push-constant change
-
Per-frame
VKNVGFrame(vertex + fragment SSBO), optionalframeCountseparate from swapchain image count -
Texture layout transitions use a dedicated upload command buffer, not the in-flight render CB
-
Descriptor pools are retired instead of destroyed while other frames may still reference them
-
Optimise texture memory flags
-
Notes
- [1] Tested on RX 6800, R7 7700, 32GB RAM.
- [2] Tested on M1 Macbook Pro
2024 - Im archive this.
Just use IMGUI
if you want - fork it and update it as you with, this is working in 2024 but im not going to update or change anything anymore.
Note about Vulkan - this nanovg_vulkan is Vulkan 1.0 project.
Vulkan 1.3 already released with about 10 years back GPU support in drivers. Vulkan 1.3 have VK_KHR_dynamic_rendering in core.
As minimal example of setup of complex pipeline look this blog - modern Vulkan with descriptor indexing, dynamic rendering and shader objects tutorial - and this repository vknew.
I think rewriting this entire project from scratch in Vulkan 1.3 - will be faster and easier than fixin some bugs here and improving it.
Notice - I wanted to fix one major bug here - DPI scale does not work, read known-bugs-related-info but I have no motivation to do it, too much effort, pointless no one need task.
Contact me in Discord or anywhere else if you want to ask some tech-info stuff, or I can add your "active" fork of this as link to this page.
added vulkan build, and cmake files, removed premake4.lua
Why I made this - I made it as a "test task for Vulkan related job" a year ago, for me this project has no real use. I would recommend for everyone use imgui if you look for production-ready Vulkan UI, also look on egui. I will not support or update this project at all, the last update was just to fix bugs and Validation errors.
Description - nanovg in C, no external dependencies. GLFW used only by one example (look examples description, there is example without using GLFW).
Contact: Join discord server
To launch - copy builded binary to example folder(or launch when this folder is current as on build example commands below). Because required fonts and images/shaders to load.
Integration
Compile src/nanovg.c and src/nanovg_vk.c as C11. Include nanovg.h then nanovg_vk.h after vulkan.h. Do not #define NANOVG_VULKAN_IMPLEMENTATION into the Vulkan header (that define is only used by example/demo.c to skip GL). C++ hosts should keep nanovg_vk.c as a C translation unit; the header is extern "C".
nvgCreateVk takes const VKNVGCreateInfo * and copies the struct. Pointers inside it must stay valid for the context lifetime:
| Field | Role |
|---|---|
gpu, device, renderpass, queue (create arg) |
Device + the render pass NanoVG will record into |
cmdBuffer |
Array of command buffers, length = in-flight count |
currentFrame |
Pointer to the in-flight index (0 .. frameCount-1) |
frameCount |
In-flight GPU buffers. 0 means use swapchainImageCount |
commandPool |
One-shot texture uploads (nvgCreateImage). Prefer your graphics pool with RESET_COMMAND_BUFFER |
graphicsQueueFamilyIndex |
Used only if commandPool is VK_NULL_HANDLE (0 is a valid family) |
ext |
Optional VK_EXT_extended_dynamic_state / _3 bits; leave false if unused |
Each frame: begin your render pass on cmdBuffer[*currentFrame], then nvgBeginFrame / draw / nvgEndFrame. NanoVG records into that command buffer; it does not begin a second pass.
Pipelines are tied to renderpass. After swapchain resize, wait idle, nvgDeleteVk, nvgCreateVk with the new pass, then recreate fonts/images.
nvgDeleteVk is safe if you never flushed (frames still null).
Images
nvgCreateImage— NanoVG owns theVkImage. Fine for demos; the samples reload vialoadDemoDataafter resize.nvgCreateImageFromHandleVk— you own image, view, and sampler. NanoVG will not destroy them. Integer ids die with the context.
Keep GPU images across resize, then re-register:
- Keep
VkImage/ view / sampler alive. nvgDeleteVk/nvgCreateVkwith the new render pass.- Call
nvgCreateImageFromHandleVkagain; store the new ids (do not assume id1).
nvgDeleteImage on a borrowed id only drops the NanoVG slot.
Working setup: example/example_vulkan.c (GLFW, multiple frames in flight) and example/example_vulkan_min_no_glfw.c.
Known bugs-related info:
- Bug - DPI scale is broken when it
!=1, Currectly - DPI scale set to 1 always. Read https://github.com/danilw/nanovg-vulkan-glfw-integration-demo/issues/1 - Bug - crash on resize on AMD-GPU only in Linux in Wayland. Read https://github.com/danilw/nanovg-vulkan-glfw-integration-demo/issues/2
- Fix what Cppcheck pointing, unused variables etc, not bugs.
- Performance - after SubiyaCryolite pull https://github.com/danilw/nanovg_vulkan/pull/7 performance of Vulkan version very close to OpenGL.
Build:
(use cmake to build on Windows, Windows/Linux supported and tested)
git clone --recursive https://github.com/danilw/nanovg_vulkan
cd nanovg_vulkan/example
mkdir build
cd build
cmake ../
make
cd ../
./build/example-vk
./build/example-vk_min_no_glfw
CMake already compiles src/nanovg_vk.c. See Integration above for embedding in another project.
Look Examples description below there link to repository with C only minimal example without dependencies not using any library.
MoltenVK note - after TOPOLOGY_TRIANGLE_LIST update(look below) this Vulkan port does work on MoltenVK(Mac/etc) but I dont have it to test so you should make cmake config to build it and launch by yourself.
Examples description:
Multiple frames in flight - example_vulkan.c is multiple frames in flight example, example_vulkan_min_no_glfw.c is single frame in flight. Clearly visible on FPS - with multiple frames about 3x better FPS.
example_vulkan.c - GLFW sample: fills VKNVGCreateInfo (including frameCount and commandPool) and recreates the context on resize.
example_vulkan_min_no_glfw.c - same NanoVG wiring without GLFW, Linux and Windows.
Old examples, they not compatible with this latest nanovg_vulkan.
Those linked demos still use the old header-only nvgCreateVk(createInfo by value) API. Prefer this repo’s samples plus Integration above.
Two external examples:
nanovg-vulkan-min-integration-demo - repository with minimal RenderPass integration example. Not using any libraries, no GLFW, only NanoVG code and C. Look description and screenshot on link.
nanovg-vulkan-glfw-integration-demo - repository with example from Vulkan-tutorial Depth buffering modified adding NanoVG integration. Using C++ and GLFW.
Remember NanoVG is not GUI, and examples about is just examples of this NanoVG integration, not GUI examples.
About RenderPass integration - copy paste code from integration examples above after your vkCmdEndRenderPass (or before vkCmdBeginRenderPass) and everything should work.
Framebuffer integration, where UI rendered in its own Framebuffer - I did not add example for this case, because it should be obvious - just replace framebuffer RenderPass with RenderPass of NanoVG integration examples above(look linked commit).
2023 update - huge performance improvement thanks to @SubiyaCryolite
SubiyaCryolite pull request Optimizations > Cached Descriptor Sets, Implied Multiple Frames in Flight, Fencing for faster perf
- Support for multiple command buffers, specifically one per swap-chain image / buffer
- Caching of descriptor sets, removing the need to create new ones per draw func or to call
vkResetDescriptorPoolper frame. - Under
example_vulkan, usingvkWaitForFencesto control rendering as opposed tovkQueueWaitIdle(seems to be the biggest perf booster). This change also has implied "multiple frames in flight" as dictated by the swap-chain image count. - Using persisted mapped buffers via
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BITinstead ofVK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, allows to skip calls tovkMap/UnmapMemoryper frame
Link to version before this change 5ba9d31, this change 10d5211.
2022 update - thanks to @nidefawl pull request with lots of changes:
In this PR:
-
Merge latest nanovg
-
Fix Stencil Strokes: I added a new enum to handle the 3 different pipelines used for stencil strokes. I wanted to also use enums for the fill-path (nvgFill) to make the code cleaner, but haven't done that yet.
-
I combined the 2 CMakeLists.txt into example/CMakeLists.txt and added some bits to make it easier to build and debug in MSVC/VSCode.
-
I added GLAD as default gl-loader for the GL3 example. It can be switched back to GLEW (I kept glew to allow easier merge with main nanovg)
-
I Increased the number of swapchain images to minimum 3 and updated the swapchain barrier to gain some extra performance.
-
Spacebar renders the demo multiple times to add some 'load' when comparing performance
2021 update - fixed all errors, this code has no Validation errors and work correctly everywhere.
Thanks to @fzwoch commits by default used TOPOLOGY_TRIANGLE_LIST, because TOPOLOGY_TRIANGLE_FAN is optional in Vulkan.
To enable TOPOLOGY_TRIANGLE_FAN uncomment #define USE_TOPOLOGY_TRIANGLE_FAN in src/nanovg_vk.c.
Depth order bug on AMD fix by @leranger 6ee1009.
Screenshot of Vulkan version from Linux:
Read original readme for more info about NanoVG API https://github.com/memononen/nanovg
