Guide

Resources, bindings and modules

Build a pipeline by connecting named shader ports to shared buffer or image storage. Kernels hold the bindings and uniforms. Resource wrappers manage host memory, GPU storage and the direction of synchronization.

Understand resource storage

buffer<T>(count) and image2d<T, Components>(width, height) own host storage and allocate GPU storage when it is needed. Resource copies and bound ports share the same storage. buffer<T>(pointer, count) borrows existing host memory. That memory must remain alive while a resource or port can use it.

Mutable data() synchronizes newer GPU contents, then marks the host copy modified. Const data() synchronizes while preserving its modification status. Use a const resource reference when inspecting a result. Obtain a fresh data() pointer after GPU work before accessing host storage. upload(device) and download(device) provide explicit transfer points.

Storage-buffer elements use 32-bit float, int and uint, or matching vec2/vec4 types with established std430 array strides. Three-component vectors and matrices are available for local shader math and supported uniforms.

Images support one or four interleaved 32-bit components with matching r32f/r32i/r32ui or rgba32f/rgba32i/rgba32ui formats. Use separate input and output resources for neighborhood filters. Portable RGBA image ports declare readonly or writeonly access.

For GL interoperation, gl_handle(device) returns a synchronized object handle. from_gl borrows an application-owned buffer or texture. Keep that object alive while the resource can use it. Call mark_gpu_modified(device) after application-issued GL writes, or mark_host_modified() after writes through a borrowed host pointer. Supply barriers appropriate to application-issued GL consumers.

Resources belong to one device. Perform GPU operations and final resource destruction on its context thread. A headless device retains its context with GPU objects and makes it current when used. Keep an application-owned context alive and current. The runtime changes program, buffer and image bindings, so restore application GL state where needed. Serialize access to mutable kernels and resources, and use separate writable storage for concurrent jobs.

Use shader modules in a larger project

An installed CMake package provides the header target and shader build helper:

find_package(shdr 1.0.0 CONFIG REQUIRED) # Find the installed header target and shader build helper.
add_executable(app main.cpp) # Build the application that binds and dispatches kernels.
target_link_libraries(app PRIVATE shdr::shdr) # Link the CPU-compatible runtime target.
shdr_target_shaders(app PRIVATE SOURCES scale.cpp another_shader.cpp) # Compile and embed both native shader sources into this target.

Link shdr::egl when the application creates a headless_device. EGL must be available when configuring that target.

Attach adapters directly to an executable or loaded shared library for automatic factory discovery. For a static archive, configure the application linker to retain the factory-registration objects.

The dynamic-library example exports creation and destruction callbacks with C linkage. Keep the library loaded while its kernel objects and pipelines are in use, and destroy those objects through the library's callback. Host and plugin must use compatible C++ runtimes and shdr versions.

Read a completed result

A GPU dispatch updates GPU storage. A const access downloads its newer contents before returning a host pointer. Keep that pointer only until the next operation that may change the resource.

const shdr::buffer<float>& completed = output; // Read through a const host view.
const float* values = completed.data(); // Synchronize a newer GPU result before accessing it.
float first_value = values[0]; // Read one value after the pipeline has completed.

Connect reusable groups of stages

A stage refers to a kernel and dispatch dimensions. A pipeline stores stages in order. Keep the kernels alive while the pipeline can run. The same buffer can connect the output of one stage to the input of the next.

auto first_group = shdr::dispatch(*first, groups); // Describe the first kernel dispatch.
auto second_group = shdr::dispatch(*second, groups); // Describe its consumer.
auto linked = first_group >> second_group; // Connect stages in their execution order.
linked.run(); // Execute every stage on the CPU.

Pipelines support += for stages or other pipelines, and >> between a stage and a pipeline or between two pipelines. Composition copies stage descriptions, which refer to existing kernels and shared resource storage. Keep those kernels alive for every use of the composed pipeline.

Read asynchronous execution before allowing multiple jobs to remain in flight, and portability before importing a new resource layout.