DAPS: Add intrin specs to nontrin specs

We no longer do intrin specs as a separate stimbuff; rather now we
do them as a specifier segment within the pipeline spec of a normal
nontrin spec.
This commit is contained in:
2026-04-18 12:02:27 -04:00
parent fc1fcae0b0
commit 632a227985
16 changed files with 441 additions and 620 deletions
+2 -5
View File
@@ -514,8 +514,6 @@ static const StimBuffApiDesc livoxGen1ApiDesc = {
{.name = "mesh"},
{.name = "pcloudIntensity"},
{.name = "pcloudAmbience"},
{.name = "negtrin"},
{.name = "postrin"},
{.name = "gyro"},
{.name = "accel"}
},
@@ -649,7 +647,7 @@ extern "C" void livoxGen1_attachDeviceReq(
}
try {
smo::intrin::validateIntrinsQualeApiPolicy(
smo::intrin::validateNoIntrinParamsOnQualeIface(
desc->qualeIfaceApi, desc->qualeIfaceApiParams);
}
catch (const std::runtime_error& e)
@@ -664,8 +662,7 @@ extern "C" void livoxGen1_attachDeviceReq(
// Unknown qualeIfaceApi
std::cerr << __func__ << ": Unsupported qualeIfaceApi '"
<< qualeIfaceApi << "' for LivoxGen1. "
"Supported values: mesh, pcloudIntensity, pcloudAmbience, "
"negtrin, postrin"
"Supported values: mesh, pcloudIntensity, pcloudAmbience"
<< std::endl;
cb.callbackFn(false, desc);
return;
+3 -2
View File
@@ -15,7 +15,8 @@ class StimulusProducer;
/**
* MeshStimulusBuffer is a specialized StimulusBuffer for mesh data.
* Intrinsic threshold params are not allowed on mesh qualeIfaceApi lines;
* use dedicated negtrin/postrin specs (docs/design/intrin-thresholds.md).
* attach postrin(...) / negtrin(...) specifiers to a pcloudAmbience nontrin
* spec instead.
*/
class MeshStimulusBuffer
: public StimulusBuffer
@@ -34,7 +35,7 @@ public:
inputEngineConstraints, outputEngineConstraints,
callbacks, flags)
{
intrin::validateIntrinsQualeApiPolicy(
intrin::validateNoIntrinParamsOnQualeIface(
deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams);
}
@@ -1,251 +0,0 @@
#ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H
#define _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H
#include <memory>
#include <cstdint>
#include <cstddef>
#include <optional>
#include <string>
#include <user/stimulusBuffer.h>
#include <user/stagingBuffer.h>
#include <user/deviceAttachmentSpec.h>
#include <user/intrinThresholdParams.h>
#include "pcloudAmbienceQualeIfaceApi.h"
namespace smo {
namespace stim_buff {
enum class IntrinStatus
{
DISABLED,
NEGTRIN,
POSTRIN,
};
struct ParsedAmbienceIntrinConfig
{
IntrinStatus status;
uint32_t interestPercentage;
uint32_t interestThreshold;
};
inline bool isAmbienceIntrinEnabled(IntrinStatus intrinStatus)
{
return intrinStatus != IntrinStatus::DISABLED;
}
inline intrin::ParsedThresholdParam parseAmbienceThresholdParam(
const std::string& paramName,
const std::string& paramValue,
intrin::ThresholdUnit unit)
{
try
{
return intrin::ParsedThresholdParam{
.value = std::stoi(paramValue),
.unit = unit,
.matchedName = paramName,
.wasSpecified = true,
};
}
catch (const std::exception& e)
{
throw std::runtime_error(
"Failed to parse '" + paramName + "' param value '" + paramValue
+ "' as integer: " + e.what());
}
}
inline ParsedAmbienceIntrinConfig buildAmbienceIntrinConfig(
IntrinStatus status,
const intrin::ParsedThresholdParam& thresholdParam,
size_t nDgramsPerFrame)
{
return ParsedAmbienceIntrinConfig{
.status = status,
.interestPercentage =
thresholdParam.unit == intrin::ThresholdUnit::Percentage
? static_cast<uint32_t>(thresholdParam.value)
: 0U,
.interestThreshold = intrin::resolveThresholdValue(
thresholdParam, nDgramsPerFrame),
};
}
inline std::optional<ParsedAmbienceIntrinConfig> tryParseAmbienceIntrinConfig(
const std::string& paramName,
const std::string& paramValue,
size_t nDgramsPerFrame,
const std::string& qualeIfaceApi)
{
using intrin::ThresholdUnit;
const bool apiIsPostrin = (qualeIfaceApi == "postrin");
const bool apiIsNegtrin = (qualeIfaceApi == "negtrin");
if (intrin::namesContain(intrin::kPosIntPcParamNames, paramName)
|| (apiIsPostrin
&& intrin::namesContain(
intrin::kIntrinInterestPcUnprefixed, paramName)))
{
return buildAmbienceIntrinConfig(
IntrinStatus::POSTRIN,
parseAmbienceThresholdParam(
paramName, paramValue, ThresholdUnit::Percentage),
nDgramsPerFrame);
}
if (intrin::namesContain(intrin::kPosIntThrParamNames, paramName)
|| (apiIsPostrin
&& intrin::namesContain(
intrin::kIntrinInterestThrUnprefixed, paramName)))
{
return buildAmbienceIntrinConfig(
IntrinStatus::POSTRIN,
parseAmbienceThresholdParam(
paramName, paramValue, ThresholdUnit::Absolute),
nDgramsPerFrame);
}
if (intrin::namesContain(intrin::kNegIntPcParamNames, paramName)
|| (apiIsNegtrin
&& intrin::namesContain(
intrin::kIntrinInterestPcUnprefixed, paramName)))
{
return buildAmbienceIntrinConfig(
IntrinStatus::NEGTRIN,
parseAmbienceThresholdParam(
paramName, paramValue, ThresholdUnit::Percentage),
nDgramsPerFrame);
}
if (intrin::namesContain(intrin::kNegIntThrParamNames, paramName)
|| (apiIsNegtrin
&& intrin::namesContain(
intrin::kIntrinInterestThrUnprefixed, paramName)))
{
return buildAmbienceIntrinConfig(
IntrinStatus::NEGTRIN,
parseAmbienceThresholdParam(
paramName, paramValue, ThresholdUnit::Absolute),
nDgramsPerFrame);
}
return std::nullopt;
}
inline ParsedAmbienceIntrinConfig parseAmbienceIntrinConfig(
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
size_t nDgramsPerFrame)
{
const auto& params = deviceAttachmentSpec->qualeIfaceApiParams;
for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt)
{
const auto& [paramName, paramValue] = *paramIt;
const auto parsedConfig = tryParseAmbienceIntrinConfig(
paramName,
paramValue,
nDgramsPerFrame,
deviceAttachmentSpec->qualeIfaceApi);
if (parsedConfig.has_value())
{ return parsedConfig.value(); }
}
return ParsedAmbienceIntrinConfig{
.status = IntrinStatus::DISABLED,
.interestPercentage = 0U,
.interestThreshold = 0U,
};
}
inline void validateAmbienceIntrinComparatorConfig(
IntrinStatus intrinStatus,
const std::optional<ParamComparator>& passbandCountComparator)
{
if (isAmbienceIntrinEnabled(intrinStatus)
&& !passbandCountComparator.has_value())
{
throw std::runtime_error(
"A PcloudAmbience intrinsic pipeline with an intrin threshold "
"must also specify either 'passband-count-gt-val' or "
"'passband-count-lt-val'");
}
}
class StimulusProducer;
/**
* Intrinsic pipeline for LivoxGen1 ambience: attaches as qualeIfaceApi
* negtrin(...) or postrin(...) with from-stimbuff=pcloudAmbience. Parses
* interest thresholds and passband comparators; sensory data still flows
* through PcloudAmbienceStimulusBuffer.
*/
class PcloudAmbienceIntrinStimulusBuffer
: public StimulusBuffer
{
public:
explicit PcloudAmbienceIntrinStimulusBuffer(
StimulusProducer& parent,
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
int histbuffMs,
const StagingBuffer::IOEngineConstraints& inputEngineConstraints,
const StagingBuffer::IOEngineConstraints& outputEngineConstraints,
const SmoCallbacks& callbacks,
cl_mem_flags flags,
size_t nDgramsPerFrame_)
: StimulusBuffer(
parent, deviceAttachmentSpec, histbuffMs,
inputEngineConstraints, outputEngineConstraints,
callbacks, flags),
nDgramsPerFrame(nDgramsPerFrame_)
{
intrin::validateIntrinsQualeApiPolicy(
deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams);
intrin::validateNoForbiddenUnitlessIntrinParams(
deviceAttachmentSpec->qualeIfaceApiParams);
const auto intrinConfig = parseAmbienceIntrinConfig(
deviceAttachmentSpec, nDgramsPerFrame_);
intrinStatus = intrinConfig.status;
intrinInterestPercentage = intrinConfig.interestPercentage;
intrinInterestThreshold = intrinConfig.interestThreshold;
passbandCountComparator = parseOptionalPcloudAmbienceParamComparator(
deviceAttachmentSpec);
validateAmbienceIntrinComparatorConfig(
intrinStatus, passbandCountComparator);
}
~PcloudAmbienceIntrinStimulusBuffer() = default;
bool shouldTriggerIntrinEvent(uint32_t ambiencePassbandCount) const
{
if (!isAmbienceIntrinEnabled(intrinStatus))
{ return false; }
return ambiencePassbandCount >= intrinInterestThreshold;
}
PcloudAmbienceIntrinStimulusBuffer(
const PcloudAmbienceIntrinStimulusBuffer&) = delete;
PcloudAmbienceIntrinStimulusBuffer& operator=(
const PcloudAmbienceIntrinStimulusBuffer&) = delete;
PcloudAmbienceIntrinStimulusBuffer(PcloudAmbienceIntrinStimulusBuffer&&) =
delete;
PcloudAmbienceIntrinStimulusBuffer& operator=(
PcloudAmbienceIntrinStimulusBuffer&&) = delete;
public:
IntrinStatus intrinStatus;
uint32_t intrinInterestPercentage;
uint32_t intrinInterestThreshold;
std::optional<ParamComparator> passbandCountComparator;
size_t nDgramsPerFrame;
};
} // namespace stim_buff
} // namespace smo
#endif // _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H
@@ -7,32 +7,10 @@
#include <stdexcept>
#include <string>
#include <user/deviceAttachmentSpec.h>
#include <vector>
namespace smo {
namespace stim_buff {
inline std::string parseRequiredFromStimbuffQualeIfaceName(
const std::vector<std::pair<std::string, std::string>>& params)
{
for (auto it = params.rbegin(); it != params.rend(); ++it)
{
if (it->first != "from-stimbuff")
{ continue; }
if (it->second.empty())
{
throw std::runtime_error(
"'from-stimbuff' must name a non-empty sensory qualeIfaceApi");
}
return it->second;
}
throw std::runtime_error(
"internal: 'from-stimbuff' missing after intrin policy validation");
}
enum ParamComparatorOp
{
OP_CMP_GT,
@@ -58,7 +36,19 @@ struct ParamComparator
}
};
inline std::optional<ParamComparator> parseOptionalPcloudAmbienceParamComparator(
struct PcloudAmbiencePassbandComparators
{
std::optional<ParamComparator> lt;
std::optional<ParamComparator> gt;
};
/* Both `passband-count-lt-val` and `passband-count-gt-val` are permitted
* simultaneously on a pcloudAmbience qualeIfaceApi: the lt comparator
* typically feeds a postrin(...) segment (triggering on unusually low
* counts), and the gt comparator typically feeds a negtrin(...) segment
* (triggering on unusually high counts).
*/
inline PcloudAmbiencePassbandComparators parsePcloudAmbiencePassbandComparators(
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec)
{
const auto& params = deviceAttachmentSpec->qualeIfaceApiParams;
@@ -68,30 +58,23 @@ inline std::optional<ParamComparator> parseOptionalPcloudAmbienceParamComparator
const int ltVal = device::DeviceAttachmentSpec::parseOptionalParamAsInt(
params, "passband-count-lt-val", kParamNotSpecified);
if (gtVal != kParamNotSpecified && ltVal != kParamNotSpecified)
{
throw std::runtime_error(
"Only one of 'passband-count-gt-val' or 'passband-count-lt-val' "
"may be specified for a PcloudAmbience intrinsic pipeline");
}
PcloudAmbiencePassbandComparators out;
if (gtVal != kParamNotSpecified)
{
return std::optional<ParamComparator>(ParamComparator{
out.gt = ParamComparator{
.op = OP_CMP_GT,
.value = static_cast<uint32_t>(gtVal),
});
};
}
if (ltVal != kParamNotSpecified)
{
return std::optional<ParamComparator>(ParamComparator{
out.lt = ParamComparator{
.op = OP_CMP_LT,
.value = static_cast<uint32_t>(ltVal),
});
};
}
return std::nullopt;
return out;
}
} // namespace stim_buff
@@ -1,23 +1,58 @@
#ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H
#define _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H
#include <memory>
#include <cstddef>
#include <user/stimulusBuffer.h>
#include <user/stagingBuffer.h>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <user/deviceAttachmentSpec.h>
#include <user/intrin.h>
#include <user/intrinThresholdParams.h>
#include <user/stagingBuffer.h>
#include <user/stimulusBuffer.h>
#include "pcloudAmbienceQualeIfaceApi.h"
namespace smo {
namespace stim_buff {
class StimulusProducer;
inline intrin::IntrinConfig parseAmbienceIntrinConfigFromParams(
std::string_view intrinKind,
const std::vector<std::pair<std::string, std::string>>& params,
size_t nDgramsPerFrame)
{
intrin::validateIntrinSegmentParams(intrinKind, params);
const auto threshold = intrin::parseOptionalThresholdParam(
params,
intrin::kIntrinInterestPcNames,
intrin::kIntrinInterestThrNames,
/*defaultValue=*/0,
/*defaultUnit=*/intrin::ThresholdUnit::Absolute);
return intrin::IntrinConfig{
.percentage =
threshold.unit == intrin::ThresholdUnit::Percentage
? static_cast<uint32_t>(threshold.value)
: 0U,
.threshold = intrin::resolveThresholdValue(
threshold, nDgramsPerFrame),
};
}
/**
* Sensory PcloudAmbience buffer: per-dgram ambience floats only.
* Intrinsic thresholds and passband comparators are only valid on dedicated
* negtrin/postrin qualeIfaceApi specs (PcloudAmbienceIntrinStimulusBuffer);
* see docs/design/intrin-thresholds.md.
* Sensory PcloudAmbience buffer: per-dgram ambience floats. A DAP spec may
* optionally attach a postrin(...) and/or a negtrin(...) specifier to this
* qualeIfaceApi; when present, interest thresholds from those specifiers and
* passband-count comparators from this spec's own qualeIfaceApi params are
* combined to decide when an intrin event should fire.
*
* Convention: the postrin pipeline pairs with passband-count-lt-val (a sparse
* ambient scene being "too good, stay here"); the negtrin pipeline pairs with
* passband-count-gt-val (a dense ambient scene being "unbearably much, get
* away"). Both comparators may be specified simultaneously on this qualeIface.
*/
class PcloudAmbienceStimulusBuffer
: public StimulusBuffer
@@ -38,9 +73,46 @@ public:
callbacks, flags),
nDgramsPerFrame(nDgramsPerFrame_)
{
intrin::validateIntrinsQualeApiPolicy(
intrin::validateNoIntrinParamsOnQualeIface(
deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams);
const auto passbandComparators =
parsePcloudAmbiencePassbandComparators(deviceAttachmentSpec);
passbandCountLtComparator = passbandComparators.lt;
passbandCountGtComparator = passbandComparators.gt;
if (!deviceAttachmentSpec->postrin.empty())
{
postrinInterestConfig = parseAmbienceIntrinConfigFromParams(
"postrin",
deviceAttachmentSpec->postrinParams,
nDgramsPerFrame_);
if (!passbandCountLtComparator.has_value())
{
throw std::runtime_error(
"pcloudAmbience DAP spec declares a postrin(...) but no "
"'passband-count-lt-val' on the pcloudAmbience qualeIface "
"params to feed it.");
}
}
if (!deviceAttachmentSpec->negtrin.empty())
{
negtrinInterestConfig = parseAmbienceIntrinConfigFromParams(
"negtrin",
deviceAttachmentSpec->negtrinParams,
nDgramsPerFrame_);
if (!passbandCountGtComparator.has_value())
{
throw std::runtime_error(
"pcloudAmbience DAP spec declares a negtrin(...) but no "
"'passband-count-gt-val' on the pcloudAmbience qualeIface "
"params to feed it.");
}
}
}
~PcloudAmbienceStimulusBuffer() = default;
@@ -52,8 +124,24 @@ public:
PcloudAmbienceStimulusBuffer& operator=(
PcloudAmbienceStimulusBuffer&&) = delete;
bool shouldTriggerPostrinEvent(uint32_t ambiencePassbandCount) const
{
if (!postrinInterestConfig.has_value()) { return false; }
return ambiencePassbandCount >= postrinInterestConfig->threshold;
}
bool shouldTriggerNegtrinEvent(uint32_t ambiencePassbandCount) const
{
if (!negtrinInterestConfig.has_value()) { return false; }
return ambiencePassbandCount >= negtrinInterestConfig->threshold;
}
public:
size_t nDgramsPerFrame;
std::optional<intrin::IntrinConfig> postrinInterestConfig;
std::optional<intrin::IntrinConfig> negtrinInterestConfig;
std::optional<ParamComparator> passbandCountLtComparator;
std::optional<ParamComparator> passbandCountGtComparator;
};
} // namespace stim_buff
@@ -33,7 +33,7 @@ public:
inputEngineConstraints, outputEngineConstraints,
callbacks, flags)
{
intrin::validateIntrinsQualeApiPolicy(
intrin::validateNoIntrinParamsOnQualeIface(
deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams);
}
@@ -14,24 +14,6 @@
#include "livoxGen1.h"
#include "pcloudStimulusProducer.h"
namespace {
void requirePcloudAmbienceFromStimbuff(
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& spec)
{
const std::string fromName =
smo::stim_buff::parseRequiredFromStimbuffQualeIfaceName(
spec->qualeIfaceApiParams);
if (fromName != "pcloudAmbience")
{
throw std::runtime_error(
"LivoxGen1 PcloudAmbience intrinsic pipelines require "
"from-stimbuff=pcloudAmbience (got '" + fromName + "').");
}
}
} // namespace
namespace smo {
namespace stim_buff {
@@ -151,8 +133,7 @@ bool PcloudStimulusProducer::supportsQualeIfaceApi(
const std::string& qualeIfaceApi)
{
return qualeIfaceApi == "mesh" || qualeIfaceApi == "pcloudIntensity" ||
qualeIfaceApi == "pcloudAmbience" || qualeIfaceApi == "negtrin" ||
qualeIfaceApi == "postrin";
qualeIfaceApi == "pcloudAmbience";
}
bool PcloudStimulusProducer::exportsQualeIfaceApi(
@@ -248,11 +229,6 @@ PcloudStimulusProducer::getAttachedStimulusBuffer(
if (std::dynamic_pointer_cast<PcloudAmbienceStimulusBuffer>(buffer))
{ return buffer; }
}
else if (qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin")
{
if (std::dynamic_pointer_cast<PcloudAmbienceIntrinStimulusBuffer>(buffer))
{ return buffer; }
}
// Type mismatch - return nullptr
return nullptr;
@@ -284,22 +260,6 @@ void PcloudStimulusProducer::destroyAttachedStimulusBuffer(
ambienceBuff.reset();
ambienceStimulusBuffer.store(nullptr, std::memory_order_release);
}
auto negIntrinBuff = negtrinAmbienceIntrinStimulusBuffer.load(
std::memory_order_acquire);
if (negIntrinBuff == buffer)
{
negIntrinBuff.reset();
negtrinAmbienceIntrinStimulusBuffer.store(
nullptr, std::memory_order_release);
}
auto posIntrinBuff = postrinAmbienceIntrinStimulusBuffer.load(
std::memory_order_acquire);
if (posIntrinBuff == buffer)
{
posIntrinBuff.reset();
postrinAmbienceIntrinStimulusBuffer.store(
nullptr, std::memory_order_release);
}
// Call base class implementation to remove from attachedStimulusBuffers
StimulusProducer::destroyAttachedStimulusBuffer(buffer);
@@ -384,38 +344,12 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer(
this->start();
return ambienceStimBuff;
}
else if (qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin")
{
requirePcloudAmbienceFromStimbuff(deviceAttachmentSpec);
auto intrinBuff = std::make_shared<PcloudAmbienceIntrinStimulusBuffer>(
*this, deviceAttachmentSpec, histbuffMs,
openClAmbienceInputConstraints, openClAmbienceInputConstraints,
*smoHooksPtr, CL_MEM_READ_WRITE,
this->nDgramsPerStagingBufferFrame);
this->stop();
addAttachedStimulusBufferIfNotExists(intrinBuff);
if (qualeIfaceApi == "negtrin")
{
negtrinAmbienceIntrinStimulusBuffer.store(
intrinBuff, std::memory_order_release);
}
else
{
postrinAmbienceIntrinStimulusBuffer.store(
intrinBuff, std::memory_order_release);
}
this->start();
return intrinBuff;
}
else
{
throw std::runtime_error(
"Unsupported qualeIfaceApi: '" + qualeIfaceApi + "' for "
"PcloudStimulusProducer. "
"Supported values: mesh, pcloudIntensity, pcloudAmbience, "
"negtrin, postrin");
"Supported values: mesh, pcloudIntensity, pcloudAmbience");
}
}
@@ -15,7 +15,6 @@
#include "meshStimulusBuffer.h"
#include "pcloudIntensityStimulusBuffer.h"
#include "pcloudAmbienceStimulusBuffer.h"
#include "pcloudAmbienceIntrinStimulusBuffer.h"
namespace smo {
namespace stim_buff {
@@ -105,10 +104,6 @@ public:
intensityStimulusBuffer;
std::atomic<std::shared_ptr<PcloudAmbienceStimulusBuffer>>
ambienceStimulusBuffer;
std::atomic<std::shared_ptr<PcloudAmbienceIntrinStimulusBuffer>>
negtrinAmbienceIntrinStimulusBuffer;
std::atomic<std::shared_ptr<PcloudAmbienceIntrinStimulusBuffer>>
postrinAmbienceIntrinStimulusBuffer;
private:
class ProduceFrameReq;