1. Overview
dasVulkan is two layers over the same vk.xml registry. The raw layer
(module vulkan) is a mechanical 1:1 binding of the Vulkan C API; the
boost layer (module vulkan_boost and its siblings) is the idiomatic
daslang surface documented on this site. The boost layer is itself generated
from vk.xml wherever the transform is mechanical, and hand-written where it
encodes judgement.
1.1. The raw layer
require vulkan gives you the entire Vulkan surface — every Vk* struct as
a daslang structure, every enum, every VkFlags as a daslang bitfield,
and ~820 command wrappers dispatching through volk. These names match the C API
exactly, so the
Vulkan specification
is their reference. You rarely call the raw layer directly — the boost layer
wraps the common paths — but it is always available as an escape hatch.
1.2. The boost layer
Five modules, re-exported together by require vulkan/vulkan_boost:
vulkan_handlesOne RAII wrapper struct per Vulkan handle. The wrapper owns its handle and destroys it in
finalizewhen anvar inscopeowner leaves scope.vulkan_commandsThe
create_*/allocate_*creators that return those owning wrappers.vulkan_structs(generated, not enumerated here)An idiomatic view struct for every
Vk*struct that carries arrays, handles, or strings:array<T>fields replace count+pointer pairs,stringreplacesconst char*, boost handle types replace raw ones, andsType/pNext/pAllocatorare erased. A hidden scratch region marshals the view back to the raw struct on demand. There are ~1000 of these; they shadow the Vulkan structs 1:1, so the spec documents their fields.vulkan_ctors(generated, not enumerated here)sType-filling constructors for every struct that carries ansType.vulkan_cmds(generated, not enumerated here)Plain boost wrappers for the
vkCmd*/vkGet*/vkQueue*command set — boost handle/struct arguments, defaulted parameters, two-call enumeration auto-collapsed toarray<T>. ~400 of these, again 1:1 with the spec.vulkan_boost/vulkan_windowThe hand-written core: device selection, combined builders, block brackets, and the windowing/swapchain/present helpers.
1.3. Ownership
Handle wrappers are value types whose finalize destroys the handle. The
rules:
Declare every owner with
var inscopeso it finalizes (in reverse order) at scope exit. A plainvar x <- create_*()will leak — handles are raw pointers with no GC safety net.Parent handles are stored as raw handles inside the wrapper, never as nested wrappers, so a wrapper copy never duplicates ownership of its parent.
For an intentional non-owning alias, use
weak_copy(it clears the_needs_deleteflag on the copy).
1.4. A worked example
The offscreen triangle, in full boost form — no hand-set sType, no user
unsafe / addr, no manual destroy:
var inscope instance <- create_instance("dasVulkan triangle", make_api_version(1u, 3u, 0u))
volkLoadInstance(boost_value_to_vk(instance))
let phys = select_physical_device(instance)
let gfx = select_graphics_queue_family(phys)
var inscope device <- create_device(phys, gfx)
volkLoadDevice(boost_value_to_vk(device))
let queue = get_device_queue(device, gfx, 0u)
var inscope target <- build_offscreen_target(device, phys, WIDTH, HEIGHT, FORMAT)
var inscope render_pass <- create_render_pass_single_color(device, FORMAT)
// CreateInfo view structs use the named-argument constructor. Handle fields
// take a weak_copy (a non-owning alias — the create_* keeps ownership);
// non-copyable array fields are move-initialized with <-. Note the C-style
// field names (renderPass, pAttachments) — see the p-prefix note above.
var fbci = FramebufferCreateInfo(renderPass = weak_copy(render_pass),
pAttachments <- [weak_copy(target.view)],
width = uint(WIDTH), height = uint(HEIGHT), layers = 1u)
var inscope framebuffer <- create_framebuffer(device, fbci)
var inscope vert <- create_shader_module(device, vert_code)
var inscope frag <- create_shader_module(device, frag_code)
var inscope layout <- create_pipeline_layout(device)
var inscope pipeline <- create_graphics_pipeline_simple(device, render_pass, layout, vert, frag, WIDTH, HEIGHT)
var inscope readback <- create_host_buffer(device, phys, buf_size)
var inscope pool <- create_command_pool(device, command_pool_info(gfx))
run_cmd_sync(device, pool, queue) $(cmd) {
record_render_pass(cmd, render_pass, framebuffer, full_area(WIDTH, HEIGHT),
clear_color(0.1f, 0.1f, 0.15f, 1.0f)) {
cmd_bind_pipeline(cmd, pipeline)
cmd_draw(cmd, 3u)
}
copy_image_to_buffer(cmd, target.image, readback, WIDTH, HEIGHT)
}
var pixels : array<uint8>
map_memory_to_array(device, readback.memory, buf_size) $(m) { pixels := m }
// ... write PPM ...
// finalizers destroy each inscope owner in reverse order — no cleanup block.
The complete, compiling versions live under examples/ in the repository
(offscreen_triangle_boost.das, compute_boost.das, enumerate.das,
window_triangle.das; offscreen_triangle.das and compute.das are the
raw references).