Check an existing GLSL compute shader before running it on CPU and GPU
shdr project — Vojtěch Vrbashdr builds a C++11 CPU adapter and embeds an OpenGL program from a supported GLSL compute source. A successful port needs both a compatible source interface and an algorithm that fits the shared execution model.
Use this guide for an initial assessment. The upstream compatibility reference is the detailed authority for the pinned version used here.
Begin with an independent invocation
The easiest candidate reads stable input and writes a distinct output element for each invocation. Elementwise transforms and out-of-place neighborhood filters naturally fit this pattern.
Keep producer/consumer communication between ordered dispatches. The CPU backend executes invocations serially. If two GPU invocations need to communicate within a workgroup, serial CPU execution alone cannot establish correctness for that shader.
For example, express a blur as input → output, using separate resources. An in-place blur can race on a GPU even when a serial run produces plausible-looking pixels.
Check the source and interface
- Is the source self-contained? — Keep the compute entry point and required helper functions in the same source.
- How is it attached? — Use
shdr_target_shaders(app PRIVATE SOURCES shader.comp)on the executable or a loaded shared module. - How is it named? — The file stem must be a valid C++ identifier;
shader.compregistersshader_kernel. - Which buffer elements are used? — 32-bit
float,int,uint, or matching two-/four-component vectors in supported array blocks. - Which image formats are used? — Supported one- or four-component 32-bit storage images, with matching declared access and formats.
- Which uniforms are used? — Supported scalar/vector/matrix values, set using matching host types.
- How many invocations run? — Local size multiplied by dispatch group count, with a logical-size bounds check.
Three-component vectors and matrices support local math and uniforms; do not assume that they are interchangeable with the supported storage-buffer array layouts. Adapt structured storage into supported scalar or vector arrays before binding it.
Start with a version directive and a guarded runtime include:
#version 310 es #ifdef __cplusplus #include "shdr.h" #endif precision highp float; precision highp int;
The guard exposes shdr declarations to C++ compilation. The build frontend prepares the GPU text for the selected profile. A version number alone cannot make unsupported language features portable.
Identify adaptations early
Workgroup shared storage, barriers, atomics, subgroup operations, samplers, derivatives, and graphics-stage interfaces require adaptation to the documented compute interface and independent-invocation model. Uniform blocks, uniform arrays, and custom structured storage layouts also need interface review.
The frontend supports a useful set of declarations and expressions, including supported fixed/local arrays, array parameters, swizzles, and constructor conversions. Some imported Vulkan-style interfaces have documented mappings, but the runtime's GPU backend uses OpenGL resource semantics. Do not describe an imported source as arbitrary Vulkan execution.
Test both compilers and both execution paths
- Build the C++11 adapter and resolve its diagnostics.
- Run a small CPU case with independently known output.
- Inspect
kernel::glsl(profile)when diagnosing the embedded GPU text. - Compile for the intended profile, then execute through the real driver path.
- Compare outputs and check buffer sizes, image formats, border rules, and padded dispatches.
Compiler acceptance, successful execution, race freedom, and numerical accuracy are separate questions. Keep integer operations within defined ranges and compare floating-point results with a justified tolerance.
The repository's public shader corpus report records accepted inputs and failures for a pinned source collection. That report is useful compatibility evidence; it is not a guarantee for every shader outside that collection.
A small reproducible baseline
Run the first-kernel example before changing a large shader. It supplies a self-contained source, typed bindings, a rounded-up dispatch, and an independent expected result. Once the portable CPU and GL cases agree, compose stages and measure the complete workload.
Prepared with AI assistance on behalf of the shdr project. Source and API claims were checked against shdr 1.0.0, commit 01652c119dad9662f48e3c056c5f988593c630d8. Verification on 15 October 2026: all 23 upstream CPU tests passed; the complete scaling example produced its expected values in CPU, EGL OpenGL ES 3.1, and mixed OpenGL/CPU modes. These are correctness checks, not hardware GPU performance measurements. Upstream source and MIT license.