Source browser
examples/images.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/// Link SSBO and image stages, checking a full two-dimensional CPU/GPU result.
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 width = 19, height = 13; // Choose dimensions that exercise partial final workgroups on both image axes.
8 shdr::buffer<float> input(width * height), output(width * height); // Allocate distinct resources for the input, intermediate stages and final result.
9 shdr::image2d<float, 1> original(width, height), filtered(width, height); // Allocate separate input and output images for neighborhood sampling.
10 shdr_examples::randomize(input, 1102u, 0.0f, 23.0f); // Fill the input with reproducible random values.
11
12 auto upload_owner = shdr::make_kernel("buffer_to_image_kernel"); // Create the linked buffer to image kernel instance.
13 shdr::kernel& upload = *upload_owner; // Retain the owned shader instance for every stage that references it.
14 upload.bind("input_values", input); // Copy the host buffer into the image shared with the blur stage.
15 upload.bind("output_image", original); // Copy the host buffer into the image shared with the blur stage.
16
17 auto blur_owner = shdr::make_kernel("blur_kernel"); // Create the linked blur kernel instance.
18 shdr::kernel& blur = *blur_owner; // Retain the owned shader instance for every stage that references it.
19 blur.bind("input_image", original); // Connect the immutable input image and the separate filtered image.
20 blur.bind("output_image", filtered); // Connect the immutable input image and the separate filtered image.
21
22 auto download_owner = shdr::make_kernel("image_to_buffer_kernel"); // Create the linked image to buffer kernel instance.
23 shdr::kernel& download = *download_owner; // Retain the owned shader instance for every stage that references it.
24 download.bind("input_image", filtered); // Copy the filtered image into the buffer used by the host result check.
25 download.bind("output_values", output); // Copy the filtered image into the buffer used by the host result check.
26
27 const shdr::extent groups((width + 7) / 8, (height + 7) / 8); // Round up both image axes to the shader's eight-by-eight local size.
28 shdr::pipeline pipeline; // Declare pipeline for the following calculation.
29 pipeline += shdr::dispatch(upload, groups); // Begin by transferring values between two shader resource types.
30 pipeline += shdr::dispatch(blur, groups); // Consume the image produced by the preceding stage and filter its neighborhood.
31 pipeline += shdr::dispatch(download, groups); // Return the filtered scalar pixels to a buffer resource.
32 run.run(pipeline); // Execute the configured pipeline on the requested backend.
33
34 const shdr::buffer<float>& read_input = input; // Use a constant buffer view so GPU writes synchronize before host reads.
35 const shdr::buffer<float>& read_output = output; // Use a constant buffer view so GPU writes synchronize before host reads.
36 for (unsigned y = 0; y < height; ++y) { // Iterate over y for this calculation.
37 for (unsigned x = 0; x < width; ++x) { // Iterate over x for this calculation.
38 float expected = 0.0f; // Start the independent scalar reference at zero.
39 for (int dy = -1; dy <= 1; ++dy) { // Iterate over dy for this calculation.
40 for (int dx = -1; dx <= 1; ++dx) { // Iterate over dx for this calculation.
41 const unsigned sx = // Choose the reference sample column before applying the border rule.
42 unsigned(std::max(0, std::min(int(width) - 1, int(x) + dx))); // Clamp this reference coordinate to the nearest valid image edge.
43 const unsigned sy = // Choose the reference sample row before applying the border rule.
44 unsigned(std::max(0, std::min(int(height) - 1, int(y) + dy))); // Clamp this reference coordinate to the nearest valid image edge.
45 expected += read_input.data()[sy * width + sx]; // Accumulate the independently clamped three-by-three input neighborhood.
46 } // End this block.
47 } // End this block.
48 shdr_examples::require_close(read_output.data()[y * width + x], // Check the computed value against the independent reference.
49 expected / 9.0f, // Average exactly nine clamped neighbor samples.
50 "image pixel " + std::to_string(y * width + x)); // Identify the flattened pixel if this reference comparison fails.
51 } // End this block.
52 } // End this block.
53 std::cout << (run.gpu() ? "GPU" : "CPU") << " buffer/image pipeline: " << width << " x " // Write this part of the example result or diagnostic.
54 << height << " pixels verified\n"; // Write this part of the example result or diagnostic.
55 return 0; // Report successful completion.
56#ifdef SHDR_HAVE_EGL // Enable this branch when its feature macro is defined.
57 } catch (const shdr_examples::context_unavailable& error) { // Report the exception from the preceding operation.
58 std::cerr << "SKIP: " << error.what() << "\n"; // Write this part of the example result or diagnostic.
59 return 77; // Report an unavailable optional GPU backend.
60#endif // Finish this conditional compilation branch.
61 } catch (const std::exception& error) { // Report the exception from the preceding operation.
62 std::cerr << error.what() << "\n"; // Write this part of the example result or diagnostic.
63 return 1; // Report a failed check or operation.
64 } // End this block.
65} // End this block.