# VEKL (Video Effects Kernel Library)

VEKL is an open-source, modular compute kernel library written in
[Slang](https://shader-slang.com/) for developing high-performance video
effects and transitions. It provides reusable primitives for pixel I/O,
texture sampling (single-level and mip-chain), coordinate transforms,
noise generation, filtering, and color blending — all compiled through
Slang's cross-platform compiler to CUDA, Metal, and other GPU backends.

> License: Apache-2.0

## Why this exists

GPU video effects share the same building blocks across every project:

- Reading and writing pixels in multiple formats (8-bit, 16-bit, 32-bit float)
- Converting between pixel layouts (RGBA, BGRA, VUYA 601/709)
- Sampling textures with nearest, bilinear, or repeat/mirror addressing
- Sampling mip chains (single buffer, byte-addressed) with trilinear lod
- Computing UV coordinates, rotations, and aspect-correct transforms
- Generating noise patterns for organic effects
- Applying separable Gaussian blur
- Blending colors with compositing modes

Rewriting these from scratch for each effect is error-prone and leads to
behavioral drift. VEKL solves this by providing:

1. **Single source of truth** — Write once in Slang, compile to any backend Slang supports
2. **Deterministic parity** — Identical algorithm, identical output across all backends
3. **Modular design** — Import only what you need via Slang's module system
4. **Format-agnostic pixel I/O** — Automatic handling of pixel storage, layout, and address modes

## Quick start

Below is a minimal vignette kernel that reads pixels, applies a distance-based
mask, and writes the result:

```cpp
import vekl;

struct VignetteParams
{
    float tintR; float tintG; float tintB; float tintA;
    float anchorX; float anchorY;
    float scaleX; float scaleY;
    float darkenStrength; float darkenMin; float darkenMax;
};

[shader("compute")]
[numthreads(16, 16, 1)]
void vignette(
    uint3 threadId : SV_DispatchThreadID,
    StructuredBuffer<uint> outgoing,
    StructuredBuffer<uint> incoming,
    RWStructuredBuffer<uint> dst,
    ConstantBuffer<FrameParams> frame,
    ConstantBuffer<VignetteParams> params
)
{
    RWTextureView output = RWTextureView(dst, frame.dstDesc);
    if (!output.Contains(threadId.xy))
        return;

    TextureView src = TextureView(incoming, frame.inDesc);
    uint2 size = src.Size();
    float2 uv = TexCoord(threadId.xy, size);

    float2 scaledUV = ScaleUV(uv, float2(1.0 / params.scaleX, 1.0 / params.scaleY));
    float2 anchorUV = ScaleUV(float2(params.anchorX, params.anchorY),
                              float2(1.0 / params.scaleX, 1.0 / params.scaleY));

    float mask = DistanceMask(scaledUV, anchorUV, params.darkenMin, params.darkenMax);

    float4 original = src.Load(threadId.xy);
    float4 tint = float4(params.tintR, params.tintG, params.tintB, 1.0);
    float strength = mask * params.darkenStrength * params.tintA;

    float4 result = float4(
        lerp(original.x, original.x * tint.x, strength),
        lerp(original.y, original.y * tint.y, strength),
        lerp(original.z, original.z * tint.z, strength),
        original.w
    );
    output.Store(threadId.xy, result);
}
```

## Directory

```
vekl/
├── vekl.slang               # Module declaration — includes aggregator files
│
├── texture.slang             # Aggregator — includes texture/* files
├── texture/
│   ├── descriptor.slang      # PixelStorage, PixelLayout, AddressMode, TextureDesc, FrameParams
│   ├── format.slang          # ToRGBA, FromRGBA, LoadPixel, StorePixel, address helpers
│   └── view.slang            # TextureView, RWTextureView with sample methods
│
├── sampling.slang            # Aggregator — includes sampling/* files
├── sampling/
│   └── coordinate.slang      # TexCoord, PixelCoord, ScaleUV, RotateUV, UniformAspectRatio
│
├── math.slang                # Aggregator — includes math/* files
├── math/
│   └── mask.slang            # DistanceMask
│
├── filter.slang              # Aggregator — includes filter/* files
├── filter/
│   └── gaussian.slang        # GaussianWeight1d, Gaussian1d
│
├── noise.slang               # Aggregator — includes noise/* files
├── noise/
│   ├── perlin.slang          # PerlinNoise, PerlinNoise2
│   └── fbm.slang             # FbmNoise
│
├── color.slang               # Aggregator — includes color/* files
├── color/
│   └── blend/
│       ├── add.slang          # BlendAdd (generic)
│       └── multiply.slang     # BlendMultiply (generic)
│
├── docs/
│   ├── reference/            # API reference (nested by concern)
│   ├── tutorials/            # Step-by-step learning guides
│   ├── how-to-guides.md      # Task-oriented recipes
│   └── explanation.md        # Architecture and design decisions
│
├── CONTRIBUTING.md           # Development guidelines
├── LICENSE                   # Apache-2.0
└── README.md                 # This file
```

## Add as a submodule

```bash
git submodule add https://github.com/exaecut/vekl.git third_party/vekl
```

Then import in your Slang shader:

```cpp
import vekl;
```

## Add as a subtree

```bash
# Add VEKL as a remote
git remote add vekl-remote https://github.com/exaecut/vekl.git

# Add the subtree (first time)
git subtree add --prefix=third_party/vekl vekl-remote main --squash

# Future updates
git subtree pull --prefix=third_party/vekl vekl-remote main --squash
```

## Compilation

VEKL is a Slang module. Compile it through the Slang compiler targeting your
preferred backend:

```bash
# Compile to SPIR-V (Vulkan)
slangc my_effect.slang -target spirv -entry my_kernel -stage compute

# Compile to Metal (MSL)
slangc my_effect.slang -target metal -entry my_kernel -stage compute

# Compile to CUDA
slangc my_effect.slang -target cuda -entry my_kernel -stage compute

# Compile to DXIL (DirectX)
slangc my_effect.slang -target dxil -entry my_kernel -stage compute
```

Slang resolves `import vekl;` by searching the include paths. Add the VEKL
directory to your include search path:

```bash
slangc -I third_party/vekl my_effect.slang ...
```

## Key concepts

### Kernel signature pattern

Every VEKL-compatible compute kernel follows this parameter layout:

```cpp
[shader("compute")]
[numthreads(16, 16, 1)]
void my_kernel(
    uint3 threadId : SV_DispatchThreadID,
    StructuredBuffer<uint> outgoing,      // slot 0: primary source buffer
    StructuredBuffer<uint> incoming,      // slot 1: secondary source (transitions/multi-pass)
    RWStructuredBuffer<uint> dst,         // slot 2: destination buffer
    ConstantBuffer<FrameParams> frame,    // slot 3: frame metadata (mandatory)
    ConstantBuffer<MyParams> params       // slot 4: effect parameters
)
```

The `FrameParams` constant buffer is mandatory — it provides frame dimensions,
pitch, pixel format, time, and progress.

### Texture views

Use `TextureView` for read-only access and `RWTextureView` for read-write
access. Both encapsulate buffer pointer + descriptor:

```cpp
TextureView src = TextureView(outgoing, frame.outDesc);
RWTextureView dst = RWTextureView(dst, frame.dstDesc);
```

### Bounds checking

Always guard against out-of-bounds access at kernel entry:

```cpp
if (!output.Contains(threadId.xy))
    return;
```

### Pixel formats

VEKL handles pixel storage (`Unorm8x4`, `Unorm16x4`, `Float32x4`) and layout
(`Rgba`, `Bgra`, `Vuya`, `Vuya709`) conversion transparently through
`TextureView.Load()` and `RWTextureView.Store()`. All shader math operates in
RGBA float4 space.

### Blend modes

VEKL provides generic blend functions that work with any vector type:

```cpp
float4 result = BlendAdd(baseColor, glowColor);
float3 darkened = BlendMultiply(baseColor.rgb, shadowColor.rgb);
```

## Documentation

- **[Tutorials](docs/tutorials/)** — Learn VEKL step-by-step
  - [Hello World: Chromatic Aberration](docs/tutorials/00-hello-world-chromatic-aberration.md)
- **[API Reference](docs/reference/)** — Complete type and function catalog
  - [texture](docs/reference/texture/) — Descriptor, format, views
  - [sampling](docs/reference/sampling/) — UV coordinate utilities
  - [math](docs/reference/math/) — Distance masks
  - [noise](docs/reference/noise/) — Perlin noise and FBM
  - [filter](docs/reference/filter/) — Gaussian blur
  - [color](docs/reference/color/) — Blend modes
- **[How-to Guides](docs/how-to-guides.md)** — Recipes for common tasks
- **[Architecture](docs/explanation.md)** — Design decisions and module structure
- **[Contributing](CONTRIBUTING.md)** — Development guidelines

## License

Apache-2.0. See `LICENSE`.
