# Contributing to shdrlib
Thank you for your interest in contributing to shdrlib! This document provides guidelines and instructions for contributing to the project.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Getting Started](#getting-started)
- [Development Workflow](#development-workflow)
- [Project Architecture](#project-architecture)
- [Coding Standards](#coding-standards)
- [Testing Requirements](#testing-requirements)
- [Documentation Guidelines](#documentation-guidelines)
- [Submitting Changes](#submitting-changes)
- [Tier-Specific Guidelines](#tier-specific-guidelines)
---
## Code of Conduct
This project follows the Rust Code of Conduct. Please be respectful and constructive in all interactions.
---
## Getting Started
### Prerequisites
- **Rust**: 1.82+ (Edition 2024)
- **Vulkan SDK**: 1.3+ (for validation layers during development)
- **Git**: For version control
### Setting Up Your Development Environment
1. **Clone the repository**:
```bash
git clone https://github.com/paulburnettjones-wq/shdrlib.git
cd shdrlib
```
2. **Build the project**:
```bash
cargo build
```
3. **Run tests**:
```bash
cargo test
```
4. **Run examples**:
```bash
cargo run --bin ex_01_triangle_100_lines
```
5. **Generate documentation**:
```bash
cargo doc --open
```
---
## Development Workflow
### Branching Strategy
- `main` - Stable, production-ready code
- `develop` - Integration branch for features
- `feature/*` - Feature development branches
- `fix/*` - Bug fix branches
- `docs/*` - Documentation improvement branches
### Typical Workflow
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/your-feature-name`
3. Make your changes
4. Write/update tests
5. Update documentation
6. Run tests: `cargo test`
7. Run clippy: `cargo clippy -- -D warnings`
8. Format code: `cargo fmt`
9. Commit with descriptive messages
10. Push to your fork
11. Open a Pull Request
---
## Project Architecture
shdrlib uses a **three-tier architecture**. Understanding this structure is crucial for contributions:
### CORE Tier (Tier 0)
- **Purpose**: Thin wrappers around Vulkan/ash
- **Philosophy**: Minimal abstraction, maximum control
- **Safety**: Manual lifetime management (can cause UB if misused)
- **Location**: `src/core/`
### EX Tier (Tier 1)
- **Purpose**: Safe, ergonomic managers with explicit configuration
- **Philosophy**: 4x-8x code reduction, zero-cost abstractions
- **Safety**: Guaranteed memory safety via Rust ownership
- **Location**: `src/ex/`
### EZ Tier (Tier 2)
- **Purpose**: High-level abstractions with intelligent defaults
- **Philosophy**: Beginner-friendly, rapid prototyping
- **Safety**: Foolproof APIs
- **Location**: `src/ez/` (currently planned)
**See `DEVELOPMENT_PLAN.md` for detailed architecture documentation.**
---
## Coding Standards
### Rust Style
- Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)
- Use `cargo fmt` with default settings
- Use `cargo clippy` and address all warnings
- Maximum line length: 100 characters (soft limit)
### Naming Conventions
```rust
// Types: PascalCase
pub struct RuntimeManager { ... }
pub enum ShaderStage { ... }
// Functions/methods: snake_case
pub fn create_shader(&self) -> Result<Shader> { ... }
// Constants: SCREAMING_SNAKE_CASE
pub const MAX_FRAMES_IN_FLIGHT: u32 = 2;
// Newtype IDs: PascalCase with suffix
pub struct ShaderId(usize);
pub struct PipelineId(usize);
```
### Error Handling
- Use `thiserror` for all error types
- Provide context in error messages
- Implement `From` conversions for error propagation
- Never use `unwrap()` or `expect()` in library code
- Document error conditions in rustdoc
Example:
```rust
#[derive(Error, Debug)]
pub enum MyError {
#[error("Operation failed: {0}")]
OperationFailed(String),
#[error("Invalid configuration: {0}")]
InvalidConfig(#[from] ConfigError),
}
```
### Documentation
- All public items must have rustdoc comments
- Include examples for complex APIs
- Document safety requirements for unsafe code
- Link to related items using `[Type]` or `[function]`
Example:
```rust
/// Creates a new shader from GLSL source code.
///
/// This function compiles the GLSL source to SPIR-V using naga,
/// then creates a Vulkan shader module.
///
/// # Arguments
///
/// * `device` - The logical device to create the shader on
/// * `source` - GLSL source code as a string
/// * `stage` - The shader stage (vertex, fragment, etc.)
///
/// # Examples
///
/// ```no_run
/// # use shdrlib::core::*;
/// let shader = Shader::from_glsl(
/// &device,
/// "#version 450\nvoid main() {}",
/// ShaderStage::Vertex
/// )?;
/// # Ok::<(), ShaderError>(())
/// ```
///
/// # Errors
///
/// Returns `ShaderError::CompilationFailed` if GLSL is invalid.
pub fn from_glsl(...) -> Result<Self, ShaderError> { ... }
```
### Inlining
- Use `#[inline]` for small accessor methods
- Use `#[inline]` for wrapper methods in EX tier
- Use `#[inline(always)]` sparingly, only for critical hot paths
- Document reasoning for `#[inline(always)]` usage
---
## Testing Requirements
### Test Coverage
- **All public APIs must have tests**
- **All error paths should be tested**
- Aim for 80%+ code coverage
### Test Organization
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_functionality() {
// Arrange
let input = setup_test_data();
// Act
let result = function_under_test(input);
// Assert
assert!(result.is_ok());
}
#[test]
fn test_error_handling() {
let result = function_with_invalid_input();
assert!(matches!(result, Err(MyError::InvalidInput(_))));
}
}
```
### Running Tests
```bash
# Run all tests
cargo test
# Run specific tier tests
cargo test --lib core::
cargo test --lib ex::
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_name
```
### Integration Tests
- Demo programs serve as integration tests
- Ensure all demos compile and run without errors
- Add new demos for significant new features
---
## Documentation Guidelines
### Markdown Files
- Follow existing structure in `md/` directory
- Use proper heading hierarchy
- Include code examples that compile
- Keep line length reasonable (~100 chars)
### Rustdoc
- First sentence should be a brief summary
- Use proper markdown formatting
- Include code examples for public APIs
- Document panics, errors, and safety requirements
### Updating Documentation
When making changes, update:
- [ ] Inline rustdoc comments
- [ ] Relevant markdown files in `md/`
- [ ] README.md if public API changes
- [ ] CHANGELOG.md with your changes
- [ ] Demo programs if behavior changes
---
## Submitting Changes
### Pull Request Process
1. **Ensure all tests pass**: `cargo test`
2. **Run clippy**: `cargo clippy -- -D warnings`
3. **Format code**: `cargo fmt --check`
4. **Update CHANGELOG.md** under "Unreleased" section
5. **Update documentation** as needed
6. **Write clear PR description** explaining:
- What changed and why
- Which tier(s) are affected
- Any breaking changes
- Related issues (if any)
### PR Title Format
```
[TIER] Brief description of change
Examples:
[CORE] Add support for dynamic rendering
[EX] Implement compute pipeline builder
[EZ] Add quick_texture helper method
[DOCS] Update EX tier usage guide
[FIX] Correct drop order in ShaderManager
```
### Commit Message Guidelines
```
Brief summary (50 chars or less)
More detailed explanation if needed. Wrap at 72 characters.
Include motivation for change and contrast with previous behavior.
- Bullet points are okay
- Use present tense: "Add feature" not "Added feature"
- Reference issues: "Fixes #123" or "Related to #456"
```
---
## Tier-Specific Guidelines
### CORE Tier Contributions
**Philosophy**: Keep it minimal and explicit
- DO: Provide thin wrappers around Vulkan objects
- DO: Use safe Rust where possible
- DO: Document lifetime requirements clearly
- DON'T: Add lifetime management or ownership tracking
- DON'T: Add convenience features (those go in EX tier)
- DON'T: Make assumptions about usage patterns
**Example Pattern**:
```rust
pub struct MyVulkanWrapper {
handle: vk::MyVulkanHandle,
device: Arc<Device>, // Only store what's needed for Drop
}
impl MyVulkanWrapper {
pub fn new(device: &Arc<Device>, info: &CreateInfo) -> Result<Self> {
// Minimal validation, then call Vulkan
let handle = unsafe {
device.handle().create_my_vulkan_object(info, None)
.map_err(|e| MyError::CreationFailed(e))?
};
Ok(Self { handle, device: Arc::clone(device) })
}
#[inline]
pub fn handle(&self) -> vk::MyVulkanHandle {
self.handle
}
}
impl Drop for MyVulkanWrapper {
fn drop(&mut self) {
unsafe {
self.device.handle().destroy_my_vulkan_object(self.handle, None);
}
}
}
```
### EX Tier Contributions
**Philosophy**: Ergonomic, explicit, and safe
- DO: Provide safe lifetime management via Arc
- DO: Use builder patterns for complex configuration
- DO: Add helper utilities for common patterns
- DO: Use newtype pattern for type safety
- DO: Keep all configuration explicit (no hidden defaults)
- DON'T: Add magic behavior or hidden state
- DON'T: Sacrifice performance for convenience
- DON'T: Make assumptions about what users want
**Example Pattern**:
```rust
pub struct Manager {
resources: Vec<Resource>,
device: Arc<Device>, // Arc for safe sharing
}
impl Manager {
#[inline]
pub fn device(&self) -> Arc<Device> {
Arc::clone(&self.device)
}
pub fn add_resource(&mut self, config: ResourceConfig) -> Result<ResourceId> {
// Explicit configuration, no defaults
let resource = Resource::new(&self.device, &config)?;
self.resources.push(resource);
Ok(ResourceId(self.resources.len() - 1))
}
}
```
### EZ Tier Contributions (Future)
**Philosophy**: Simple, with intelligent defaults
- DO: Provide one-liner setup where possible
- DO: Use intelligent defaults for common cases
- DO: Focus on learning and rapid prototyping
- DO: Maintain ability to drop down to EX/CORE
- DON'T: Sacrifice correctness for simplicity
- DON'T: Lock users into limited APIs
---
## Review Process
### What Reviewers Look For
1. **Correctness**: Does the code work as intended?
2. **Safety**: Are there any memory safety issues?
3. **Performance**: Does it maintain zero-cost abstractions?
4. **Tests**: Are all paths tested?
5. **Documentation**: Is it clear and complete?
6. **Style**: Does it follow project conventions?
7. **Tier Alignment**: Does it fit the tier's philosophy?
### Response Time
- Initial review: Within 1 week
- Follow-up reviews: Within 3 days
- Merge: After approval + CI passes
---
## Getting Help
### Resources
- **Documentation**: `md/` directory and rustdoc
- **Examples**: `demos/` directory
- **Architecture**: `DEVELOPMENT_PLAN.md`
- **Status**: `PROJECT_STATUS.md`
### Questions?
- Open a GitHub Discussion
- Tag relevant maintainers
- Check existing issues and documentation
---
## License
By contributing to shdrlib, you agree that your contributions will be dual-licensed under MIT and Apache-2.0, consistent with the project's licensing.
---
Thank you for contributing to shdrlib.