blob: c7d5f7dc0295694d91a5be6f98365df3825688c1 [file] [log] [blame]
/*
* Copyright (C) 2019-2020 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "PipeLiteExecutor"
#include <algorithm>
#include "PipeLiteExecutor.h"
#include "PSysDAG.h"
#include "FormatUtils.h"
#include "iutils/CameraDump.h"
#include "SyncManager.h"
// CIPF backends
extern "C" {
#include <ia_cipf_css/ia_cipf_css.h>
#include <ia_pal_types_isp_ids_autogen.h>
}
using std::vector;
using std::string;
using std::map;
using std::shared_ptr;
namespace icamera {
static const int32_t sStatKernels[] = {
ia_pal_uuid_isp_bxt_awbstatistics,
ia_pal_uuid_isp_awbstatistics_2_0,
ia_pal_uuid_isp_bxt_dvsstatistics
};
static const int32_t sSisKernels[] = {
ia_pal_uuid_isp_sis_1_0_a
};
PipeLiteExecutor::PipeLiteExecutor(int cameraId, const ExecutorPolicy &policy,
vector<string> exclusivePGs, PSysDAG *psysDag,
shared_ptr<IGraphConfig> gc)
: mCameraId(cameraId),
mStreamId(-1),
mName(policy.exeName),
mPGNames(policy.pgList),
mOpModes(policy.opModeList),
mGraphConfig(gc),
mIsInputEdge(false),
mIsOutputEdge(false),
mNotifyPolicy(POLICY_FRAME_FIRST),
mAdaptor(nullptr),
mPolicyManager(nullptr),
mShareReferPool(nullptr),
mLastStatsSequence(-1),
mExclusivePGs(exclusivePGs),
mPSysDag(psysDag),
mkernelsCountWithStats(0)
{
}
PipeLiteExecutor::~PipeLiteExecutor()
{
while (!mPGExecutors.empty()) {
ExecutorUnit& unit = mPGExecutors.back();
if (unit.pg.get()) {
unit.pg->deInit();
}
mPGExecutors.pop_back();
}
releaseBuffers();
}
int PipeLiteExecutor::initPipe()
{
LOG1("@%s:%s", __func__, getName());
CheckError(mGraphConfig == nullptr, BAD_VALUE, "%s, the graph config is NULL, BUG!", __func__);
NodesPtrVector programGroups;
vector<IGraphType::PipelineConnection> connVector;
int ret = mGraphConfig->pipelineGetConnections(mPGNames, &connVector);
CheckError(connVector.empty(), ret, "Failed to get connections for executor:%s", mName.c_str());
ret = createPGs();
CheckError(ret != OK, ret, "Failed to create PGs for executor: %s", ret, mName.c_str());
ret = analyzeConnections(connVector);
CheckError(ret != OK, ret, "Failed to analyze connections for executor: %s", ret, mName.c_str());
ret = configurePGs();
CheckError(ret != OK, ret, "Failed to configure connections for executor: %s", ret, mName.c_str());
assignDefaultPortsForTerminals();
return OK;
}
int PipeLiteExecutor::analyzeConnections(const vector<IGraphType::PipelineConnection>& connVector)
{
ia_uid firstStageId = mPGExecutors.front().stageId;
ia_uid lastStageId = mPGExecutors.back().stageId;
for (auto const& connection : connVector) {
LOG2("%s: terminal %d (%d): %dx%d, 0x%x", getName(),
connection.portFormatSettings.terminalId, connection.portFormatSettings.enabled,
connection.portFormatSettings.width, connection.portFormatSettings.height,
connection.portFormatSettings.fourcc);
LOG2("%s: connection source %d, %d, %d, has edge %d", getName(),
connection.connectionConfig.mSourceStage, connection.connectionConfig.mSourceTerminal,
connection.connectionConfig.mSourceIteration, connection.hasEdgePort);
LOG2("%s: connection sink %d, %d, %d, type %d", getName(),
connection.connectionConfig.mSinkStage, connection.connectionConfig.mSinkTerminal,
connection.connectionConfig.mSinkIteration, connection.connectionConfig.mConnectionType);
storeTerminalInfo(connection);
if (connection.portFormatSettings.enabled == 0) {
// No actions are needed for the disabled connections.
continue;
}
// If the connection's sink stage is same as the first stage/pg id in this executor,
// then it means the connection belongs to input terminal pairs.
if (connection.connectionConfig.mSinkStage == firstStageId && connection.hasEdgePort) {
mIsInputEdge = true;
}
// If the connection's source stage is same as the last stage/pg id in this executor,
// then it means the connection belongs to output terminal pairs.
// SIS is output terminal but it doesn't belong to any stream, so it is not real edge output.
if (connection.connectionConfig.mSourceStage == lastStageId
&& connection.hasEdgePort
&& connection.connectionConfig.mSourceTerminal != connection.connectionConfig.mSinkTerminal) {
mIsOutputEdge = true;
}
}
return OK;
}
int PipeLiteExecutor::storeTerminalInfo(const IGraphType::PipelineConnection& connection)
{
FrameInfo info;
info.mWidth = connection.portFormatSettings.width;
info.mHeight = connection.portFormatSettings.height;
info.mFormat = connection.portFormatSettings.fourcc;
ia_uid curTerminal = connection.portFormatSettings.terminalId;
ia_uid sinkTerminal = connection.connectionConfig.mSinkTerminal;
ia_uid sourceTerminal = connection.connectionConfig.mSourceTerminal;
ia_uid sinkStage = connection.connectionConfig.mSinkStage;
ia_uid sourceStage = connection.connectionConfig.mSourceStage;
TerminalDescriptor desc;
desc.terminal = 0;
desc.stageId = 0;
desc.sinkTerminal = sinkTerminal;
desc.sourceTerminal = sourceTerminal;
desc.sinkStage = sinkStage;
desc.sourceStage = sourceStage;
desc.frameDesc = info;
desc.enabled = true;
desc.hasConnection = true;
desc.assignedPort = INVALID_PORT;
desc.usrStreamId = connection.stream ? connection.stream->streamId() : -1;
if (connection.portFormatSettings.enabled) {
mConnectionMap[sinkTerminal]= sourceTerminal;
}
// Check if there is new input terminal
if (sinkStage && mTerminalsDesc.find(sinkTerminal) == mTerminalsDesc.end()) {
ExecutorUnit* unit = findPGExecutor(sinkStage);
if (unit) {
desc.terminal = sinkTerminal;
desc.stageId = sinkStage;
mTerminalsDesc[desc.terminal] = desc;
unit->inputTerminals.push_back(desc.terminal);
}
}
// Check if there is new output terminal
if (sourceStage && mTerminalsDesc.find(sourceTerminal) == mTerminalsDesc.end()) {
ExecutorUnit* unit = findPGExecutor(sourceStage);
if (unit) {
desc.terminal = sourceTerminal;
desc.stageId = sourceStage;
desc.hasConnection = (sinkTerminal != sourceTerminal);
mTerminalsDesc[desc.terminal] = desc;
unit->outputTerminals.push_back(desc.terminal);
}
}
if (mTerminalsDesc.find(curTerminal) != mTerminalsDesc.end()) {
mTerminalsDesc[curTerminal].enabled = connection.portFormatSettings.enabled;
}
return OK;
}
int PipeLiteExecutor::createPGs()
{
for (auto const& pgName : mPGNames) {
int pgId = mGraphConfig->getPgIdByPgName(pgName);
CheckError(pgId == -1, BAD_VALUE, "Cannot get PG ID for %s", pgName.c_str());
ExecutorUnit pgUnit;
pgUnit.pgId = pgId;
pgUnit.stageId = psys_2600_pg_uid(pgId);
pgUnit.pg = std::shared_ptr<PGCommon>(new PGCommon(pgId, pgName, pgUnit.stageId + 1));
// Please refer to ia_cipf_css.h for terminalBaseUid
pgUnit.pg->setShareReferPool(mShareReferPool);
mPGExecutors.push_back(pgUnit);
int ret = pgUnit.pg->init();
CheckError(ret != OK, UNKNOWN_ERROR, "create PG %d error", pgId);
}
return OK;
}
int PipeLiteExecutor::configurePGs()
{
mkernelsCountWithStats = 0;
for (auto &unit : mPGExecutors) {
map<ia_uid, FrameInfo> inputInfos;
map<ia_uid, FrameInfo> outputInfos;
vector<ia_uid> disabledTerminals;
getTerminalFrameInfos(unit.inputTerminals, inputInfos);
getTerminalFrameInfos(unit.outputTerminals, outputInfos);
getDisabledTerminalsForPG(unit.stageId, disabledTerminals);
unit.pg->setInputInfo(inputInfos);
unit.pg->setOutputInfo(outputInfos);
unit.pg->setDisabledTerminals(disabledTerminals);
IGraphType::StageAttr stageAttr;
if (mGraphConfig->getPgRbmValue(unit.pg->getName(), &stageAttr) == OK) {
LOG1("%s: Set rbm for pgId %d, pgName: %s bytes %d",
__func__, unit.pgId, unit.pg->getName(), stageAttr.rbm_bytes);
unit.pg->setRoutingBitmap(stageAttr.rbm, stageAttr.rbm_bytes);
}
unit.pg->prepare(mAdaptor, mStreamId);
int statsCount = getStatKernels(unit.pgId, unit.statKernelUids);
mkernelsCountWithStats += statsCount;
statsCount = getSisKernels(unit.pgId, unit.sisKernelUids);
mkernelsCountWithStats += statsCount;
}
return OK;
}
/**
* Assign ports for terminals as internal default value
* Input ports may be overwritten with output ports of producer in setInputTerminals()
*/
int PipeLiteExecutor::assignDefaultPortsForTerminals()
{
Port portTable[] = {MAIN_PORT, SECOND_PORT, THIRD_PORT, FORTH_PORT, INVALID_PORT};
for (auto &unit : mPGExecutors) {
int outPortIndex = 0;
for (auto terminal : unit.outputTerminals) {
TerminalDescriptor& termDesc = mTerminalsDesc[terminal];
if (termDesc.enabled && termDesc.hasConnection) {
CheckError(portTable[outPortIndex] == INVALID_PORT, BAD_VALUE,
"Port unavailable for output term %d:%d", unit.pgId, terminal);
termDesc.assignedPort = portTable[outPortIndex];
outPortIndex++;
}
}
int inPortIndex = 0;
for (auto terminal : unit.inputTerminals) {
TerminalDescriptor& termDesc = mTerminalsDesc[terminal];
if (termDesc.enabled && termDesc.hasConnection) {
CheckError(portTable[inPortIndex] == INVALID_PORT, BAD_VALUE,
"Port unavailable for input term %d", terminal);
termDesc.assignedPort = portTable[inPortIndex];
inPortIndex++;
}
}
}
return OK;
}
void PipeLiteExecutor::getOutputTerminalPorts(std::map<ia_uid, Port>& terminals) const
{
getTerminalPorts(mPGExecutors.back().outputTerminals, terminals);
}
void PipeLiteExecutor::getInputTerminalPorts(std::map<ia_uid, Port>& terminals) const
{
getTerminalPorts(mPGExecutors.front().inputTerminals, terminals);
}
int PipeLiteExecutor::setInputTerminals(const std::map<ia_uid, Port>& sourceTerminals)
{
// In edge PGs accepts input ports arrangement from external
ExecutorUnit& inUnit = mPGExecutors.front();
for (auto sinkTerminal : inUnit.inputTerminals) {
if (mConnectionMap.find(sinkTerminal) == mConnectionMap.end()) {
continue;
}
ia_uid sourceTerminal = mConnectionMap[sinkTerminal];
if (sourceTerminals.find(sourceTerminal) != sourceTerminals.end()) {
mTerminalsDesc[sinkTerminal].assignedPort = sourceTerminals.at(sourceTerminal);
LOG2("pg %s get external %d -> input %d, port %d", getName(),
sourceTerminal, sinkTerminal, mTerminalsDesc[sinkTerminal].assignedPort);
}
}
// Link internal PGs (sink PG accepts input ports arrangement from source PG (output ports)
// source PG(output ports) -> (input ports)sink PG
for (unsigned int i = 1; i < mPGExecutors.size(); i++) {
for (auto sinkTerminal : mPGExecutors[i].inputTerminals) {
if (!mTerminalsDesc[sinkTerminal].enabled) {
continue;
}
if (mConnectionMap.find(sinkTerminal) != mConnectionMap.end()) {
ia_uid sourceTerminal = mConnectionMap[sinkTerminal];
mTerminalsDesc[sinkTerminal].assignedPort = mTerminalsDesc[sourceTerminal].assignedPort;
}
}
}
// Set frame info to BufferQueue
map<Port, stream_t> inputInfo;
map<Port, stream_t> outputInfo;
ExecutorUnit& outUnit = mPGExecutors.back();
for (auto terminal : inUnit.inputTerminals) {
if (mTerminalsDesc[terminal].assignedPort == INVALID_PORT) {
continue;
}
stream_t inputConfig;
CLEAR(inputConfig);
inputConfig.width = mTerminalsDesc[terminal].frameDesc.mWidth;
inputConfig.height = mTerminalsDesc[terminal].frameDesc.mHeight;
inputConfig.format = mTerminalsDesc[terminal].frameDesc.mFormat;
inputConfig.id = mTerminalsDesc[terminal].usrStreamId;
inputInfo[mTerminalsDesc[terminal].assignedPort] = inputConfig;
}
for (auto terminal : outUnit.outputTerminals) {
if (mTerminalsDesc[terminal].assignedPort == INVALID_PORT) {
continue;
}
stream_t outputConfig;
CLEAR(outputConfig);
outputConfig.width = mTerminalsDesc[terminal].frameDesc.mWidth;
outputConfig.height = mTerminalsDesc[terminal].frameDesc.mHeight;
outputConfig.format = mTerminalsDesc[terminal].frameDesc.mFormat;
outputConfig.id = mTerminalsDesc[terminal].usrStreamId;
outputInfo[mTerminalsDesc[terminal].assignedPort] = outputConfig;
}
BufferQueue::setFrameInfo(inputInfo, outputInfo);
return OK;
}
int PipeLiteExecutor::start()
{
LOG1("%s executor:%s", __func__, mName.c_str());
mProcessThread = new ProcessThread(this);
AutoMutex l(mBufferQueueLock);
allocBuffers();
dumpPGs();
mLastStatsSequence = -1;
mThreadRunning = true;
mProcessThread->run(mName.c_str(), PRIORITY_NORMAL);
return OK;
}
void PipeLiteExecutor::stop()
{
LOG1("%s executor:%s", __func__, mName.c_str());
mProcessThread->requestExitAndWait();
// Thread is not running. It is safe to clear the Queue
clearBufferQueues();
delete mProcessThread;
}
void PipeLiteExecutor::notifyStop()
{
LOG1("%s executor:%s", __func__, mName.c_str());
mProcessThread->requestExit();
{
AutoMutex l(mBufferQueueLock);
mThreadRunning = false;
// Wakeup the thread to exit
mFrameAvailableSignal.signal();
mOutputAvailableSignal.signal();
}
}
int PipeLiteExecutor::releaseStatsBuffer(const shared_ptr<CameraBuffer> &statsBuf)
{
LOG3A("%s executor:%s", __func__, mName.c_str());
AutoMutex lock(mStatsBuffersLock);
mStatsBuffers.push(statsBuf);
return OK;
}
bool PipeLiteExecutor::hasOutputTerminal(ia_uid sinkTerminal)
{
if (mConnectionMap.find(sinkTerminal) == mConnectionMap.end()) {
return false;
}
ExecutorUnit& unit = mPGExecutors.back();
for (auto sourceTerminal : unit.outputTerminals) {
if (mConnectionMap[sinkTerminal] == sourceTerminal) {
return true;
}
}
return false;
}
int PipeLiteExecutor::getStatKernels(int pgId, vector<ia_uid>& kernels)
{
kernels.clear();
for (unsigned int i = 0; i < ARRAY_SIZE(sStatKernels); i++) {
int pgIdOfKernel = -1;
int status = mGraphConfig->getPgIdForKernel(mStreamId, sStatKernels[i], &pgIdOfKernel);
if (status == OK && pgIdOfKernel == pgId) {
kernels.push_back(sStatKernels[i]);
}
}
LOG1("pg %d has %d stat kernels", pgId, kernels.size());
return kernels.size();
}
int PipeLiteExecutor::getSisKernels(int pgId, vector<ia_uid>& kernels)
{
kernels.clear();
for (unsigned int i = 0; i < ARRAY_SIZE(sSisKernels); i++) {
int pgIdOfKernel = -1;
int status = mGraphConfig->getPgIdForKernel(mStreamId, sSisKernels[i], &pgIdOfKernel);
if (status == OK && pgIdOfKernel == pgId) {
kernels.push_back(sSisKernels[i]);
}
}
LOG1("pg %d has %d sis kernels", pgId, kernels.size());
return kernels.size();
}
bool PipeLiteExecutor::isSameStreamConfig(const stream_t& internal, const stream_t& external,
ConfigMode configMode, bool checkStreamId) const
{
// The internal format is ia_fourcc based format, so need to convert it to V4L2 format.
int internalFormat = graphconfig::utils::getV4L2Format(internal.format);
int internalStride = CameraUtils::getStride(internalFormat, internal.width);
int externalStride = CameraUtils::getStride(external.format, external.width);
LOG1("%s: %s, id:%d, internal: %s(%dx%d: %d)(id %d), external: %s(%dx%d: %d) (id %d) usage:%d",
__func__, mName.c_str(), mStreamId,
CameraUtils::format2string(internalFormat).c_str(),
internal.width, internal.height, internalStride, internal.id,
CameraUtils::format2string(external.format).c_str(),
external.width, external.height, externalStride, external.id, external.usage);
if (checkStreamId && internal.id >= 0) {
return internal.id == external.id;
}
/*
* WA: PG accept GRBG format but actual input data is of RGGB format,
* PG use its kernel to crop to GRBG
*/
if ((internalFormat == V4L2_PIX_FMT_SGRBG10 || internalFormat == V4L2_PIX_FMT_SGRBG12)
&& (external.format == V4L2_PIX_FMT_SRGGB10 || external.format == V4L2_PIX_FMT_SRGGB12)) {
return true;
}
bool sameHeight = internal.height == external.height ||
internal.height == ALIGN_32(external.height);
if (internalFormat == external.format && sameHeight &&
(internal.width == external.width || internalStride == externalStride)) {
return true;
}
return false;
}
/**
* Check if there is any valid buffer(not null) in the given port/buffer pairs.
*
* return true if there is at least one not null buffer.
*/
bool PipeLiteExecutor::hasValidBuffers(const CameraBufferPortMap& buffers)
{
for (const auto& item : buffers) {
if (item.second) return true;
}
return false;
}
int PipeLiteExecutor::processNewFrame()
{
PERF_CAMERA_ATRACE();
int ret = OK;
CameraBufferPortMap inBuffers, outBuffers;
// Wait frame buffers.
{
ConditionLock lock(mBufferQueueLock);
ret = waitFreeBuffersInQueue(lock, inBuffers, outBuffers);
// Already stopped
if (!mThreadRunning) return -1;
if (ret != OK) return OK; // Wait frame buffer error should not involve thread exit.
CheckError(inBuffers.empty() || outBuffers.empty(),
UNKNOWN_ERROR, "Failed to get input or output buffers.");
for (auto& output: mOutputQueue) {
output.second.pop();
}
for (auto& input: mInputQueue) {
input.second.pop();
}
}
// Check if the executor needs to run the actual pipeline.
// It only needs to run when there is at least one valid output buffer.
if (!hasValidBuffers(outBuffers)) {
// Return buffers if the executor is NOT an input edge.
if (!mIsInputEdge) {
for (const auto& item : inBuffers) {
mBufferProducer->qbuf(item.first, item.second);
}
}
return OK;
}
// Fill real buffer to run pipe
for (auto &item : outBuffers) {
if (item.second.get() == nullptr) {
item.second = mInternalOutputBuffers[item.first];
}
}
vector<shared_ptr<CameraBuffer>> outStatsBuffers;
vector<EventType> eventType;
// Should find first not none input buffer instead of always use the first one.
shared_ptr<CameraBuffer> inBuf = inBuffers.begin()->second;
CheckError(!inBuf, UNKNOWN_ERROR, "@%s: no valid input buffer", __func__);
long inBufSequence = inBuf->getSequence();
v4l2_buffer_t inV4l2Buf = *inBuf->getV4L2Buffer().Get();
TuningMode tuningMode = mPSysDag->getTuningMode(inBufSequence);
LOG2("%s:Id:%d run pipe start for buffer:%ld", mName.c_str(), mCameraId, inBufSequence);
if (PlatformData::isEnableFrameSyncCheck(mCameraId)) {
shared_ptr<CameraBuffer> cInBuffer = inBuffers[MAIN_PORT];
int vc = cInBuffer->getVirtualChannel();
while ((!SyncManager::getInstance()->vcSynced(vc)) && mThreadRunning)
usleep(1);
if (gLogLevel & CAMERA_DEBUG_LOG_VC_SYNC) {
int seq = cInBuffer->getSequence();
SyncManager::getInstance()->printVcSyncCount();
LOGVCSYNC("[start runPipe], CPU-timestamp:%lu, sequence:%d, vc:%d, kernel-timestamp:%.3lfms, endl",
CameraUtils::systemTime(),
seq,
cInBuffer->getVirtualChannel(),
cInBuffer->getTimestamp().tv_sec*1000.0 + cInBuffer->getTimestamp().tv_usec/1000.0);
}
SyncManager::getInstance()->updateVcSyncCount(vc);
// Run pipe with buffers
ret = runPipe(inBuffers, outBuffers, outStatsBuffers, eventType);
LOGVCSYNC("[done runPipe], CPU-timestamp:%lu, sequence:%ld, vc:%d, kernel-timestamp:%.3lfms, endl",
CameraUtils::systemTime(),
cInBuffer->getSequence(),
cInBuffer->getVirtualChannel(),
cInBuffer->getTimestamp().tv_sec*1000.0 + cInBuffer->getTimestamp().tv_usec/1000.0);
} else {
// Run pipe with buffers
ret = runPipe(inBuffers, outBuffers, outStatsBuffers, eventType);
}
CheckError((ret != OK), UNKNOWN_ERROR, "@%s: failed to run pipe", __func__);
LOG2("%s:Id:%d run pipe end for buffer:%ld", mName.c_str(), mCameraId, inBufSequence);
// Remove internal output buffers
for (auto &item : outBuffers) {
if (item.second.get() == mInternalOutputBuffers[item.first].get()) {
item.second = nullptr;
}
}
if (mNotifyPolicy == POLICY_FRAME_FIRST) {
// For general case, notify frame prior to stats to make sure its consumers can get
// the frame buffers as early as possible.
notifyFrameDone(inV4l2Buf, outBuffers);
notifyStatsDone(tuningMode, inV4l2Buf, outStatsBuffers, eventType);
} else if (mNotifyPolicy == POLICY_STATS_FIRST) {
// Notify stats first and then handle frame buffers to make sure the next executor
// can get this executor's IQ result.
notifyStatsDone(tuningMode, inV4l2Buf, outStatsBuffers, eventType);
// After the stats notified, we need to update the IPU parameters as well to get the
// latest AIQ result.
mPSysDag->prepareIpuParams(inBufSequence, true);
notifyFrameDone(inV4l2Buf, outBuffers);
} else {
LOGW("Invalid notify policy:%d, should never happen.", mNotifyPolicy);
}
// Return buffers for the executor which is NOT an input edge
if (!mIsInputEdge) {
for (auto const& portBufferPair : inBuffers) {
// Queue buffer to producer
mBufferProducer->qbuf(portBufferPair.first, portBufferPair.second);
}
}
return OK;
}
int PipeLiteExecutor::registerInBuffers(Port port, const shared_ptr<CameraBuffer> &inBuf)
{
return OK;
}
int PipeLiteExecutor::registerOutBuffers(Port port, const shared_ptr<CameraBuffer> &camBuffer)
{
return OK;
}
int PipeLiteExecutor::runPipe(map<Port, shared_ptr<CameraBuffer> > &inBuffers,
map<Port, shared_ptr<CameraBuffer> > &outBuffers,
vector<shared_ptr<CameraBuffer> > &outStatsBuffers,
vector<EventType> &eventType)
{
PERF_CAMERA_ATRACE();
CheckError((inBuffers.empty() || outBuffers.empty()), BAD_VALUE,
"Error in pipe iteration input/output bufs");
int ret = OK;
if (mPolicyManager) {
// Check if need to wait other executors.
ret = mPolicyManager->wait(mName);
}
// Accept external buffers for in/out edge PGs
getTerminalBuffersFromExternal(mPGExecutors.front().inputTerminals, inBuffers,
mPGExecutors.front().inputBuffers);
getTerminalBuffersFromExternal(mPGExecutors.back().outputTerminals, outBuffers,
mPGExecutors.back().outputBuffers);
// Get ISP parameters
const ia_binary_data *ipuParameters = nullptr;
long sequence = inBuffers.begin()->second ? inBuffers.begin()->second->getSequence() : -1;
TRACE_LOG_PROCESS(mName.c_str(), "runPipe", MAKE_COLOR(sequence), sequence);
if (mAdaptor) {
ipuParameters = mAdaptor->getIpuParameter(sequence, mStreamId);
if (!ipuParameters) {
LOG1("%s: executor %s doesn't run for sequence %ld due to no pal",
__func__, mName.c_str(), sequence);
return OK;
}
}
LOG2("%s: Executor %s run with input: %zu, output: %zu, sequence: %ld",
__func__, mName.c_str(), inBuffers.size(), outBuffers.size(), sequence);
outStatsBuffers.clear();
eventType.clear();
int statTotalNum = 0;
for (unsigned int pgIndex = 0; pgIndex < mPGExecutors.size(); pgIndex++) {
ExecutorUnit& unit = mPGExecutors[pgIndex];
// Prepare stats buffers for 3A/sis
vector<ia_binary_data*> pgStatsDatas;
// For 3A stats
unsigned int statsCount = unit.statKernelUids.size();
for (unsigned int counter = 0; counter < statsCount; counter++) {
if (mStatsBuffers.empty()) {
LOGW("No available stats buffer.");
break;
}
outStatsBuffers.push_back(mStatsBuffers.front());
eventType.push_back(EVENT_PSYS_STATS_BUF_READY);
ia_binary_data* buffer = (ia_binary_data*)mStatsBuffers.front()->getBufferAddr();
CheckError(buffer == nullptr, BAD_VALUE, "buffer is null pointer.");
buffer->size = 0; // Clear it, then the stats memory is from p2p
buffer->data = 0;
pgStatsDatas.push_back(buffer);
mStatsBuffers.pop();
}
unsigned int sisCount = unit.sisKernelUids.size();
for (unsigned int counter = 0; counter < sisCount; counter++) {
if (mStatsBuffers.empty()) {
LOGW("No available stats buffer.");
break;
}
outStatsBuffers.push_back(mStatsBuffers.front());
eventType.push_back(EVENT_PSYS_STATS_SIS_BUF_READY);
ia_binary_data* buffer = (ia_binary_data*)mStatsBuffers.front()->getBufferAddr();
pgStatsDatas.push_back(buffer);
mStatsBuffers.pop();
}
// Run PGs
// Update sequence only for the 1st input buffer currently
unit.inputBuffers.begin()->second->setSequence(sequence);
ret = unit.pg->iterate(unit.inputBuffers,
unit.outputBuffers,
(statsCount > 0) ? pgStatsDatas[0] : nullptr, // Currently PG handles one stats buffer only
ipuParameters);
CheckError((ret != OK), ret, "%s: error in pipe iteration with %d", mName.c_str(), ret);
statTotalNum += statsCount;
if (sisCount > 0) {
handleSisStats(unit.outputBuffers, outStatsBuffers[statTotalNum]); // Currently handle one sis output only
}
statTotalNum += sisCount;
}
return OK;
}
int PipeLiteExecutor::handleSisStats(map<ia_uid, shared_ptr<CameraBuffer>>& frameBuffers, const shared_ptr<CameraBuffer> &outStatsBuffers)
{
LOG2("%s:", __func__);
ia_binary_data* statBuf = (ia_binary_data*)outStatsBuffers->getBufferAddr();
CheckError((statBuf == nullptr), BAD_VALUE, "Error getting buffer for sis a stats");
statBuf->data = nullptr;
statBuf->size = 0;
for (auto iterm : frameBuffers) {
ia_uid uid = iterm.first;
if (uid == psys_ipu6_isa_lb_output_sis_a_uid) {
statBuf->data = iterm.second->getBufferAddr();
statBuf->size = iterm.second->getBufferSize();
outStatsBuffers->setUserBufferInfo(-1, iterm.second->getWidth(), iterm.second->getHeight());
return OK;
}
}
return UNKNOWN_ERROR;
}
int PipeLiteExecutor::notifyFrameDone(const v4l2_buffer_t& inV4l2Buf, const CameraBufferPortMap& outBuf)
{
PERF_CAMERA_ATRACE();
for (auto const& portBufferPair : outBuf) {
shared_ptr<CameraBuffer> outBuf = portBufferPair.second;
Port port = portBufferPair.first;
// If the output buffer is nullptr, that means user doesn't request that buffer,
// so it doesn't need to be handled here.
if (!outBuf) continue;
outBuf->updateV4l2Buffer(inV4l2Buf);
// If it's output edge, the buffer should be returned to PSysDag,
// otherwise they should be returned to its consumer.
if (mIsOutputEdge) {
mPSysDag->onFrameDone(port, outBuf);
} else {
for (auto &it : mBufferConsumerList) {
it->onFrameAvailable(port, outBuf);
}
}
}
return OK;
}
int PipeLiteExecutor::notifyStatsDone(TuningMode tuningMode,
const v4l2_buffer_t& inV4l2Buf,
const vector<shared_ptr<CameraBuffer>> &outStatsBuffers,
const vector<EventType> &eventType)
{
PERF_CAMERA_ATRACE();
// The executor does not produce stats, so no need to notify.
if (outStatsBuffers.empty()) return OK;
/**
* Notice for EVENT_PSYS_STATS_BUF_READY:
* dvs stat & 3a stat come from different PG, and they are decoded separately
* in decodeStatsData().
* Fortunately stats data are stored in aiqResultStorage separately,
* and user will get them from storage instead of EventData.
* So here we can send one event after all stat buffers are decoded/stored/released.
*/
int psysStatBufferCount = 0;
for (auto type : eventType) {
if (type == EVENT_PSYS_STATS_BUF_READY) {
psysStatBufferCount++;
}
}
int statsIndex = 0;
for (auto statsBuf : outStatsBuffers) {
if (!statsBuf) continue;
if (mStreamId == STILL_STREAM_ID) {
LOG2("%s: No statistics data for still pipe in buffer", __func__);
releaseStatsBuffer(statsBuf);
continue;
} else if (inV4l2Buf.sequence <= mLastStatsSequence) {
// Ignore old statistics for Raw reprocessing
LOG2("%s: new sequence %d is less than last sequence %ld", __func__,
inV4l2Buf.sequence, mLastStatsSequence);
releaseStatsBuffer(statsBuf);
continue;
}
ia_binary_data *hwStatsData = (ia_binary_data *)(statsBuf->getBufferAddr());
if (hwStatsData->data == nullptr || hwStatsData->size == 0) {
LOGW("%s: No statistics data in buffer", __func__);
releaseStatsBuffer(statsBuf);
continue;
}
statsBuf->updateV4l2Buffer(inV4l2Buf);
// Decode the statistics data
if (eventType[statsIndex] == EVENT_PSYS_STATS_BUF_READY) {
mAdaptor->decodeStatsData(tuningMode, statsBuf, mGraphConfig);
psysStatBufferCount--;
}
// Notify listeners after all buffers done for type STATS_BUF_READY
// Notify immediately for other types
if (eventType[statsIndex] != EVENT_PSYS_STATS_BUF_READY
|| !psysStatBufferCount) {
EventDataStatsReady statsReadyData;
statsReadyData.sequence = statsBuf->getSequence();
statsReadyData.timestamp.tv_sec = statsBuf->getTimestamp().tv_sec;
statsReadyData.timestamp.tv_usec = statsBuf->getTimestamp().tv_usec;
EventData eventData;
eventData.type = eventType[statsIndex];
eventData.buffer = statsBuf;
eventData.data.statsReady = statsReadyData;
notifyListeners(eventData);
}
releaseStatsBuffer(statsBuf);
statsIndex++;
}
if (mStreamId == VIDEO_STREAM_ID && inV4l2Buf.sequence > mLastStatsSequence) {
mLastStatsSequence = inV4l2Buf.sequence;
}
return OK;
}
int PipeLiteExecutor::allocBuffers()
{
LOG1("%s executor:%s", __func__, mName.c_str());
releaseBuffers();
// Allocate buffer between PGs (internal)
for (auto const& item : mTerminalsDesc) {
const TerminalDescriptor& termDesc = item.second;
if (!termDesc.enabled) {
continue;
}
if (termDesc.assignedPort != INVALID_PORT
&& !(findPGExecutor(termDesc.sinkStage) && findPGExecutor(termDesc.sourceStage))) {
// Don't allocate buffer here for external connection (has valid port)
continue;
}
// Allocated already
if (mPGBuffers.find(termDesc.terminal) != mPGBuffers.end()) {
continue;
}
int srcFmt = termDesc.frameDesc.mFormat;
int srcWidth = termDesc.frameDesc.mWidth;
int srcHeight = termDesc.frameDesc.mHeight;
int size = PGCommon::getFrameSize(srcFmt, srcWidth, srcHeight, true);
shared_ptr<CameraBuffer> buf = CameraBuffer::create(mCameraId,
BUFFER_USAGE_PSYS_INPUT, V4L2_MEMORY_USERPTR, size, 0, srcFmt, srcWidth, srcHeight);
CheckError(!buf, NO_MEMORY, "@%s: Allocate producer buffer failed", __func__);
mPGBuffers[termDesc.sinkTerminal] = buf;
mPGBuffers[termDesc.sourceTerminal] = buf;
}
for (auto &unit : mPGExecutors) {
// Assign internal buffers for terminals of PGs according to connection
for (auto &terminal : unit.inputTerminals) {
if (mPGBuffers.find(terminal) != mPGBuffers.end()) {
unit.inputBuffers[terminal] = mPGBuffers[terminal];
}
}
for (auto &terminal : unit.outputTerminals) {
if (mPGBuffers.find(terminal) != mPGBuffers.end()) {
unit.outputBuffers[terminal] = mPGBuffers[terminal];
}
}
// Allocate stats buffers if needed.
unsigned int statsBufferCount = unit.statKernelUids.size();
if (!statsBufferCount) {
continue;
}
for (unsigned int i = 0; i < MAX_BUFFER_COUNT * statsBufferCount; i++) {
shared_ptr<CameraBuffer> statsBuf = CameraBuffer::create(mCameraId,
BUFFER_USAGE_PSYS_STATS, V4L2_MEMORY_USERPTR, sizeof(ia_binary_data), i);
CheckError(!statsBuf, NO_MEMORY, "Executor %s: Allocate stats buffer failed", mName.c_str());
AutoMutex lock(mStatsBuffersLock);
mStatsBuffers.push(statsBuf);
}
}
// Allocate buffers for producer executor (external)
// Ignore input edge due to no producer
if (!mIsInputEdge) {
for (auto const& terminal : mPGExecutors.front().inputTerminals) {
Port inputPort = mTerminalsDesc[terminal].assignedPort;
int srcFmt = mTerminalsDesc[terminal].frameDesc.mFormat;
int srcWidth = mTerminalsDesc[terminal].frameDesc.mWidth;
int srcHeight = mTerminalsDesc[terminal].frameDesc.mHeight;
// Get frame size with aligned height taking in count for internal buffers.
// To garantee PSYS kernel like GDC always get enough buffer size to process.
int size = PGCommon::getFrameSize(srcFmt, srcWidth, srcHeight, true);
for (int i = 0; i < MAX_BUFFER_COUNT; i++) {
// Prepare internal frame buffer for its producer.
shared_ptr<CameraBuffer> buf = CameraBuffer::create(mCameraId,
BUFFER_USAGE_PSYS_INPUT, V4L2_MEMORY_USERPTR, size, i, srcFmt, srcWidth, srcHeight);
CheckError(!buf, NO_MEMORY, "@%s: Allocate producer buffer failed", __func__);
mInternalBuffers[inputPort].push_back(buf);
mBufferProducer->qbuf(inputPort, buf);
}
}
}
// Allocate internal output buffers to support pipe execution without user output buffer
for (auto const &item : mOutputFrameInfo) {
int fmt = item.second.format;
int width = item.second.width;
int height = item.second.height;
int size = CameraUtils::getFrameSize(fmt, width, height, true);
shared_ptr<CameraBuffer> buf = CameraBuffer::create(mCameraId,
BUFFER_USAGE_PSYS_INPUT, V4L2_MEMORY_USERPTR, size, 0, fmt, width, height);
CheckError(!buf, NO_MEMORY, "@%s: Allocate internal output buffer failed", __func__);
mInternalOutputBuffers[item.first]= buf;
}
return OK;
}
void PipeLiteExecutor::releaseBuffers()
{
LOG1("%s executor:%s", __func__, mName.c_str());
// Release internel frame buffers
mInternalOutputBuffers.clear();
mInternalBuffers.clear();
mPGBuffers.clear();
// Release stats buffers
{
AutoMutex lock(mStatsBuffersLock);
while (!mStatsBuffers.empty()) mStatsBuffers.pop();
}
}
PipeLiteExecutor::ExecutorUnit* PipeLiteExecutor::findPGExecutor(ia_uid stageId)
{
for (unsigned int i = 0; i < mPGExecutors.size(); i++) {
if (mPGExecutors[i].stageId == stageId) {
return &mPGExecutors[i];
}
}
return nullptr;
}
void PipeLiteExecutor::getTerminalPorts(const vector<ia_uid>& terminals,
map<ia_uid, Port>& terminalPortMap) const
{
terminalPortMap.clear();
for (auto terminal : terminals) {
const TerminalDescriptor& termDesc = mTerminalsDesc.at(terminal);
if (termDesc.enabled && termDesc.assignedPort != INVALID_PORT) {
terminalPortMap[terminal] = termDesc.assignedPort;
}
}
}
void PipeLiteExecutor::getTerminalFrameInfos(const vector<ia_uid>& terminals,
map<ia_uid, FrameInfo>& infoMap) const
{
infoMap.clear();
for (auto terminal : terminals) {
const TerminalDescriptor& termDesc = mTerminalsDesc.at(terminal);
if (termDesc.enabled) {
infoMap[terminal] = termDesc.frameDesc;
}
}
}
void PipeLiteExecutor::getDisabledTerminalsForPG(ia_uid stageId, vector<ia_uid>& terminals) const
{
terminals.clear();
for (auto const item : mTerminalsDesc) {
const TerminalDescriptor& termDesc = item.second;
if (termDesc.stageId == stageId && !termDesc.enabled) {
terminals.push_back(termDesc.terminal);
}
}
}
void PipeLiteExecutor::getTerminalBuffersFromExternal(
const vector<ia_uid>& terminals,
const map<Port, shared_ptr<CameraBuffer> >& externals,
map<ia_uid, shared_ptr<CameraBuffer> >& internals) const
{
for (auto &terminal : terminals) {
Port port = mTerminalsDesc.at(terminal).assignedPort;
if (externals.find(port) != externals.end()) {
internals[terminal] = externals.at(port);
}
}
}
void PipeLiteExecutor::dumpPGs() const
{
if (!Log::isDebugLevelEnable(CAMERA_DEBUG_LOG_LEVEL2)) return;
LOG2("============= dump PGs for executor %s =================", getName());
if (mIsInputEdge) {
LOG2("This is input edge");
}
if (mIsOutputEdge) {
LOG2("This is output edge");
}
for (auto const &unit : mPGExecutors) {
ia_uid stageId = unit.stageId;
LOG2(" PG: %d: %s, stageId %d",
unit.pgId, unit.pg ? unit.pg->getName() : "GPU-TNR", stageId);
LOG2(" InTerms: %zu", unit.inputTerminals.size());
for (auto const &term : unit.inputTerminals) {
shared_ptr<CameraBuffer> buffer= nullptr;
if (mPGBuffers.find(term) != mPGBuffers.end()) {
buffer = mPGBuffers.at(term);
}
const TerminalDescriptor& termDesc = mTerminalsDesc.at(term);
if (termDesc.enabled) {
LOG2(" %d: %dx%d, 0x%x, port %d, buf %p",
termDesc.terminal - termDesc.stageId - 1,
termDesc.frameDesc.mWidth, termDesc.frameDesc.mHeight,
termDesc.frameDesc.mFormat,
termDesc.assignedPort, buffer.get());
} else {
LOG2(" %d: %dx%d, 0x%x, disabled",
termDesc.terminal - termDesc.stageId - 1,
termDesc.frameDesc.mWidth, termDesc.frameDesc.mHeight,
termDesc.frameDesc.mFormat);
}
}
LOG2(" OutTerms: %zu", unit.outputTerminals.size());
for (auto const &term : unit.outputTerminals) {
shared_ptr<CameraBuffer> buffer= nullptr;
if (mPGBuffers.find(term) != mPGBuffers.end()) {
buffer = mPGBuffers.at(term);
}
const TerminalDescriptor& termDesc = mTerminalsDesc.at(term);
if (termDesc.enabled) {
LOG2(" %d: %dx%d, 0x%x, port %d, buf %p",
termDesc.terminal - termDesc.stageId - 1,
termDesc.frameDesc.mWidth, termDesc.frameDesc.mHeight,
termDesc.frameDesc.mFormat,
termDesc.assignedPort, buffer.get());
} else {
LOG2(" %d: %dx%d, 0x%x, disabled",
termDesc.terminal - termDesc.stageId - 1,
termDesc.frameDesc.mWidth, termDesc.frameDesc.mHeight,
termDesc.frameDesc.mFormat);
}
}
}
LOG2("============= dump done for %s =================", getName());
}
}