vulkane 0.6.0

Vulkan API bindings generated entirely from vk.xml, with a complete safe RAII wrapper covering compute and graphics: instance/device/queue, buffer, image, sampler, render pass, framebuffer, graphics + compute pipelines, swapchain, a VMA-style sub-allocator with TLSF + linear pools and defragmentation, sync primitives (fences, binary + timeline semaphores, sync2 barriers), query pools, and optional GLSL/WGSL/HLSL→SPIR-V compilation via naga or shaderc. Supports Vulkan 1.2.175 onward — swap vk.xml and rebuild.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# Vulkane

Vulkan for Rust: complete bindings generated from the official `vk.xml`
specification, plus a safe RAII wrapper that covers compute and graphics
end-to-end — from instance creation through shadow mapping and deferred
shading.

[![CI](https://github.com/ciresnave/vulkane/actions/workflows/ci.yml/badge.svg)](https://github.com/ciresnave/vulkane/actions/workflows/ci.yml)
[![Crates.io](https://img.shields.io/crates/v/vulkane.svg)](https://crates.io/crates/vulkane)
[![docs.rs](https://docs.rs/vulkane/badge.svg)](https://docs.rs/vulkane)
[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)

## What is Vulkane?

Vulkane generates **complete** Vulkan API bindings from `vk.xml`, the
official machine-readable specification maintained by Khronos. Every
type, constant, struct, enum, function pointer, and dispatch table is
derived at build time. **Nothing is hardcoded.**

To target a different Vulkan version, swap `vk.xml` and rebuild.

Vulkane exposes Vulkan through two complementary APIs:

- **`vulkane::raw`** — direct FFI bindings, exactly as the spec
  defines them. Maximum control, zero overhead.
- **`vulkane::safe`** — RAII wrappers with automatic cleanup,
  `Result`-based error handling, typed flags, and convenience
  helpers. Covers compute **and** graphics:

  - **Instance / Device**`Instance`, `PhysicalDevice`,
    `PhysicalDeviceGroup`, `Device`, `Queue`, `DeviceFeatures`
    builder (1.0 / 1.1 / 1.2 / 1.3 features).
  - **Memory**`Buffer`, `Image`, `ImageView`, `Sampler`,
    `DeviceMemory`, plus a **VMA-style sub-allocator** (`Allocator`)
    with TLSF + linear pools, custom pools, dedicated allocations,
    persistent mapping, defragmentation, and budget queries.
  - **Convenience helpers**`Buffer::new_bound`,
    `Image::new_2d_bound`, `Queue::upload_buffer<T>`,
    `Queue::upload_image_rgba`, `Queue::one_shot` — collapse the
    5-step allocate-bind pattern into one call.
  - **Compute**`ComputePipeline`, `PipelineLayout`,
    `DescriptorSet`, `ShaderModule`, specialization constants,
    pipeline cache, push constants.
  - **Graphics**`RenderPass` (with `simple_color` shortcut),
    `Framebuffer`, `GraphicsPipelineBuilder` (depth bias, `CompareOp`,
    `InputRate`, multi-attachment blend, dynamic viewport/scissor),
    `Surface` (Win32 / Wayland / Xlib / Xcb / Metal), `Swapchain`.
  - **Synchronization** — typed `PipelineStage` / `AccessFlags`
    (plus 64-bit `PipelineStage2` / `AccessFlags2` for Sync2),
    `Fence`, `Semaphore` (binary + timeline), `ImageBarrier::color`
    / `::depth`, `ClearValue`, `QueryPool`.
  - **Derive macros**`#[derive(Vertex)]` auto-generates vertex
    input layouts from `#[repr(C)]` structs (optional `derive`
    feature).
  - **Raw escape hatch**`Device::dispatch()` /
    `Instance::dispatch()` expose the full dispatch tables so you
    can drop to raw Vulkan for anything the safe wrapper doesn't
    cover yet.

## Quick Start

```toml
[dependencies]
vulkane = { version = "0.4", features = ["fetch-spec"] }
```

```rust
use vulkane::safe::{
    ApiVersion, Buffer, BufferCreateInfo, BufferUsage, CommandPool,
    DeviceCreateInfo, DeviceMemory, Fence, Instance, InstanceCreateInfo,
    MemoryPropertyFlags, PipelineStage, AccessFlags, QueueCreateInfo,
    QueueFlags,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let instance = Instance::new(InstanceCreateInfo {
        application_name: Some("hello-vulkane"),
        api_version: ApiVersion::V1_0,
        ..Default::default()
    })?;

    let physical = instance
        .enumerate_physical_devices()?
        .into_iter()
        .find(|pd| pd.find_queue_family(QueueFlags::TRANSFER).is_some())
        .ok_or("no compatible GPU")?;
    let qf = physical.find_queue_family(QueueFlags::TRANSFER).unwrap();

    let device = physical.create_device(DeviceCreateInfo {
        queue_create_infos: &[QueueCreateInfo::single(qf)],
        ..Default::default()
    })?;
    let queue = device.get_queue(qf, 0);

    // One-call buffer allocation (no manual memory_requirements + find_type + bind).
    let (buffer, mut memory) = Buffer::new_bound(
        &device, &physical,
        BufferCreateInfo { size: 1024, usage: BufferUsage::TRANSFER_DST },
        MemoryPropertyFlags::HOST_VISIBLE | MemoryPropertyFlags::HOST_COHERENT,
    )?;

    // One-shot command recording + submit + wait.
    queue.one_shot(&device, qf, |rec| {
        rec.fill_buffer(&buffer, 0, 1024, 0xDEADBEEF);
        rec.memory_barrier(
            PipelineStage::TRANSFER, PipelineStage::HOST,
            AccessFlags::TRANSFER_WRITE, AccessFlags::HOST_READ,
        );
        Ok(())
    })?;

    let mapped = memory.map()?;
    assert_eq!(&mapped.as_slice()[..4], &0xDEADBEEFu32.to_ne_bytes());
    println!("GPU filled the buffer with 0xDEADBEEF — it works!");
    Ok(())
}
```

## Bundled Examples

15 examples ship with the crate, from basic compute through advanced
graphics techniques. All are headless (runnable in CI) except
`windowed_triangle`.

| Example | Technique |
| --- | --- |
| [`device_info`]vulkane/examples/device_info.rs | Raw API: instance, physical device enumeration, queue families |
| [`fill_buffer`]vulkane/examples/fill_buffer.rs | Safe API: `vkCmdFillBuffer` round trip |
| [`compute_square`]vulkane/examples/compute_square.rs | Compute: SPIR-V, descriptor set, pipeline, dispatch, verify |
| [`compute_image_invert`]vulkane/examples/compute_image_invert.rs | Compute: 2D storage image, layout transitions, per-pixel verify |
| [`compile_shader`]vulkane/examples/compile_shader.rs | Compile GLSL/WGSL → SPIR-V via naga (`--features naga`) |
| [`headless_triangle`]vulkane/examples/headless_triangle.rs | Graphics: render pass, pipeline, draw, readback |
| [`textured_quad`]vulkane/examples/textured_quad.rs | Graphics: texture upload, sampler, WGSL fragment shader |
| [`windowed_triangle`]vulkane/examples/windowed_triangle.rs | Windowed: winit + surface + swapchain + present loop |
| [`buffer_upload`]vulkane/examples/buffer_upload.rs | `Queue::one_shot` staging upload pattern |
| [`allocator_compute`]vulkane/examples/allocator_compute.rs | `Allocator::create_buffer` — 2 lines vs 5 |
| [`raw_interop`]vulkane/examples/raw_interop.rs | `Device::dispatch()` + `.raw()` escape hatch |
| [`depth_prepass`]vulkane/examples/depth_prepass.rs | Depth-only pass + color EQUAL — early-Z prepass |
| [`instanced_mesh`]vulkane/examples/instanced_mesh.rs | 100 triangles via `InputRate::INSTANCE` |
| [`shadow_map`]vulkane/examples/shadow_map.rs | Two-pass shadow mapping: depth bias, comparison sampler, uniform buffers |
| [`deferred_shading`]vulkane/examples/deferred_shading.rs | G-buffer (3 MRT) + fullscreen Phong lighting pass |
| [`derive_vertex`]vulkane/examples/derive_vertex.rs | `#[derive(Vertex)]` auto-generated vertex layouts (`--features derive`) |

```bash
cargo run -p vulkane --features fetch-spec --example headless_triangle
cargo run -p vulkane --features fetch-spec --example shadow_map
cargo run -p vulkane --features fetch-spec,derive --example derive_vertex
```

## `#[derive(Vertex)]`

Enable the `derive` feature to auto-generate vertex input layouts:

```toml
vulkane = { version = "0.4", features = ["fetch-spec", "derive"] }
```

```rust
use vulkane::Vertex;

#[derive(Vertex, Clone, Copy)]
#[repr(C)]
struct MyVertex {
    position: [f32; 3],  // R32G32B32_SFLOAT, location 0
    normal:   [f32; 3],  // R32G32B32_SFLOAT, location 1
    uv:       [f32; 2],  // R32G32_SFLOAT,    location 2
}

// In pipeline setup:
let bindings = [MyVertex::binding(0)];
let attributes = MyVertex::attributes(0);
builder.vertex_input(&bindings, &attributes)
```

Strides, offsets, and Vulkan format enums are computed at compile time.
Supports `f32`, `[f32; 2..4]`, `u32`, `[u32; 2..4]`, `i32`,
`[i32; 2..3]`, `[u8; 4]`, `u16`, `i16`. For per-instance data, use
`MyStruct::instance_binding(n)` instead of `::binding(n)`.

## Precompiled shader registry

For applications that ship precompiled `.spv` artifacts (embedded with
[`include_bytes!`] or shipped alongside the binary),
[`vulkane::safe::ShaderRegistry`] gives you a small, shared abstraction
for looking up shaders by name — plus an optional runtime disk
override for shader developers iterating without a full rebuild.

```rust
use vulkane::safe::{ShaderRegistry, ShaderSource};

// Embedded at compile time. `include_bytes!` resolves paths relative
// to the file that invokes it, so the macro call lives in your crate.
const EMBEDDED: &[ShaderSource] = &[
    ShaderSource { name: "doubler", spv: include_bytes!("shaders/doubler.spv") },
    ShaderSource { name: "reduce",  spv: include_bytes!("shaders/reduce.spv")  },
];

fn app_shaders() -> ShaderRegistry {
    ShaderRegistry::new()
        .with_embedded(EMBEDDED)
        .with_env_override("MY_APP_SHADER_OVERRIDE_DIR")
}

// Later, when you have a Device:
let module = app_shaders().load_module(&device, "doubler")?;
```

Lookup order when an override var is configured:

1. `$MY_APP_SHADER_OVERRIDE_DIR/doubler.spv` on disk (if the directory
   and file both exist).
2. Fall back to the embedded table.

`ShaderRegistry::load(name)` returns the SPIR-V bytes,
`load_words(name)` returns a `Vec<u32>` ready for
[`ShaderModule::from_spirv`], and `load_module(&device, name)` wraps
the whole pipeline. Lookup errors flow through
[`ShaderLoadError`](https://docs.rs/vulkane/latest/vulkane/safe/enum.ShaderLoadError.html)
into the unified `Error::ShaderLoad` variant.

[`include_bytes!`]: https://doc.rust-lang.org/stable/std/macro.include_bytes.html

## Runtime shader compilation

Vulkane offers two optional, independent back-ends for compiling shader
source to SPIR-V at runtime. Enable one, the other, or both — they are
completely separate modules.

### `naga` — pure Rust (WGSL + subset of GLSL)

Zero external build dependencies. Best choice when:

- You want a pure-Rust build (no CMake, no C++ toolchain).
- You are targeting modern GLSL or WGSL.
- WGSL's combined image-samplers fit your rendering code.

```toml
vulkane = { version = "0.4", features = ["naga"] }
```

```rust
use vulkane::safe::naga::compile_glsl;
use naga::ShaderStage;

let spirv = compile_glsl(
    r#"#version 450
       layout(local_size_x = 64) in;
       layout(set = 0, binding = 0, std430) buffer Data { uint v[]; };
       void main() { v[gl_GlobalInvocationID.x] *= 2; }"#,
    ShaderStage::Compute,
)?;
// Pass straight into ShaderModule::from_spirv(&device, &spirv).
```

### `shaderc` — Khronos glslang (full GLSL + HLSL)

Wraps the Khronos reference compiler. Best choice when:

- You need **full GLSL** support (`#include`, `GL_*` extensions, older
  core versions, legacy sample shaders).
- You are compiling **HLSL** for a Vulkan target.
- You want optimization passes (`OptimizationLevel::Size` or
  `Performance`) or explicit macro defines / include resolution.

```toml
vulkane = { version = "0.4", features = ["shaderc"] }
```

```rust
use vulkane::safe::shaderc::{compile_glsl, ShaderKind};

let spirv = compile_glsl(
    r#"#version 450
       layout(local_size_x = 64) in;
       void main() {}"#,
    ShaderKind::Compute,
    "doubler.comp",   // virtual file name (used in error messages and #include resolution)
    "main",           // entry-point name
)?;
```

For fine-grained control — HLSL input, optimization level, macro
defines, include callbacks, target Vulkan version — use
`compile_with_options`:

```rust
use vulkane::safe::shaderc::{compile_with_options, ShaderKind, SourceLanguage};
use shaderc::OptimizationLevel;

let spirv = compile_with_options(
    hlsl_source,
    ShaderKind::Fragment,
    "shader.hlsl",
    "main",
    |opts| {
        opts.set_source_language(SourceLanguage::HLSL);
        opts.set_optimization_level(OptimizationLevel::Size);
        opts.add_macro_definition("USE_PBR", Some("1"));
    },
)?;
```

#### Build requirements for `shaderc`

`shaderc-rs` locates libshaderc in this order:

1. **`SHADERC_LIB_DIR`** env var — point to a directory containing
   `libshaderc_combined`.
2. **`VULKAN_SDK`** env var — installing the
   [LunarG Vulkan SDK]https://vulkan.lunarg.com/ sets this
   automatically, and ships a prebuilt `libshaderc_combined`. **This is
   the easiest path.**
3. **`pkg-config`** / system libraries (typical on Linux distros that
   package `shaderc`).
4. **Source build fallback** — compiles glslang from C++ source.
   Requires CMake ≥ 3.17 (newer CMake may need
   `CMAKE_POLICY_VERSION_MINIMUM=3.5` until shaderc-sys updates its
   pinned sources), Python 3, and a working C++ toolchain. First build
   takes 1–3 minutes.

If neither the SDK nor a system package is available and you don't
want to build from source, use the `naga` feature instead.

#### When to pick which

| Need                                   | `naga` | `shaderc` |
| -------------------------------------- | :----: | :-------: |
| Pure Rust build (no C++ toolchain)     |||
| Modern GLSL (core, no extensions)      |||
| Full GLSL (`#include`, `GL_*` exts)    |||
| HLSL input                             |||
| WGSL input                             |||
| Optimization passes                    |||
| Smallest binary / fastest cold build   |||

Both features can be enabled simultaneously — pick per shader at the
call site.

### `slang` — Khronos Slang (modules, generics, autodiff)

[Slang](https://shader-slang.com/) is Khronos's modern shading language.
Pick it when you need:

- **Modules, generics, and interfaces** — real code reuse in shaders.
- **Automatic differentiation** — tag a function `[Differentiable]` and
  the compiler generates forward (`__fwd_diff`) and backward
  (`__bwd_diff`) variants. A single kernel definition yields both the
  forward and backward SPIR-V, which is a game-changer for ML compute
  on Vulkan.
- **Explicit entry points** — one `.slang` file can expose many compute
  / vertex / fragment entry points; compile each to its own SPIR-V blob
  from the same parsed module.

```toml
vulkane = { version = "0.4", features = ["slang"] }
```

Slang compilation is stateful — a session holds the SPIR-V target and
search paths, modules are loaded by name through those paths, and each
entry point is compiled on demand:

```rust
use vulkane::safe::slang::SlangSession;

// Given kernels/autodiff.slang with [Differentiable] functions and
// both `forward` and `backward` compute entry points:
let session = SlangSession::with_search_paths(&["kernels"])?;
let module  = session.load_file("autodiff")?;                // parses once
let fwd     = module.compile_entry_point("forward")?;        // Vec<u32> SPIR-V
let bwd     = module.compile_entry_point("backward")?;       // Vec<u32> SPIR-V
```

Or for a one-off compile:

```rust
use vulkane::safe::slang::compile_slang_file;

let spirv = compile_slang_file("kernels", "trivial", "main")?;
```

#### Build requirements for `slang`

`shader-slang` locates the Slang compiler via:

1. **`VULKAN_SDK`** env var — the LunarG Vulkan SDK ships `slangc` and
   the Slang runtime library. **Easiest path** (same as `shaderc`).
2. **`SLANG_DIR`** env var — a standalone Slang install from
   [shader-slang/slang releases]https://github.com/shader-slang/slang/releases.
3. **`SLANG_INCLUDE_DIR`** + **`SLANG_LIB_DIR`** — split paths.

At runtime, `slang.dll` / `libslang.so` / `libslang.dylib` must be
discoverable (same directory as the executable, or on the library
search path).

#### Current limitation

`shader-slang` 0.1.0 on crates.io only supports loading Slang modules
from disk (via search-path resolution). Inline source strings will be
supported once a newer `shader-slang` release exposes them; until then,
keep your Slang code in `.slang` files.

## Why Vulkane over ash?

| Aspect | Vulkane | ash |
| --- | --- | --- |
| Source of truth | `vk.xml` at build time | Hand-curated bindings |
| New Vulkan version | Swap vk.xml, rebuild | Wait for crate update |
| Safe wrapper | Built-in: compute + graphics + allocator + sync | Raw FFI only |
| Sub-allocator | TLSF + linear pools + defrag built in | BYO (`gpu-allocator`) |
| Vertex layout | `#[derive(Vertex)]` | Manual |
| Pipeline builder | Depth bias, CompareOp, multi-attach, dynamic viewport | N/A (raw structs) |
| Allocation helpers | `Buffer::new_bound`, `Queue::upload_buffer<T>` | N/A |
| GLSL/WGSL→SPIR-V | Optional `naga` (pure Rust), `shaderc` (glslang), or `slang` (Slang + autodiff) feature | N/A |
| Raw escape hatch | `device.dispatch()` + `.raw()` | N/A (always raw) |
| Maturity | New (0.4) | Battle-tested |

## Features

| Feature | Description |
| --- | --- |
| `build-support` (default) | XML parsing and code generation during build |
| `fetch-spec` | Auto-download vk.xml from Khronos GitHub |
| `naga` | `compile_glsl` + `compile_wgsl` → SPIR-V at runtime (pure Rust) |
| `shaderc` | `compile_glsl` / `compile_with_options` → SPIR-V via Khronos glslang (full GLSL + HLSL) |
| `slang` | `SlangSession` / `compile_slang_file` → SPIR-V via Khronos Slang (modules, generics, autodiff) |
| `derive` | `#[derive(Vertex)]` proc macro for vertex layouts |

## Providing vk.xml

1. **`VK_XML_PATH`** env var — point to any local `vk.xml`
2. **Local copy** at `spec/registry/Vulkan-Docs/xml/vk.xml`
3. **Auto-download** (`--features fetch-spec`), optionally pinned
   with `VK_VERSION=1.3.250`

## Supported Vulkan Versions

**1.2.175** through the latest release. The minimum is set by the
`VK_MAKE_API_VERSION` macros introduced in that version.

## Architecture

```text
vk.xml → vulkan_gen (roxmltree parser + codegen) → vulkane crate
                                                    ├── raw/   (generated FFI bindings)
                                                    ├── safe/  (RAII wrapper)
                                                    └── vulkane_derive (proc macros)
```

## License

Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE)
- MIT License ([LICENSE-MIT]LICENSE-MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in vulkane by you, as defined in the Apache-2.0
license, shall be dual-licensed as above, without any additional terms
or conditions.