Add SDL3 GPU backend (Vulkan/Metal) with OpenGL fallback
This commit is contained in:
86
CLAUDE.md
86
CLAUDE.md
@@ -4,7 +4,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a cross-platform Shadertoy-like fragment shader viewer built with SDL3 + OpenGL 3.3. The application loads and displays GLSL shaders from the `shaders/` directory with runtime switching capabilities.
|
||||
This is a cross-platform Shadertoy-like fragment shader viewer built with SDL3. It supports two rendering backends:
|
||||
- **SDL3 GPU** (default): renders via Vulkan on Linux/Windows and Metal on macOS.
|
||||
- **OpenGL 3.3** (fallback): the original backend, used automatically if SDL3 GPU init fails.
|
||||
|
||||
Selection at launch:
|
||||
```bash
|
||||
./shadertoy --backend=gpu # force SDL3 GPU
|
||||
./shadertoy --backend=opengl # force OpenGL
|
||||
./shadertoy --backend=auto # default: GPU first, fall back to OpenGL on failure
|
||||
```
|
||||
|
||||
## Build and Development Commands
|
||||
|
||||
@@ -30,13 +39,21 @@ make macos_debug
|
||||
make linux_debug
|
||||
```
|
||||
|
||||
### Compiling Shaders to SPIR-V
|
||||
The SDL3 GPU backend reads compiled SPIR-V (`.frag.spv`) at runtime. Run this after touching any `.vk.glsl` file:
|
||||
```bash
|
||||
cmake --build build --target compile_shaders
|
||||
```
|
||||
Requires `glslc` (Debian/Ubuntu: `apt install glslang-tools`; macOS: `brew install glslang`).
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
./shadertoy [SHADER_PATH] [-F|--fullscreen]
|
||||
./shadertoy [SHADER_NAME_OR_PATH] [-F|--fullscreen] [--backend=auto|gpu|opengl]
|
||||
|
||||
# Examples:
|
||||
./shadertoy shaders/test.frag.glsl
|
||||
./shadertoy -F shaders/fractal_pyramid.frag.glsl
|
||||
./shadertoy test # auto backend, shaders/test/
|
||||
./shadertoy --backend=gpu seascape # force SDL3 GPU (Vulkan/Metal)
|
||||
./shadertoy -F shaders/fractal_pyramid # explicit folder path, fullscreen
|
||||
```
|
||||
|
||||
**Runtime Controls:**
|
||||
@@ -46,28 +63,49 @@ make linux_debug
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Design
|
||||
All application logic resides in `src/main.cpp` (~469 lines) - a monolithic design that's straightforward to understand. Key components:
|
||||
### Layered design
|
||||
- `src/main.cpp` — SDL3 window, event loop, shader-list scanning, dispatch to a backend.
|
||||
- `src/rendering/shader_backend.hpp` — abstract `IShaderBackend` interface + `ShaderMetadata` / `ShaderUniforms` / `ShaderProgramSpec` shared types. Two factories: `makeOpenGLBackend()`, `makeSdl3GpuBackend()`.
|
||||
- `src/rendering/opengl_shader_backend.{hpp,cpp}` — OpenGL 3.3 implementation. Loads `<folder>/<name>.gl.glsl` at runtime.
|
||||
- `src/rendering/sdl3gpu/sdl3gpu_shader_backend.{hpp,cpp}` — SDL3 GPU implementation. Loads `<folder>/<name>.frag.spv` (Linux/Windows) or `<folder>/<name>.frag.msl` (macOS); shares one passthrough vertex shader from `shaders/_common/passthrough.vert.{spv,msl}`.
|
||||
- `src/rendering/sdl3gpu/shader_factory.hpp` — small helper that loads a shader binary/source from disk and creates an `SDL_GPUShader`.
|
||||
|
||||
1. **Shader Loading System** - Automatic directory scanning of `.glsl` files, sorted alphabetically
|
||||
2. **OpenGL Rendering** - Single fullscreen quad with fragment shader using GL_TRIANGLE_STRIP
|
||||
3. **Event Loop** - SDL3-based with vsync (SDL_GL_SwapWindow + 1ms delay)
|
||||
4. **Resource Path Resolution** - Multi-path fallback system for executable, relative, and macOS bundle paths
|
||||
### Shader folder layout
|
||||
One folder per shader, under `shaders/`:
|
||||
```
|
||||
shaders/<name>/
|
||||
<name>.gl.glsl # OpenGL GLSL 3.30 (handwritten)
|
||||
<name>.vk.glsl # Vulkan GLSL 4.50 (handwritten)
|
||||
<name>.frag.msl # Metal Shading Language (handwritten)
|
||||
<name>.frag.spv # generated by `make compile_shaders` from .vk.glsl
|
||||
meta.txt # Name / Author / iChannelN
|
||||
shaders/_common/
|
||||
passthrough.vk.glsl # shared fullscreen-triangle vertex (Vulkan)
|
||||
passthrough.vert.spv # generated
|
||||
passthrough.vert.msl # handwritten Metal
|
||||
```
|
||||
Folders starting with `_` or `.` are skipped by the scanner. The `meta.txt` parser is in `Rendering::parseMetaFile`.
|
||||
|
||||
### Backend selection logic (`main.cpp`)
|
||||
1. Parse `--backend=...` flag (default `auto`).
|
||||
2. If not OpenGL: create a window with no flags, instantiate `Sdl3GpuShaderBackend`. If `init()` fails AND choice was `auto`, destroy the window and continue. If choice was `gpu`, exit with error.
|
||||
3. Otherwise: create a window with `SDL_WINDOW_OPENGL` and instantiate `OpenGLShaderBackend`.
|
||||
|
||||
### Global State (main.cpp)
|
||||
```cpp
|
||||
shader_list_ // Vector of discovered .glsl shader paths
|
||||
shader_list_ // Vector<ShaderEntry { folder, base_name }>
|
||||
current_shader_index_ // Active shader in rotation
|
||||
current_program_ // OpenGL shader program handle
|
||||
shader_start_ticks_ // Base time for iTime uniform calculation
|
||||
window_ // SDL3 window pointer
|
||||
shaders_directory_ // Shader directory path (resolved at startup)
|
||||
backend_ // unique_ptr<IShaderBackend>
|
||||
shaders_directory_ // Shaders root path (resolved at startup)
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
- **SDL3** - Window/input management, OpenGL context
|
||||
- **GLAD** - OpenGL 3.3 loader (statically linked via `third_party/glad/`)
|
||||
- **C++17 stdlib** - filesystem, fstream, vector, algorithms
|
||||
- **SDL3** ≥ 3.2 — windowing, events, GPU API (`SDL_gpu.h`)
|
||||
- **GLAD** — OpenGL 3.3 loader (used only by the OpenGL backend, statically linked via `third_party/glad/`)
|
||||
- **C++17 stdlib** — filesystem, fstream, vector, algorithms
|
||||
- Build-time tool: `glslc` (only required to regenerate `.frag.spv`)
|
||||
|
||||
### Platform-Specific Code
|
||||
Uses preprocessor defines (`WINDOWS_BUILD`, `MACOS_BUILD`, `LINUX_BUILD`) for:
|
||||
@@ -77,6 +115,22 @@ Uses preprocessor defines (`WINDOWS_BUILD`, `MACOS_BUILD`, `LINUX_BUILD`) for:
|
||||
|
||||
## Shader System
|
||||
|
||||
### Authoring a shader (manual steps)
|
||||
For each new shader, three handwritten files live in `shaders/<name>/`:
|
||||
1. **`<name>.gl.glsl`** — OpenGL GLSL 3.30 (the original Shadertoy-style format described below).
|
||||
2. **`<name>.vk.glsl`** — Vulkan GLSL 4.50 with explicit `layout(set=3, binding=0) uniform ShadertoyUBO { float iTime; vec2 iResolution; } u;` and `layout(set=2, binding=N) uniform sampler2D` for any texture channel.
|
||||
3. **`<name>.frag.msl`** — Metal Shading Language. Entry point must be named `<name>_fs`. Uniforms come in via `[[buffer(0)]]`; samplers via `[[texture(N)]] [[sampler(N)]]`.
|
||||
|
||||
After editing any `.vk.glsl`, rebuild SPIR-V: `cmake --build build --target compile_shaders`.
|
||||
|
||||
The MSL is **not** auto-generated — write it by hand following the same convention as `aee_2026/source/core/rendering/sdl3gpu/msl/*.msl.h`.
|
||||
|
||||
### Shadertoy → Vulkan/Metal porting cheatsheet
|
||||
- `iResolution` is `vec2` here (no `.xy`). Reconstruct vec3 manually if needed.
|
||||
- Replace `texture(iChannel0, ...)` calls — no texture channels currently wired through the GPU backend (planned).
|
||||
- The shared vertex stage emits `vUV` already in Shadertoy convention `(0,0)` bottom-left → `(1,1)` top-right; multiply by `iResolution` to get `fragCoord`.
|
||||
- The Y axis is flipped in NDC inside the shared vertex shader so Vulkan/Metal render right-side-up like OpenGL.
|
||||
|
||||
### Shader Format (GLSL 3.3 Core)
|
||||
All shaders must follow this structure:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user