# Vulkan Basics for shdrlib Users
A gentle introduction to Vulkan concepts you need to know.
## What is Vulkan?
**Vulkan** is a modern graphics API that gives you explicit control over the GPU. Unlike older APIs (OpenGL, DirectX 11), you manage memory, synchronization, and state yourself.
**Why Vulkan?**
- ✅ **Performance** - Less driver overhead
- ✅ **Explicit control** - You decide what happens
- ✅ **Modern** - Designed for multi-core CPUs
- ✅ **Cross-platform** - Windows, Linux, macOS (via MoltenVK), Android
**The trade-off:** More code, steeper learning curve
**shdrlib's solution:** Three tiers of abstraction:
- **EZ** - Hides most complexity
- **EX** - Balances control and ergonomics
- **CORE** - Direct Vulkan access
---
## Core Concepts
### 1. Instance
The **Vulkan instance** is your connection to the Vulkan API.
**Analogy:** Opening the Vulkan "library"
**What it does:**
- Loads Vulkan drivers
- Enumerates available GPUs
- Enables layers (validation, profiling)
**In shdrlib:**
```rust
// EZ: Automatic
let renderer = EzRenderer::new()?;
// CORE: Manual
let instance = Instance::new(&entry, &instance_info)?;
```
---
### 2. Physical Device (GPU)
A **physical device** represents your GPU hardware.
**What you can query:**
- GPU name and vendor
- Memory sizes
- Supported features
- Queue families
**In shdrlib:**
- **EZ/EX:** Automatically selects best GPU
- **CORE:** You choose explicitly
**Example:**
```rust
// CORE tier: enumerate and select
let physical_devices = instance.enumerate_physical_devices()?;
let gpu = physical_devices.first().unwrap();
```
---
### 3. Logical Device
A **logical device** is your interface to the GPU.
**Analogy:** A "connection" to talk to the GPU
**What it does:**
- Creates GPU resources (buffers, images)
- Submits work to queues
- Manages memory allocations
**In shdrlib:**
```rust
// EZ: Automatic
let renderer = EzRenderer::new()?;
// EX: Via RuntimeManager
let runtime = RuntimeManager::new(config)?;
let device = runtime.device();
// CORE: Manual
let device = Device::new(&instance, physical_device, &info)?;
```
---
### 4. Queues
**Queues** are how you submit work to the GPU.
**Types:**
- **Graphics** - Rendering (triangles, lines, etc.)
- **Compute** - General computation (matrix math, physics)
- **Transfer** - Copying data (CPU→GPU, GPU→GPU)
**Most GPUs have multiple queues** that can work in parallel.
**In shdrlib:**
- **EZ:** Queues are hidden
- **EX:** RuntimeManager manages them
- **CORE:** You create and submit to them explicitly
---
### 5. Command Buffers
**Command buffers** record GPU commands for later execution.
**Analogy:** A "to-do list" for the GPU
**Why record commands?**
- **Reuse** - Record once, submit many times
- **Efficiency** - Batch multiple operations
- **Multi-threading** - Record in parallel
**Typical commands:**
- `draw()` - Render geometry
- `dispatch()` - Run compute shader
- `copy_buffer()` - Transfer data
- `begin_rendering()` / `end_rendering()` - Start/stop rendering
**In shdrlib:**
```rust
// EZ: Abstracted in render_frame()
renderer.render_frame(|frame| {
frame.draw(3, 1, 0, 0); // Records draw command
Ok(())
})?;
// EX: Access via Frame
let frame = runtime.begin_frame()?;
let cmd = frame.command_buffer;
// ... record commands ...
// CORE: Manual recording
let cmd_buffer = CommandBuffer::new(...)?;
cmd_buffer.begin()?;
cmd_buffer.draw(3, 1, 0, 0);
cmd_buffer.end()?;
```
---
### 6. Buffers
**Buffers** are contiguous memory on the GPU.
**Common types:**
- **Vertex buffer** - Stores vertex data (positions, colors, UVs)
- **Index buffer** - Stores triangle indices
- **Uniform buffer** - Stores shader constants (matrices, time, etc.)
- **Storage buffer** - General read/write data
**In shdrlib:**
```rust
// EZ: One-liners
let vertices = renderer.create_vertex_buffer(&data)?;
// EX: Helper functions
use shdrlib::ex::helpers::*;
let buffer = create_vertex_buffer(&device, &data, "vertices")?;
// CORE: Manual
let buffer = Buffer::new(&device, &buffer_info)?;
```
---
### 7. Images and Textures
**Images** are 2D/3D data on the GPU (textures, render targets).
**Properties:**
- **Format** - R8G8B8A8, R32F, D32_SFLOAT, etc.
- **Usage** - Sampled (texture), Color attachment (render target)
- **Layout** - Optimal, General, etc.
**In shdrlib:**
```rust
// EZ: Simple creation
let texture = renderer.create_texture(256, 256, Format::R8G8B8A8_UNORM)?;
// EX: More options
let image = create_texture(&device, 512, 512, Format::R32_SFLOAT, "render_target")?;
// CORE: Manual creation with full control
let image = Image::new(&device, &image_info)?;
```
---
### 8. Shaders
**Shaders** are programs that run on the GPU.
**Common stages:**
- **Vertex** - Processes vertices (positioning)
- **Fragment** - Processes pixels (coloring)
- **Compute** - General computation
**Shader languages:**
- **GLSL** - OpenGL shading language (what shdrlib uses)
- **HLSL** - DirectX shading language
- **WGSL** - WebGPU shading language
- **SPIR-V** - Vulkan's binary format (compiled output)
**In shdrlib:**
```rust
// shdrlib compiles GLSL → SPIR-V at runtime using naga
// EZ: Compile inline
let pipeline = renderer.quick_pipeline(VERT_GLSL, FRAG_GLSL)?;
// EX: Managed shaders
let shader_id = shaders.add_shader(GLSL, ShaderStage::Vertex, "my_shader")?;
// CORE: Manual compilation
let shader = Shader::from_glsl(&device, GLSL, ShaderStage::Fragment)?;
```
---
### 9. Pipelines
A **pipeline** is a complete configuration for rendering or compute.
**Graphics pipeline includes:**
- Shaders (vertex, fragment, etc.)
- Vertex input layout
- Rasterization state (culling, depth test)
- Blend state (transparency)
- Viewport and scissor
**Compute pipeline includes:**
- Compute shader
- Push constants
- Descriptor layout
**In shdrlib:**
```rust
// EZ: One-liner
let pipeline = renderer.quick_pipeline(VERT, FRAG)?;
// EX: Builder pattern
let pipeline = PipelineBuilder::new()
.vertex_shader(vert_module, "main")
.fragment_shader(frag_module, "main")
.color_attachment_formats(vec![Format::R8G8B8A8_UNORM])
.build(&device)?;
// CORE: Manual
let pipeline = Pipeline::new(&device, &pipeline_info)?;
```
---
### 10. Descriptors
**Descriptors** bind resources (buffers, textures) to shaders.
**Analogy:** Function parameters for shaders
**Structure:**
- **Descriptor Set Layout** - Declares what bindings exist
- **Descriptor Pool** - Allocates descriptor sets
- **Descriptor Set** - Actual bindings (buffer A, texture B)
**GLSL example:**
```glsl
layout(set = 0, binding = 0) uniform Camera {
mat4 view;
mat4 proj;
} camera;
layout(set = 0, binding = 1) uniform sampler2D myTexture;
```
**In shdrlib:**
```rust
// EX: Helper builders
let layout = DescriptorLayoutBuilder::new()
.add_binding(0, DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX)
.add_binding(1, DescriptorType::COMBINED_IMAGE_SAMPLER, ShaderStageFlags::FRAGMENT)
.build(&device)?;
// CORE: Manual creation
let layout = DescriptorSetLayout::new(&device, &layout_info)?;
```
---
### 11. Synchronization
**Synchronization** ensures GPU operations happen in the right order.
**Primitives:**
- **Semaphores** - GPU-GPU sync (queue to queue)
- **Fences** - CPU-GPU sync (wait for GPU from CPU)
- **Barriers** - Memory dependencies
**Common patterns:**
- Wait for previous frame before rendering
- Wait for image acquire before rendering
- Wait for render complete before present
**In shdrlib:**
- **EZ:** All automatic
- **EX:** RuntimeManager handles frame sync
- **CORE:** You manage explicitly
---
### 12. Render Loop
**Typical Vulkan render loop:**
```
1. Acquire swapchain image (get next frame)
2. Record command buffer
- Begin rendering
- Bind pipeline
- Bind descriptor sets
- Draw calls
- End rendering
3. Submit command buffer to queue
4. Present swapchain image (display frame)
5. Wait for fence (prevent CPU racing ahead)
```
**In shdrlib:**
**EZ tier:**
```rust
loop {
renderer.render_frame(|frame| {
// ... draw ...
Ok(())
})?;
}
```
**EX tier:**
```rust
loop {
let frame = runtime.begin_frame()?;
// ... record commands ...
runtime.end_frame(&Default::default())?;
}
```
**CORE tier:** You implement the loop yourself.
---
## Vulkan vs OpenGL
### OpenGL (Old Way)
```c
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glDrawArrays(GL_TRIANGLES, 0, 3);
```
- **Implicit state** - Driver tracks everything
- **Hidden allocations** - Driver manages memory
- **Less control** - Driver makes decisions
- **Easy to use** - Less code
### Vulkan (New Way)
```rust
cmd_buffer.bind_vertex_buffers(0, &[buffer], &[0]);
cmd_buffer.draw(3, 1, 0, 0);
```
- **Explicit state** - You track everything
- **Manual allocations** - You manage memory
- **Full control** - You decide everything
- **More code** - But more predictable
**shdrlib bridges the gap:** Use EZ for OpenGL-like simplicity, CORE for full Vulkan control.
---
## Memory in Vulkan
### Memory Types
Vulkan has different types of GPU memory:
- **Device Local** - Fast GPU memory (VRAM)
- **Host Visible** - CPU-accessible (slower)
- **Host Cached** - Cached CPU memory
- **Host Coherent** - Automatically synced
### Common Patterns
**Static data (geometry):**
```
CPU → Staging Buffer → Device Local Buffer
```
**Dynamic data (uniforms):**
```
CPU → Host Visible Buffer → GPU reads directly
```
**In shdrlib:**
- **EZ:** Memory managed automatically
- **EX:** Helpers choose optimal memory
- **CORE:** You select memory types
---
## Common Vulkan Patterns
### Pattern 1: Static Mesh Rendering
```
1. Upload vertex data to device-local buffer
2. Create pipeline with vertex input layout
3. Each frame:
- Bind pipeline
- Bind vertex buffer
- Draw
```
### Pattern 2: Dynamic Uniform Updates
```
1. Create host-visible uniform buffer
2. Each frame:
- Map memory
- Update data (e.g., camera matrix)
- Unmap
- Bind descriptor set
- Draw
```
### Pattern 3: Compute Shader Dispatch
```
1. Create compute pipeline
2. Create storage buffers (input/output)
3. Bind pipeline
4. Bind descriptor sets
5. Dispatch(x, y, z)
6. Memory barrier
7. Read results
```
---
## Debugging Vulkan
### Validation Layers
**Validation layers** catch errors at runtime:
```
VUID-vkCmdDraw-None-02859: Shader requires geometryShader feature
```
**shdrlib enables them automatically in debug builds.**
### Common Errors
**"Out of memory"**
- Creating too many resources
- Not destroying old resources
- GPU VRAM exhausted
**"Device lost"**
- Driver crash
- Infinite loop in shader
- Invalid GPU commands
**"Validation error"**
- Using resources incorrectly
- Wrong synchronization
- Missing features
See [Troubleshooting](troubleshooting.md) for solutions.
---
## Resources for Learning More
### Official Vulkan Resources
- [Vulkan Tutorial](https://vulkan-tutorial.com/) - Comprehensive C++ tutorial
- [Vulkan Guide](https://vkguide.dev/) - Modern Vulkan guide
- [Vulkan Spec](https://www.khronos.org/registry/vulkan/) - Official specification
### shdrlib Resources
- [EZ Tier Guide](../guides/ez-tier-guide.md) - High-level API
- [EX Tier Guide](../guides/ex-tier-guide.md) - Production tier
- [CORE Tier Guide](../guides/core-tier-guide.md) - Low-level control
- [Examples](../../demos/) - Working code samples
### Tools
- **RenderDoc** - Graphics debugger
- **Nsight Graphics** - NVIDIA profiler
- **Radeon GPU Profiler** - AMD profiler
---
## Summary
**Key takeaways:**
1. **Vulkan is explicit** - You control everything
2. **More code, more control** - Trade-off worth it for performance
3. **shdrlib helps** - Three tiers match your needs
4. **Start simple** - Begin with EZ, drop to CORE when needed
5. **Use validation** - Catches bugs early
**Next steps:**
- Try the [Quick Start Tutorial](quickstart.md)
- Run the [Examples](../../demos/)
- Read the tier guides
---
**Last Updated:** October 30, 2025