Source browser

examples/evt3_flow_checks.hpp

Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.

File fingerprint
1/** @file evt3_flow_checks.hpp
2 * @brief Synthetic event-plane fixtures and an independent optical-flow oracle.
3 * A plane t=100*x+50*y microseconds has normal velocity (8000,4000) pixels/second.
4 * Chain decoding and flow estimation through the same event buffers.
5 */
6#ifndef SHDR_EXAMPLES_EVT3_FLOW_CHECKS_HPP // Open this include guard or optional feature branch.
7#define SHDR_EXAMPLES_EVT3_FLOW_CHECKS_HPP // Define SHDR_EXAMPLES_EVT3_FLOW_CHECKS_HPP for the declarations that follow.
8#include "runner.hpp" // Include runner.hpp declarations.
9#include "evt3_support.hpp" // Include evt3_support.hpp declarations.
10#include <algorithm> // Include algorithm declarations.
11#include <cmath> // Include cmath declarations.
12#include <cstdint> // Include cstdint declarations.
13#include <vector> // Include vector declarations.
14
15namespace shdr_examples { // Group these helpers within their namespace.
16
17/** @brief Build time-ordered direct EVT3 events for a translating temporal plane.
18 * crossing_32 selects a time origin just before the low timestamp word wraps.
19 * Synchronization-only words establish the preceding 24-bit wraps, so the same
20 * fixture exercises the decoder and exact timestamp subtraction in the flow stage.
21 */
22inline std::vector<std::uint16_t> // Declare std for the following calculation.
23evt3_planar_words(unsigned width = 32, unsigned height = 24, bool crossing_32 = false) { // Set width from this expression.
24    if (!width || !height || width > 2048u || height > 2048u) // Check this condition before entering the following branch.
25        throw shdr::error("event-plane dimensions must be between 1 and 2048"); // Report event-plane dimensions must be between 1 and 2048.
26    struct pixel { // Declare pixel.
27        unsigned x, y, time; // Declare x for the following calculation.
28    }; // Finish this declaration.
29    std::vector<pixel> pixels; // Declare pixels for the following calculation.
30    for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
31        for (unsigned x = 0; x < width; ++x) // Iterate over x for this calculation.
32            pixels.push_back(pixel{x, y, 100u * x + 50u * y}); // Apply push back to these arguments.
33    std::stable_sort(pixels.begin(), pixels.end(), [](const pixel& a, const pixel& b) { // Apply stable sort to these arguments.
34        return a.time < b.time; // Return a.time < b.time.
35    }); // Finish this declaration.
36    std::vector<std::uint16_t> words; // Declare words for the following calculation.
37    if (crossing_32) // Check this condition before entering the following branch.
38        for (unsigned wrap = 0; wrap < 255u; ++wrap) { // Iterate over wrap for this calculation.
39            words.push_back(0x8fffu); // Apply push back to these arguments.
40            words.push_back(0x8000u); // Apply push back to these arguments.
41        } // End this block.
42    const std::uint64_t origin = crossing_32 ? (std::uint64_t(1) << 32) - 2000u : 0u; // Set origin from this expression.
43    for (const pixel& p : pixels) { // Visit each p required by this loop.
44        const std::uint64_t timestamp = origin + p.time; // Set timestamp from this expression.
45        words.push_back(static_cast<std::uint16_t>(0x8000u | ((timestamp >> 12u) & 4095u))); // Apply push back to these arguments.
46        words.push_back(static_cast<std::uint16_t>(0x6000u | (timestamp & 4095u))); // Apply push back to these arguments.
47        words.push_back(static_cast<std::uint16_t>(p.y)); // Apply push back to these arguments.
48        words.push_back(static_cast<std::uint16_t>(0x2000u | p.x)); // Apply push back to these arguments.
49    } // End this block.
50    return words; // Return words.
51} // End this block.
52
53/** @brief Attach a bounded local-plane flow stage to decoded event storage.
54 * The decoder offsets provide the compact event count on the GPU. A dispatch
55 * covering allocated capacity uses that device-resident count to bound its work.
56 */
57inline std::unique_ptr<shdr::kernel> evt3_flow_kernel(evt3_decoder& decoder, // Declare evt3 flow kernel with the parameters continued below.
58                                                      shdr::buffer<shdr::glsl::vec4>& output, // Declare output for the following calculation.
59                                                      unsigned history = 128u) { // Set history from this expression.
60    auto shader = shdr::make_kernel("evt3_flow_kernel"); // Create the linked evt3 flow kernel instance.
61    shader->bind("events", decoder.events) // Connect this shader port to its application resource.
62        .bind("offsets", decoder.offsets) // Connect this shader port to its application resource.
63        .bind("flow", output) // Connect this shader port to its application resource.
64        .set<unsigned>("blocks", decoder.blocks()) // Supply this shader uniform with its typed application value.
65        .set<float>("radius", 3.0f) // Supply this shader uniform with its typed application value.
66        .set<unsigned>("history", history) // Supply this shader uniform with its typed application value.
67        .set<unsigned>("time_window", 10000u); // Supply this shader uniform with its typed application value.
68    return shader; // Return shader.
69} // End this block.
70
71namespace detail { // Group these helpers within their namespace.
72
73// Solve the full three-parameter normal equations in double precision as an
74// independent reference for the timestamp-plane slopes and intercept.
75inline shdr::glsl::vec4 evt3_reference_flow(const std::vector<shdr::glsl::uvec4>& events, // Declare evt3 reference flow with the parameters continued below.
76                                            unsigned index, // Declare index for the following calculation.
77                                            float radius, // Declare radius for the following calculation.
78                                            unsigned history, // Declare history for the following calculation.
79                                            unsigned time_window) { // Declare time window for the following calculation.
80    const shdr::glsl::uvec4 center = events[index]; // Set center from this expression.
81    if (center.y & 0x80000000u) // Check this condition before entering the following branch.
82        return shdr::glsl::vec4(0.0f); // Return shdr::glsl::vec4(0.0f).
83    double normal[3][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 1, 0}}; // Use this floating-point value or resource in the following calculation.
84    const std::uint64_t current = (std::uint64_t(center.w) << 32) | center.z; // Set current from this expression.
85    unsigned neighbors = 0; // Set neighbors from this expression.
86    for (unsigned distance = 1; distance <= std::min(128u, history) && distance <= index; // Iterate over distance for this calculation.
87         ++distance) { // Continue the expression using distance.
88        const shdr::glsl::uvec4 previous = events[index - distance]; // Set previous from this expression.
89        if ((previous.y & 0x80000000u) || (previous.y & 0x1800u) != (center.y & 0x1800u)) // Check this condition before entering the following branch.
90            continue; // Skip the remaining work for this loop iteration.
91        const std::uint64_t timestamp = (std::uint64_t(previous.w) << 32) | previous.z; // Set timestamp from this expression.
92        if (timestamp > current || current - timestamp > time_window) // Check this condition before entering the following branch.
93            continue; // Skip the remaining work for this loop iteration.
94        const double dx = double(previous.x) - center.x, // Set dx from this expression.
95                     dy = double(previous.y & 2047u) - double(center.y & 2047u); // Set dy from this expression.
96        if (dx * dx + dy * dy > double(radius) * radius) // Check this condition before entering the following branch.
97            continue; // Skip the remaining work for this loop iteration.
98        const double row[3] = {dx, dy, 1.0}, dt = -double(current - timestamp); // Set row[3] from this expression.
99        for (unsigned r = 0; r < 3; ++r) { // Iterate over r for this calculation.
100            for (unsigned c = 0; c < 3; ++c) // Iterate over c for this calculation.
101                normal[r][c] += row[r] * row[c]; // Update normal[r][c] with this contribution.
102            normal[r][3] += row[r] * dt; // Update normal[r][3] with this contribution.
103        } // End this block.
104        ++neighbors; // Continue the expression using neighbors.
105    } // End this block.
106    if (neighbors < 6u) // Check this condition before entering the following branch.
107        return shdr::glsl::vec4(0.0f); // Return shdr::glsl::vec4(0.0f).
108    for (unsigned c = 0; c < 3; ++c) { // Iterate over c for this calculation.
109        unsigned pivot = c; // Set pivot from this expression.
110        for (unsigned r = c + 1; r < 3; ++r) // Iterate over r for this calculation.
111            if (std::fabs(normal[r][c]) > std::fabs(normal[pivot][c])) // Check this condition before entering the following branch.
112                pivot = r; // Set pivot from this expression.
113        if (std::fabs(normal[pivot][c]) < 1e-9) // Check this condition before entering the following branch.
114            return shdr::glsl::vec4(0.0f); // Return shdr::glsl::vec4(0.0f).
115        for (unsigned j = c; j < 4; ++j) // Iterate over j for this calculation.
116            std::swap(normal[pivot][j], normal[c][j]); // Apply swap to these arguments.
117        const double divisor = normal[c][c]; // Set divisor from this expression.
118        for (unsigned j = c; j < 4; ++j) // Iterate over j for this calculation.
119            normal[c][j] /= divisor; // Update normal[c][j] with this contribution.
120        for (unsigned r = 0; r < 3; ++r) // Iterate over r for this calculation.
121            if (r != c) { // Check this condition before entering the following branch.
122                const double multiplier = normal[r][c]; // Set multiplier from this expression.
123                for (unsigned j = c; j < 4; ++j) // Iterate over j for this calculation.
124                    normal[r][j] -= multiplier * normal[c][j]; // Update normal[r][j] with this contribution.
125            } // End this block.
126    } // End this block.
127    const double a = normal[0][3], b = normal[1][3], gradient = a * a + b * b; // Set a from this expression.
128    if (gradient <= 0.000001) // Check this condition before entering the following branch.
129        return shdr::glsl::vec4(0.0f); // Return shdr::glsl::vec4(0.0f).
130    return shdr::glsl::vec4( // Return shdr::glsl::vec4(.
131        float(a * 1000000.0 / gradient), float(b * 1000000.0 / gradient), float(neighbors), 1.0f); // Apply float to these arguments.
132} // End this block.
133
134inline shdr::glsl::uvec4 evt3_flow_record(unsigned x, unsigned packed_y, std::uint64_t time) { // Implement evt3 flow record for the operations below.
135    return shdr::glsl::uvec4( // Return shdr::glsl::uvec4(.
136        x, packed_y, static_cast<unsigned>(time), static_cast<unsigned>(time >> 32)); // Continue the expression using x, packed y, static cast.
137} // End this block.
138
139// Interleaved camera/polarity records, triggers, a stale high timestamp word and
140// an out-of-order timestamp make every neighborhood rejection observable.
141inline void evt3_mixed_flow_check(runner& run) { // Implement evt3 mixed flow check for the operations below.
142    const std::uint64_t origin = (std::uint64_t(1) << 32) - 200u; // Set origin from this expression.
143    std::vector<shdr::glsl::uvec4> records; // Declare records for the following calculation.
144    records.push_back(evt3_flow_record(2u, 2u, 0u)); // Apply push back to these arguments.
145    for (unsigned y = 0; y < 5; ++y) // Iterate over y for this calculation.
146        for (unsigned x = 0; x < 5; ++x) { // Iterate over x for this calculation.
147            const unsigned relative = 100u * x + 50u * y; // Set relative from this expression.
148            if (relative > 300u || (x == 2u && y == 2u)) // Check this condition before entering the following branch.
149                continue; // Skip the remaining work for this loop iteration.
150            const std::uint64_t time = origin + relative; // Set time from this expression.
151            records.push_back(evt3_flow_record(x, y, time)); // Apply push back to these arguments.
152            records.push_back(evt3_flow_record(x, y | 2048u, time - 17u)); // Apply push back to these arguments.
153            records.push_back(evt3_flow_record(x, y | 4096u, time - 31u)); // Apply push back to these arguments.
154            records.push_back(evt3_flow_record(1u, 0x80000001u, time)); // Apply push back to these arguments.
155        } // End this block.
156    std::stable_sort( // Apply stable sort to these arguments.
157        records.begin(), records.end(), [](const shdr::glsl::uvec4& a, const shdr::glsl::uvec4& b) { // Apply begin to these arguments.
158            return a.w < b.w || (a.w == b.w && a.z < b.z); // Return a.w < b.w || (a.w == b.w && a.z < b.z).
159        }); // Finish this declaration.
160    records.push_back( // Apply push back to these arguments.
161        evt3_flow_record(2u, 1u, origin + 400u)); // Future data must be ignored by the final fit.
162    records.push_back(evt3_flow_record(2u, 2u, origin + 300u)); // Apply push back to these arguments.
163    shdr::buffer<shdr::glsl::uvec4> events(records.size()); // Construct events with these initial values.
164    std::copy(records.begin(), records.end(), events.data()); // Apply copy to these arguments.
165    shdr::buffer<unsigned> offsets{static_cast<unsigned>(records.size())}; // Declare offsets for the following calculation.
166    shdr::buffer<shdr::glsl::vec4> output(records.size()); // Construct output with these initial values.
167    auto shader = shdr::make_kernel("evt3_flow_kernel"); // Create the linked evt3 flow kernel instance.
168    shader->bind("events", events) // Connect this shader port to its application resource.
169        .bind("offsets", offsets) // Connect this shader port to its application resource.
170        .bind("flow", output) // Connect this shader port to its application resource.
171        .set<unsigned>("blocks", 0u) // Supply this shader uniform with its typed application value.
172        .set<float>("radius", 3.0f) // Supply this shader uniform with its typed application value.
173        .set<unsigned>("history", 128u) // Supply this shader uniform with its typed application value.
174        .set<unsigned>("time_window", 250u); // Supply this shader uniform with its typed application value.
175    run.run(shdr::pipeline(shdr::dispatch( // Execute the configured pipeline on the requested backend.
176        *shader, shdr::extent((static_cast<unsigned>(records.size()) + 63u) / 64u)))); // Apply extent to these arguments.
177    const shdr::glsl::vec4* actual = // Set actual from this expression.
178        static_cast<const shdr::buffer<shdr::glsl::vec4>&>(output).data(); // Apply data to these arguments.
179    for (unsigned index = 0; index < records.size(); ++index) { // Iterate over index for this calculation.
180        const shdr::glsl::vec4 expected = evt3_reference_flow(records, index, 3.0f, 128u, 250u); // Set expected from this expression.
181        require_close(actual[index].x, expected.x, "mixed-event flow X", 1.5f); // Check the computed value against the independent reference.
182        require_close(actual[index].y, expected.y, "mixed-event flow Y", 1.5f); // Check the computed value against the independent reference.
183        require_close(actual[index].z, expected.z, "mixed-event neighbor count", 0.0f); // Check the computed value against the independent reference.
184        require_close(actual[index].w, expected.w, "mixed-event fit validity", 0.0f); // Check the computed value against the independent reference.
185    } // End this block.
186    require_close(actual[records.size() - 1u].x, 8000.0f, "filtered temporal-plane X", 1.5f); // Check the computed value against the independent reference.
187    require_close(actual[records.size() - 1u].y, 4000.0f, "filtered temporal-plane Y", 1.5f); // Check the computed value against the independent reference.
188    // Require two independent spatial directions for a two-dimensional plane fit.
189    for (unsigned index = 0; index < records.size(); ++index) // Iterate over index for this calculation.
190        records[index] = evt3_flow_record(index % 3u, 2u, origin + 50u * index); // Set records[index] from this expression.
191    std::copy(records.begin(), records.end(), events.data()); // Apply copy to these arguments.
192    shader->set<unsigned>("time_window", 10000u); // Supply this shader uniform with its typed application value.
193    run.run(shdr::pipeline(shdr::dispatch( // Execute the configured pipeline on the requested backend.
194        *shader, shdr::extent((static_cast<unsigned>(records.size()) + 63u) / 64u)))); // Apply extent to these arguments.
195    actual = static_cast<const shdr::buffer<shdr::glsl::vec4>&>(output).data(); // Use a constant buffer view so GPU writes synchronize before host reads.
196    for (unsigned index = 0; index < records.size(); ++index) // Iterate over index for this calculation.
197        if (actual[index] != shdr::glsl::vec4(0.0f)) // Check this condition before entering the following branch.
198            throw shdr::error("collinear events produced a valid plane fit"); // Report collinear events produced a valid plane fit.
199    // This six-neighbor geometry has an exactly zero horizontal timestamp slope.
200    // Early covariance division can invent a tiny slope and amplify it into false velocity.
201    // The fitted vertical slope is one fifth of a microsecond per pixel.
202    const std::uint64_t axis_time = (std::uint64_t(1) << 32) + 100u; // Keep exact timestamp subtraction near a low-word boundary.
203    records = { // Reproduce the integer geometry of a difficult recorded-event neighborhood.
204        evt3_flow_record(394u, 573u, axis_time - 1u), // Include the first event one microsecond before the center.
205        evt3_flow_record(393u, 573u, axis_time - 1u), // Include the second older event on the same pixel row.
206        evt3_flow_record(394u, 574u, axis_time), // Include a same-time neighbor four pixels to the right.
207        evt3_flow_record(393u, 574u, axis_time), // Include a same-time neighbor three pixels to the right.
208        evt3_flow_record(392u, 574u, axis_time), // Include a same-time neighbor two pixels to the right.
209        evt3_flow_record(394u, 572u, axis_time), // Supply spatial variation that makes the plane fit nondegenerate.
210        evt3_flow_record(390u, 575u, axis_time)}; // Place the evaluated center after all six preceding neighbors.
211    std::copy(records.begin(), records.end(), events.data()); // Replace the input prefix with the zero-axis regression fixture.
212    offsets.data()[0] = static_cast<unsigned>(records.size()); // Restrict shader output to the seven supplied records.
213    shader->set<float>("radius", 5.0f); // Include the diagonal neighbor exactly five pixels from the center.
214    run.run(shdr::pipeline(shdr::dispatch(*shader, shdr::extent(1u)))); // Fit the same regression neighborhood on the selected backend.
215    actual = static_cast<const shdr::buffer<shdr::glsl::vec4>&>(output).data(); // Read completed flow through a constant buffer view.
216    const unsigned center = static_cast<unsigned>(records.size() - 1u); // Select the event with all six regression neighbors.
217    const shdr::glsl::vec4 expected = evt3_reference_flow(records, center, 5.0f, 128u, 10000u); // Solve the independent double-precision normal equations.
218    require_close(expected.x, 0.0f, "zero-axis oracle horizontal velocity", 0.00001f); // Verify zero horizontal velocity in the independent fit.
219    require_close(expected.y, 5000000.0f, "zero-axis oracle vertical velocity", 1.0f); // Verify the fixture's known normal velocity in pixels per second.
220    require_close(actual[center].x, expected.x, "zero-axis flow X", 0.00001f); // Detect any false velocity caused by rounding a mathematically zero slope.
221    require_close(actual[center].y, expected.y, "zero-axis flow Y", 1.0f); // Bound the vertical error relative to the large known velocity.
222    require_close(actual[center].z, 6.0f, "zero-axis neighbor count", 0.0f); // Require all six intended neighbors to participate.
223    require_close(actual[center].w, 1.0f, "zero-axis fit validity", 0.0f); // Require the noncollinear regression geometry to produce a valid fit.
224} // End this block.
225
226// Fused arithmetic can produce a positive rounded determinant for large collinear integer covariances.
227// Exact pixel-direction tests must reject these neighborhoods before solving any timestamp slope.
228inline void evt3_collinear_history_check(runner& run) { // Check long singular neighborhoods on the selected CPU or GPU backend.
229    for (unsigned count : {64u, 65u, 128u, 129u}) { // Exercise both workgroup boundaries and the maximum neighbor history.
230        const std::uint64_t origin = (std::uint64_t(1) << 32) + 1000u; // Keep all preceding timestamps within a short exact window.
231        std::vector<shdr::glsl::uvec4> records; // Build a timestamp-ordered line of repeated pixel locations.
232        for (int age = int(count) - 1; age > 0; --age) { // Emit the oldest event first and approach the evaluated center.
233            const int position = age == 1 ? 1 : (age % 2 ? -2 : 2); // Break covariance power-of-two factors while staying on one line.
234            const unsigned x = unsigned(100 + 2 * position), y = unsigned(100 + position); // Keep all pixels on the exact integer line X minus 100 equals twice Y minus 100.
235            records.push_back(evt3_flow_record(x, y, origin - unsigned(age))); // Give every candidate a valid earlier timestamp.
236        } // Finish the long collinear neighbor sequence.
237        records.push_back(evt3_flow_record(100u, 100u, origin)); // Include the evaluated center on the same spatial line.
238        shdr::buffer<shdr::glsl::uvec4> input(records.size()); // Allocate the complete singular neighborhood.
239        std::copy(records.begin(), records.end(), input.data()); // Load exactly the generated pixel and timestamp records.
240        shdr::buffer<unsigned> offsets{count}; // Provide the event count directly for this isolated flow-kernel fixture.
241        shdr::buffer<shdr::glsl::vec4> output(count); // Receive one invalid zero flow record for every input event.
242        auto shader = shdr::make_kernel("evt3_flow_kernel"); // Create the same native flow module used by decoded-event pipelines.
243        shader->bind("events", input).bind("offsets", offsets).bind("flow", output); // Connect the direct regression input and its result resource.
244        shader->set<unsigned>("blocks", 0u).set<unsigned>("history", 128u); // Read the single total offset and allow the maximum supported history.
245        shader->set<float>("radius", 5.0f).set<unsigned>("time_window", 10000u); // Set a radius that includes every candidate while retaining the standard fit thresholds.
246        const shdr::pipeline pipeline(shdr::dispatch(*shader, shdr::extent((count + 63u) / 64u))); // Cover all records and ignore padded invocations normally.
247        for (unsigned duplicates = 0u; duplicates < 2u; ++duplicates) { // Check both a true spatial line and repeated center positions.
248            if (duplicates != 0u) { // Replace the line with a neighborhood of identical pixel coordinates.
249                for (unsigned index = 0u; index < count; ++index) { // Keep all timestamps ordered while removing spatial variation.
250                    records[index] = evt3_flow_record(100u, 100u, origin - (count - 1u - index)); // Make every record a duplicate of the center's pixel.
251                } // Finish the rank-zero neighborhood.
252                std::copy(records.begin(), records.end(), input.data()); // Upload the changed positions for the second regression run.
253            } // Retain the original line geometry during the first run.
254            run.run(pipeline); // Execute the complete singular neighborhood on the selected backend.
255            const shdr::glsl::vec4* actual = static_cast<const shdr::buffer<shdr::glsl::vec4>&>(output).data(); // Read synchronized results through the constant buffer interface.
256            for (unsigned index = 0u; index < count; ++index) { // Check every prefix and the longest available neighborhood.
257                const shdr::glsl::vec4 expected = evt3_reference_flow(records, index, 5.0f, 128u, 10000u); // Confirm singularity with the independent double-precision solver.
258                if (expected != shdr::glsl::vec4(0.0f)) { // Require the regression fixture to remain mathematically degenerate.
259                    throw shdr::error("collinear-history oracle unexpectedly accepted a plane"); // Report an invalid fixture or independent solver regression.
260                } // The independent oracle rejects this spatial geometry.
261                if (actual[index] != expected) { // Reject a fabricated valid flow or any nonzero invalid output component.
262                    throw shdr::error("long collinear history produced a valid plane fit"); // Catch determinant residues caused by fused floating-point products.
263                } // Both backends must publish the same exact invalid result.
264            } // Finish all prefix checks for this singular geometry.
265        } // Finish line and repeated-center regressions.
266    } // Finish every bounded history size.
267} // Finish exact spatial rank validation.
268} // namespace detail
269
270/** @brief Verify decoded planes and GPU/CPU flow against a scalar double oracle.
271 * Both a small timestamp origin and a uint32-boundary crossing use one linked
272 * decode-and-flow pipeline. Invalid fits publish zero and valid fits publish a
273 * count with normal velocity. The oracle checks every compact output event.
274 */
275inline void check_evt3_flow(runner& run) { // Implement check evt3 flow for the operations below.
276    for (unsigned crossing = 0; crossing < 2; ++crossing) { // Iterate over crossing for this calculation.
277        const std::vector<std::uint16_t> words = evt3_planar_words(32, 24, crossing != 0); // Set words from this expression.
278        evt3_decoder decoder(static_cast<unsigned>(words.size()), 127); // Construct decoder with these initial values.
279        decoder.load(words); // Apply load to these arguments.
280        shdr::buffer<shdr::glsl::vec4> output(decoder.events.size()); // Construct output with these initial values.
281        auto shader = evt3_flow_kernel(decoder, output); // Set shader from this expression.
282        shdr::pipeline pipeline = decoder.reset_pipeline(); // Set pipeline from this expression.
283        pipeline += shdr::dispatch( // Add a shader dispatch with the required workgroup counts.
284            *shader, shdr::extent((static_cast<unsigned>(output.size()) + 63u) / 64u)); // Apply extent to these arguments.
285        run.run(pipeline); // Execute the configured pipeline on the requested backend.
286        const unsigned count = decoder.event_count(); // Set count from this expression.
287        if (count != 32u * 24u) // Check this condition before entering the following branch.
288            throw shdr::error("temporal-plane event count mismatch"); // Report temporal-plane event count mismatch.
289        const shdr::glsl::uvec4* decoded = // Set decoded from this expression.
290            static_cast<const shdr::buffer<shdr::glsl::uvec4>&>(decoder.events).data(); // Apply data to these arguments.
291        const std::vector<shdr::glsl::uvec4> events(decoded, decoded + count); // Construct events with these initial values.
292        const shdr::glsl::vec4* actual = // Set actual from this expression.
293            static_cast<const shdr::buffer<shdr::glsl::vec4>&>(output).data(); // Apply data to these arguments.
294        unsigned valid = 0; // Set valid from this expression.
295        for (unsigned index = 0; index < count; ++index) { // Iterate over index for this calculation.
296            const shdr::glsl::vec4 expected = // Set expected from this expression.
297                detail::evt3_reference_flow(events, index, 3.0f, 128u, 10000u); // Apply evt3 reference flow to these arguments.
298            require_close(actual[index].x, expected.x, "event-plane flow X", 1.5f); // Check the computed value against the independent reference.
299            require_close(actual[index].y, expected.y, "event-plane flow Y", 1.5f); // Check the computed value against the independent reference.
300            require_close(actual[index].z, expected.z, "event-plane neighbor count", 0.0f); // Check the computed value against the independent reference.
301            require_close(actual[index].w, expected.w, "event-plane fit validity", 0.0f); // Check the computed value against the independent reference.
302            if (expected.w != 0.0f) { // Check this condition before entering the following branch.
303                ++valid; // Continue the expression using valid.
304                require_close( // Check the computed value against the independent reference.
305                    actual[index].x, 8000.0f, "translating-plane normal velocity X", 1.5f); // Continue the expression using actual, index, x.
306                require_close( // Check the computed value against the independent reference.
307                    actual[index].y, 4000.0f, "translating-plane normal velocity Y", 1.5f); // Continue the expression using actual, index, y.
308            } // End this block.
309        } // End this block.
310        if (valid < count / 2u) // Check this condition before entering the following branch.
311            throw shdr::error("temporal plane produced too few valid flow fits"); // Report temporal plane produced too few valid flow fits.
312        std::cout << "EVT3 normal flow: " << valid << '/' << count // Write this part of the example result or diagnostic.
313                  << " valid events, velocity (8000,4000) pixels/s" // Write this part of the example result or diagnostic.
314                  << (crossing ? " across a 32-bit timestamp boundary" : "") << '\n'; // Write this part of the example result or diagnostic.
315    } // End this block.
316    detail::evt3_mixed_flow_check(run); // Apply evt3 mixed flow check to these arguments.
317    detail::evt3_collinear_history_check(run); // Reject long collinear and repeated-pixel neighborhoods independently of floating-point contraction.
318} // End this block.
319} // namespace shdr_examples
320#endif // Finish this conditional compilation branch.