Source browser
examples/imaging.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#include <algorithm> // Include algorithm declarations.
3#include <random> // Include random declarations.
4#include <vector> // Include vector declarations.
5
6namespace { // Group these helpers within their namespace.
7// Pixel phases follow the format descriptions in the native demosaicing shader.
8unsigned channel(unsigned x, unsigned y, unsigned pattern) { // Implement channel for the operations below.
9 const unsigned phase = (y % 2) * 2 + x % 2; // Set phase from this expression.
10 const unsigned red = pattern == 0 ? 0 : pattern == 1 ? 3 : pattern == 2 ? 1 : 2; // Set red from this expression.
11 return phase == red ? 0 : phase == 3 - red ? 2 : 1; // Return phase == red ? 0 : phase == 3 - red ? 2 : 1.
12} // End this block.
13
14// Pack bytes into standard uint storage, shaders extract bytes explicitly, so host endianness is irrelevant.
15std::vector<unsigned> pack(const std::vector<unsigned>& samples, unsigned packing) { // Implement pack for the operations below.
16 std::vector<unsigned> bytes; // Declare bytes for the following calculation.
17 for (std::size_t i = 0; i < samples.size();) { // Iterate over i for this calculation.
18 if (packing == 0) { // Check this condition before entering the following branch.
19 bytes.push_back(samples[i++]); // Apply push back to these arguments.
20 } else if (packing == 1) { // Check this condition before entering the following branch.
21 unsigned value = samples[i++]; // Set value from this expression.
22 bytes.push_back(value & 255); // Apply push back to these arguments.
23 bytes.push_back(value >> 8); // Apply push back to these arguments.
24 } else if (packing == 2) { // Check this condition before entering the following branch.
25 unsigned lows = 0; // Set lows from this expression.
26 for (unsigned j = 0; j < 4; ++j) { // Iterate over j for this calculation.
27 bytes.push_back(samples[i + j] >> 2); // Apply push back to these arguments.
28 lows |= (samples[i + j] & 3) << (2 * j); // Update lows with this contribution.
29 } // End this block.
30 bytes.push_back(lows); // Apply push back to these arguments.
31 i += 4; // Update i with this contribution.
32 } else { // Pack each RAW12 pair into two high bytes and one shared low-nibble byte.
33 bytes.push_back(samples[i] >> 4); // Apply push back to these arguments.
34 bytes.push_back(samples[i + 1] >> 4); // Apply push back to these arguments.
35 bytes.push_back((samples[i] & 15) | ((samples[i + 1] & 15) << 4)); // Apply push back to these arguments.
36 i += 2; // Update i with this contribution.
37 } // End this block.
38 } // End this block.
39 std::vector<unsigned> words((bytes.size() + 3) / 4, 0); // Construct words with these initial values.
40 for (std::size_t i = 0; i < bytes.size(); ++i) // Iterate over i for this calculation.
41 words[i / 4] |= bytes[i] << ((i % 4) * 8); // Update words[i / 4] with this contribution.
42 return words; // Return words.
43} // End this block.
44
45// Check every pixel against a host interpolation oracle for all 16 packing/phase combinations.
46void demosaic(shdr_examples::runner& run) { // Implement demosaic for the operations below.
47 const unsigned width = 8, height = 6, count = width * height; // Set width from this expression.
48 const unsigned depths[] = {8, 16, 10, 12}; // Declare depths for the following calculation.
49 std::mt19937 random(12345); // Construct random with these initial values.
50 auto unpack = shdr::make_kernel("bayer_unpack_kernel"); // Create the linked bayer unpack kernel instance.
51 auto debayer = shdr::make_kernel("debayer_kernel"); // Create the linked debayer kernel instance.
52 auto luma = shdr::make_kernel("luminance_kernel"); // Create the linked luminance kernel instance.
53 shdr::buffer<unsigned> decoded(count); // Construct decoded with these initial values.
54 shdr::buffer<glsl::vec4> colors(count); // Construct colors with these initial values.
55 shdr::buffer<float> gray(count); // Construct gray with these initial values.
56 unpack->bind("samples", decoded).set("count", count); // Connect this shader port to its application resource.
57 debayer->bind("samples", decoded) // Connect this shader port to its application resource.
58 .bind("colors", colors) // Connect this shader port to its application resource.
59 .set("width", width) // Apply set to these arguments.
60 .set("height", height); // Apply set to these arguments.
61 luma->bind("colors", colors).bind("gray", gray).set("count", count); // Connect this shader port to its application resource.
62 const shdr::pipeline p = shdr::dispatch(*unpack, shdr::extent(1)) >> // Add a shader dispatch with the required workgroup counts.
63 shdr::dispatch(*debayer, shdr::extent(1, 1)) >> // Add a shader dispatch with the required workgroup counts.
64 shdr::dispatch(*luma, shdr::extent(1)); // Add a shader dispatch with the required workgroup counts.
65 for (unsigned packing = 0; packing < 4; ++packing) // Iterate over packing for this calculation.
66 for (unsigned pattern = 0; pattern < 4; ++pattern) { // Iterate over pattern for this calculation.
67 const unsigned maximum = (1u << depths[packing]) - 1u; // Set maximum from this expression.
68 std::vector<unsigned> samples(count); // Construct samples with these initial values.
69 for (unsigned i = 0; i < count; ++i) // Iterate over i for this calculation.
70 samples[i] = random() & maximum; // Set samples[i] from this expression.
71 const std::vector<unsigned> words = pack(samples, packing); // Set words from this expression.
72 shdr::buffer<unsigned> packed(words.size()); // Construct packed with these initial values.
73 std::copy(words.begin(), words.end(), packed.data()); // Apply copy to these arguments.
74 unpack->bind("packed_words", packed) // Connect this shader port to its application resource.
75 .set("packing", packing) // Apply set to these arguments.
76 .set("bit_depth", depths[packing]); // Apply set to these arguments.
77 debayer->set("pattern", pattern).set("bit_depth", depths[packing]); // Apply set to these arguments.
78 run.run(p); // Execute the configured pipeline on the requested backend.
79 const unsigned* actual_samples = // Set actual samples from this expression.
80 static_cast<const shdr::buffer<unsigned>&>(decoded).data(); // Apply data to these arguments.
81 const glsl::vec4* actual_colors = // Set actual colors from this expression.
82 static_cast<const shdr::buffer<glsl::vec4>&>(colors).data(); // Apply data to these arguments.
83 const float* actual_gray = static_cast<const shdr::buffer<float>&>(gray).data(); // Use a constant buffer view so GPU writes synchronize before host reads.
84 for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
85 for (unsigned x = 0; x < width; ++x) { // Iterate over x for this calculation.
86 const unsigned i = y * width + x; // Set i from this expression.
87 if (actual_samples[i] != samples[i]) // Check this condition before entering the following branch.
88 throw std::runtime_error("packed Bayer decoding differs"); // Report packed Bayer decoding differs.
89 float rgb[3] = {0, 0, 0}; // Set rgb[3] from this expression.
90 for (unsigned c = 0; c < 3; ++c) { // Iterate over c for this calculation.
91 if (channel(x, y, pattern) == c) // Check this condition before entering the following branch.
92 rgb[c] = float(samples[i]) / float(maximum); // Set rgb[c] from this expression.
93 else { // Average neighboring measurements of the missing color channel.
94 double sum = 0; // Use this floating-point value or resource in the following calculation.
95 unsigned weight = 0; // Set weight from this expression.
96 for (int dy = -1; dy <= 1; ++dy) // Iterate over dy for this calculation.
97 for (int dx = -1; dx <= 1; ++dx) { // Iterate over dx for this calculation.
98 const unsigned nx = static_cast<unsigned>( // Set nx from this expression.
99 std::max(0, std::min(int(width) - 1, int(x) + dx))); // Apply max to these arguments.
100 const unsigned ny = static_cast<unsigned>( // Set ny from this expression.
101 std::max(0, std::min(int(height) - 1, int(y) + dy))); // Apply max to these arguments.
102 if (channel(nx, ny, pattern) == c) { // Check this condition before entering the following branch.
103 sum += samples[ny * width + nx]; // Update sum with this contribution.
104 ++weight; // Continue the expression using weight.
105 } // End this block.
106 } // End this block.
107 rgb[c] = weight ? float(sum / (weight * double(maximum))) : 0; // Set rgb[c] from this expression.
108 } // End this block.
109 shdr_examples::require_close( // Check the computed value against the independent reference.
110 actual_colors[i][c], rgb[c], "Bayer interpolation"); // Continue the expression using actual colors, i, c.
111 } // End this block.
112 shdr_examples::require_close(actual_colors[i].w, 1, "Bayer alpha"); // Check the computed value against the independent reference.
113 shdr_examples::require_close(actual_gray[i], // Check the computed value against the independent reference.
114 rgb[0] * 0.2126f + rgb[1] * 0.7152f + // Continue the expression using rgb, rgb.
115 rgb[2] * 0.0722f, // Continue the expression using rgb.
116 "Bayer luminance"); // Provide this text argument for the operation above.
117 } // End this block.
118 } // End this block.
119 std::cout << "All four Bayer phases and Bayer8/Bayer16/RAW10/RAW12 packings verified\n"; // Write this part of the example result or diagnostic.
120} // End this block.
121
122// A multi-stage lossless compressor allocates capacity once and records compact stream length in offsets.
123void compression(shdr_examples::runner& run) { // Implement compression for the operations below.
124 const unsigned count = 513, block_size = 32, blocks = (count + block_size - 1) / block_size; // Set count from this expression.
125 shdr::buffer<unsigned> values(count), sizes(blocks), offsets(blocks + 1), encoded(count * 2), // Declare values with the parameters continued below.
126 restored(count); // Apply restored to these arguments.
127 std::mt19937 random(54321); // Construct random with these initial values.
128 unsigned* input = values.data(); // Set input from this expression.
129 for (unsigned i = 0; i < count;) { // Iterate over i for this calculation.
130 const unsigned value = random() % 256, span = 3 + random() % 12; // Set value from this expression.
131 for (unsigned j = 0; j < span && i < count; ++j) // Iterate over j for this calculation.
132 input[i++] = value; // Set input[i++] from this expression.
133 } // End this block.
134 auto counter = shdr::make_kernel("rle_count_kernel"); // Create the linked rle count kernel instance.
135 auto prefix = shdr::make_kernel("rle_offsets_kernel"); // Create the linked rle offsets kernel instance.
136 auto encoder = shdr::make_kernel("rle_encode_kernel"); // Create the linked rle encode kernel instance.
137 auto decoder = shdr::make_kernel("rle_decode_kernel"); // Create the linked rle decode kernel instance.
138 counter->bind("values", values) // Connect this shader port to its application resource.
139 .bind("sizes", sizes) // Connect this shader port to its application resource.
140 .set("count", count) // Apply set to these arguments.
141 .set("block_size", block_size); // Apply set to these arguments.
142 prefix->bind("sizes", sizes).bind("offsets", offsets).set("blocks", blocks); // Connect this shader port to its application resource.
143 encoder->bind("values", values) // Connect this shader port to its application resource.
144 .bind("offsets", offsets) // Connect this shader port to its application resource.
145 .bind("encoded", encoded) // Connect this shader port to its application resource.
146 .set("count", count) // Apply set to these arguments.
147 .set("block_size", block_size); // Apply set to these arguments.
148 decoder->bind("encoded", encoded) // Connect this shader port to its application resource.
149 .bind("offsets", offsets) // Connect this shader port to its application resource.
150 .bind("restored", restored) // Connect this shader port to its application resource.
151 .set("count", count) // Apply set to these arguments.
152 .set("block_size", block_size); // Apply set to these arguments.
153 const shdr::extent groups((blocks + 63) / 64); // Construct groups with these initial values.
154 const shdr::pipeline p = shdr::dispatch(*counter, groups) >> // Add a shader dispatch with the required workgroup counts.
155 shdr::dispatch(*prefix, shdr::extent(1)) >> // Add a shader dispatch with the required workgroup counts.
156 shdr::dispatch(*encoder, groups) >> shdr::dispatch(*decoder, groups); // Add a shader dispatch with the required workgroup counts.
157 run.run(p); // Execute the configured pipeline on the requested backend.
158 const unsigned* decoded = static_cast<const shdr::buffer<unsigned>&>(restored).data(); // Use a constant buffer view so GPU writes synchronize before host reads.
159 const unsigned* locations = static_cast<const shdr::buffer<unsigned>&>(offsets).data(); // Use a constant buffer view so GPU writes synchronize before host reads.
160 const unsigned* words = static_cast<const shdr::buffer<unsigned>&>(encoded).data(); // Use a constant buffer view so GPU writes synchronize before host reads.
161 unsigned expected_position = 0; // Set expected position from this expression.
162 for (unsigned b = 0; b < blocks; ++b) { // Iterate over b for this calculation.
163 if (locations[b] != expected_position) // Check this condition before entering the following branch.
164 throw std::runtime_error("RLE prefix mismatch"); // Report RLE prefix mismatch.
165 unsigned i = b * block_size, end = std::min(count, (b + 1) * block_size); // Set i from this expression.
166 while (i < end) { // Find the next complete run in this tile.
167 const unsigned value = input[i]; // Set value from this expression.
168 unsigned span = 1; // Set span from this expression.
169 while (i + span < end && input[i + span] == value) // Extend the run across consecutive copies of the same value.
170 ++span; // Continue the expression using span.
171 if (words[expected_position] != value || words[expected_position + 1] != span) // Check this condition before entering the following branch.
172 throw std::runtime_error("RLE encoded pair mismatch"); // Report RLE encoded pair mismatch.
173 expected_position += 2; // Update expected position with this contribution.
174 i += span; // Update i with this contribution.
175 } // End this block.
176 } // End this block.
177 if (locations[blocks] != expected_position) // Check this condition before entering the following branch.
178 throw std::runtime_error("RLE compact length mismatch"); // Report RLE compact length mismatch.
179 for (unsigned i = 0; i < count; ++i) // Iterate over i for this calculation.
180 if (decoded[i] != input[i]) // Check this condition before entering the following branch.
181 throw std::runtime_error("lossless RLE roundtrip mismatch"); // Report lossless RLE roundtrip mismatch.
182 std::cout << "Lossless tiled RLE: " << count << " input words, " << expected_position // Write this part of the example result or diagnostic.
183 << " encoded words + " << blocks + 1 << " offsets\n"; // Write this part of the example result or diagnostic.
184} // End this block.
185} // namespace
186
187int main(int argc, char** argv) { // Run this example and validate its results.
188 try { // Run the operation while retaining the error handling below.
189 shdr_examples::runner run(argc, argv); // Construct run with these initial values.
190 demosaic(run); // Apply demosaic to these arguments.
191 compression(run); // Apply compression to these arguments.
192 return 0; // Report successful completion.
193 } // End this block.
194#ifdef SHDR_HAVE_EGL // Enable this branch when its feature macro is defined.
195 catch (const shdr_examples::context_unavailable& e) { // Handle the indicated exception from the preceding operation.
196 std::cerr << "SKIP: " << e.what() << '\n'; // Write this part of the example result or diagnostic.
197 return 77; // Report an unavailable optional GPU backend.
198 } // End this block.
199#endif // Finish this conditional compilation branch.
200 catch (const std::exception& e) { // Handle the indicated exception from the preceding operation.
201 std::cerr << e.what() << '\n'; // Write this part of the example result or diagnostic.
202 return 1; // Report a failed check or operation.
203 } // End this block.
204} // End this block.