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
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
# CORE Tier Development Documentation


## Overview

The CORE tier (Tier 0) is the foundational layer of shdrlib, providing **marginally more ergonomic wrappers** around `ash` (Vulkan) and `glslang` (shader compilation). This tier is the "assembly language" of the library - it just wraps the raw objects and provides basic type safety. **CORE does NOT manage lifetimes or resource dependencies** - that's the job of the EX tier (ShaderManager/RuntimeManager).

Think of CORE as: `ash` + `glslang` + Rust types + basic `Drop` cleanup = CORE objects

## Design Philosophy


### Sterile, Persistent, Contained

- **Sterile**: No hidden state, side effects, or magic behavior
- **Persistent**: Objects are just dumb wrappers - they do what you tell them
- **Contained**: Each object wraps its corresponding Vulkan/GLSL thing

### Lifetime Management = NOT CORE'S JOB

**IMPORTANT**: CORE objects do NOT enforce correct destruction order or lifetime dependencies!
- Creating a Pipeline after dropping the Device? That's UB, but CORE won't stop you
- **EX tier (ShaderManager)** owns objects and manages lifetimes correctly
- **EX tier (RuntimeManager)** Arc's everything for safe sharing
- CORE is for people who know what they're doing OR for EX to build on top of

### Basic Drop Cleanup

CORE objects implement `Drop` to clean up their Vulkan handles:
- Each object destroys its own handle
- No smart dependency tracking
- User/EX tier must ensure correct drop order

### Safety Boundaries

- Minimal `unsafe` usage, isolated to Vulkan FFI boundaries
- All public APIs are safe Rust (but can still cause UB if misused)
- `unsafe` blocks are well-documented with safety invariants
- Validation layers enabled in debug builds

## Core Objects


All CORE objects are just thin wrappers around their Vulkan equivalents. They provide:
- Type safety
- Basic `Drop` cleanup
- Rust-friendly APIs
- **NO lifetime management** (that's EX tier's job)

### 1. Instance (`core::instance`)

Wraps `ash::Instance` - the Vulkan instance.

**Responsibilities**:
- Create Vulkan instance
- Enumerate physical devices
- Handle instance-level extensions
- Toggle validation layers

**Key Types**:
```rust
pub struct Instance {
    instance: ash::Instance,
    entry: ash::Entry,
    #[cfg(debug_assertions)]
    debug_utils: Option<ash::extensions::ext::DebugUtils>,
    #[cfg(debug_assertions)]
    debug_messenger: Option<vk::DebugUtilsMessengerEXT>,
}

pub struct InstanceCreateInfo {
    pub app_name: String,
    pub app_version: u32,
    pub enable_validation: bool,  // Auto-enabled in debug builds
    pub extensions: Vec<String>,
}
```

**Error Handling**:
```rust
pub enum InstanceError {
    CreationFailed(vk::Result),
    ExtensionNotSupported(String),
    LayerNotSupported(String),
    ValidationLayersNotAvailable,
}
```

### 2. Device (`core::device`)

Wraps `vk::Device` - the logical device.

**Responsibilities**:
- Logical device creation
- Store device handle for other objects to use
- Query device properties, memory types, queue families

**Key Types**:
```rust
pub struct Device {
    logical_device: ash::Device,
    physical_device: vk::PhysicalDevice,
    memory_properties: vk::PhysicalDeviceMemoryProperties,
}

pub struct PhysicalDeviceInfo {
    device: vk::PhysicalDevice,
    properties: vk::PhysicalDeviceProperties,
    features: vk::PhysicalDeviceFeatures,
    queue_families: Vec<vk::QueueFamilyProperties>,
    memory_properties: vk::PhysicalDeviceMemoryProperties,
}

impl Device {
    // Query helpers
    pub fn find_memory_type(&self, type_filter: u32, properties: vk::MemoryPropertyFlags) -> Option<u32>;
    pub fn get_queue_family_properties(&self) -> &[vk::QueueFamilyProperties];
    pub fn find_queue_family(&self, flags: vk::QueueFlags) -> Option<u32>;
}
```

**Error Handling**:
```rust
pub enum DeviceError {
    CreationFailed(vk::Result),
    NoSuitableDevice,
    ExtensionNotSupported(String),
    MemoryTypeNotFound,
    QueueFamilyNotFound,
}
```

### 3. Queue (`core::queue`)

Wraps `vk::Queue` - the command submission queue.

**Responsibilities**:
- Get queue from device
- Submit command buffers
- Wait for queue idle

**Key Types**:
```rust
pub struct Queue {
    queue: vk::Queue,
    family_index: u32,
    queue_index: u32,
}
```

**Error Handling**:
```rust
pub enum QueueError {
    SubmitFailed(vk::Result),
    WaitFailed(vk::Result),
}
```

### 4. CommandPool (`core::command`)

Wraps `vk::CommandPool` - allocates command buffers.

**Responsibilities**:
- Create command pool
- Allocate command buffers
- Reset command pool

**Key Types**:
```rust
pub struct CommandPool {
    pool: vk::CommandPool,
    family_index: u32,
}
```

**Error Handling**:
```rust
pub enum CommandPoolError {
    CreationFailed(vk::Result),
    AllocationFailed(vk::Result),
    ResetFailed(vk::Result),
}
```

### 5. CommandBuffer (`core::command`)

Wraps `vk::CommandBuffer` - records GPU commands.

**Responsibilities**:
- Begin/end recording
- Record draw/dispatch/copy commands
- Bind pipelines and resources

**Key Types**:
```rust
pub struct CommandBuffer {
    buffer: vk::CommandBuffer,
    level: vk::CommandBufferLevel,
}

// IMPORTANT: Command recording accepts raw vk::* handles, not CORE wrappers!
// This 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]);
    pub fn bind_vertex_buffers(&self, buffers: &[vk::Buffer]);
    pub fn draw(&self, vertex_count: u32, instance_count: u32, ...);
    // ... etc
}
```

**Error Handling**:
```rust
pub enum CommandBufferError {
    BeginFailed(vk::Result),
    EndFailed(vk::Result),
    InvalidState,
}
```

### 6. Shader (`core::shader`)

Handles GLSL shader compilation and SPIR-V management.

**Responsibilities**:
- GLSL source compilation to SPIR-V
- SPIR-V validation
- Shader module creation
- Shader reflection (optional)

**Key Types**:
```rust
pub struct Shader {
    module: vk::ShaderModule,
    stage: vk::ShaderStageFlags,
    entry_point: String,
    // NOTE: SPIR-V is NOT stored after module creation
    // If you need reflection, do it before dropping the SPIR-V
}

pub struct ShaderCompiler {
    // glslang or shaderc compiler instance
}

pub struct ShaderReflection {
    // Descriptor set layout info
    // Push constant ranges
    // Input/output variables
}
```

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

**Error Handling**:
```rust
pub enum ShaderError {
    CompilationFailed {
        message: String,
        line: Option<u32>,  // Line number if available
    },
    InvalidSpirv,
    ModuleCreationFailed(vk::Result),
    InvalidEntryPoint,
    ReflectionFailed(String),
}
```

### 7. Pipeline (`core::pipeline`)

Represents Vulkan graphics and compute pipelines.

**Responsibilities**:
- Pipeline state object creation
- Pipeline layout management
- Descriptor set layout handling
- Pipeline cache management (optional)

**Key Types**:
```rust
pub struct Pipeline {
    pipeline: vk::Pipeline,
    layout: PipelineLayout,  // Pipeline OWNS its layout
    bind_point: vk::PipelineBindPoint,
}

pub struct PipelineLayout {
    layout: vk::PipelineLayout,
    descriptor_layouts: Vec<vk::DescriptorSetLayout>,
    push_constant_ranges: Vec<vk::PushConstantRange>,
}

// NOTE: Pipeline owns PipelineLayout
// This is pragmatic - they're always used together anyway
// Layout gets dropped when Pipeline drops (correct order guaranteed)
```

**Pipeline Creation Flow**:
1. Create descriptor set layouts
2. Create pipeline layout
3. Configure pipeline state
4. Compile pipeline
5. Cache pipeline (optional)

**Error Handling**:
```rust
pub enum PipelineError {
    CreationFailed(vk::Result),
    InvalidLayout,
    ShaderStageMismatch,
    DescriptorLayoutInvalid,
}
```

### 8. Memory (`core::memory`)

Low-level memory management primitives.

**Responsibilities**:
- Device memory allocation
- Buffer creation and binding
- Image creation and binding
- Memory mapping utilities

**Key Types**:
```rust
pub struct Buffer {
    buffer: vk::Buffer,
    memory: vk::DeviceMemory,
    size: vk::DeviceSize,
    usage: vk::BufferUsageFlags,
}

pub struct Image {
    image: vk::Image,
    memory: vk::DeviceMemory,
    view: vk::ImageView,  // Always created - images need views to be used
    extent: vk::Extent3D,
    format: vk::Format,
}

// NOTE: No MemoryAllocator struct - just functions
// Allocator implies state tracking, which CORE doesn't do

// Memory allocation functions
pub fn allocate_buffer(
    device: &Device,
    size: u64,
    usage: vk::BufferUsageFlags,
    properties: vk::MemoryPropertyFlags,
) -> Result<Buffer, MemoryError>;

pub fn allocate_image(
    device: &Device,
    extent: vk::Extent3D,
    format: vk::Format,
    usage: vk::ImageUsageFlags,
    properties: vk::MemoryPropertyFlags,
) -> Result<Image, MemoryError>;
```

**Memory Management Strategy**:
- Direct Vulkan memory allocation (just wrapping `vkAllocateMemory`)
- User controls everything
- No tracking of map state (user's responsibility)
- No automatic pooling

**Error Handling**:
```rust
pub enum MemoryError {
    AllocationFailed(vk::Result),
    MapFailed(vk::Result),
    BindFailed(vk::Result),
    OutOfDeviceMemory,
    OutOfHostMemory,
}
```

### 9. Fence (`core::sync`)

Wraps `vk::Fence` - CPU-GPU synchronization.

**Responsibilities**:
- Create fence
- Wait for fence
- Reset fence

**Key Types**:
```rust
pub struct Fence {
    fence: vk::Fence,
}
```

**Error Handling**:
```rust
pub enum FenceError {
    CreationFailed(vk::Result),
    WaitFailed(vk::Result),
    ResetFailed(vk::Result),
}
```

### 10. Semaphore (`core::sync`)

Wraps `vk::Semaphore` - GPU-GPU synchronization.

**Responsibilities**:
- Create semaphore
- Signal/wait operations (via queue submit)

**Key Types**:
```rust
pub struct Semaphore {
    semaphore: vk::Semaphore,
}
```

**Error Handling**:
```rust
pub enum SemaphoreError {
    CreationFailed(vk::Result),
}
```

### 11. Surface (`core::surface`)

Wraps `vk::SurfaceKHR` - window surface for rendering.

**Responsibilities**:
- Create surface from platform-specific handle (Win32, Xlib, Wayland, etc.)
- Query surface capabilities
- **Note**: Users provide window handle from their windowing library (winit, SDL, GLFW, etc.)

**Key Types**:
```rust
pub struct Surface {
    surface: vk::SurfaceKHR,
    surface_loader: ash::extensions::khr::Surface,
}

// Surface is REQUIRED for window rendering (not optional!)
```

**Error Handling**:
```rust
pub enum SurfaceError {
    CreationFailed(vk::Result),
    QueryFailed(vk::Result),
}
```

### 12. Swapchain (`core::swapchain`)

Wraps `vk::SwapchainKHR` - image presentation.

**Responsibilities**:
- Create swapchain
- Acquire next image
- Present image

**Key Types**:
```rust
pub struct Swapchain {
    swapchain: vk::SwapchainKHR,
    swapchain_loader: ash::extensions::khr::Swapchain,
    images: Vec<vk::Image>,
    image_views: Vec<vk::ImageView>,
    format: vk::Format,
    extent: vk::Extent2D,
}

// Swapchain is REQUIRED for Phase 1 - EZ tier needs it for default rendering
```

**Error Handling**:
```rust
pub enum SwapchainError {
    CreationFailed(vk::Result),
    AcquireImageFailed(vk::Result),
    PresentFailed(vk::Result),
    OutOfDate,  // Needs resize
    Suboptimal, // Still works but should resize
}
```

### 13. Descriptor Pool (`core::descriptor`)

Wraps `vk::DescriptorPool` - allocates descriptor sets.

**Responsibilities**:
- Create descriptor pool
- Allocate descriptor sets
- Reset pool

**Key Types**:
```rust
pub struct DescriptorPool {
    pool: vk::DescriptorPool,
}
```

**Error Handling**:
```rust
pub enum DescriptorPoolError {
    CreationFailed(vk::Result),
    AllocationFailed(vk::Result),
    ResetFailed(vk::Result),
}
```

### 14. Descriptor Set (`core::descriptor`)

Wraps `vk::DescriptorSet` - binds resources to shaders.

**Responsibilities**:
- Represent allocated descriptor set
- Update descriptor bindings
- **Note**: DescriptorSet is NOT separately dropped (pool owns them)

**Key Types**:
```rust
pub struct DescriptorSet {
    set: vk::DescriptorSet,
    // No Drop impl - pool owns this
}

pub struct DescriptorSetLayout {
    layout: vk::DescriptorSetLayout,
}

// Descriptor update API
pub fn update_descriptor_sets(
    device: &Device,
    writes: &[DescriptorWrite],
);

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>,
    },
}
```

**Error Handling**:
```rust
pub enum DescriptorError {
    LayoutCreationFailed(vk::Result),
    UpdateFailed(vk::Result),
}
```

### 15. RenderPass (`core::renderpass`) - OPTIONAL

Wraps `vk::RenderPass` - defines rendering structure.

**Note**: Render passes are OPTIONAL in Phase 1. We recommend using **dynamic rendering** (Vulkan 1.3+) instead:
- Simpler API (no render pass objects)
- More flexible
- Modern approach
- Less boilerplate

**If you need traditional render passes**:
```rust
pub struct RenderPass {
    render_pass: vk::RenderPass,
}

pub struct Framebuffer {
    framebuffer: vk::Framebuffer,
    render_pass: vk::RenderPass,  // Reference (not owned)
}
```

**Dynamic Rendering (Recommended)**:
```rust
// No RenderPass/Framebuffer objects needed!
// Just start rendering directly in command buffer:
command_buffer.begin_rendering(&vk::RenderingInfo {
    render_area: ...,
    color_attachments: &[...],
    depth_attachment: ...,
    ...
});
```

**Render Pass Strategy Decision**: 
- **Phase 1**: Support BOTH traditional and dynamic rendering
- **Phase 2**: Focus on dynamic rendering, traditional becomes legacy
- **Phase 3**: Potentially deprecate traditional render passes

## Module Organization


```
src/core/
├── mod.rs              # Public API exports
├── instance.rs         # Instance wrapper
├── device.rs           # Device wrapper
├── queue.rs            # Queue wrapper
├── command.rs          # CommandPool + CommandBuffer
├── shader.rs           # Shader compilation + module + reflection
├── pipeline.rs         # Pipeline objects
├── memory.rs           # Memory + Buffer + Image (no allocator struct)
├── sync.rs             # Fence + Semaphore
├── surface.rs          # Surface wrapper
├── swapchain.rs        # Swapchain wrapper
├── descriptor.rs       # Descriptor pool/set/layout
└── utils.rs            # Helper functions
```

## API Design Principles


### 1. Just Wrap It

CORE objects are thin wrappers - no cleverness:
```rust
// Good: Direct wrapper
pub fn create_buffer(&self, create_info: &vk::BufferCreateInfo) -> Result<Buffer, MemoryError>;

// Also Good: Slightly more ergonomic wrapper
pub fn create_buffer(
    &self,
    size: u64,
    usage: vk::BufferUsageFlags,
) -> Result<Buffer, MemoryError>;

// Bad: Too much magic (save for EX/EZ)
pub fn create_vertex_buffer<T>(data: &[T]) -> Result<Buffer, MemoryError>;
```

### 2. Objects Are Dumb

```rust
// CORE objects don't know about each other
let pipeline = Pipeline::new(...)?;  // Doesn't hold reference to device
let device = Device::new(...)?;

drop(device);  // ⚠️ Pipeline still exists but is now invalid!
// This is OK at CORE level - EX tier prevents this
```

### 3. Minimal Error Context

```rust
// CORE errors are thin wrappers around Vulkan errors
pub enum CoreError {
    VulkanError(vk::Result),
    CompilationFailed(String),  // Only for shader compilation
}

// EX/EZ tiers add better context
```

## Implementation Guidelines


### Safety Requirements

1. All `unsafe` blocks must have a `// SAFETY:` comment explaining why it's safe
2. Validate Vulkan handles are non-null before use (in debug builds)
3. Never expose raw pointers in public APIs
4. Use newtype patterns for Vulkan handles when it improves safety

### What CORE Does NOT Do

- ❌ Lifetime management between objects
- ❌ Automatic resource cleanup order
- ❌ Smart reference counting
- ❌ Validation of object compatibility
- ❌ Default pipeline states
- ❌ Automatic memory pooling

### What CORE DOES Do

- ✅ Wrap Vulkan handles in Rust types
- ✅ Implement `Drop` for each handle
- ✅ Convert Vulkan errors to Rust `Result`
- ✅ Provide slightly more ergonomic function signatures
- ✅ Compile GLSL to SPIR-V

### Testing Strategy

- Unit tests for each module
- Test that `Drop` properly cleans up (use validation layers)
- Test error paths
- Integration tests showing basic usage (but expect crashes if used wrong!)

### Documentation Standards

- Document that CORE does NOT enforce lifetimes
- Show examples of how EX tier uses CORE correctly
- Warn about footguns in rustdoc comments
- Link to EX tier for "safe" usage

## Performance Considerations


### Zero-Cost Abstractions

- CORE wrappers should inline to raw Vulkan calls
- Use `#[inline]` liberally
- Avoid allocations except where necessary (e.g., shader compilation)
- Structs should be `repr(transparent)` or `repr(C)` where appropriate

### Benchmarking

- Compare against raw ash usage
- Ensure no overhead for wrapper types
- Profile shader compilation times

## Dependencies


### Required Crates

```toml
[dependencies]
ash = "0.38"              # Vulkan bindings
thiserror = "1.0"         # Error handling
```

### For Shader Compilation

```toml
[dependencies]
# Choose one:

shaderc = "0.8"           # Google's shader compiler (easier)
# OR

glslang = "0.4"           # Khronos reference compiler (more control)
```

### Optional But Recommended

```toml
[dependencies]
# Reflection is "optional" but you'll need it for descriptor layouts

spirv-reflect = "0.2"     # SPIR-V reflection
# OR

rspirv = "0.11"           # Alternative SPIR-V parser
```

## Phase 1 Implementation Checklist


**CRITICAL**: Phase 1 must include everything needed to render a triangle. 
That means: Instance, Device, Queue, Commands, Shaders, Pipelines, Memory, Sync, Surface, Swapchain, AND Descriptors.

All Phase 1 objects are just basic Vulkan wrappers. They should be "dumb" and simple.

### Instance Module

- [ ] Wrap `ash::Instance` creation
- [ ] Enumerate physical devices
- [ ] **Validation layer toggle (auto-enable in debug builds)**
- [ ] Debug messenger setup
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Device Module

- [ ] Wrap `ash::Device` creation
- [ ] Store physical device handle
- [ ] Query device properties
- [ ] **Query memory types (find_memory_type)**
- [ ] **Query queue families (find_queue_family)**
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Queue Module

- [ ] Wrap `vk::Queue` retrieval
- [ ] Submit command buffers
- [ ] Wait idle
- [ ] Error handling
- [ ] Minimal docs

### CommandPool Module

- [ ] Wrap `vk::CommandPool` creation
- [ ] Allocate command buffers
- [ ] Reset pool
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### CommandBuffer Module

- [ ] Wrap `vk::CommandBuffer`
- [ ] Begin/end recording
- [ ] **Command recording accepts raw vk::* handles (not CORE wrappers)**
- [ ] Basic commands: bind pipeline, bind descriptors, bind vertex buffers, draw
- [ ] Error handling
- [ ] Minimal docs

### Shader Module

- [ ] GLSL → SPIR-V compilation (via shaderc or glslang)
- [ ] **SPIR-V reflection for descriptor layouts**
- [ ] Wrap `vk::ShaderModule` creation
- [ ] **Don't store SPIR-V after module creation**
- [ ] Basic `Drop` impl
- [ ] Error handling with line numbers
- [ ] Minimal docs

### Pipeline Module

- [ ] Wrap `vk::Pipeline` creation (graphics)
- [ ] Wrap `vk::PipelineLayout` creation
- [ ] **Pipeline OWNS PipelineLayout** (correct drop order guaranteed)
- [ ] Basic builder for pipeline state (all ~20 states)
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Memory Module

- [ ] Wrap `vk::Buffer` creation
- [ ] Wrap `vk::Image` creation
- [ ] **Image view ALWAYS created with image**
- [ ] Memory allocation + binding (functions, not allocator struct)
- [ ] Memory mapping helpers
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Sync Module

- [ ] Wrap `vk::Fence` creation
- [ ] Fence wait/reset
- [ ] Wrap `vk::Semaphore` creation
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Surface Module (REQUIRED, not optional)

- [ ] Wrap `vk::SurfaceKHR` creation from platform handle
- [ ] Query surface capabilities
- [ ] Query surface formats/present modes
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Swapchain Module (REQUIRED for EZ tier)

- [ ] Wrap `vk::SwapchainKHR` creation
- [ ] Acquire next image
- [ ] Present image
- [ ] Handle resize/out-of-date/suboptimal
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

### Descriptor Module (REQUIRED - moved from "future")

- [ ] Wrap `vk::DescriptorPool` creation
- [ ] Allocate descriptor sets
- [ ] Wrap `vk::DescriptorSetLayout` creation
- [ ] **Update descriptor sets with clear API (DescriptorWrite struct)**
- [ ] Update buffers, images, samplers
- [ ] Basic `Drop` impl (pool only, not sets)
- [ ] Error handling
- [ ] Minimal docs

### RenderPass Module (OPTIONAL - Phase 1)

- [ ] **Support both traditional AND dynamic rendering**
- [ ] Wrap `vk::RenderPass` creation (traditional)
- [ ] Wrap `vk::Framebuffer` creation (traditional)
- [ ] Command buffer begin_rendering (dynamic)
- [ ] Document recommended approach (dynamic rendering)
- [ ] Basic `Drop` impl
- [ ] Error handling
- [ ] Minimal docs

## Future Considerations


### Compute Pipelines

- Compute pipeline creation (similar to graphics)
- Dispatch commands

### Render Passes (Maybe?)

- RenderPass creation
- Framebuffer creation
- **DECISION MADE**: Support both traditional and dynamic rendering in Phase 1
- Recommend dynamic rendering as the default
- Traditional render passes for compatibility

### Advanced Memory

- Dedicated allocations
- Memory budget queries
- But still no smart pooling (that's EX tier)

### Additional Descriptor Features

- Descriptor indexing
- Update after bind
- Variable descriptor count
- These are advanced, probably Phase 2+

### Sampler Objects

- Wrap `vk::Sampler` creation
- Sampler configuration
- Basic `Drop` impl

## Hello Triangle Example (CORE tier)


This example shows the bare minimum to render a triangle using CORE tier directly.
**Warning**: This is NOT safe production code - just a proof of concept.

```rust
use shdrlib::core::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Create instance with validation layers
    let instance = Instance::new(InstanceCreateInfo {
        app_name: "Hello Triangle".to_string(),
        app_version: 1,
        enable_validation: true,
        extensions: vec!["VK_KHR_surface".to_string()],
    })?;
    
    // 2. Create surface (from winit or SDL)
    let surface = Surface::from_window(&instance, &window)?;
    
    // 3. Pick physical device
    let physical_devices = instance.enumerate_physical_devices()?;
    let physical_device = physical_devices[0]; // Just pick first one
    
    // 4. Create logical device
    let device = Device::new(&instance, physical_device, &["VK_KHR_swapchain"])?;
    
    // 5. Get queue
    let graphics_queue_family = device.find_queue_family(vk::QueueFlags::GRAPHICS)
        .ok_or("No graphics queue")?;
    let queue = Queue::get(&device, graphics_queue_family, 0)?;
    
    // 6. Create swapchain
    let swapchain = Swapchain::new(&device, &surface, vk::Extent2D {
        width: 800,
        height: 600,
    })?;
    
    // 7. Compile shaders
    let vert_shader = Shader::from_glsl(&device, VERTEX_SHADER_SRC, ShaderStage::Vertex)?;
    let frag_shader = Shader::from_glsl(&device, FRAGMENT_SHADER_SRC, ShaderStage::Fragment)?;
    
    // 8. Create descriptor set layout (even if empty)
    let desc_layout = DescriptorSetLayout::new(&device, &[])?;
    
    // 9. Create pipeline
    let pipeline = Pipeline::builder()
        .vertex_shader(&vert_shader, "main")
        .fragment_shader(&frag_shader, "main")
        .descriptor_layout(&desc_layout)
        .viewport(vk::Viewport { /* ... */ })
        .scissor(vk::Rect2D { /* ... */ })
        .build(&device)?;
    
    // 10. Create command pool and buffer
    let command_pool = CommandPool::new(&device, graphics_queue_family)?;
    let command_buffer = command_pool.allocate_primary()?;
    
    // 11. Create sync objects
    let image_available = Semaphore::new(&device)?;
    let render_finished = Semaphore::new(&device)?;
    let in_flight = Fence::new(&device, true)?; // Signaled
    
    // 12. Render loop
    loop {
        // Wait for previous frame
        in_flight.wait()?;
        in_flight.reset()?;
        
        // Acquire swapchain image
        let (image_index, _) = swapchain.acquire_next_image(&image_available)?;
        
        // Record commands
        command_buffer.begin()?;
        command_buffer.begin_rendering(&vk::RenderingInfo {
            render_area: vk::Rect2D {
                offset: vk::Offset2D { x: 0, y: 0 },
                extent: vk::Extent2D { width: 800, height: 600 },
            },
            color_attachments: &[vk::RenderingAttachmentInfo {
                image_view: swapchain.image_views[image_index as usize],
                image_layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
                load_op: vk::AttachmentLoadOp::CLEAR,
                store_op: vk::AttachmentStoreOp::STORE,
                clear_value: vk::ClearValue {
                    color: vk::ClearColorValue {
                        float32: [0.0, 0.0, 0.0, 1.0],
                    },
                },
                ..Default::default()
            }],
            ..Default::default()
        });
        command_buffer.bind_pipeline(vk::PipelineBindPoint::GRAPHICS, pipeline.handle());
        command_buffer.draw(3, 1, 0, 0); // 3 vertices, 1 instance
        command_buffer.end_rendering();
        command_buffer.end()?;
        
        // Submit
        queue.submit(
            &[command_buffer.handle()],
            &[image_available.handle()],
            &[render_finished.handle()],
            in_flight.handle(),
        )?;
        
        // Present
        swapchain.present(&queue, image_index, &[render_finished.handle()])?;
    }
    
    // Drop order is critical here!
    // CORE doesn't enforce this - you must get it right
    drop(in_flight);
    drop(render_finished);
    drop(image_available);
    drop(command_pool);
    drop(pipeline);
    drop(desc_layout);
    drop(frag_shader);
    drop(vert_shader);
    drop(swapchain);
    drop(queue);
    drop(device);
    drop(surface);
    drop(instance);
    
    Ok(())
}

const VERTEX_SHADER_SRC: &str = r#"
#version 450


layout(location = 0) out vec3 fragColor;

vec2 positions[3] = vec2[](
    vec2(0.0, -0.5),
    vec2(0.5, 0.5),
    vec2(-0.5, 0.5)
);

vec3 colors[3] = vec3[](
    vec3(1.0, 0.0, 0.0),
    vec3(0.0, 1.0, 0.0),
    vec3(0.0, 0.0, 1.0)
);

void main() {
    gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
    fragColor = colors[gl_VertexIndex];
}
"#;

const FRAGMENT_SHADER_SRC: &str = r#"
#version 450


layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;

void main() {
    outColor = vec4(fragColor, 1.0);
}
"#;
```

**What This Example Demonstrates**:
- ✅ Complete CORE tier usage
- ✅ Every major object in use
- ✅ Dynamic rendering (no render passes!)
- ✅ Minimal descriptor setup
- ✅ Proper drop order (manual)

**What's Wrong With This**:
- ❌ Manual drop order (easy to mess up)
- ❌ No error recovery
- ❌ No resize handling
- ❌ No resource tracking
- ❌ Lots of boilerplate

**This is why you use EX tier instead!**

The same triangle in EX tier would be ~30 lines instead of 150+.

## Integration with Higher Tiers


### How EX Tier Uses CORE

The EX tier (ShaderManager + RuntimeManager) builds on CORE by:

**ShaderManager**:
- **Owns** all CORE objects
- Manages correct construction/destruction order
- Pairs shaders with pipelines
- Tracks object lifetimes
- Example:
  ```rust
  struct ShaderManager {
      instance: core::Instance,
      device: core::Device,
      shaders: Vec<core::Shader>,
      pipelines: Vec<core::Pipeline>,
      // ... etc
  }
  // Drop order is automatic - Rust drops fields in declaration order!
  ```

**RuntimeManager**:
- **Arc's** objects from ShaderManager
- Handles rendering operations
- Manages command buffers and queues
- Example:
  ```rust
  struct RuntimeManager {
      device: Arc<core::Device>,
      queue: Arc<core::Queue>,
      // ... etc
  }
  ```

### How EZ Tier Uses CORE + EX

The EZ tier wraps ShaderManager and RuntimeManager:
- Provides high-level "just render this" APIs
- Hides CORE complexity entirely
- Automatic resource management
- Sensible defaults for everything

### Example: Safe Usage Through Tiers


**CORE (unsafe if used wrong)**:
```rust
let instance = core::Instance::new()?;
let device = core::Device::new(&instance, ...)?;
let shader = core::Shader::from_glsl(&device, source)?;
drop(device);  // ⚠️ UB! shader still exists
```

**EX (safe, explicit)**:
```rust
let mut manager = ex::ShaderManager::new()?;
let shader_id = manager.add_shader(source)?;
let pipeline_id = manager.create_pipeline(shader_id)?;
// Drop order handled automatically
```

**EZ (safe, automatic)**:
```rust
let renderer = ez::Renderer::new()?;
renderer.render_shader(source)?;  // Does everything
```

## Notes


### Key Principles

- **CORE is just a wrapper** - Think "ash + better types"
- **CORE is unsafe to use alone** - You can easily create UB
- **EX makes it safe** - ShaderManager owns everything correctly
- **EZ makes it easy** - One-liners for common tasks

### What Makes CORE Different from Raw Ash

1. Rust types instead of raw Vulkan handles
2. `Drop` implementations for cleanup
3. `Result` instead of raw error codes
4. Slightly more ergonomic function signatures
5. GLSL compilation built-in
6. SPIR-V reflection built-in

### Audit Fixes Applied

✅ **Removed MemoryAllocator struct** - Just functions now, no state tracking  
✅ **Pipeline owns PipelineLayout** - Pragmatic ownership, correct drop order  
✅ **Shader reflection added** - Required for building descriptor layouts  
✅ **SPIR-V not stored** - Dropped after module creation to save memory  
✅ **CommandBuffer accepts raw vk handles** - Avoids borrow checker hell  
✅ **Descriptors moved to Phase 1** - Can't render without them  
✅ **Surface marked REQUIRED** - Not optional if we support window rendering  
✅ **Swapchain marked REQUIRED** - EZ tier needs it for defaults  
✅ **Better shader error context** - Line numbers for compilation errors  

### Final Polish Applied

✅ **Validation layer toggle** - Auto-enabled in debug builds  
✅ **Device query methods** - find_memory_type, find_queue_family  
✅ **Descriptor update API** - Clear DescriptorWrite struct  
✅ **Image view handling** - Always created with image (not optional)  
✅ **Render pass strategy** - Support both traditional and dynamic rendering  
✅ **Hello Triangle example** - Complete working example showing all parts  

**Final Score: 10/10** - Ready for implementation!

### What CORE Doesn't Try To Do

- ❌ Be safe to use standalone
- ❌ Manage object lifetimes
- ❌ Provide defaults
- ❌ Validate object compatibility
- ❌ Pool resources

### When To Use CORE Directly

- You're building your own EX-like layer
- You need absolute control
- You're integrating with existing Vulkan code
- You know exactly what you're doing

### When To Use EX/EZ Instead

- You're building an app/game (use EZ)
- You want safety guarantees (use EX)
- You don't want to think about drop order (use EX/EZ)
- You want defaults (use EZ)

---

**Remember**: CORE is the foundation. It's "correct" but not "safe". That's by design.