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.