333 lines
11 KiB
C++
333 lines
11 KiB
C++
// Copyright 2020 The Android Open Source Project
|
|
//
|
|
// 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.
|
|
#include "PixelStreamManager.h"
|
|
|
|
#include <android-base/logging.h>
|
|
#include <vndk/hardware_buffer.h>
|
|
|
|
#include <algorithm>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
#include "PixelFormatUtils.h"
|
|
|
|
namespace android {
|
|
namespace automotive {
|
|
namespace computepipe {
|
|
namespace runner {
|
|
namespace stream_manager {
|
|
|
|
PixelMemHandle::PixelMemHandle(int bufferId, int streamId, int additionalUsageFlags)
|
|
: mBufferId(bufferId),
|
|
mStreamId(streamId),
|
|
mBuffer(nullptr),
|
|
mUsage(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | additionalUsageFlags) {
|
|
}
|
|
|
|
PixelMemHandle::~PixelMemHandle() {
|
|
if (mBuffer) {
|
|
AHardwareBuffer_release(mBuffer);
|
|
}
|
|
}
|
|
|
|
int PixelMemHandle::getStreamId() const {
|
|
return mStreamId;
|
|
}
|
|
|
|
proto::PacketType PixelMemHandle::getType() const {
|
|
return proto::PacketType::PIXEL_DATA;
|
|
}
|
|
uint64_t PixelMemHandle::getTimeStamp() const {
|
|
return mTimestamp;
|
|
}
|
|
|
|
uint32_t PixelMemHandle::getSize() const {
|
|
return 0;
|
|
}
|
|
|
|
const char* PixelMemHandle::getData() const {
|
|
return nullptr;
|
|
}
|
|
|
|
AHardwareBuffer* PixelMemHandle::getHardwareBuffer() const {
|
|
return mBuffer;
|
|
}
|
|
|
|
/* Sets frame info */
|
|
Status PixelMemHandle::setFrameData(uint64_t timestamp, const InputFrame& inputFrame) {
|
|
// Allocate a new buffer if it is currently null.
|
|
FrameInfo frameInfo = inputFrame.getFrameInfo();
|
|
if (mBuffer == nullptr) {
|
|
mDesc.format = PixelFormatToHardwareBufferFormat(frameInfo.format);
|
|
mDesc.height = frameInfo.height;
|
|
mDesc.width = frameInfo.width;
|
|
mDesc.layers = 1;
|
|
mDesc.rfu0 = 0;
|
|
mDesc.rfu1 = 0;
|
|
mDesc.stride = frameInfo.stride;
|
|
mDesc.usage = mUsage;
|
|
int err = AHardwareBuffer_allocate(&mDesc, &mBuffer);
|
|
|
|
if (err != 0 || mBuffer == nullptr) {
|
|
LOG(ERROR) << "Failed to allocate hardware buffer with error " << err;
|
|
return Status::NO_MEMORY;
|
|
}
|
|
|
|
// Update mDesc with the actual descriptor with which the buffer was created. The actual
|
|
// stride could be different from the specified stride.
|
|
AHardwareBuffer_describe(mBuffer, &mDesc);
|
|
}
|
|
|
|
// Verifies that the input frame data has the same type as the allocated buffer.
|
|
if (frameInfo.width != mDesc.width || frameInfo.height != mDesc.height ||
|
|
PixelFormatToHardwareBufferFormat(frameInfo.format) != mDesc.format) {
|
|
LOG(ERROR) << "Variable image sizes from the same stream id is not supported.";
|
|
return Status::INVALID_ARGUMENT;
|
|
}
|
|
|
|
// Locks the frame for copying the input frame data.
|
|
void* mappedBuffer = nullptr;
|
|
int err = AHardwareBuffer_lock(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr,
|
|
&mappedBuffer);
|
|
if (err != 0 || mappedBuffer == nullptr) {
|
|
LOG(ERROR) << "Unable to lock a realased hardware buffer.";
|
|
return Status::INTERNAL_ERROR;
|
|
}
|
|
|
|
// Copies the input frame data.
|
|
int bytesPerPixel = numBytesPerPixel(static_cast<AHardwareBuffer_Format>(mDesc.format));
|
|
// The stride for hardware buffer is specified in pixels while the stride
|
|
// for InputFrame data structure is specified in bytes.
|
|
if (mDesc.stride * bytesPerPixel == frameInfo.stride) {
|
|
memcpy(mappedBuffer, inputFrame.getFramePtr(), mDesc.stride * mDesc.height * bytesPerPixel);
|
|
} else {
|
|
for (int y = 0; y < frameInfo.height; y++) {
|
|
memcpy((uint8_t*)mappedBuffer + mDesc.stride * y * bytesPerPixel,
|
|
inputFrame.getFramePtr() + y * frameInfo.stride,
|
|
std::min(frameInfo.stride, mDesc.stride * bytesPerPixel));
|
|
}
|
|
}
|
|
|
|
AHardwareBuffer_unlock(mBuffer, nullptr);
|
|
mTimestamp = timestamp;
|
|
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
int PixelMemHandle::getBufferId() const {
|
|
return mBufferId;
|
|
}
|
|
|
|
void PixelStreamManager::setEngineInterface(std::shared_ptr<StreamEngineInterface> engine) {
|
|
std::lock_guard lock(mLock);
|
|
mEngine = engine;
|
|
}
|
|
|
|
Status PixelStreamManager::setMaxInFlightPackets(uint32_t maxPackets) {
|
|
std::lock_guard lock(mLock);
|
|
if (mBuffersInUse.size() > maxPackets) {
|
|
LOG(ERROR) << "Cannot set max in flight packets after graph has already started.";
|
|
return Status::ILLEGAL_STATE;
|
|
}
|
|
|
|
mMaxInFlightPackets = maxPackets;
|
|
std::lock_guard stateLock(mStateLock);
|
|
mState = CONFIG_DONE;
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
Status PixelStreamManager::freePacket(int bufferId) {
|
|
std::lock_guard lock(mLock);
|
|
|
|
auto it = mBuffersInUse.find(bufferId);
|
|
|
|
if (it == mBuffersInUse.end()) {
|
|
std::lock_guard stateLock(mStateLock);
|
|
// If the graph has already been stopped, we free the buffers
|
|
// asynchronously, so return SUCCESS if freePacket is called later.
|
|
if (mState == STOPPED) {
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
LOG(ERROR) << "Unable to find the mem handle. Duplicate release may possible have been "
|
|
"called";
|
|
return Status::INVALID_ARGUMENT;
|
|
}
|
|
|
|
it->second.outstandingRefCount -= 1;
|
|
if (it->second.outstandingRefCount == 0) {
|
|
mBuffersReady.push_back(it->second.handle);
|
|
mBuffersInUse.erase(it);
|
|
}
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
void PixelStreamManager::freeAllPackets() {
|
|
std::lock_guard lock(mLock);
|
|
|
|
for (auto [bufferId, buffer] : mBuffersInUse) {
|
|
mBuffersReady.push_back(buffer.handle);
|
|
}
|
|
mBuffersInUse.clear();
|
|
}
|
|
|
|
Status PixelStreamManager::queuePacket(const char* /*data*/, const uint32_t /*size*/,
|
|
uint64_t /*timestamp*/) {
|
|
LOG(ERROR) << "Trying to queue a semantic packet to a pixel stream manager";
|
|
return Status::ILLEGAL_STATE;
|
|
}
|
|
|
|
Status PixelStreamManager::queuePacket(const InputFrame& frame, uint64_t timestamp) {
|
|
std::lock_guard lock(mLock);
|
|
|
|
// State has to be running for the callback to go back.
|
|
{
|
|
std::lock_guard stateLock(mStateLock);
|
|
if (mState != RUNNING) {
|
|
LOG(ERROR) << "Packet cannot be queued when state is not RUNNING. Current state is"
|
|
<< mState;
|
|
return Status::ILLEGAL_STATE;
|
|
}
|
|
}
|
|
|
|
if (mEngine == nullptr) {
|
|
LOG(ERROR) << "Stream to engine interface is not set";
|
|
return Status::ILLEGAL_STATE;
|
|
}
|
|
|
|
if (mBuffersInUse.size() >= mMaxInFlightPackets) {
|
|
LOG(INFO) << "Too many frames in flight. Skipping frame at timestamp " << timestamp;
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
// A unique id per buffer is maintained by incrementing the unique id from the previously
|
|
// created buffer. The unique id is therefore the number of buffers already created.
|
|
if (mBuffersReady.empty()) {
|
|
mBuffersReady.push_back(std::make_shared<PixelMemHandle>(mBuffersInUse.size(), mStreamId));
|
|
}
|
|
|
|
// The previously used buffer is pushed to the back of the vector. Picking the last used buffer
|
|
// may be more cache efficient if accessing through CPU, so we use that strategy here.
|
|
std::shared_ptr<PixelMemHandle> memHandle = mBuffersReady[mBuffersReady.size() - 1];
|
|
mBuffersReady.resize(mBuffersReady.size() - 1);
|
|
|
|
BufferMetadata bufferMetadata;
|
|
bufferMetadata.outstandingRefCount = 1;
|
|
bufferMetadata.handle = memHandle;
|
|
|
|
mBuffersInUse.emplace(memHandle->getBufferId(), bufferMetadata);
|
|
|
|
Status status = memHandle->setFrameData(timestamp, frame);
|
|
if (status != Status::SUCCESS) {
|
|
LOG(ERROR) << "Setting frame data failed with error code " << status;
|
|
return status;
|
|
}
|
|
|
|
// Dispatch packet to the engine asynchronously in order to avoid circularly
|
|
// waiting for each others' locks.
|
|
std::thread t([this, memHandle]() {
|
|
Status status = mEngine->dispatchPacket(memHandle);
|
|
if (status != Status::SUCCESS) {
|
|
mEngine->notifyError(std::string(__func__) + ":" + std::to_string(__LINE__) +
|
|
" Failed to dispatch packet");
|
|
}
|
|
});
|
|
t.detach();
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
Status PixelStreamManager::handleExecutionPhase(const RunnerEvent& e) {
|
|
std::lock_guard<std::mutex> lock(mStateLock);
|
|
if (mState == CONFIG_DONE && e.isPhaseEntry()) {
|
|
mState = RUNNING;
|
|
return Status::SUCCESS;
|
|
}
|
|
if (mState == RESET) {
|
|
// Cannot get to running phase from reset state without config phase
|
|
return Status::ILLEGAL_STATE;
|
|
}
|
|
if (mState == RUNNING && e.isAborted()) {
|
|
// Transition back to config completed
|
|
mState = CONFIG_DONE;
|
|
return Status::SUCCESS;
|
|
}
|
|
if (mState == RUNNING) {
|
|
return Status::ILLEGAL_STATE;
|
|
}
|
|
return Status::SUCCESS;
|
|
}
|
|
|
|
Status PixelStreamManager::handleStopWithFlushPhase(const RunnerEvent& e) {
|
|
return handleStopImmediatePhase(e);
|
|
}
|
|
|
|
Status PixelStreamManager::handleStopImmediatePhase(const RunnerEvent& e) {
|
|
std::lock_guard<std::mutex> lock(mStateLock);
|
|
if (mState == CONFIG_DONE || mState == RESET) {
|
|
return ILLEGAL_STATE;
|
|
}
|
|
/* Cannot have stop completed if we never entered stop state */
|
|
if (mState == RUNNING && (e.isAborted() || e.isTransitionComplete())) {
|
|
return ILLEGAL_STATE;
|
|
}
|
|
/* We are being asked to stop */
|
|
if (mState == RUNNING && e.isPhaseEntry()) {
|
|
mState = STOPPED;
|
|
std::thread t([this]() {
|
|
freeAllPackets();
|
|
mEngine->notifyEndOfStream();
|
|
});
|
|
t.detach();
|
|
return SUCCESS;
|
|
}
|
|
/* Other Components have stopped, we can transition back to CONFIG_DONE */
|
|
if (mState == STOPPED && e.isTransitionComplete()) {
|
|
mState = CONFIG_DONE;
|
|
return SUCCESS;
|
|
}
|
|
/* We were stopped, but stop was aborted. */
|
|
if (mState == STOPPED && e.isAborted()) {
|
|
mState = RUNNING;
|
|
return SUCCESS;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
std::shared_ptr<MemHandle> PixelStreamManager::clonePacket(std::shared_ptr<MemHandle> handle) {
|
|
if (!handle) {
|
|
LOG(ERROR) << "PixelStreamManager - Received null memhandle.";
|
|
return nullptr;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(mLock);
|
|
int bufferId = handle->getBufferId();
|
|
auto it = mBuffersInUse.find(bufferId);
|
|
if (it == mBuffersInUse.end()) {
|
|
LOG(ERROR) << "PixelStreamManager - Attempting to clone an already freed packet.";
|
|
return nullptr;
|
|
}
|
|
it->second.outstandingRefCount += 1;
|
|
return handle;
|
|
}
|
|
|
|
PixelStreamManager::PixelStreamManager(std::string name, int streamId)
|
|
: StreamManager(name, proto::PacketType::PIXEL_DATA), mStreamId(streamId) {
|
|
}
|
|
|
|
} // namespace stream_manager
|
|
} // namespace runner
|
|
} // namespace computepipe
|
|
} // namespace automotive
|
|
} // namespace android
|