StimBuff: Use a single StagingBuffer for all StimFrames

We now allocate all the stimFrames for a StimBuffer using a
single StagingBuffer. This gives us all the benefits we're
looking for (pinning, alignment, etc).
This commit is contained in:
2025-11-19 03:08:16 -04:00
parent 3f04d1b387
commit 41b8385cb2
8 changed files with 93 additions and 57 deletions
+22 -15
View File
@@ -6,7 +6,9 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <new> #include <new>
#include <memory>
#include <user/stimulusFrame.h> #include <user/stimulusFrame.h>
#include <user/frameAssemblyDesc.h>
#include <user/sequenceLock.h> #include <user/sequenceLock.h>
namespace smo { namespace smo {
@@ -24,32 +26,35 @@ class SpMcRingBuffer
{ {
public: public:
/** EXPLANATION: /** EXPLANATION:
* Constructor initializes the ring buffer with the given constraints and * Constructor initializes the ring buffer with FrameAssemblyDesc.
* number of buffers. Allocates slots vector with properly constructed * Allocates frames vector with properly constructed StimulusFrame instances,
* StimulusFrame instances. * each initialized with a SlotDesc from the FrameAssemblyDesc.
*/ */
explicit SpMcRingBuffer( explicit SpMcRingBuffer(
size_t nBuffers_, const std::shared_ptr<FrameAssemblyDesc> &frameAssemblyDesc_)
const StagingBuffer::IOEngineConstraints& inputEngineConstraints, :
const StagingBuffer::IOEngineConstraints& outputEngineConstraints, nBuffers(frameAssemblyDesc_ ? frameAssemblyDesc_->slots.size() : 0),
size_t nSlotsPerStimFrame) frameAssemblyDesc(frameAssemblyDesc_),
: nBuffers(nBuffers_), slots(nBuffers) // Default-construct all frames
// Default-construct all frames
slots(nBuffers)
{ {
if (!frameAssemblyDesc)
{
throw std::invalid_argument(std::string(__func__)
+ ": SpMcRingBuffer: frameAssemblyDesc must not be null");
}
if (nBuffers == 0) if (nBuffers == 0)
{ {
throw std::invalid_argument(std::string(__func__) throw std::invalid_argument(std::string(__func__)
+ ": SpMcRingBuffer: nBuffers must be > 0"); + ": SpMcRingBuffer: frameAssemblyDesc must have at least one "
"slot");
} }
// Re-invoke constructors w/placement new on default-constructed frames // Re-invoke constructors w/placement new on default-constructed frames
for (size_t i = 0; i < nBuffers; ++i) for (size_t i = 0; i < nBuffers; ++i)
{ {
slots[i].~StimulusFrame(); // Destroy default-constructed object slots[i].~StimulusFrame(); // Destroy default-constructed object
new (&slots[i]) StimulusFrame( new (&slots[i]) StimulusFrame(frameAssemblyDesc->slots[i]);
inputEngineConstraints, outputEngineConstraints,
nSlotsPerStimFrame);
} }
} }
@@ -94,7 +99,9 @@ public:
size_t nBuffers; size_t nBuffers;
private: private:
// Frames vector: each frame contains a sequence lock and staging buffer // FrameAssemblyDesc describing the memory layout
std::shared_ptr<FrameAssemblyDesc> frameAssemblyDesc;
// Frames vector: each frame contains a sequence lock and SlotDesc
std::vector<StimulusFrame> slots; std::vector<StimulusFrame> slots;
}; };
+8 -6
View File
@@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <user/spMcRingBuffer.h> #include <user/spMcRingBuffer.h>
#include <user/stagingBuffer.h> #include <user/stagingBuffer.h>
#include <user/frameAssemblyDesc.h>
#include "stimulusFrame.h" #include "stimulusFrame.h"
#include "deviceAttachmentSpec.h" #include "deviceAttachmentSpec.h"
@@ -30,15 +31,15 @@ public:
&deviceAttachmentSpec, &deviceAttachmentSpec,
int histbuffMs, int histbuffMs,
const StagingBuffer::IOEngineConstraints& inputEngineConstraints, const StagingBuffer::IOEngineConstraints& inputEngineConstraints,
const StagingBuffer::IOEngineConstraints& outputEngineConstraints, const StagingBuffer::IOEngineConstraints& outputEngineConstraints)
size_t nSlotsPerStimFrame)
: parent(parent), : parent(parent),
deviceAttachmentSpec(deviceAttachmentSpec), deviceAttachmentSpec(deviceAttachmentSpec),
histbuffMs(histbuffMs), histbuffMs(histbuffMs),
ringBuffer( stagingBuffer(
static_cast<size_t>(histbuffMs / CONFIG_STIMBUFF_FRAME_PERIOD_MS), inputEngineConstraints,
inputEngineConstraints, outputEngineConstraints, outputEngineConstraints,
nSlotsPerStimFrame) static_cast<size_t>(histbuffMs / CONFIG_STIMBUFF_FRAME_PERIOD_MS)),
ringBuffer(static_cast<std::shared_ptr<FrameAssemblyDesc>>(stagingBuffer))
{} {}
virtual ~StimulusBuffer() = default; virtual ~StimulusBuffer() = default;
@@ -53,6 +54,7 @@ public:
StimulusProducer& parent; StimulusProducer& parent;
std::shared_ptr<device::DeviceAttachmentSpec> deviceAttachmentSpec; std::shared_ptr<device::DeviceAttachmentSpec> deviceAttachmentSpec;
int histbuffMs; int histbuffMs;
StagingBuffer stagingBuffer;
SpMcRingBuffer ringBuffer; SpMcRingBuffer ringBuffer;
}; };
+4 -8
View File
@@ -2,7 +2,7 @@
#define _ATTACHMENT_SUPPORT_STIMULUS_FRAME_H #define _ATTACHMENT_SUPPORT_STIMULUS_FRAME_H
#include <cstdint> #include <cstdint>
#include <user/stagingBuffer.h> #include <user/frameAssemblyDesc.h>
#include <user/sequenceLock.h> #include <user/sequenceLock.h>
namespace smo { namespace smo {
@@ -69,12 +69,8 @@ public:
*/ */
StimulusFrame() = default; StimulusFrame() = default;
StimulusFrame( StimulusFrame(const FrameAssemblyDesc::SlotDesc& slotDesc_)
const StagingBuffer::IOEngineConstraints& inputEngineConstraints, : slotDesc(slotDesc_)
const StagingBuffer::IOEngineConstraints& outputEngineConstraints,
size_t nSlots)
: stagingBuffer(
inputEngineConstraints, outputEngineConstraints, nSlots)
{} {}
~StimulusFrame() = default; ~StimulusFrame() = default;
@@ -88,7 +84,7 @@ public:
public: public:
SequenceLock lock; SequenceLock lock;
SimultaneityStamp simultaneityStamp; SimultaneityStamp simultaneityStamp;
StagingBuffer stagingBuffer; FrameAssemblyDesc::SlotDesc slotDesc;
}; };
} // namespace stim_buff } // namespace stim_buff
+2 -3
View File
@@ -23,11 +23,10 @@ public:
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec, const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
int histbuffMs, int histbuffMs,
const StagingBuffer::IOEngineConstraints& inputEngineConstraints, const StagingBuffer::IOEngineConstraints& inputEngineConstraints,
const StagingBuffer::IOEngineConstraints& outputEngineConstraints, const StagingBuffer::IOEngineConstraints& outputEngineConstraints)
size_t nSlotsPerStimFrame)
: StimulusBuffer( : StimulusBuffer(
parent, deviceAttachmentSpec, histbuffMs, parent, deviceAttachmentSpec, histbuffMs,
inputEngineConstraints, outputEngineConstraints, nSlotsPerStimFrame) inputEngineConstraints, outputEngineConstraints)
{} {}
~MeshStimulusBuffer() = default; ~MeshStimulusBuffer() = default;
@@ -23,11 +23,10 @@ public:
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec, const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
int histbuffMs, int histbuffMs,
const StagingBuffer::IOEngineConstraints& inputEngineConstraints, const StagingBuffer::IOEngineConstraints& inputEngineConstraints,
const StagingBuffer::IOEngineConstraints& outputEngineConstraints, const StagingBuffer::IOEngineConstraints& outputEngineConstraints)
size_t nSlotsPerStimFrame)
: StimulusBuffer( : StimulusBuffer(
parent, deviceAttachmentSpec, histbuffMs, parent, deviceAttachmentSpec, histbuffMs,
inputEngineConstraints, outputEngineConstraints, nSlotsPerStimFrame) inputEngineConstraints, outputEngineConstraints)
{} {}
~PcloudAmbienceStimulusBuffer() = default; ~PcloudAmbienceStimulusBuffer() = default;
@@ -24,11 +24,10 @@ public:
&deviceAttachmentSpec, &deviceAttachmentSpec,
int histbuffMs, int histbuffMs,
const StagingBuffer::IOEngineConstraints& inputEngineConstraints, const StagingBuffer::IOEngineConstraints& inputEngineConstraints,
const StagingBuffer::IOEngineConstraints& outputEngineConstraints, const StagingBuffer::IOEngineConstraints& outputEngineConstraints)
size_t nSlotsPerStimFrame)
: StimulusBuffer( : StimulusBuffer(
parent, deviceAttachmentSpec, histbuffMs, parent, deviceAttachmentSpec, histbuffMs,
inputEngineConstraints, outputEngineConstraints, nSlotsPerStimFrame) inputEngineConstraints, outputEngineConstraints)
{} {}
~PcloudIntensityStimulusBuffer() = default; ~PcloudIntensityStimulusBuffer() = default;
@@ -18,10 +18,24 @@ extern const SmoCallbacks* smoHooksPtr;
// OpenCL kernels are used to collate and produce our StimFrames. // OpenCL kernels are used to collate and produce our StimFrames.
static StagingBuffer::IOEngineConstraints openClInputConstraints( static StagingBuffer::IOEngineConstraints openClInputConstraints(
/** FIXME:
* This should eventually be aligned to 4B and padded to 12B.
*/
// slotStartAlignmentByteVal (page alignment) // slotStartAlignmentByteVal (page alignment)
sizeof(float) * 3, sizeof(float) * 4,
// slotPadToNBytes (pointer size) // slotPadToNBytes (pointer size)
sizeof(void *), sizeof(float) * 4,
// frameStartAlignmentByteVal (page alignment)
static_cast<size_t>(sysconf(_SC_PAGE_SIZE)),
// framePadToNBytes (pointer size)
static_cast<size_t>(sysconf(_SC_PAGE_SIZE)));
// OpenCL kernels are used to collate and produce our StimFrames.
static StagingBuffer::IOEngineConstraints openClMeshInputConstraints(
// slotStartAlignmentByteVal (page alignment)
static_cast<size_t>(sysconf(_SC_PAGE_SIZE)),
// slotPadToNBytes (pointer size)
sizeof(float) * 3,
// frameStartAlignmentByteVal (page alignment) // frameStartAlignmentByteVal (page alignment)
static_cast<size_t>(sysconf(_SC_PAGE_SIZE)), static_cast<size_t>(sysconf(_SC_PAGE_SIZE)),
// framePadToNBytes (pointer size) // framePadToNBytes (pointer size)
@@ -29,7 +43,7 @@ static StagingBuffer::IOEngineConstraints openClInputConstraints(
static StagingBuffer::IOEngineConstraints openClIntensityInputConstraints( static StagingBuffer::IOEngineConstraints openClIntensityInputConstraints(
// slotStartAlignmentByteVal (page alignment) // slotStartAlignmentByteVal (page alignment)
sizeof(float), static_cast<size_t>(sysconf(_SC_PAGE_SIZE)),
// slotPadToNBytes (intensity value size) // slotPadToNBytes (intensity value size)
sizeof(float), sizeof(float),
// frameStartAlignmentByteVal (page alignment) // frameStartAlignmentByteVal (page alignment)
@@ -39,9 +53,9 @@ static StagingBuffer::IOEngineConstraints openClIntensityInputConstraints(
static StagingBuffer::IOEngineConstraints openClAmbienceInputConstraints( static StagingBuffer::IOEngineConstraints openClAmbienceInputConstraints(
// slotStartAlignmentByteVal (page alignment) // slotStartAlignmentByteVal (page alignment)
sizeof(float), static_cast<size_t>(sysconf(_SC_PAGE_SIZE)),
// slotPadToNBytes (pointer size) // slotPadToNBytes (pointer size)
sizeof(void *), sizeof(float),
// frameStartAlignmentByteVal (page alignment) // frameStartAlignmentByteVal (page alignment)
static_cast<size_t>(sysconf(_SC_PAGE_SIZE)), static_cast<size_t>(sysconf(_SC_PAGE_SIZE)),
// framePadToNBytes (pointer size) // framePadToNBytes (pointer size)
@@ -68,10 +82,11 @@ collationBuffer(
StagingBuffer::IOEngineConstraints::openClInputConstraints, StagingBuffer::IOEngineConstraints::openClInputConstraints,
StagingBuffer::IOEngineConstraints::openClInputConstraints, StagingBuffer::IOEngineConstraints::openClInputConstraints,
nDgramsPerStagingBufferFrame), nDgramsPerStagingBufferFrame),
tempStimulusFrame( tempStimulusFrameMem(0),
StagingBuffer::IOEngineConstraints::openClInputConstraints, tempStimulusFrame(FrameAssemblyDesc::SlotDesc{
StagingBuffer::IOEngineConstraints::openClInputConstraints, 0,
nDgramsPerStagingBufferFrame) reinterpret_cast<uint8_t*>(&tempStimulusFrameMem),
sizeof(tempStimulusFrameMem)})
{ {
if (smoHooksPtr->OptionParser_getOptions().verbose) if (smoHooksPtr->OptionParser_getOptions().verbose)
{ {
@@ -204,19 +219,24 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer(
// Parse qualeIfaceApi to determine buffer type // Parse qualeIfaceApi to determine buffer type
const std::string& qualeIfaceApi = deviceAttachmentSpec->qualeIfaceApi; const std::string& qualeIfaceApi = deviceAttachmentSpec->qualeIfaceApi;
// Calculate nPointsPerStimFrame based on return mode // Calculate nPointsPerDgram based on return mode
size_t nPointsPerDgram = livoxProto1::Device::getNPointsPerDgram( size_t nPointsPerDgram = livoxProto1::Device::getNPointsPerDgram(
static_cast<int>(device->currentReturnMode)); static_cast<int>(device->currentReturnMode));
size_t nPointsPerStimFrame = this->nDgramsPerStagingBufferFrame
* nPointsPerDgram;
if (qualeIfaceApi == "mesh") if (qualeIfaceApi == "mesh")
{ {
/* Calculate slotStrideNBytes:
* nDgramsPerStagingBufferFrame * nPointsPerDgram * sizeof(float) * 3
*/
size_t slotStrideNBytes = this->nDgramsPerStagingBufferFrame
* nPointsPerDgram * sizeof(float) * 3;
// Reuse openClMeshInputConstraints, only modify slotPadToNBytes
openClMeshInputConstraints.slotPadToNBytes = slotStrideNBytes;
std::cout << __func__ << ": $$$$$$$ Creating MeshStimulusBuffer" << std::endl; std::cout << __func__ << ": $$$$$$$ Creating MeshStimulusBuffer" << std::endl;
auto meshBuffer = std::make_shared<MeshStimulusBuffer>( auto meshBuffer = std::make_shared<MeshStimulusBuffer>(
*this, deviceAttachmentSpec, histbuffMs, *this, deviceAttachmentSpec, histbuffMs,
openClInputConstraints, openClInputConstraints, openClMeshInputConstraints, openClMeshInputConstraints);
nPointsPerStimFrame);
std::cout << __func__ << ": $$$$$$$ Created MeshStimulusBuffer" << std::endl; std::cout << __func__ << ": $$$$$$$ Created MeshStimulusBuffer" << std::endl;
meshStimulusBuffer = meshBuffer; meshStimulusBuffer = meshBuffer;
@@ -225,11 +245,18 @@ std::cout << __func__ << ": $$$$$$$ Created MeshStimulusBuffer" << std::endl;
} }
else if (qualeIfaceApi == "pcloudIntensity") else if (qualeIfaceApi == "pcloudIntensity")
{ {
/* Calculate slotStrideNBytes:
* nDgramsPerStagingBufferFrame * nPointsPerDgram * sizeof(float) * 1
*/
size_t slotStrideNBytes = this->nDgramsPerStagingBufferFrame
* nPointsPerDgram * sizeof(float) * 1;
// Reuse openClIntensityInputConstraints, only modify slotPadToNBytes
openClIntensityInputConstraints.slotPadToNBytes = slotStrideNBytes;
std::cout << __func__ << ": $$$$$$$ Creating PcloudIntensityStimulusBuffer" << std::endl; std::cout << __func__ << ": $$$$$$$ Creating PcloudIntensityStimulusBuffer" << std::endl;
auto intensityBuffer = std::make_shared<PcloudIntensityStimulusBuffer>( auto intensityBuffer = std::make_shared<PcloudIntensityStimulusBuffer>(
*this, deviceAttachmentSpec, histbuffMs, *this, deviceAttachmentSpec, histbuffMs,
openClIntensityInputConstraints, openClIntensityInputConstraints, openClIntensityInputConstraints, openClIntensityInputConstraints);
nPointsPerStimFrame);
std::cout << __func__ << ": $$$$$$$ Created PcloudIntensityStimulusBuffer" << std::endl; std::cout << __func__ << ": $$$$$$$ Created PcloudIntensityStimulusBuffer" << std::endl;
intensityStimulusBuffer = intensityBuffer; intensityStimulusBuffer = intensityBuffer;
@@ -238,11 +265,17 @@ std::cout << __func__ << ": $$$$$$$ Created PcloudIntensityStimulusBuffer" << st
} }
else if (qualeIfaceApi == "pcloudAmbience") else if (qualeIfaceApi == "pcloudAmbience")
{ {
std::cout << __func__ << ": $$$$$$$ Creating PcloudAmbienceStimulusBuffer" << std::endl; /* Calculate slotStrideNBytes:
* nDgramsPerStagingBufferFrame * sizeof(float)
*/
size_t slotStrideNBytes = this->nDgramsPerStagingBufferFrame
* sizeof(float);
// Reuse openClAmbienceInputConstraints, only modify slotPadToNBytes
openClAmbienceInputConstraints.slotPadToNBytes = slotStrideNBytes;
auto ambienceBuffer = std::make_shared<PcloudAmbienceStimulusBuffer>( auto ambienceBuffer = std::make_shared<PcloudAmbienceStimulusBuffer>(
*this, deviceAttachmentSpec, histbuffMs, *this, deviceAttachmentSpec, histbuffMs,
openClAmbienceInputConstraints, openClAmbienceInputConstraints, openClAmbienceInputConstraints, openClAmbienceInputConstraints);
nDgramsPerStagingBufferFrame);
std::cout << __func__ << ": $$$$$$$ Created PcloudAmbienceStimulusBuffer" << std::endl; std::cout << __func__ << ": $$$$$$$ Created PcloudAmbienceStimulusBuffer" << std::endl;
ambienceStimulusBuffer = ambienceBuffer; ambienceStimulusBuffer = ambienceBuffer;
@@ -86,6 +86,7 @@ public:
StagingBuffer assemblyBuffer; StagingBuffer assemblyBuffer;
IoUringAssemblyEngine ioUringAssemblyEngine; IoUringAssemblyEngine ioUringAssemblyEngine;
StagingBuffer collationBuffer; StagingBuffer collationBuffer;
size_t tempStimulusFrameMem;
StimulusFrame tempStimulusFrame; StimulusFrame tempStimulusFrame;
std::shared_ptr<MeshStimulusBuffer> meshStimulusBuffer; std::shared_ptr<MeshStimulusBuffer> meshStimulusBuffer;
std::shared_ptr<PcloudIntensityStimulusBuffer> intensityStimulusBuffer; std::shared_ptr<PcloudIntensityStimulusBuffer> intensityStimulusBuffer;