Source browser
examples/pipeline.cpp
Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.
File fingerprint
1#include "runner.hpp" // Include runner.hpp declarations.
2
3/// Build and verify three linked stages from the exact same C++/GLSL sources.
4int main(int argc, char** argv) { // Run this example and validate its results.
5 try { // Run the operation while retaining the error handling below.
6 shdr_examples::runner run(argc, argv); // Select ordinary CPU execution or the requested headless GPU profile.
7 const unsigned count = 257; // Uneven sizes exercise shader bounds guards.
8 shdr::buffer<float> input(count), scaled(count), clipped(count), result(1); // Allocate one resource for each stage boundary and a one-element reduction result.
9 shdr_examples::randomize(input, 1101u, -4.0f, 12.0f); // Fill the input with reproducible random values.
10
11 auto affine_owner = shdr::make_kernel("affine_kernel"); // Create the linked affine kernel instance.
12 shdr::kernel& affine = *affine_owner; // Keep the affine instance alive while its pipeline stages refer to it.
13 affine.bind("input_values", input); // Read the resource produced by the preceding stage or the original host input.
14 affine.bind("output_values", scaled); // Write the resource consumed by the next stage or the final host check.
15 affine.set<unsigned>("count", count); // Guard the partial final workgroup using the actual number of input values.
16 affine.set<float>("scale", 2.0f); // Double each input element in the first stage.
17 affine.set<float>("offset", 1.0f); // Add one after scaling each element.
18
19 auto clip_owner = shdr::make_kernel("clamp_kernel"); // Create the linked clamp kernel instance.
20 shdr::kernel& clip = *clip_owner; // Keep the clipping instance alive through pipeline execution.
21 clip.bind("input_values", scaled); // Read the resource produced by the preceding stage or the original host input.
22 clip.bind("output_values", clipped); // Write the resource consumed by the next stage or the final host check.
23 clip.set<unsigned>("count", count); // Guard the partial final workgroup using the actual number of input values.
24 clip.set<float>("lower", 0.0f); // Clip values below zero in the second stage.
25 clip.set<float>("upper", 8.0f); // Clip values above eight in the second stage.
26
27 auto sum_owner = shdr::make_kernel("sum_kernel"); // Create the linked sum kernel instance.
28 shdr::kernel& sum = *sum_owner; // Keep the reduction instance alive through pipeline execution.
29 sum.bind("input_values", clipped); // Read the resource produced by the preceding stage or the original host input.
30 sum.bind("output_values", result); // Write the resource consumed by the next stage or the final host check.
31 sum.set<unsigned>("count", count); // Guard the partial final workgroup using the actual number of input values.
32
33 // Resources are linked by binding the same object to adjacent shader ports.
34 shdr::pipeline pipeline = shdr::dispatch(affine, shdr::extent((count + 63) / 64)) >> // Start with enough 64-wide groups to cover all original inputs.
35 shdr::dispatch(clip, shdr::extent((count + 31) / 32)) >> // Run the linked clip shader with its own 32-wide workgroup size.
36 shdr::dispatch(sum, shdr::extent(1)); // Finish with one deterministic reduction invocation over the clipped resource.
37 run.run(pipeline); // Execute the configured pipeline on the requested backend.
38
39 float expected = 0.0f; // Start the independent host reference sum at zero.
40 const shdr::buffer<float>& read_input = input; // Use a constant buffer view so GPU writes synchronize before host reads.
41 for (unsigned i = 0; i < count; ++i) // Apply the same scale, offset and clipping formula to every original input.
42 expected += std::min(8.0f, std::max(0.0f, read_input.data()[i] * 2.0f + 1.0f)); // Accumulate the reference from the original inputs and the stated scale, offset and clipping formula.
43 const shdr::buffer<float>& read_result = result; // Use a constant buffer view so GPU writes synchronize before host reads.
44 shdr_examples::require_close(read_result.data()[0], expected, "pipeline sum"); // Check the computed value against the independent reference.
45 std::cout << (run.gpu() ? "GPU" : "CPU") << " buffer pipeline: " << read_result.data()[0] // Write this part of the example result or diagnostic.
46 << "\n"; // Write this part of the example result or diagnostic.
47 return 0; // Report successful completion.
48#ifdef SHDR_HAVE_EGL // Enable this branch when its feature macro is defined.
49 } catch (const shdr_examples::context_unavailable& error) { // Report the exception from the preceding operation.
50 std::cerr << "SKIP: " << error.what() << "\n"; // Write this part of the example result or diagnostic.
51 return 77; // Report an unavailable optional GPU backend.
52#endif // Finish this conditional compilation branch.
53 } catch (const std::exception& error) { // Report the exception from the preceding operation.
54 std::cerr << error.what() << "\n"; // Write this part of the example result or diagnostic.
55 return 1; // Report a failed check or operation.
56 } // End this block.
57} // End this block.