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. See vulkan-backend-roadmap.md and
ADR-006.
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).
Known gaps and why¶
- Shared command layer still has room to grow: 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/vulkan_canvas_present. The lower-levelexamples/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. Remaining work is broader representative-scene parity and device-specific hardening, not the basic rendering mechanism.
- 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 path needs broader scenes: 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.
- Cross-platform Canvas presentation is incomplete: current Vulkan Canvas window presentation is wired for Win32. Other native surface types and broader resize/device-loss behavior remain future work.
- Larger Canvas validation scenes remain: the visual parity smoke now covers the core P0 Vulkan paths, but it should still grow into larger representative Canvas scenes and more text / image-effect combinations.
- Native platform backends remain separate work: Metal is still reserved, and DirectWrite/CoreText are text-backend adapter slots rather than render backends.
Next steps¶
- 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.