shdrlib 0.1.0

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
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
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# Copilot Instructions for shdrlib


## Project Overview

**shdrlib** is a three-tiered Vulkan shader compilation and rendering framework built in Rust. This is a **backend library** focused on shader compilation and rendering, not a game engine or windowing framework.

all documentation is stored in the /md/{tier} directory [
    core/[
        CORE_DEV.md
        CORE_IMPLEMENTATION.md
    ]
    ex/[
        EX_DEV.md
        EX_IMPLEMENTATION_ROADMAP.md
        EX_ARCHITECTURE.md
        EX_QUICK_DECISIONS.md
    ]
    ez/
]





## Architecture & Design Philosophy


### Three-Tier System

This crate implements a strict three-tier abstraction model:

1. **CORE (Tier 0)**: Thin wrappers around `ash` (Vulkan) and `glslang`/`shaderc` (shader compilation)
   - "The assembly language of the library"
   - Minimal abstraction, maximum control
   - **Does NOT manage lifetimes or resource dependencies**
   - Objects are "dumb wrappers" with basic `Drop` cleanup
   - **Unsafe to use alone** - can cause UB if misused

2. **EX (Tier 1 - Explicit)**: Ergonomic layer with explicit control
   - `ShaderManager`: Owns objects and manages lifetimes correctly
   - `RuntimeManager`: Handles rendering operations with Arc'd resources
   - Explicit configuration with minimal magic
   - Builder patterns for complex object construction

3. **EZ (Tier 2 - Easy)**: High-level abstraction with intelligent defaults
   - Wraps both EX managers and CORE functions
   - Minimal configuration required
   - Sensible defaults using Rust's `Default` trait
   - Optimized for rapid development

### Core Design Principles


#### Zero-Cost Abstractions

- All tiers compile down to identical machine code
- Heavy use of `#[inline]` for wrapper functions
- No runtime overhead compared to raw Vulkan usage
- Aggressive compiler optimizations leveraged

#### Sterile, Persistent, Contained Objects

- **Sterile**: No hidden state, side effects, or magic behavior
- **Persistent**: Consistent behavior across tiers with compile-time guarantees
- **Contained**: Clear boundaries and responsibilities

#### Tier Interoperability

- Users can seamlessly drop down to lower tiers
- EZ functions accept CORE objects
- EX managers work with CORE primitives
- No lock-in at any abstraction level

## Code Style & Conventions


### Naming Conventions

- **Modules**: `snake_case` (e.g., `shader_manager`, `pipeline_builder`)
- **Types**: `PascalCase` (e.g., `ShaderManager`, `RuntimeManager`)
- **Functions**: `snake_case` (e.g., `create_pipeline`, `find_memory_type`)
- **Constants**: `SCREAMING_SNAKE_CASE` (e.g., `DEFAULT_TIMEOUT`, `MAX_DESCRIPTOR_SETS`)
- **Tier prefixes**: Use `core::`, `ex::`, `ez::` module prefixes

### Error Handling

- Use `thiserror` for all error types
- Errors should be tier-specific:
  - **CORE**: Thin wrappers around Vulkan errors (`VulkanError(vk::Result)`)
  - **EX**: More context added (`ShaderManagerError`, `RuntimeManagerError`)
  - **EZ**: User-friendly messages with recovery suggestions
- Include line numbers for shader compilation errors
- Use `Result<T, E>` for all fallible operations
- Never use `.unwrap()` or `.expect()` in library code (only in examples/tests)

### Ownership & Lifetime Management


#### CORE Tier Rules

- Objects are thin wrappers with basic `Drop` cleanup
- **Do NOT** enforce lifetime dependencies between objects
- **Do NOT** hold references to other CORE objects
- Each object destroys its own Vulkan handle in `Drop`
- Users/EX tier must ensure correct drop order manually

Example CORE pattern:
```rust
pub struct Pipeline {
    pipeline: vk::Pipeline,
    layout: PipelineLayout,  // Pipeline OWNS layout (correct drop order)
    bind_point: vk::PipelineBindPoint,
}
```

#### EX Tier Rules

- `ShaderManager` **OWNS** all CORE objects
- Fields declared in correct destruction order (Rust drops fields in declaration order)
- `RuntimeManager` uses `Arc<T>` for shared ownership
- Explicit lifetime management with clear ownership

Example EX pattern:
```rust
pub struct ShaderManager {
    instance: core::Instance,
    device: core::Device,
    shaders: Vec<core::Shader>,
    pipelines: Vec<core::Pipeline>,
    // Drop order: pipelines → shaders → device → instance
}
```

#### EZ Tier Rules

- Automatic resource management
- Hide lifetime complexity from users
- Sensible defaults for all optional parameters

### Safety & Unsafe Code


#### When to use `unsafe`

- Only at Vulkan FFI boundaries
- Wrapping raw Vulkan function calls
- Interacting with raw pointers from Vulkan

#### Safety Requirements

- Every `unsafe` block MUST have a `// SAFETY:` comment explaining invariants
- Validate Vulkan handles are non-null (in debug builds)
- Never expose raw pointers in public APIs
- Use newtype patterns for Vulkan handles when it improves safety

Example:
```rust
pub fn create_device(&self) -> Result<Device, DeviceError> {
    // SAFETY: instance handle is valid, create_info is properly initialized
    let device = unsafe { 
        self.instance.create_device(physical_device, &create_info, None)
    }?;
    Ok(Device { device })
}
```

### Documentation Standards


#### Rustdoc Requirements

- Every public item MUST have documentation
- CORE tier docs MUST warn about footguns and lifetime issues
- Include usage examples for non-trivial functions
- Link to higher tier alternatives when relevant
- Document all error conditions

Example CORE documentation:
```rust
/// Creates a new shader from GLSL source code.
///
/// # Warnings
/// 
/// This function does NOT enforce that the device is still valid.
/// Dropping the device before the shader causes undefined behavior.
/// Use `ex::ShaderManager` for safe lifetime management.
///
/// # Errors
///
/// Returns `ShaderError::CompilationFailed` if GLSL compilation fails.
/// The error includes line numbers when available.
///
/// # Example
/// 
/// ```rust
/// let shader = Shader::from_glsl(&device, VERTEX_SHADER_SRC, ShaderStage::Vertex)?;
/// ```
pub fn from_glsl(device: &Device, source: &str, stage: ShaderStage) -> Result<Self, ShaderError>
```

### Shader Compilation Patterns


#### GLSL to SPIR-V Pipeline

1. GLSL source → compiler (shaderc or glslang)
2. SPIR-V bytecode generation
3. **OPTIONAL**: Reflection pass for descriptor layouts
4. SPIR-V validation (debug builds only)
5. Shader module creation
6. **SPIR-V dropped** - module is all you need

#### Do NOT Store SPIR-V

```rust
// ❌ BAD - wastes memory
pub struct Shader {
    module: vk::ShaderModule,
    spirv: Vec<u32>,  // Don't store this!
}

// ✅ GOOD - minimal footprint
pub struct Shader {
    module: vk::ShaderModule,
    stage: vk::ShaderStageFlags,
    entry_point: String,
}
```

#### Shader Reflection

- Perform reflection BEFORE creating shader module
- Extract descriptor set layouts, push constants, input/output variables
- Store only the extracted metadata, not the SPIR-V

### Command Buffer Patterns


#### Accept Raw Vulkan Handles

CommandBuffer methods should accept raw `vk::*` handles, not CORE wrappers:

```rust
// ✅ GOOD - avoids borrow checker hell
impl CommandBuffer {
    pub fn bind_pipeline(&self, bind_point: vk::PipelineBindPoint, pipeline: vk::Pipeline);
    pub fn bind_descriptor_sets(&self, layout: vk::PipelineLayout, sets: &[vk::DescriptorSet]);
}

// ❌ BAD - causes borrow checker issues
impl CommandBuffer {
    pub fn bind_pipeline(&self, bind_point: vk::PipelineBindPoint, pipeline: &Pipeline);
}
```

### Memory Management


#### No Smart Allocator at CORE

- Just wrap `vkAllocateMemory` directly
- Provide helper functions, not allocator structs
- No tracking of allocation state

```rust
// ✅ GOOD - simple function
pub fn allocate_buffer(
    device: &Device,
    size: u64,
    usage: vk::BufferUsageFlags,
    properties: vk::MemoryPropertyFlags,
) -> Result<Buffer, MemoryError>

// ❌ BAD - implies state tracking
pub struct MemoryAllocator {
    allocations: Vec<Allocation>,
}
```

#### Buffer & Image Patterns

- `Buffer` owns its memory
- `Image` **always** owns an `ImageView` (not optional)
- Memory is bound at creation time

### Descriptor Patterns


#### Clear Update API

Use a dedicated struct for descriptor updates:

```rust
pub struct DescriptorWrite {
    pub set: vk::DescriptorSet,
    pub binding: u32,
    pub descriptor_type: vk::DescriptorType,
    pub resource: DescriptorResource,
}

pub enum DescriptorResource {
    Buffer { buffer: vk::Buffer, offset: u64, range: u64 },
    Image { view: vk::ImageView, layout: vk::ImageLayout, sampler: Option<vk::Sampler> },
}
```

#### DescriptorSet Ownership

- `DescriptorPool` owns all allocated sets
- `DescriptorSet` has NO `Drop` impl (pool handles cleanup)

### Pipeline Patterns


#### Pipeline Owns Layout

```rust
pub struct Pipeline {
    pipeline: vk::Pipeline,
    layout: PipelineLayout,  // Owned - correct drop order guaranteed
    bind_point: vk::PipelineBindPoint,
}
```

This is pragmatic - pipelines and layouts are always used together, and this ensures correct destruction order.

### Render Pass Strategy


#### Prefer Dynamic Rendering

- Use Vulkan 1.3+ dynamic rendering by default
- Support traditional render passes for compatibility
- **Recommend dynamic rendering** in all documentation

```rust
// ✅ PREFERRED - dynamic rendering (Vulkan 1.3+)
command_buffer.begin_rendering(&vk::RenderingInfo {
    render_area: ...,
    color_attachments: &[...],
    depth_attachment: ...,
});

// ⚠️ LEGACY - traditional render passes (still supported)
let render_pass = RenderPass::new(&device, ...)?;
let framebuffer = Framebuffer::new(&device, &render_pass, ...)?;
```

## Testing Standards


### Test Organization

- Unit tests in same file as implementation (using `#[cfg(test)] mod tests`)
- Integration tests in `tests/` directory
- Examples in `examples/` directory

### CORE Tier Testing

- Test that `Drop` properly cleans up (use validation layers)
- Test error paths explicitly
- **Expect crashes if used wrong** - that's the point of CORE
- Use `#[should_panic]` for UB tests (educational)

### EX Tier Testing

- Test safe lifetime management
- Test that managers prevent UB
- Test builder patterns
- Integration tests showing realistic usage

### EZ Tier Testing

- Test default behaviors
- Test common rendering scenarios
- Test error recovery
- End-to-end rendering tests

## Performance Guidelines


### Optimization Priorities

1. Zero allocations in hot paths (rendering loop)
2. Inline wrapper functions liberally with `#[inline]`
3. Use `repr(transparent)` or `repr(C)` for wrapper types
4. Benchmark against raw `ash` usage to ensure zero overhead
5. Profile shader compilation times

### Benchmarking

- Compare CORE wrapper overhead vs raw Vulkan
- Measure EX tier manager overhead (should be zero)
- Track shader compilation performance
- Use `criterion` for microbenchmarks

## Dependencies Management


### Required Dependencies

```toml
[dependencies]
ash = "0.38"              # Vulkan bindings
thiserror = "1.0"         # Error handling
shaderc = "0.8"           # Shader compilation
spirv-reflect = "0.2"     # SPIR-V reflection
```

### Optional Dependencies

- Consider `gpu-allocator` for EX tier (smarter memory management)
- Consider `winit` for examples (windowing - but NOT in the library itself)

### Do NOT Include

- ❌ Windowing libraries (users provide their own)
- ❌ Input handling
- ❌ Asset loading
- ❌ Scene graphs or game engine features

## Common Patterns


### Builder Pattern

Use builders for complex object construction:

```rust
pub struct PipelineBuilder {
    vertex_shader: Option<ShaderStageInfo>,
    fragment_shader: Option<ShaderStageInfo>,
    viewport: Option<vk::Viewport>,
    // ... ~20 pipeline states
}

impl PipelineBuilder {
    pub fn vertex_shader(mut self, shader: &Shader, entry: &str) -> Self {
        self.vertex_shader = Some(ShaderStageInfo { shader, entry });
        self
    }
    
    pub fn build(self, device: &Device) -> Result<Pipeline, PipelineError> {
        // Validate required fields
        // Create pipeline
    }
}
```

### RAII Pattern

Use Rust's `Drop` for automatic cleanup:

```rust
impl Drop for Device {
    fn drop(&mut self) {
        unsafe {
            self.logical_device.destroy_device(None);
        }
    }
}
```

### Newtype Pattern

Use newtypes for type safety:

```rust
pub struct ShaderId(usize);
pub struct PipelineId(usize);

// Type system prevents mixing shader IDs with pipeline IDs
```

## What NOT to Do


### Anti-Patterns to Avoid


#### ❌ Don't Add Magic at CORE Level

```rust
// BAD - too much magic for CORE
pub fn create_vertex_buffer<T>(data: &[T]) -> Result<Buffer, MemoryError>;

// GOOD - just wrap the API
pub fn create_buffer(size: u64, usage: vk::BufferUsageFlags) -> Result<Buffer, MemoryError>;
```

#### ❌ Don't Store Unnecessary Data

```rust
// BAD - wastes memory
pub struct Shader {
    spirv: Vec<u32>,  // Drop after creating module!
}
```

#### ❌ Don't Mix Tier Responsibilities

```rust
// BAD - CORE managing lifetimes (that's EX's job)
pub struct Pipeline {
    device: Arc<Device>,  // No! CORE objects don't know about each other
}
```

#### ❌ Don't Use Default Values at CORE

```rust
// BAD - CORE should be explicit
pub fn create_buffer(size: u64) -> Result<Buffer, MemoryError> {
    create_buffer_impl(size, DEFAULT_USAGE)  // No defaults in CORE!
}
```

#### ❌ Don't Panic in Library Code

```rust
// BAD - never panic
let device = Device::new(...).expect("Failed to create device");

// GOOD - return Result
let device = Device::new(...)?;
```

## Version Control & Commits


### Commit Message Style

- Use conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, etc.
- Include tier in scope: `feat(core): add shader reflection`
- Reference issue numbers when applicable

### Branch Strategy

- `main`: stable, working code
- `dev`: active development
- Feature branches: `feature/tier-name-description`

## AI Assistant Guidelines


When helping with this codebase:

1. **Always respect the three-tier architecture** - don't blur the lines
2. **CORE tier code should be minimal** - resist the urge to add features
3. **Prioritize safety at EX/EZ tiers** - but not at CORE
4. **Document lifetime footguns clearly** - users need to know the risks
5. **Suggest dropping to lower tiers** when users need more control
6. **Reference DEVELOPMENT_PLAN.md and CORE_DEV.md** for design decisions
7. **Use rustdoc format** for all documentation
8. **Include safety comments** for all `unsafe` blocks
9. **Validate against Rust idioms** - this is idiomatic Rust, not C++ in Rust syntax
10. **Think zero-cost** - if a change adds overhead, it needs strong justification

## Special Considerations


### Validation Layers

- Auto-enable in debug builds: `#[cfg(debug_assertions)]`
- Allow user override in release builds
- Include debug messenger in CORE tier

### Windows vs Cross-Platform

- Primary development on Windows
- Use PowerShell-compatible commands
- Test on Linux/macOS when possible
- Platform-specific code behind `cfg` flags

### Rust Edition

- Using Rust 2024 edition (`edition = "2024"`)
- Leverage latest Rust features when stable
- Document MSRV (Minimum Supported Rust Version) when determined

---

## Quick Reference


### When to Use Each Tier


- **Use CORE** when:
  - Building your own manager/framework
  - Need absolute control
  - Integrating with existing Vulkan code
  - You know exactly what you're doing

- **Use EX** when:
  - Need explicit control with safety guarantees
  - Building a custom renderer
  - Want ergonomic APIs without magic
  - Need to mix and match components

- **Use EZ** when:
  - Building an app/game quickly
  - Want sensible defaults
  - Don't want to think about Vulkan details
  - Rapid prototyping

### Key Mantras


1. **CORE is just wrappers** - Think "ash + better types"
2. **CORE is unsafe to use alone** - You can easily create UB
3. **EX makes it safe** - ShaderManager owns everything correctly
4. **EZ makes it easy** - One-liners for common tasks
5. **Zero-cost abstractions** - All tiers compile to identical code
6. **Progressive disclosure** - Start simple, drop down when needed
7. **Backend focus** - No windowing, input, or game engine features

---

**Remember**: This is a Vulkan rendering library, not a game engine. Stay focused on shader compilation and rendering. Let users bring their own window/input handling.