Repository navigation
Conversation
The lab was pinned to Dawn chromium/6670 (Aug 2024), fetched by a Python script that cloned Dawn, glm and tinygltf into libs/ and built Dawn from source. That setup only ever worked on Windows and no longer configures with current toolchains. Build system - Dawn is pinned to release v20261002.154047 in cmake/LabDawn.cmake. LAB_DAWN selects where it comes from: the prebuilt archive Dawn's CI publishes (default), a source build, or an installed Dawn. - GLFW 3.4+ is taken from the system if present, fetched otherwise. - Only dawn::webgpu_dawn is linked; the Dawn-internal targets and the proc table setup are gone. - CMake presets (dev, release, dev-asan) replace the manual steps. Samples are only built when the lab is the top-level project. - The Python scripts are removed. Port to the current webgpu_cpp.h - Surfaces are created by our own glue (src/platform) from GLFW's native handles, for Wayland, X11 and Win32. No packaged Dawn ships webgpu_glfw. - Webgpu no longer opens a hidden window to probe the surface format (it did so with an instance that was not created yet) and no longer needs GLFW at all. - Vertex format sizes are a switch instead of a table indexed by enum value; the enum has been renumbered since. - Surfaces are sized in framebuffer pixels and the resize callback reports pixels. - StringView, ShaderSourceWGSL, TexelCopy*, the new callback signatures, SuccessOptimal/SuccessSuboptimal and reordered descriptor fields. - A frame without a surface texture is skipped instead of rendered to. The library builds without warnings with GCC 16 and Clang 23. C++23 is now required. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Closing the last window called glfwTerminate(), which on Wayland drops the display connection. The device releases its swapchain later, when Webgpu is destroyed, and then talked to the dead connection: every windowed sample crashed on exit. A closed window is now only hidden and unregistered. GLFW destroys the native windows and shuts down at program exit, after all GPU objects. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With the environment variable set to N, lab::tick() closes all windows
after N calls. Every sample can then be run by a script or CI:
LAB_EXIT_AFTER_FRAMES=120 ./sample_texture
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Missing designated initializers and unused lambda captures, reported by GCC 16 and Clang 23 with -Wall -Wextra. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- launch.json gets a gdb configuration next to the MSVC one, so "F5 runs the sample that is open" works on both. - settings.json uses the CMake presets. - c_cpp_properties.json is removed: it hard-coded a Windows/MSVC IntelliSense setup (see issue #3). - .clangd points clangd at the compile commands of the dev preset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- add_uniform_buffer() created the layout entry at binding 0 whatever binding index it was given. Unnoticed so far because every sample binds its uniforms at 0. - The default render function passed firstVertex and dropped firstInstance when calling DrawIndexed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The label was kept as a const char* that could outlive its string. - current_capacity was read uninitialized on an empty buffer. - Buffer<T> copies the bytes of T to and from GPU memory, so T now has to be trivially copyable. test_buffers.cpp, which uploaded std::string objects, is removed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
It used to print a message and carry on with an empty shader, which surfaced much later as a confusing pipeline error. The exception names the file and the working directory, since a relative path that does not resolve is the usual cause. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A header should not add a using-directive to the global namespace. Code that wants `100ms` writes `using namespace std::chrono_literals;` itself. Also rename two template parameters that used reserved names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Makes the lab usable on CI machines and in VMs. The adapter in use is printed at startup. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- sample_uniform_buffer joined a thread running `while (true)`, so the process never ended after its window was closed. The timer is now restarted from the main loop. - test_windows called destructors explicitly on objects that were destroyed again at the end of their scope, one of them from inside a callback owned by the object it destroyed. - sample_buffer shared plain bools between two threads. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- tests/ holds doctest tests that need a GPU or software adapter but no window. The render tests draw into a texture and compare pixels: a triangle over the clear color, vertex attributes, a uniform buffer at a non-zero binding, indexed and instanced drawing. - Every sample is registered as a CTest test that has to run 60 frames without printing an error. These open windows and carry the label "samples", so `ctest -LE samples` runs headless. - CI runs the headless tests on a software adapter. - The dev-asan preset aborts on undefined behaviour. Its leak detection is off because GPU drivers leak a little when they are unloaded. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
LAB_DAWN=source cloned Dawn with GIT_SHALLOW. CMake's shallow clone still fetches the tip of every branch and tag, and Dawn has thousands of them, so the clone took longer than building Dawn. The source archive of the pinned tag is 27 MB. Both Dawn downloads now fail after 60 seconds without data instead of hanging the configure step forever. Building Dawn from source this way takes about five minutes on a 32-core machine and all tests pass against it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Headers move from src/objects and src/extra to a flat include/ and sources from src/detail to src/. Only include/ is on the include path of projects using the lab, so they no longer see <objects/...>, <extra/...> or the private lab_state.h. No code changes besides the include paths. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The old Pipeline was shader, fixed state, the buffers to draw and the bind groups in one mutable object, and render_frame() was the only way to render: one pipeline, one pass, one frame. Anything more meant copying fifty lines of raw WebGPU, as the graph samples did. New shape - lab::Gpu (was Webgpu), lab::Window, lab::Surface, lab::Shader, lab::Buffer<T>, lab::Texture, lab::Pipeline, lab::Frame, lab::RenderPass. Every object exposes the WebGPU object it wraps through handle(). - A Pipeline is immutable and built for one target format. Its description holds vertex layouts (lab::vertex<T>, lab::instance<T>) and fixed state. There is no finalize(). - What is drawn is a lab::Draw: vertex buffers, index buffer, bind groups, counts. Counts default to what the buffers hold. - Bind groups are plain wgpu::BindGroup values made by pipeline.bind_group(); their layouts come from the shader. - A RenderPass takes any number of draw calls, a Frame any number of passes, onto a Surface or into a Texture. render_frame() remains as the one-line form. - Buffer<T>: write(), blocking read(), read_async(), fill in place at creation. The lab adds the usage flags these need. The jthread based mapping API and MappedVRAM are gone. - Shader compiles on construction. Files are looked up from the working directory and from the directory of the executable. Fixed by construction - Objects share reference-counted state instead of holding references to each other, so they are movable and can be destroyed in any order. A native window is kept until no Gpu can still own a swapchain for it. - The surface follows its window size when a frame is acquired and no longer takes over the window's resize callback. It handles outdated and lost surface textures. - The surface format is chosen per surface, preferring an 8-bit format: an HDR capable desktop lists RGBA16Float first. - Global state shrinks to one private runtime (GLFW, open windows, live Gpus, the clock). GLFW callbacks use the window user pointer. Errors and logging - Mistakes the lab can detect throw lab::Error where they are made and name the object: buffers that do not fit a pipeline's layout, a pipeline drawn into another target format, out-of-range writes. - Creating shaders, pipelines and bind groups turns WebGPU validation errors into exceptions on the spot. Errors reported later are thrown by the next tick(). - Objects without a label are named after the source location they are created at. - Log output goes through lab::log with levels, a sink and LAB_LOG. Also new - Window: polling for keys and mouse (key_pressed, mouse_delta, scroll) next to callbacks, framebuffer_size(), aspect(). - lab::delta_seconds(). - Texture::read(), Texture::save_png() and LAB_CAPTURE_DIR, which makes every surface save one frame as a PNG. Samples are rewritten as a numbered sequence, one concept each, with a shader named after its sample. test_uniform_buffers and test_tinygltf became tests or went away, as did the unused shaders and the Blender backup file. Tests: 31 test cases render into textures and compare pixels, check the error messages and exercise window, surface and gpu lifetimes in every destruction order. Tests that open a window carry the CTest label "display". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Depth
- SurfaceOptions::depth gives a surface a depth buffer that it keeps at
the size of the window.
- A pass into a texture takes one through PassOptions::depth.
- TargetFormat carries the depth format, so a pipeline for a target
with depth gets a depth test (PipelineDesc::depth_compare and
depth_write, by default "less" and writing).
Samplers
- lab::sampler(gpu, {filter, address_mode}) returns a wgpu::Sampler to
put into a bind group.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- lab::ComputePipeline(gpu, shader), with bind_group() like the render pipeline and run() to dispatch and submit in one call. - lab::ComputePass dispatches within a lab::Frame, so that a render pass of the same frame can draw what was just computed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- 10_house finally renders the glTF house from samples/assets: depth buffer, orbit camera with mouse and scroll wheel, simple lighting, and a ground textured from an image file through a repeating sampler. - 11_boids simulates a flock in a compute shader and draws it in the same frame. Simulation and rendering share the same two buffers. - 12_offscreen renders into a small texture and shows it magnified in a second pass. S saves the texture as a PNG. - 05_texture reads its texture through a sampler. lab::find_file() locates files of a program from the working directory or from next to the executable, the way lab::Shader already does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CI gets a job that checks the formatting with a pinned clang-format. The boids sample gets stronger alignment, which makes flocks form within seconds. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A render pass onto a surface asked for the depth view and the color view in one argument list. Getting the color view is what makes the surface adapt to a new window size, and when the depth view happened to be evaluated first it was the one of the old size: WebGPU rejected the frame after a resize. The color view is now fetched first. Found by a new test that resizes a window with a depth buffer. Also adds LAB_WINDOW_SYSTEM=x11|wayland to override the window system GLFW picks. Unsetting WAYLAND_DISPLAY is not enough to get X11 in a Wayland session, so this is what makes the X11 path testable there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The release build warned that the padded copy could overflow for absurd sizes. Only the last 1 to 3 bytes need padding, the rest is written as it is. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`emcmake cmake --preset web` builds every sample as a web page, plus an index page with thumbnails, into build/web/site. A Pages workflow publishes that from main; the CI builds it on every push. - WebGPU is the browser's own, through Dawn's Emscripten port of the very Dawn release the native build is pinned to (cmake/emdawnwebgpu.remoteport.py). GLFW comes from the contrib.glfw3 port, and the canvas follows the size of the browser window. - src/platform/lab_surface_web.cpp is the web counterpart of the GLFW surface glue: the surface is created from the canvas, presenting is left to the browser, and tick() awaits the next animation frame, so the `while (lab::tick())` loop of every sample runs unchanged. That needs ASYNCIFY, which also covers the lab's blocking waits. - Exceptions are switched on for the web build; the lab reports mistakes with them. - 09_multi_window is left out on the web, a page has one canvas. Verified in a headless Firefox 157: the triangle, graph, instancing and house samples render; the house loads its model and texture from the packed files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dawn's D3D12 backend compiles shaders through dxcompiler.dll and dxil.dll (or d3dcompiler_47.dll), and only looks for them in the directory of the executable: nothing else is accepted by the way it calls LoadLibraryEx. The Dawn archive does not ship them, so on CI every test failed to create a device. cmake/LabWindowsRuntime.cmake downloads the DirectX Shader Compiler release, takes d3dcompiler_47.dll from the Windows SDK if there is one, and lab_copy_runtime_dlls() copies them next to a target, which happens for every executable the lab builds. Projects that use the lab call it for their own executables. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Windows does not delete a file that is still open, which made this the one test that failed there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Repository owner
locked and limited conversation to collaborators
Oct 6, 2026
Repository owner
unlocked this conversation
Oct 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AI overhaul