Source browser
examples/hybrid.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/// Interleave GPU and CPU stages, shared buffers synchronize at backend changes.
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 CPU execution or the requested headless GPU profile.
7 const unsigned count = 129; // Use a nonmultiple of the workgroup size to exercise the shader bounds guards.
8 shdr::buffer<float> input(count), scaled(count), clipped(count), result(1); // Allocate distinct resources for the input, intermediate stages and final result.
9 shdr_examples::randomize(input, 1103u, 0.0f, 4.0f); // Fill the input with reproducible random values.
10 auto affine_owner = shdr::make_kernel("affine_kernel"); // Create the linked affine kernel instance.
11 shdr::kernel& affine = *affine_owner; // Retain the owned shader instance for every stage that references it.
12 affine.bind("input_values", input); // Read the resource produced by the preceding stage or the original host input.
13 affine.bind("output_values", scaled); // Write the shared resource consumed by the next stage or the final host check.
14 affine.set<unsigned>("count", count); // Limit the shader to the actual number of valid buffer elements.
15 affine.set<float>("scale", 4.0f); // Configure the first stage's multiplicative gain.
16 affine.set<float>("offset", -1.0f); // Configure the bias added after the first stage scales each input.
17
18 auto clip_owner = shdr::make_kernel("clamp_kernel"); // Create the linked clamp kernel instance.
19 shdr::kernel& clip = *clip_owner; // Retain the owned shader instance for every stage that references it.
20 clip.bind("input_values", scaled); // Read the resource produced by the preceding stage or the original host input.
21 clip.bind("output_values", clipped); // Write the shared resource consumed by the next stage or the final host check.
22 clip.set<unsigned>("count", count); // Limit the shader to the actual number of valid buffer elements.
23 clip.set<float>("lower", 0.0f); // Set the lower clipping bound in the second stage.
24 clip.set<float>("upper", 7.0f); // Set the upper clipping bound in the second stage.
25
26 auto sum_owner = shdr::make_kernel("sum_kernel"); // Create the linked sum kernel instance.
27 shdr::kernel& sum = *sum_owner; // Retain the owned shader instance for every stage that references it.
28 sum.bind("input_values", clipped); // Read the resource produced by the preceding stage or the original host input.
29 sum.bind("output_values", result); // Write the shared resource consumed by the next stage or the final host check.
30 sum.set<unsigned>("count", count); // Limit the shader to the actual number of valid buffer elements.
31
32 shdr::pipeline pipeline = // Set pipeline from this expression.
33 shdr::dispatch(affine, shdr::extent((count + 63) / 64)) >> // Start the pipeline with the affine shader and enough 64-wide groups.
34 shdr::dispatch(clip, shdr::extent((count + 31) / 32), shdr::backend::cpu) >> // Force clipping onto the CPU so the shared buffer crosses both backend boundaries.
35 shdr::dispatch(sum, shdr::extent(1)); // Return to the selected backend for the final one-invocation reduction.
36 run.run(pipeline); // Execute the configured pipeline on the requested backend.
37 float expected = 0.0f; // Start the independent scalar reference at zero.
38 const shdr::buffer<float>& read_input = input; // Use a constant buffer view so GPU writes synchronize before host reads.
39 for (unsigned i = 0; i < count; ++i) // Iterate over i for this calculation.
40 expected += std::min(7.0f, std::max(0.0f, read_input.data()[i] * 4.0f - 1.0f)); // Apply the same affine and clipping formula directly to the original input.
41 const shdr::buffer<float>& read_result = result; // Use a constant buffer view so GPU writes synchronize before host reads.
42 shdr_examples::require_close(read_result.data()[0], expected, "hybrid pipeline sum"); // Check the computed value against the independent reference.
43 std::cout << (run.gpu() ? "GPU/CPU/GPU" : "CPU") << " pipeline: " << read_result.data()[0] // Write this part of the example result or diagnostic.
44 << "\n"; // Write this part of the example result or diagnostic.
45 return 0; // Report successful completion.
46#ifdef SHDR_HAVE_EGL // Enable this branch when its feature macro is defined.
47 } catch (const shdr_examples::context_unavailable& error) { // Report the exception from the preceding operation.
48 std::cerr << "SKIP: " << error.what() << "\n"; // Write this part of the example result or diagnostic.
49 return 77; // Report an unavailable optional GPU backend.
50#endif // Finish this conditional compilation branch.
51 } catch (const std::exception& error) { // Report the exception from the preceding operation.
52 std::cerr << error.what() << "\n"; // Write this part of the example result or diagnostic.
53 return 1; // Report a failed check or operation.
54 } // End this block.
55} // End this block.