Source browser

examples/evt3_support.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_support.hpp
2 * @brief Reusable host wiring for a compact, streaming EVT3 example.
3 * Native shaders parse packed words, resolve block state, scan output lengths,
4 * and emit into disjoint intervals. Host readback is required only by a consumer.
5 */
6#ifndef SHDR_EXAMPLES_EVT3_SUPPORT_HPP // Open this include guard or optional feature branch.
7#define SHDR_EXAMPLES_EVT3_SUPPORT_HPP // Define SHDR_EXAMPLES_EVT3_SUPPORT_HPP for the declarations that follow.
8#include "shdr.h" // Include shdr.h declarations.
9#include <algorithm> // Include algorithm declarations.
10#include <cstdint> // Include cstdint declarations.
11#include <random> // Include random declarations.
12#include <vector> // Include vector declarations.
13
14namespace shdr_examples { // Group these helpers within their namespace.
15
16/** @brief Stateful four-stage decoder. reset_pipeline() adds a GPU/CPU reset stage.
17 * Each uvec4 event stores X, packed Y/polarity/camera, and low/high uint32 timestamp.
18 * Triggers use channel ID as X and set bit31 of the second component, with edge in bit0.
19 * Output ordering follows input ordering. Metadata extensions are skipped. Use
20 * separate decoder objects for independently queued streams or concurrent workers.
21 */
22class evt3_decoder { // Declare evt3 decoder.
23    unsigned capacity_, block_size_, count_, blocks_; // Declare capacity for the following calculation.
24    std::unique_ptr<shdr::kernel> summary_, seed_, prefix_, emit_, reset_; // Declare summary for the following calculation.
25    shdr::buffer<unsigned> packed_, sizes_; // Declare packed for the following calculation.
26    shdr::buffer<shdr::glsl::uvec4> summaries_, seeds_; // Declare summaries for the following calculation.
27    shdr::pipeline stream_, independent_; // Declare stream for the following calculation.
28    static unsigned capacity(unsigned n) { // Implement capacity for the operations below.
29        if (!n || n > 0xffffffffu / 12u) // Check this condition before entering the following branch.
30            throw shdr::error( // Report this failed operation.
31                "EVT3 word capacity must be positive and fit worst-case output offsets"); // Provide this text argument for the operation above.
32        return n; // Return n.
33    } // End this block.
34    static unsigned block(unsigned n) { // Implement block for the operations below.
35        if (!n || n > 4096u) // Check this condition before entering the following branch.
36            throw shdr::error("EVT3 block size must be between 1 and 4096 words"); // Report EVT3 block size must be between 1 and 4096 words.
37        return n; // Return n.
38    } // End this block.
39
40  public: // Set public access for the following class members.
41    shdr::buffer<shdr::glsl::uvec4> events; // Declare events for the following calculation.
42    shdr::buffer<unsigned> offsets; // Declare offsets for the following calculation.
43    shdr::buffer<shdr::glsl::uvec4> state; // Declare state for the following calculation.
44
45    /** @brief Allocate reusable storage, including worst-case twelve events per word. */
46    explicit evt3_decoder(unsigned max_words, unsigned block_size = 128) // Set block size from this expression.
47        : capacity_(capacity(max_words)), block_size_(block(block_size)), count_(0), blocks_(0), // Declare capacity with the parameters continued below.
48          summary_(shdr::make_kernel("evt3_summary_kernel")), // Create the linked evt3 summary kernel instance.
49          seed_(shdr::make_kernel("evt3_seed_kernel")), // Create the linked evt3 seed kernel instance.
50          prefix_(shdr::make_kernel("rle_offsets_kernel")), // Create the linked rle offsets kernel instance.
51          emit_(shdr::make_kernel("evt3_emit_kernel")), // Create the linked evt3 emit kernel instance.
52          reset_(shdr::make_kernel("evt3_reset_kernel")), packed_((capacity_ + 1u) / 2u), // Create the linked evt3 reset kernel instance.
53          sizes_((capacity_ + block_size_ - 1u) / block_size_), summaries_(sizes_.size() * 3u), // Apply sizes to these arguments.
54          seeds_(sizes_.size() * 2u), events(capacity_ * 12u), offsets(sizes_.size() + 1u), // Apply seeds to these arguments.
55          state(2) { // Apply state to these arguments.
56        summary_->bind("packed_words", packed_) // Connect this shader port to its application resource.
57            .bind("summaries", summaries_) // Connect this shader port to its application resource.
58            .set("block_size", block_size_); // Apply set to these arguments.
59        seed_->bind("summaries", summaries_) // Connect this shader port to its application resource.
60            .bind("seeds", seeds_) // Connect this shader port to its application resource.
61            .bind("sizes", sizes_) // Connect this shader port to its application resource.
62            .bind("stream_state", state); // Connect this shader port to its application resource.
63        prefix_->bind("sizes", sizes_).bind("offsets", offsets); // Connect this shader port to its application resource.
64        emit_->bind("packed_words", packed_) // Connect this shader port to its application resource.
65            .bind("seeds", seeds_) // Connect this shader port to its application resource.
66            .bind("offsets", offsets) // Connect this shader port to its application resource.
67            .bind("events", events) // Connect this shader port to its application resource.
68            .set("block_size", block_size_); // Apply set to these arguments.
69        reset_->bind("stream_state", state); // Connect this shader port to its application resource.
70        load(std::vector<std::uint16_t>()); // Apply load to these arguments.
71    } // End this block.
72    /** @brief Copy words into packed storage and adjust dispatches while preserving decoder state. */
73    void load(const std::vector<std::uint16_t>& words) { // Implement load for the operations below.
74        if (words.size() > capacity_) // Check this condition before entering the following branch.
75            throw shdr::error("EVT3 batch exceeds allocated capacity"); // Report EVT3 batch exceeds allocated capacity.
76        count_ = static_cast<unsigned>(words.size()); // Set count from this expression.
77        blocks_ = (count_ + block_size_ - 1u) / block_size_; // Set blocks from this expression.
78        unsigned* data = packed_.data(); // Set data from this expression.
79        for (unsigned i = 0; i < (count_ + 1u) / 2u; ++i) { // Iterate over i for this calculation.
80            data[i] = words[2u * i]; // Set data[i] from this expression.
81            if (2u * i + 1u < count_) // Check this condition before entering the following branch.
82                data[i] |= unsigned(words[2u * i + 1u]) << 16u; // Update data[i] with this contribution.
83        } // End this block.
84        summary_->set("word_count", count_); // Apply set to these arguments.
85        emit_->set("word_count", count_); // Apply set to these arguments.
86        seed_->set("blocks", blocks_); // Apply set to these arguments.
87        prefix_->set("blocks", blocks_); // Apply set to these arguments.
88        const shdr::extent groups(std::max(1u, (blocks_ + 63u) / 64u)); // Construct groups with these initial values.
89        stream_ = shdr::dispatch(*summary_, groups) >> shdr::dispatch(*seed_, shdr::extent(1)) >> // Add a shader dispatch with the required workgroup counts.
90                  shdr::dispatch(*prefix_, shdr::extent(1)) >> shdr::dispatch(*emit_, groups); // Add a shader dispatch with the required workgroup counts.
91        independent_ = shdr::dispatch(*reset_, shdr::extent(1)) >> // Add a shader dispatch with the required workgroup counts.
92                       shdr::dispatch(*summary_, groups) >> // Add a shader dispatch with the required workgroup counts.
93                       shdr::dispatch(*seed_, shdr::extent(1)) >> // Add a shader dispatch with the required workgroup counts.
94                       shdr::dispatch(*prefix_, shdr::extent(1)) >> shdr::dispatch(*emit_, groups); // Add a shader dispatch with the required workgroup counts.
95    } // End this block.
96    /** @brief Execute this for consecutive batches from the same stream. */
97    const shdr::pipeline& stream_pipeline() const { // Implement stream pipeline for the operations below.
98        return stream_; // Return stream.
99    } // End this block.
100    /** @brief Execute this for independent batches, with deterministic initial state. */
101    const shdr::pipeline& reset_pipeline() const { // Implement reset pipeline for the operations below.
102        return independent_; // Return independent.
103    } // End this block.
104    /** @brief Return the encoded word count in the current batch. */
105    unsigned word_count() const { // Implement word count for the operations below.
106        return count_; // Return count.
107    } // End this block.
108    /** @brief Return the number of active block summaries and output intervals. */
109    unsigned blocks() const { // Implement blocks for the operations below.
110        return blocks_; // Return blocks.
111    } // End this block.
112    /** @brief Read compact output length, synchronizing this buffer after GPU work. */
113    unsigned event_count() const { // Implement event count for the operations below.
114        return offsets.data()[blocks_]; // Return offsets.data()[blocks ].
115    } // End this block.
116}; // Finish this declaration.
117
118/** @brief Pack decoded CD events into X11/Y10/polarity1/time42 uint64 records.
119 * This CPU consumer downloads completed GPU results and packs CD pixel events.
120 * The lossless layout accepts X11, Y10 and time42 values, with range errors
121 * reported as exceptions. The original uvec4 records support larger geometry.
122 */
123inline std::vector<std::uint64_t> // Declare std for the following calculation.
124evt3_compact_cd_to64(const shdr::buffer<shdr::glsl::uvec4>& decoded, unsigned count) { // Apply evt3 compact cd to64 to these arguments.
125    if (count > decoded.size()) // Check this condition before entering the following branch.
126        throw shdr::error("EVT3 compact count exceeds decoded storage"); // Report EVT3 compact count exceeds decoded storage.
127    const shdr::glsl::uvec4* events = decoded.data(); // Set events from this expression.
128    std::vector<std::uint64_t> packed; // Declare packed for the following calculation.
129    packed.reserve(count); // Apply reserve to these arguments.
130    for (unsigned i = 0; i < count; ++i) { // Iterate over i for this calculation.
131        const shdr::glsl::uvec4& event = events[i]; // Set event from this expression.
132        if (event.y & 0x80000000u) // Check this condition before entering the following branch.
133            continue; // Skip the remaining work for this loop iteration.
134        const unsigned y = event.y & 2047u; // Set y from this expression.
135        if (event.x >= 2048u || y >= 1024u) // Check this condition before entering the following branch.
136            throw shdr::error("64-bit EVT3 CD packing requires X<2048 and Y<1024"); // Report 64-bit EVT3 CD packing requires X<2048 and Y<1024.
137        if (event.w >= 1024u) // Check this condition before entering the following branch.
138            throw shdr::error("64-bit EVT3 CD packing requires timestamps below 2^42 microseconds"); // Report 64-bit EVT3 CD packing requires timestamps below 2^42 microseconds.
139        const std::uint64_t timestamp = (std::uint64_t(event.w) << 32u) | event.z; // Set timestamp from this expression.
140        const unsigned polarity = (event.y >> 11u) & 1u; // Set polarity from this expression.
141        packed.push_back(std::uint64_t(event.x) | (std::uint64_t(y) << 11u) | // Apply push back to these arguments.
142                         (std::uint64_t(polarity) << 21u) | (timestamp << 22u)); // Apply uint64 t to these arguments.
143    } // End this block.
144    return packed; // Return packed.
145} // End this block.
146
147/** @brief Generate reproducible vector-heavy input, crossing blocks and timestamp wraps. */
148inline std::vector<std::uint16_t> evt3_random_words(unsigned count, unsigned seed = 12345) { // Set seed from this expression.
149    std::mt19937 random(seed); // Construct random with these initial values.
150    std::vector<std::uint16_t> words; // Declare words for the following calculation.
151    words.reserve(count); // Apply reserve to these arguments.
152    unsigned high = 4093u; // Set high from this expression.
153    for (unsigned row = 0; words.size() < count; ++row) { // Iterate over row for this calculation.
154        const unsigned header[] = {0x8000u | (high & 4095u), // Declare header for the following calculation.
155                                   0x6000u | (unsigned(random()) & 4095u), // Apply unsigned to these arguments.
156                                   unsigned(random()) & 4095u, // Apply unsigned to these arguments.
157                                   0x3000u | (unsigned(random()) % 1000u) | // Apply unsigned to these arguments.
158                                       ((unsigned(random()) & 1u) << 11u)}; // Apply unsigned to these arguments.
159        for (unsigned value : header) // Visit each value required by this loop.
160            if (words.size() < count) // Check this condition before entering the following branch.
161                words.push_back(static_cast<std::uint16_t>(value)); // Apply push back to these arguments.
162        for (unsigned j = 0; j < 13 && words.size() < count; ++j) { // Iterate over j for this calculation.
163            unsigned value; // Declare value for the following calculation.
164            if (j == 4) // Check this condition before entering the following branch.
165                value = 0xa000u | ((random() & 15u) << 8u) | (random() & 1u); // Set value from this expression.
166            else if (j == 9) // Insert a scalar CD event between vector masks.
167                value = 0x2000u | (random() & 4095u); // Set value from this expression.
168            else // Alternate twelve-bit and eight-bit vector-mask words.
169                value = (j & 1u ? 0x5000u : 0x4000u) | (random() & (j & 1u ? 255u : 4095u)); // Set value from this expression.
170            words.push_back(static_cast<std::uint16_t>(value)); // Apply push back to these arguments.
171        } // End this block.
172        high = (high + 1u) & 4095u; // Set high from this expression.
173    } // End this block.
174    return words; // Return words.
175} // End this block.
176} // namespace shdr_examples
177#endif // Finish this conditional compilation branch.