Vulkan Backend — Progress Summary¶
Status snapshot: July 2026 · Validated on: NVIDIA GeForce RTX 2080 Ti and
NVIDIA GeForce GTX 1060 3GB ·
Enable with: cmake -S . -B build -DWHATSCANVAS_ENABLE_VULKAN=ON (Vulkan SDK
required). Vulkan is compiled into the OpenGL package target and selected at
runtime with Canvas::Backend::Vulkan. The completed milestone history is
summarized below; architectural rationale lives in the internal
doc/internal/architecture/command-layer.md decision record.
The stable v1 Vulkan scope is complete: offscreen rendering, the documented Canvas operations, filters, text, external images, and Win32 presentation are implemented and tested. The expansion areas later in this page are optional; they do not indicate that the Canvas or stable-v1 product scope is unfinished.
What works today¶
The Vulkan source is compiled unconditionally, but it becomes a real backend
only when WHATSCANVAS_ENABLE_VULKAN is set and a Vulkan SDK is found. Without
that option it remains an inert factory entry. The current CMake configuration
registers 19 core Vulkan tests plus backend-selection and wrap-external coverage
(21 tests under the vulkan label; run ctest -L vulkan). The default
(Vulkan-off) build stays green.
| Milestone | Capability | Test |
|---|---|---|
| Bring-up | Instance, physical-device selection (discrete preferred), logical device + graphics queue | WhatsCanvasVulkanDeviceTests |
| M1 | Command pool, single-time submit + fence, memory/buffer helpers | (exercised by M2) |
| M2 | Offscreen render target (image + view + clear render pass + framebuffer); readPixelsRGBA via image→staging→host |
WhatsCanvasVulkanRenderTargetTests |
| M3 | Solid geometry through a real graphics pipeline; triangles / lines / points; SPIR-V shaders | WhatsCanvasVulkanSolidGeometryTests |
| M4 | Analytic-AA coverage, fragment-evaluated multi-stop gradients, and all 14 Canvas blend modes | WhatsCanvasVulkanPaintTests, WhatsCanvasVulkanAATests, WhatsCanvasVulkanBlendModeTests |
| M5 | Sampled textures (create/upload/partial update), mipmaps, sampling/tile modes, tint, color matrix, and textured-quad draw | WhatsCanvasVulkanTextureTests, WhatsCanvasVulkanMipmapTests, WhatsCanvasVulkanImageColorTests |
| M6 | Offscreen-layer compositing with layer alpha (saveLayer mechanism) | WhatsCanvasVulkanLayerTests |
| M7 | Analytic-AA coverage-mask path clipping, nested clip intersection, and rectangular scissor fast path | WhatsCanvasVulkanClipTests, WhatsCanvasVulkanClipCommandTests |
| ADR-006 | Backend-neutral DrawList + Vulkan translator (solid, textured, gradient, shadow, and clip primitives) |
WhatsCanvasVulkanDrawListTests, WhatsCanvasVulkanCommandTests |
| Text / glyph atlas | Vector text geometry, shader-gradient text, glyph-atlas textured quads, and dirty-rect atlas texture updates | WhatsCanvasVulkanTextTests |
| Image filters | Image/backdrop blur, Clamp/Decal edges, color treatment, grain, adaptive downsampling, and Software pixel parity | WhatsCanvasVulkanImageFilterTests |
| OpenGL offscreen snapshots | renderCommandsToImageResource uses the shared CommandDrawListEncoder for layer/snapshot replay |
WhatsCanvasRenderTargetPoolTests |
IRenderDevice parity¶
All 12 required methods are implemented on Vulkan.
- Implemented:
initializeBackend,finalizeBackend,createRenderTarget,readPixelsRGBA,createImageResourceRGBA,createImageResourceFromImageData,updateImageResourceRGBA,createClipMaskResource,resourceStats,renderCommandsToImageResource,filterImageResource,wrapExternalImageResource. renderCommandsToImageResourcerenders aCommandstream into an offscreen target and returns it as an owned sampled texture through a GPU-local image copy (via the backend-neutral command translation).filterImageResourceruns separable RGBA Gaussian blur in Vulkan fragment pipelines. Large kernels use a 2x blur target and a full-resolution restore pass; saturation, brightness, contrast, and stable grain run in the final pass without a CPU readback.wrapExternalImageResourcewraps a foreignVkImage(carried by the now 64-bitImageResourceHandle) in a non-owning texture resource (owns only its view + sampler); the borrowed image is assumed RGBA8 inSHADER_READ_ONLY.VulkanRenderDevice::nativeImageHandlereturns an owned texture'sVkImageas a handle for round-tripping (WhatsCanvasVulkanExternalImageTests).
Optional expansion and validation areas¶
- Shared command layer can be converged further: Vulkan can replay real command streams, and OpenGL uses the shared command encoder for offscreen snapshots. The remaining architecture work is moving more regular OpenGL flush paths onto the same primitive stream without regressing the production renderer.
- Windowed presentation (M8 swapchain): Canvas-level
OutputTarget::ToWindow(...)+Canvas::present()is implemented for Win32 Vulkan and exercised byexamples/present/vulkan_main.cpp. The lower-leveltests/integration/vulkan_presentremains a standalone swapchain validation path. Cross-platform surfaces, broader resize/device-loss coverage, and a CTest gate remain follow-ups because windowed presentation is environment dependent. - DrawList lifetime and scope: solid, textured, gradient, shadow, and
clip-fill primitives are implemented (
executeDrawListrecords them in one render pass; clip-fill modulates the fill alpha by a coverage-mask texture's red channel). A teardown crash during development was root-caused with AddressSanitizer to a test lifetime bug (aDrawListholding a texture ref must be released beforefinalizeBackend), not a rendering bug. - Command translation (ADR-006):
executeCommandsreads a real WhatsCanvasCommandstream and translates path fills/strokes, vertex-color (baked gradient) paths, points (sized), lines (width), and images (DrawImagedest-rect + UVs + alpha) to Vulkan draws (canvas->NDC via the same ortho the GL path uses), without touching the OpenGL command execution.renderCommandsToImageResourcerenders a command stream into an offscreen target and returns it as an owned sampled texture. Images also honor tint and a 4x4 color matrix (fragment push constants). Fragment-evaluated gradients (linear/radial, up to 8 stops, clamp/repeat/mirror/decal tile modes via a UBO) translateDrawPathDatashader gradients, matching the OpenGL gradient shader. Verified byWhatsCanvasVulkanCommandTests,WhatsCanvasVulkanImageColorTests, andWhatsCanvasVulkanGradientTests, ASan-clean. Text commands are translated as vector triangle geometry: WhatsCanvas tessellates glyph outlines into local-space triangles, while glyph-atlas text is handled through sampled image commands. Both paths become solid-color or shader-gradient fills using the Vulkan text/image pipelines, with the gradient evaluated in raw local space to match the OpenGL text shader (WhatsCanvasVulkanTextTests). Analytic-AA edge coverage feathers solid fills to match OpenGL (WhatsCanvasVulkanAATests). All 14 Canvas blend modes (SrcOver/Src/Dst/Clear/SrcIn/DstIn/SrcOut/DstOut/ SrcAtop/DstAtop/Xor/Add/Multiply/Screen) mirrorglBlendFuncSeparateexactly (WhatsCanvasVulkanBlendModeTests). Gaussian drop shadows translate: the white silhouette is rendered offscreen, separable-Gaussian-blurred (CPU, identical kernel math to the GL passes), and composited as a tinted textured quad in stream order (WhatsCanvasVulkanShadowTests; both path silhouettes -- shapes + vector text -- and bitmap/glyph-atlas image silhouettes). Clipped fills translate: the clip paths are rasterized (with their analytic-AA coverage) into a coverage mask (nested clips intersect). Solid path fills, vector text, points and lines are drawn through the M7 clip pipeline (sampling the mask at each fragment's screen position). Clipped gradient and image fills are rendered in isolation and clipped on the CPU (alpha multiplied by the coverage) then composited -- mirroring the GL clip-mask fragment path (WhatsCanvasVulkanClipCommandTestscovers fills, text, points, gradient, and image). - Analytic-AA feathering / multi-stop fragment gradients: the coverage path and fragment-evaluated multi-stop gradients are implemented. Additional representative scenes and device-specific hardening are optional coverage expansion, not missing rendering mechanisms.
- Mipmapped image sampling:
createImageResourceFromImageDatagenerates a full mip chain (blit) when requested, andDrawImageSampling::MipmapLinearselects a trilinear sampler, matching the OpenGL mipmap path (WhatsCanvasVulkanMipmapTests). - Glyph atlas text coverage can grow: Vulkan can render glyph-atlas text quads through the sampled texture pipeline and validates dirty-rect atlas texture updates, but text shadows, clipped atlas text, and larger text pixel-parity scenes still need coverage.
- Not the default backend: normal builds still default to OpenGL or OpenGLES.
- Vulkan presentation scope: Canvas window presentation is implemented for Win32. Other native Vulkan surface types and broader device-loss coverage are optional platform extensions.
- Larger Canvas validation scenes are optional: the visual-parity smoke covers the stable-v1 Vulkan paths. More text and image-effect combinations can be added when a target workload needs them.
- Other native backends: Metal is implemented as a standalone render target and powers the validated iOS host. DirectWrite and CoreText remain text backend adapters; D3D rendering is not part of the stable v1 scope.
Optional future work¶
- Expand representative Canvas-level validation scenes for Vulkan/OpenGL parity.
- Move the remaining arbitrary clipped textured-image fallback onto a dual-texture Vulkan mask pipeline.
- Continue migrating OpenGL execution paths to the shared command encoder in small, testable slices.
- Expand the Win32 Canvas present path into a portable surface-aware path with broader resize and device-loss validation.