From 3f04d1b3873adc7e44a2586de71ecf467b873979 Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Sun, 16 Nov 2025 16:09:35 -0400 Subject: [PATCH] Stimulus[Buffer|Frame]: initial impl, unoptimized for mem use --- include/user/spMcRingBuffer.h | 119 +++++++----------- include/user/stagingBuffer.h | 11 ++ include/user/stimulusBuffer.h | 14 +-- include/user/stimulusFrame.h | 27 ++++ stimBuffApis/livoxGen1/meshStimulusBuffer.h | 8 +- .../livoxGen1/pcloudAmbienceStimulusBuffer.h | 8 +- .../livoxGen1/pcloudIntensityStimulusBuffer.h | 8 +- .../livoxGen1/pcloudStimulusProducer.cpp | 54 ++++++-- .../livoxGen1/pcloudStimulusProducer.h | 1 + 9 files changed, 152 insertions(+), 98 deletions(-) diff --git a/include/user/spMcRingBuffer.h b/include/user/spMcRingBuffer.h index e37fcc2..afadc9e 100644 --- a/include/user/spMcRingBuffer.h +++ b/include/user/spMcRingBuffer.h @@ -2,13 +2,15 @@ #define _SP_MC_RING_BUFFER_H #include -#include #include #include -#include +#include +#include +#include #include namespace smo { +namespace stim_buff { /** * @brief Single-producer, multi-consumer ring buffer w/per-slot sequence locks @@ -20,120 +22,83 @@ namespace smo { */ class SpMcRingBuffer { -public: - class InputEngineConstraints - { - public: - InputEngineConstraints( - size_t slotStartAlignmentNBytes_, - size_t slotPadToNBytes_) - : slotStartAlignmentNBytes(slotStartAlignmentNBytes_), - slotPadToNBytes(slotPadToNBytes_) - {} - - ~InputEngineConstraints() = default; - - // Input-engine layout/constraints - size_t slotStartAlignmentNBytes; // power-of-2 alignment (e.g., 4096) - size_t slotPadToNBytes; // minimum size per slot - }; - public: /** EXPLANATION: * Constructor initializes the ring buffer with the given constraints and - * number of slots. Calculates stride and allocates data buffer and sequence - * locks array. + * number of buffers. Allocates slots vector with properly constructed + * StimulusFrame instances. */ explicit SpMcRingBuffer( - size_t nSlots_, - const InputEngineConstraints& constraints_) - : nSlots(nSlots_), strideNBytes(0), bufferNBytes(0), - constraints(constraints_) + size_t nBuffers_, + const StagingBuffer::IOEngineConstraints& inputEngineConstraints, + const StagingBuffer::IOEngineConstraints& outputEngineConstraints, + size_t nSlotsPerStimFrame) + : nBuffers(nBuffers_), + // Default-construct all frames + slots(nBuffers) { - if (nSlots == 0) + if (nBuffers == 0) { throw std::invalid_argument(std::string(__func__) - + ": SpMcRingBuffer: nSlots must be > 0"); + + ": SpMcRingBuffer: nBuffers must be > 0"); } - computeStrideAndBufferSize(); - // Allocate data buffer: bufferNBytes (aligned up to alignment) - data.resize(bufferNBytes); - // Initialize sequence locks array: one lock per slot - // Use unique_ptr array since SequenceLock is not copyable or movable - sequenceLocks = std::make_unique(nSlots); + // Re-invoke constructors w/placement new on default-constructed frames + for (size_t i = 0; i < nBuffers; ++i) + { + slots[i].~StimulusFrame(); // Destroy default-constructed object + new (&slots[i]) StimulusFrame( + inputEngineConstraints, outputEngineConstraints, + nSlotsPerStimFrame); + } } ~SpMcRingBuffer() = default; - // Non-copyable, movable + // Non-copyable, non-movable (slots are non-movable) SpMcRingBuffer(const SpMcRingBuffer&) = delete; SpMcRingBuffer& operator=(const SpMcRingBuffer&) = delete; - SpMcRingBuffer(SpMcRingBuffer&&) = default; - SpMcRingBuffer& operator=(SpMcRingBuffer&&) = default; + SpMcRingBuffer(SpMcRingBuffer&&) = delete; + SpMcRingBuffer& operator=(SpMcRingBuffer&&) = delete; public: /** - * @brief Get a reference to data at the specified slot + * @brief Get a reference to the StimulusFrame at the specified slot * - * @tparam T The type of data stored in the slot * @param slotIndex The index of the slot (0-based) - * @return Reference to T at the slot - * @throws std::out_of_range if slotIndex >= nSlots + * @return Reference to StimulusFrame at the slot + * @throws std::out_of_range if slotIndex >= nBuffers */ - template - T& getDataAtSlot(size_t slotIndex) + StimulusFrame& getDataAtSlot(size_t slotIndex) { - if (slotIndex >= nSlots) + if (slotIndex >= nBuffers) { throw std::out_of_range(std::string(__func__) - + ": SpMcRingBuffer: slotIndex must be < nSlots"); + + ": SpMcRingBuffer: slotIndex must be < nBuffers"); } - - size_t offset = slotIndex * strideNBytes; - return *reinterpret_cast(data.data() + offset); + return slots[slotIndex]; } SequenceLock& getSequenceLockAtSlot(size_t slotIndex) { - if (slotIndex >= nSlots) + if (slotIndex >= nBuffers) { throw std::out_of_range(std::string(__func__) - + ": SpMcRingBuffer: slotIndex must be < nSlots"); + + ": SpMcRingBuffer: slotIndex must be < nBuffers"); } - return sequenceLocks[slotIndex]; + return slots[slotIndex].lock; } -private: - void computeStrideAndBufferSize() - { - // Stride is the maximum of alignment and padding - strideNBytes = std::max( - constraints.slotStartAlignmentNBytes, - constraints.slotPadToNBytes); - - // Buffer size is nSlots * strideNBytes, aligned up to alignment - size_t rawSize = nSlots * strideNBytes; - bufferNBytes = ((rawSize + constraints.slotStartAlignmentNBytes - 1) - / constraints.slotStartAlignmentNBytes) - * constraints.slotStartAlignmentNBytes; - } - - // Buffer data - std::vector data; - - // Sequence locks array: one lock per slot - // Use unique_ptr array since SequenceLock is not copyable or movable - std::unique_ptr sequenceLocks; - public: // Layout/invariants - size_t nSlots; - size_t strideNBytes; - size_t bufferNBytes; - InputEngineConstraints constraints; + size_t nBuffers; + +private: + // Frames vector: each frame contains a sequence lock and staging buffer + std::vector slots; }; +} // namespace stim_buff } // namespace smo #endif // _SP_MC_RING_BUFFER_H diff --git a/include/user/stagingBuffer.h b/include/user/stagingBuffer.h index aa6762b..81882ec 100644 --- a/include/user/stagingBuffer.h +++ b/include/user/stagingBuffer.h @@ -31,6 +31,9 @@ public: class IOEngineConstraints { public: + // Default constructor creates uninitialized constraints + IOEngineConstraints() = default; + IOEngineConstraints( size_t slotStartAlignmentByteVal_, size_t slotPadToNBytes_, @@ -65,6 +68,12 @@ public: }; public: + /** EXPLANATION: + * Default constructor creates uninitialized buffer. + * Must be properly initialized using placement new with the parameterized constructor. + */ + StagingBuffer() = default; + /** EXPLANATION: * We use the input and output engine constraints to determine the total * amount of memory required internally to assemble a single frame with @@ -144,6 +153,8 @@ private: struct MmapDeleter { size_t size; + // Default constructor for use with default-constructed StagingBuffer + MmapDeleter() : size(0) {} MmapDeleter(size_t s) : size(s) {} void operator()(uint8_t* ptr) const diff --git a/include/user/stimulusBuffer.h b/include/user/stimulusBuffer.h index d8639e4..cd898c9 100644 --- a/include/user/stimulusBuffer.h +++ b/include/user/stimulusBuffer.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "stimulusFrame.h" #include "deviceAttachmentSpec.h" @@ -28,15 +29,16 @@ public: const std::shared_ptr &deviceAttachmentSpec, int histbuffMs, - const SpMcRingBuffer::InputEngineConstraints& ringBufferConstraints) + const StagingBuffer::IOEngineConstraints& inputEngineConstraints, + const StagingBuffer::IOEngineConstraints& outputEngineConstraints, + size_t nSlotsPerStimFrame) : parent(parent), deviceAttachmentSpec(deviceAttachmentSpec), histbuffMs(histbuffMs), - frames_(static_cast(histbuffMs / CONFIG_STIMBUFF_FRAME_PERIOD_MS)), - ringBufferConstraints(ringBufferConstraints), ringBuffer( static_cast(histbuffMs / CONFIG_STIMBUFF_FRAME_PERIOD_MS), - ringBufferConstraints) + inputEngineConstraints, outputEngineConstraints, + nSlotsPerStimFrame) {} virtual ~StimulusBuffer() = default; @@ -51,10 +53,6 @@ public: StimulusProducer& parent; std::shared_ptr deviceAttachmentSpec; int histbuffMs; - std::vector frames_; - -protected: - SpMcRingBuffer::InputEngineConstraints ringBufferConstraints; SpMcRingBuffer ringBuffer; }; diff --git a/include/user/stimulusFrame.h b/include/user/stimulusFrame.h index 39f9606..40e533c 100644 --- a/include/user/stimulusFrame.h +++ b/include/user/stimulusFrame.h @@ -2,6 +2,8 @@ #define _ATTACHMENT_SUPPORT_STIMULUS_FRAME_H #include +#include +#include namespace smo { namespace stim_buff { @@ -61,7 +63,32 @@ typedef uint64_t SimultaneityStamp; class StimulusFrame { public: + /** EXPLANATION: + * Default constructor creates uninitialized frame. + * Must be properly initialized using placement new with the parameterized constructor. + */ + StimulusFrame() = default; + + StimulusFrame( + const StagingBuffer::IOEngineConstraints& inputEngineConstraints, + const StagingBuffer::IOEngineConstraints& outputEngineConstraints, + size_t nSlots) + : stagingBuffer( + inputEngineConstraints, outputEngineConstraints, nSlots) + {} + + ~StimulusFrame() = default; + + // Non-copyable, movable + StimulusFrame(const StimulusFrame&) = delete; + StimulusFrame& operator=(const StimulusFrame&) = delete; + StimulusFrame(StimulusFrame&&) = default; + StimulusFrame& operator=(StimulusFrame&&) = default; + +public: + SequenceLock lock; SimultaneityStamp simultaneityStamp; + StagingBuffer stagingBuffer; }; } // namespace stim_buff diff --git a/stimBuffApis/livoxGen1/meshStimulusBuffer.h b/stimBuffApis/livoxGen1/meshStimulusBuffer.h index 3bf3551..67b80fc 100644 --- a/stimBuffApis/livoxGen1/meshStimulusBuffer.h +++ b/stimBuffApis/livoxGen1/meshStimulusBuffer.h @@ -3,6 +3,7 @@ #include #include +#include namespace smo { namespace stim_buff { @@ -21,9 +22,12 @@ public: StimulusProducer& parent, const std::shared_ptr& deviceAttachmentSpec, int histbuffMs, - const SpMcRingBuffer::InputEngineConstraints& ringBufferConstraints) + const StagingBuffer::IOEngineConstraints& inputEngineConstraints, + const StagingBuffer::IOEngineConstraints& outputEngineConstraints, + size_t nSlotsPerStimFrame) : StimulusBuffer( - parent, deviceAttachmentSpec, histbuffMs, ringBufferConstraints) + parent, deviceAttachmentSpec, histbuffMs, + inputEngineConstraints, outputEngineConstraints, nSlotsPerStimFrame) {} ~MeshStimulusBuffer() = default; diff --git a/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h b/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h index a45f887..564a27f 100644 --- a/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h +++ b/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h @@ -3,6 +3,7 @@ #include #include +#include namespace smo { namespace stim_buff { @@ -21,9 +22,12 @@ public: StimulusProducer& parent, const std::shared_ptr& deviceAttachmentSpec, int histbuffMs, - const SpMcRingBuffer::InputEngineConstraints& ringBufferConstraints) + const StagingBuffer::IOEngineConstraints& inputEngineConstraints, + const StagingBuffer::IOEngineConstraints& outputEngineConstraints, + size_t nSlotsPerStimFrame) : StimulusBuffer( - parent, deviceAttachmentSpec, histbuffMs, ringBufferConstraints) + parent, deviceAttachmentSpec, histbuffMs, + inputEngineConstraints, outputEngineConstraints, nSlotsPerStimFrame) {} ~PcloudAmbienceStimulusBuffer() = default; diff --git a/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h b/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h index 6776ad3..9507a68 100644 --- a/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h +++ b/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h @@ -3,6 +3,7 @@ #include #include +#include namespace smo { namespace stim_buff { @@ -22,9 +23,12 @@ public: const std::shared_ptr &deviceAttachmentSpec, int histbuffMs, - const SpMcRingBuffer::InputEngineConstraints& ringBufferConstraints) + const StagingBuffer::IOEngineConstraints& inputEngineConstraints, + const StagingBuffer::IOEngineConstraints& outputEngineConstraints, + size_t nSlotsPerStimFrame) : StimulusBuffer( - parent, deviceAttachmentSpec, histbuffMs, ringBufferConstraints) + parent, deviceAttachmentSpec, histbuffMs, + inputEngineConstraints, outputEngineConstraints, nSlotsPerStimFrame) {} ~PcloudIntensityStimulusBuffer() = default; diff --git a/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp b/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp index 6f86bee..50bd404 100644 --- a/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp +++ b/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp @@ -7,8 +7,9 @@ #include #include #include -#include "pcloudStimulusProducer.h" #include +#include +#include "pcloudStimulusProducer.h" namespace smo { namespace stim_buff { @@ -16,8 +17,15 @@ namespace stim_buff { extern const SmoCallbacks* smoHooksPtr; // OpenCL kernels are used to collate and produce our StimFrames. -static SpMcRingBuffer::InputEngineConstraints openClInputConstraints( - static_cast(sysconf(_SC_PAGE_SIZE)), sizeof(void *)); +static StagingBuffer::IOEngineConstraints openClInputConstraints( + // slotStartAlignmentByteVal (page alignment) + sizeof(float) * 3, + // slotPadToNBytes (pointer size) + sizeof(void *), + // frameStartAlignmentByteVal (page alignment) + static_cast(sysconf(_SC_PAGE_SIZE)), + // framePadToNBytes (pointer size) + static_cast(sysconf(_SC_PAGE_SIZE))); static StagingBuffer::IOEngineConstraints openClIntensityInputConstraints( // slotStartAlignmentByteVal (page alignment) @@ -27,7 +35,17 @@ static StagingBuffer::IOEngineConstraints openClIntensityInputConstraints( // frameStartAlignmentByteVal (page alignment) static_cast(sysconf(_SC_PAGE_SIZE)), // framePadToNBytes (pointer size) - sizeof(void *)); + static_cast(sysconf(_SC_PAGE_SIZE))); + +static StagingBuffer::IOEngineConstraints openClAmbienceInputConstraints( + // slotStartAlignmentByteVal (page alignment) + sizeof(float), + // slotPadToNBytes (pointer size) + sizeof(void *), + // frameStartAlignmentByteVal (page alignment) + static_cast(sysconf(_SC_PAGE_SIZE)), + // framePadToNBytes (pointer size) + static_cast(sysconf(_SC_PAGE_SIZE))); PcloudStimulusProducer::PcloudStimulusProducer( const std::shared_ptr &deviceAttachmentSpec, @@ -37,6 +55,7 @@ PcloudStimulusProducer::PcloudStimulusProducer( : StimulusProducer( deviceAttachmentSpec, device->componentThread->getIoService()), +nDgramsPerStagingBufferFrame(nDgramsPerStagingBufferFrame), device(device), formatDesc(formatDesc), openClCollatingAndMeshingEngine(*this), @@ -46,6 +65,10 @@ assemblyBuffer( nDgramsPerStagingBufferFrame), ioUringAssemblyEngine(*this, nDgramsPerStagingBufferFrame), collationBuffer( + StagingBuffer::IOEngineConstraints::openClInputConstraints, + StagingBuffer::IOEngineConstraints::openClInputConstraints, + nDgramsPerStagingBufferFrame), +tempStimulusFrame( StagingBuffer::IOEngineConstraints::openClInputConstraints, StagingBuffer::IOEngineConstraints::openClInputConstraints, nDgramsPerStagingBufferFrame) @@ -181,30 +204,47 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer( // Parse qualeIfaceApi to determine buffer type const std::string& qualeIfaceApi = deviceAttachmentSpec->qualeIfaceApi; + // Calculate nPointsPerStimFrame based on return mode + size_t nPointsPerDgram = livoxProto1::Device::getNPointsPerDgram( + static_cast(device->currentReturnMode)); + size_t nPointsPerStimFrame = this->nDgramsPerStagingBufferFrame + * nPointsPerDgram; + if (qualeIfaceApi == "mesh") { +std::cout << __func__ << ": $$$$$$$ Creating MeshStimulusBuffer" << std::endl; auto meshBuffer = std::make_shared( - *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints); + *this, deviceAttachmentSpec, histbuffMs, + openClInputConstraints, openClInputConstraints, + nPointsPerStimFrame); +std::cout << __func__ << ": $$$$$$$ Created MeshStimulusBuffer" << std::endl; meshStimulusBuffer = meshBuffer; attachedStimulusBuffers.push_back(meshBuffer); return meshBuffer; } else if (qualeIfaceApi == "pcloudIntensity") { +std::cout << __func__ << ": $$$$$$$ Creating PcloudIntensityStimulusBuffer" << std::endl; auto intensityBuffer = std::make_shared( *this, deviceAttachmentSpec, histbuffMs, - openClInputConstraints); + openClIntensityInputConstraints, openClIntensityInputConstraints, + nPointsPerStimFrame); +std::cout << __func__ << ": $$$$$$$ Created PcloudIntensityStimulusBuffer" << std::endl; intensityStimulusBuffer = intensityBuffer; attachedStimulusBuffers.push_back(intensityBuffer); return intensityBuffer; } else if (qualeIfaceApi == "pcloudAmbience") { +std::cout << __func__ << ": $$$$$$$ Creating PcloudAmbienceStimulusBuffer" << std::endl; auto ambienceBuffer = std::make_shared( - *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints); + *this, deviceAttachmentSpec, histbuffMs, + openClAmbienceInputConstraints, openClAmbienceInputConstraints, + nDgramsPerStagingBufferFrame); +std::cout << __func__ << ": $$$$$$$ Created PcloudAmbienceStimulusBuffer" << std::endl; ambienceStimulusBuffer = ambienceBuffer; attachedStimulusBuffers.push_back(ambienceBuffer); return ambienceBuffer; diff --git a/stimBuffApis/livoxGen1/pcloudStimulusProducer.h b/stimBuffApis/livoxGen1/pcloudStimulusProducer.h index 57a6fff..f761732 100644 --- a/stimBuffApis/livoxGen1/pcloudStimulusProducer.h +++ b/stimBuffApis/livoxGen1/pcloudStimulusProducer.h @@ -79,6 +79,7 @@ protected: public: void produceFrameReq(smo::Callback callback); + size_t nDgramsPerStagingBufferFrame; std::shared_ptr device; PcloudFormatDesc formatDesc; OpenClCollatingAndMeshingEngine openClCollatingAndMeshingEngine;