Fork of SubiyaCryolite/nanovg_vulkan (itself based on danilw/nanovg_vulkan). Branch modules-vulkan carries the Modules fixes. https://github.com/SubiyaCryolite/nanovg_vulkan
Find a file
Ifunga Ndana 0f7110feaf merge
2026-09-07 15:12:58 +02:00
example Introduce Vulkan backend implementation (nanovg_vk). Added Vulkan device context, texture management, pipeline creation, and descriptor handling. 2026-09-07 15:10:14 +02:00
glad-3.2-compat Add glad, use instead of glew. 2022-02-10 14:39:02 +01:00
glew-cmake@7c2b7514f6 Fix GL3 example build. Add submodule deps 2022-02-10 11:26:16 +01:00
glfw@97da62a027 Fix GL3 example build. Add submodule deps 2022-02-10 11:26:16 +01:00
obsolete Made old GL backends obsolete, added docs 2014-04-20 12:26:05 +03:00
src Introduce Vulkan backend implementation (nanovg_vk). Added Vulkan device context, texture management, pipeline creation, and descriptor handling. 2026-09-07 15:10:14 +02:00
.clang-format Added ability to enable pass dynamic state structs as creation info args. Will allow querying and execution at runtim 2024-08-26 16:59:03 +02:00
.gitignore Enhance Vulkan example with resource path handling and update CMake requirements. Added demoResourcePath function for dynamic resource loading and improved font loading logic. Updated .gitignore to exclude build directories and cache files. 2026-09-07 15:10:14 +02:00
.gitmodules Fix GL3 example build. Add submodule deps 2022-02-10 11:26:16 +01:00
LICENSE.txt Clean up 2013-11-12 08:39:44 +01:00
README.md Introduce Vulkan backend implementation (nanovg_vk). Added Vulkan device context, texture management, pipeline creation, and descriptor handling. 2026-09-07 15:10:14 +02:00

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_ANTIALIAS flag (specialization constants)

  • Using Push constants in the Vertex Shader

  • Bind vertex buffer once per flush; vkCmdDraw uses firstVertex

  • 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), optional frameCount separate 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.


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 the VkImage. Fine for demos; the samples reload via loadDemoData after 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:

  1. Keep VkImage / view / sampler alive.
  2. nvgDeleteVk / nvgCreateVk with the new render pass.
  3. Call nvgCreateImageFromHandleVk again; store the new ids (do not assume id 1).

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.



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 vkResetDescriptorPool per frame.
  • Under example_vulkan, using vkWaitForFences to control rendering as opposed to vkQueueWaitIdle (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_BIT instead of VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT , allows to skip calls to vkMap/UnmapMemory per 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:

  1. Merge latest nanovg

  2. 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.

  3. 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.

  4. 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)

  5. I Increased the number of swapchain images to minimum 3 and updated the swapchain barrier to gain some extra performance.

  6. 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:

nvgvk


Read original readme for more info about NanoVG API https://github.com/memononen/nanovg