Compare commits
10 Commits
dbe6bdee9a
...
f99e92d1f5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f99e92d1f5 | ||
|
|
b66be87ab9 | ||
|
|
19c112d3bb | ||
|
|
9c3758afc8 | ||
|
|
27b5c7c7d5 | ||
|
|
dc0e2379a8 | ||
|
|
7a7191c17d | ||
|
|
4e8c4be649 | ||
|
|
de38f526b9 | ||
|
|
4d429cf212 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
.opencode/**
|
||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "learn-wgpu"
|
||||
version = "0.1.0"
|
||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "learn-wgpu"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
1350
docs/01-rainbow-triangle.md
Normal file
1350
docs/01-rainbow-triangle.md
Normal file
File diff suppressed because it is too large
Load Diff
28
docs/README.md
Normal file
28
docs/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# learn-wgpu
|
||||
|
||||
An educational book teaching Rust graphics programming with wgpu 29 + winit 0.30.
|
||||
|
||||
Built for backend/systems Rust developers who know Rust thoroughly but have zero
|
||||
graphics programming experience. Every sentence teaches GPU concepts — not Rust syntax.
|
||||
|
||||
## Required Reading Order
|
||||
|
||||
1. [The Graphics Pipeline](concepts/graphics-pipeline.md) — GPU vs CPU, the 5-stage pipeline
|
||||
2. [Coordinate Systems](concepts/coordinate-systems.md) — NDC, homogeneous coordinates, viewport
|
||||
3. [Shader Basics](concepts/shader-basics.md) — WGSL, vertex/fragment shaders, interpolation
|
||||
4. [Build a Rainbow Triangle](01-rainbow-triangle.md) — The complete step-by-step guide
|
||||
5. [Troubleshooting](TROUBLESHOOTING.md) — Common errors and fixes (reference as needed)
|
||||
6. [Glossary](concepts/GLOSSARY.md) — Every term defined (reference as needed)
|
||||
|
||||
> **Reading order matters:** Coordinate systems must be understood before shader basics,
|
||||
> because the shader walkthrough references NDC coordinates.
|
||||
|
||||
## Guides
|
||||
|
||||
- **01 — Rainbow Triangle** — The minimal complete program: one triangle, smooth color interpolation
|
||||
- More guides coming soon (textures, lighting, compute)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rust stable (edition 2024)
|
||||
- Linux x86_64 with Vulkan drivers (`libvulkan1`)
|
||||
133
docs/TROUBLESHOOTING.md
Normal file
133
docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Troubleshooting
|
||||
|
||||
## 1. `cargo build` fails with "no matching package"
|
||||
|
||||
**Symptom:** Build fails with `could not find package` for wgpu or winit.
|
||||
|
||||
**Cause:** Version typo in `Cargo.toml`. wgpu and winit have specific major versions.
|
||||
|
||||
**Fix:** Verify your dependency versions:
|
||||
```toml
|
||||
wgpu = "29"
|
||||
winit = "0.30"
|
||||
tokio = { version = "1", features = ["rt", "macros"] }
|
||||
bytemuck = { version = "1", features = ["derive"] }
|
||||
log = "0.4"
|
||||
simple_logger = "5"
|
||||
```
|
||||
|
||||
## 2. Window opens but shows black screen
|
||||
|
||||
**Symptom:** Window appears but is entirely black. No triangle visible.
|
||||
|
||||
**Cause:** One (or more) of several common rendering misconfigurations:
|
||||
- Pipeline not set: `pass.set_pipeline()` not called
|
||||
- Vertex buffer not bound: `pass.set_vertex_buffer()` not called
|
||||
- Draw call wrong: `draw()` arguments incorrect (must be `draw(0..3, 0..1)` — two ranges)
|
||||
- Triangle wound clockwise: `FrontFace::Ccw` requires counter-clockwise vertex order
|
||||
- Format mismatch: `ColorTargetState::format` must match `SurfaceConfiguration::format`
|
||||
- Present omitted: `surface_texture.present()` must be called to show rendered frame
|
||||
|
||||
**Fix:** Check each item above. Verify the pipeline is set, buffer is bound, draw uses two `Range<u32>` arguments, vertices are CCW, formats match, and `present()` is called.
|
||||
|
||||
## 3. Washed-out or wrong colors
|
||||
|
||||
**Symptom:** Triangle renders but colors look dull or incorrect.
|
||||
|
||||
**Cause:** `SurfaceConfiguration::view_formats` missing `add_srgb_suffix()`. Without sRGB gamma correction, colors appear washed out.
|
||||
|
||||
**Fix:** Ensure `view_formats` includes the sRGB variant:
|
||||
```rust
|
||||
view_formats: vec![format.add_srgb_suffix()],
|
||||
```
|
||||
|
||||
## 4. Panic: "No adapter found"
|
||||
|
||||
**Symptom:** Program crashes immediately with `No GPU adapter found`.
|
||||
|
||||
**Cause:** No Vulkan driver installed. wgpu on Linux requires Vulkan.
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
sudo apt install libvulkan1 mesa-vulkan-drivers
|
||||
vulkaninfo # verify installation
|
||||
```
|
||||
|
||||
## 5. Panic: "Surface lost"
|
||||
|
||||
**Symptom:** Program crashes with a surface lost error during rendering.
|
||||
|
||||
**Cause:** Display server restarted or GPU context was reset. The [Surface](concepts/GLOSSARY.md#Surface) is permanently invalidated.
|
||||
|
||||
**Fix:** In the tutorial, this means the window needs to be reopened. In production code, handle the `Lost` variant of `CurrentSurfaceTexture` by recreating the surface via `Instance::create_surface()`.
|
||||
|
||||
## 6. Wayland surface crashes or doesn't render
|
||||
|
||||
**Symptom:** Program crashes or shows blank window on Wayland.
|
||||
|
||||
**Cause:** winit's Wayland backend may have compatibility issues with certain compositors.
|
||||
|
||||
**Fix:** Force X11 backend:
|
||||
```bash
|
||||
WINIT_UNIX_BACKEND=x11 cargo run
|
||||
```
|
||||
|
||||
## 7. Window won't close
|
||||
|
||||
**Symptom:** Clicking the X button or pressing Escape doesn't close the window.
|
||||
|
||||
**Cause:** `ApplicationHandler::exiting()` not implemented, or `event_loop_ctl.exit()` not called.
|
||||
|
||||
**Fix:** Ensure `exiting()` (note: NOT `closing()`) is implemented:
|
||||
```rust
|
||||
fn exiting(&mut self, event_loop_ctl: &ActiveEventLoop) {
|
||||
event_loop_ctl.exit();
|
||||
}
|
||||
```
|
||||
|
||||
## 8. CPU at 100%
|
||||
|
||||
**Symptom:** One CPU core at 100% usage.
|
||||
|
||||
**Cause:** `ControlFlow::Poll` drives `RedrawRequested` events continuously. Every frame renders and queues another redraw. This is the expected behavior for a `ControlFlow::Poll` render loop.
|
||||
|
||||
**Fix:** No fix needed — this is expected for a continuous redraw demo. For production, switch to `ControlFlow::Wait` and call `request_redraw()` only when state changes.
|
||||
|
||||
## 9. Shader compilation or pipeline creation error
|
||||
|
||||
**Symptom:** Program panics during `create_render_pipeline` with a shader validation error.
|
||||
|
||||
**Cause:** `@location` numbers in WGSL don't match `shader_location` numbers in Rust's `VertexAttribute`, or `@builtin(position)` is missing from vertex shader output.
|
||||
|
||||
**Fix:** Check the log output for validation messages. Verify:
|
||||
- WGSL `@location(0)` matches Rust `shader_location: 0`
|
||||
- WGSL `@location(1)` matches Rust `shader_location: 1`
|
||||
- Vertex shader outputs `@builtin(position) clip_position: vec4<f32>`
|
||||
|
||||
## 10. Triangle shows one solid color instead of gradient
|
||||
|
||||
**Symptom:** Triangle renders but is a single uniform color instead of smoothly blending.
|
||||
|
||||
**Cause:** Fragment shader returns a constant color instead of passing through `input.vertex_color`. The [rasterizer](concepts/GLOSSARY.md#Rasterizer) interpolates vertex colors automatically, but only if the fragment shader uses them.
|
||||
|
||||
**Fix:** Ensure fragment shader returns the interpolated vertex color:
|
||||
```wgsl
|
||||
@fragment
|
||||
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(input.vertex_color, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
Not this (which returns a solid color):
|
||||
```wgsl
|
||||
return vec4<f32>(1.0, 1.0, 1.0, 1.0); // wrong: solid white
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Glossary](concepts/GLOSSARY.md) — Every term defined
|
||||
- [Graphics Pipeline](concepts/graphics-pipeline.md) — Pipeline stage overview
|
||||
- [Coordinate Systems](concepts/coordinate-systems.md) — NDC and transformation spaces
|
||||
- [Shader Basics](concepts/shader-basics.md) — WGSL and shader concepts
|
||||
- [wgpu documentation](https://docs.rs/wgpu/29.0.0/) — Official wgpu 29 API docs
|
||||
- [wgpu repository](https://github.com/gfx-rs/wgpu) — Examples and issue tracker
|
||||
@@ -70,6 +70,10 @@ A compiled GPU configuration bundling: [[vertex shader]](#vertex-shader) + [[fra
|
||||
|
||||
The strategy passed to `device.poll()`. `PollType::Wait` blocks the calling thread until all pending GPU work finishes — equivalent to a fence wait. `PollType::Poll` checks for completed work once and returns immediately, regardless of whether work is done. For the rainbow triangle, `Wait` is correct: we need the GPU to finish the frame before requesting the next surface texture.
|
||||
|
||||
## Present Mode
|
||||
|
||||
How the display compositor handles frame buffer presentation: `PresentMode::Mailbox` uses triple buffering for tear-free rendering, `PresentMode::Fifo` provides VSYNC-locked double buffering, `PresentMode::Immediate` renders without synchronization (may show tearing).
|
||||
|
||||
## Ndc
|
||||
|
||||
Normalized Device Coordinates. The GPU's native intermediate coordinate space. X and Y range from -1.0 (left/bottom) to +1.0 (right/top). Z ranges from 0.0 (near clipping plane) to 1.0 (far clipping plane). Geometry is mapped into NDC by the GPU after perspective division. Anything outside this cube is clipped. See [[coordinate-systems.md]](coordinate-systems.md).
|
||||
@@ -110,6 +114,10 @@ Controls what happens to the [[framebuffer]](#framebuffer) at the end of a rende
|
||||
|
||||
wgpu's connection to a window's display buffer. Created via `instance.create_surface(window)`, the surface is like a bound socket — it is tied to a specific window and cannot be unlinked. The surface manages the [[swapchain]](#swapchain) and provides new framebuffers via `surface.get_current_texture()`. If the window is resized, the surface must be reconfigured with a new `SurfaceConfiguration`.
|
||||
|
||||
## Surface Configuration
|
||||
|
||||
The `SurfaceConfiguration` struct that allocates swapchain framebuffers: format, dimensions, present mode, and view formats. Reconfigured on window resize via `Surface::configure()`.
|
||||
|
||||
## Swapchain
|
||||
|
||||
A ring buffer of 2-3 [[framebuffer]](#framebuffer) textures managed by the GPU driver. The display hardware reads from the front buffer. The application renders to the back buffer. When the frame is complete, the buffers swap: the back buffer becomes the front (displayed), and the old front becomes the available back buffer for the next frame. This prevents screen tearing by ensuring the display never reads a frame mid-update.
|
||||
|
||||
3
src/main.rs
Normal file
3
src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
Reference in New Issue
Block a user