blob: ae4cc8c74ff71a4bd0e9909b61345b88081e0d93 [file] [log] [blame]
// Copyright (C) 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 "LocalPrebuiltGraph.h"
#include <android-base/logging.h>
#include <dlfcn.h>
#include <functional>
#include <iostream>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <vector>
#include "ClientConfig.pb.h"
#include "InputFrame.h"
#include "PrebuiltGraph.h"
#include "RunnerComponent.h"
#include "prebuilt_interface.h"
#include "types/Status.h"
namespace android {
namespace automotive {
namespace computepipe {
namespace graph {
#define LOAD_FUNCTION(name) \
{ \
std::string func_name = std::string("PrebuiltComputepipeRunner_") + #name; \
mPrebuiltGraphInstance->mFn##name = \
dlsym(mPrebuiltGraphInstance->mHandle, func_name.c_str()); \
if (mPrebuiltGraphInstance->mFn##name == nullptr) { \
initialized = false; \
LOG(ERROR) << std::string(dlerror()) << std::endl; \
} \
}
std::mutex LocalPrebuiltGraph::mCreationMutex;
LocalPrebuiltGraph* LocalPrebuiltGraph::mPrebuiltGraphInstance = nullptr;
// Function to confirm that there would be no further changes to the graph configuration. This
// needs to be called before starting the graph.
Status LocalPrebuiltGraph::handleConfigPhase(const runner::ClientConfig& e) {
if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
return Status::ILLEGAL_STATE;
}
// handleConfigPhase is a blocking call, so abort call is pointless for this RunnerEvent.
if (e.isAborted()) {
return Status::INVALID_ARGUMENT;
} else if (e.isTransitionComplete()) {
return Status::SUCCESS;
}
std::string config = e.getSerializedClientConfig();
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(const unsigned char*,
size_t))mFnUpdateGraphConfig;
PrebuiltComputepipeRunner_ErrorCode errorCode =
mappedFn(reinterpret_cast<const unsigned char*>(config.c_str()), config.length());
if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
return static_cast<Status>(static_cast<int>(errorCode));
}
// Set the pixel stream callback function. The same function will be called for all requested
// pixel output streams.
if (mEngineInterface.lock() != nullptr) {
auto pixelCallbackFn = (PrebuiltComputepipeRunner_ErrorCode(*)(
void (*)(void* cookie, int, int64_t, const uint8_t* pixels, int width, int height,
int step, int format)))mFnSetOutputPixelStreamCallback;
PrebuiltComputepipeRunner_ErrorCode errorCode =
pixelCallbackFn(LocalPrebuiltGraph::OutputPixelStreamCallbackFunction);
if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
return static_cast<Status>(static_cast<int>(errorCode));
}
// Set the serialized stream callback function. The same callback function will be invoked
// for all requested serialized output streams.
auto streamCallbackFn = (PrebuiltComputepipeRunner_ErrorCode(*)(
void (*)(void* cookie, int, int64_t, const unsigned char*,
size_t)))mFnSetOutputStreamCallback;
errorCode = streamCallbackFn(LocalPrebuiltGraph::OutputStreamCallbackFunction);
if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
return static_cast<Status>(static_cast<int>(errorCode));
}
// Set the callback function for when the graph terminates.
auto terminationCallback = (PrebuiltComputepipeRunner_ErrorCode(*)(
void (*)(void* cookie, const unsigned char*,
size_t)))mFnSetGraphTerminationCallback;
errorCode = terminationCallback(LocalPrebuiltGraph::GraphTerminationCallbackFunction);
if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
return static_cast<Status>(static_cast<int>(errorCode));
}
}
return Status::SUCCESS;
}
// Starts the graph.
Status LocalPrebuiltGraph::handleExecutionPhase(const runner::RunnerEvent& e) {
if (mGraphState.load() != PrebuiltGraphState::STOPPED) {
return Status::ILLEGAL_STATE;
}
if (e.isAborted()) {
// Starting the graph is a blocking call and cannot be aborted in between.
return Status::INVALID_ARGUMENT;
} else if (e.isTransitionComplete()) {
return Status::SUCCESS;
}
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(void*))mFnStartGraphExecution;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(reinterpret_cast<void*>(this));
if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
mGraphState.store(PrebuiltGraphState::RUNNING);
}
return static_cast<Status>(static_cast<int>(errorCode));
}
// Stops the graph while letting the graph flush output packets in flight.
Status LocalPrebuiltGraph::handleStopWithFlushPhase(const runner::RunnerEvent& e) {
if (mGraphState.load() != PrebuiltGraphState::RUNNING) {
return Status::ILLEGAL_STATE;
}
if (e.isAborted()) {
return Status::INVALID_ARGUMENT;
} else if (e.isTransitionComplete()) {
return Status::SUCCESS;
}
return StopGraphExecution(/* flushOutputFrames = */ true);
}
// Stops the graph and cancels all the output packets.
Status LocalPrebuiltGraph::handleStopImmediatePhase(const runner::RunnerEvent& e) {
if (mGraphState.load() != PrebuiltGraphState::RUNNING) {
return Status::ILLEGAL_STATE;
}
if (e.isAborted()) {
return Status::INVALID_ARGUMENT;
} else if (e.isTransitionComplete()) {
return Status::SUCCESS;
}
return StopGraphExecution(/* flushOutputFrames = */ false);
}
Status LocalPrebuiltGraph::handleResetPhase(const runner::RunnerEvent& e) {
if (mGraphState.load() != PrebuiltGraphState::STOPPED) {
return Status::ILLEGAL_STATE;
}
if (e.isAborted()) {
return Status::INVALID_ARGUMENT;
} else if (e.isTransitionComplete()) {
return Status::SUCCESS;
}
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnResetGraph;
mappedFn();
return Status::SUCCESS;
}
LocalPrebuiltGraph* LocalPrebuiltGraph::GetPrebuiltGraphFromLibrary(
const std::string& prebuilt_library,
std::weak_ptr<PrebuiltEngineInterface> engineInterface) {
std::unique_lock<std::mutex> lock(LocalPrebuiltGraph::mCreationMutex);
if (mPrebuiltGraphInstance != nullptr) {
mPrebuiltGraphInstance = new LocalPrebuiltGraph();
}
if (mPrebuiltGraphInstance->mGraphState.load() != PrebuiltGraphState::UNINITIALIZED) {
return mPrebuiltGraphInstance;
}
mPrebuiltGraphInstance->mHandle = dlopen(prebuilt_library.c_str(), RTLD_NOW);
if (mPrebuiltGraphInstance->mHandle) {
bool initialized = true;
// Load config and version number first.
const unsigned char* (*getVersionFn)() =
(const unsigned char* (*)())dlsym(mPrebuiltGraphInstance->mHandle,
"PrebuiltComputepipeRunner_GetVersion");
if (getVersionFn != nullptr) {
mPrebuiltGraphInstance->mGraphVersion =
std::string(reinterpret_cast<const char*>(getVersionFn()));
} else {
LOG(ERROR) << std::string(dlerror());
initialized = false;
}
void (*getSupportedGraphConfigsFn)(const void**, size_t*) =
(void (*)(const void**,
size_t*))dlsym(mPrebuiltGraphInstance->mHandle,
"PrebuiltComputepipeRunner_GetSupportedGraphConfigs");
if (getSupportedGraphConfigsFn != nullptr) {
size_t graphConfigSize;
const void* graphConfig;
getSupportedGraphConfigsFn(&graphConfig, &graphConfigSize);
if (graphConfigSize > 0) {
initialized &= mPrebuiltGraphInstance->mGraphConfig.ParseFromString(
std::string(reinterpret_cast<const char*>(graphConfig), graphConfigSize));
}
} else {
LOG(ERROR) << std::string(dlerror());
initialized = false;
}
// Null callback interface is not acceptable.
if (initialized && engineInterface.lock() == nullptr) {
initialized = false;
}
LOAD_FUNCTION(GetErrorCode);
LOAD_FUNCTION(GetErrorMessage);
LOAD_FUNCTION(ResetGraph);
LOAD_FUNCTION(UpdateGraphConfig);
LOAD_FUNCTION(SetInputStreamData);
LOAD_FUNCTION(SetInputStreamPixelData);
LOAD_FUNCTION(SetOutputStreamCallback);
LOAD_FUNCTION(SetOutputPixelStreamCallback);
LOAD_FUNCTION(SetGraphTerminationCallback);
LOAD_FUNCTION(StartGraphExecution);
LOAD_FUNCTION(StopGraphExecution);
LOAD_FUNCTION(StartGraphProfiling);
LOAD_FUNCTION(StopGraphProfiling);
LOAD_FUNCTION(GetDebugInfo);
// This is the only way to create this object and there is already a
// lock around object creation, so no need to hold the graphState lock
// here.
if (initialized) {
mPrebuiltGraphInstance->mGraphState.store(PrebuiltGraphState::STOPPED);
mPrebuiltGraphInstance->mEngineInterface = engineInterface;
}
}
return mPrebuiltGraphInstance;
}
LocalPrebuiltGraph::~LocalPrebuiltGraph() {
if (mHandle) {
dlclose(mHandle);
}
}
Status LocalPrebuiltGraph::GetStatus() const {
if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
return Status::ILLEGAL_STATE;
}
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnGetErrorCode;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
return static_cast<Status>(static_cast<int>(errorCode));
}
std::string LocalPrebuiltGraph::GetErrorMessage() const {
if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
return "Graph has not been initialized";
}
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(unsigned char*, size_t,
size_t*))mFnGetErrorMessage;
size_t errorMessageSize = 0;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(nullptr, 0, &errorMessageSize);
std::vector<unsigned char> errorMessage(errorMessageSize);
errorCode = mappedFn(&errorMessage[0], errorMessage.size(), &errorMessageSize);
if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
return "Unable to get error message from the graph.";
}
return std::string(reinterpret_cast<char*>(&errorMessage[0]),
reinterpret_cast<char*>(&errorMessage[0]) + errorMessage.size());
}
Status LocalPrebuiltGraph::SetInputStreamData(int streamIndex, int64_t timestamp,
const std::string& streamData) {
if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
return Status::ILLEGAL_STATE;
}
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(int, int64_t, const unsigned char*,
size_t))mFnSetInputStreamData;
PrebuiltComputepipeRunner_ErrorCode errorCode =
mappedFn(streamIndex, timestamp,
reinterpret_cast<const unsigned char*>(streamData.c_str()),
streamData.length());
return static_cast<Status>(static_cast<int>(errorCode));
}
Status LocalPrebuiltGraph::SetInputStreamPixelData(int streamIndex, int64_t timestamp,
const runner::InputFrame& inputFrame) {
if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
return Status::ILLEGAL_STATE;
}
auto mappedFn =
(PrebuiltComputepipeRunner_ErrorCode(*)(int, int64_t, const uint8_t*, int, int, int,
PrebuiltComputepipeRunner_PixelDataFormat))
mFnSetInputStreamPixelData;
PrebuiltComputepipeRunner_ErrorCode errorCode =
mappedFn(streamIndex, timestamp, inputFrame.getFramePtr(),
inputFrame.getFrameInfo().width, inputFrame.getFrameInfo().height,
inputFrame.getFrameInfo().stride,
static_cast<PrebuiltComputepipeRunner_PixelDataFormat>(
static_cast<int>(inputFrame.getFrameInfo().format)));
return static_cast<Status>(static_cast<int>(errorCode));
}
Status LocalPrebuiltGraph::StopGraphExecution(bool flushOutputFrames) {
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(bool))mFnStopGraphExecution;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(flushOutputFrames);
if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
mGraphState.store(flushOutputFrames ? PrebuiltGraphState::FLUSHING
: PrebuiltGraphState::STOPPED);
}
return static_cast<Status>(static_cast<int>(errorCode));
}
Status LocalPrebuiltGraph::StartGraphProfiling() {
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnStartGraphProfiling;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
return static_cast<Status>(static_cast<int>(errorCode));
}
Status LocalPrebuiltGraph::StopGraphProfiling() {
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnStopGraphProfiling;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
return static_cast<Status>(static_cast<int>(errorCode));
}
std::string LocalPrebuiltGraph::GetDebugInfo() {
if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
return "";
}
auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(unsigned char*, size_t,
size_t*))mFnGetDebugInfo;
size_t debugInfoSize = 0;
PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(nullptr, 0, &debugInfoSize);
std::vector<unsigned char> debugInfo(debugInfoSize);
errorCode = mappedFn(&debugInfo[0], debugInfo.size(), &debugInfoSize);
if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
return "";
}
return std::string(reinterpret_cast<char*>(&debugInfo[0]),
reinterpret_cast<char*>(&debugInfo[0]) + debugInfo.size());
}
void LocalPrebuiltGraph::OutputStreamCallbackFunction(void* cookie, int streamIndex,
int64_t timestamp, const unsigned char* data,
size_t data_size) {
LocalPrebuiltGraph* graph = reinterpret_cast<LocalPrebuiltGraph*>(cookie);
CHECK(graph);
std::shared_ptr<PrebuiltEngineInterface> engineInterface = graph->mEngineInterface.lock();
if (engineInterface != nullptr) {
engineInterface->DispatchSerializedData(streamIndex, timestamp,
std::string(data, data + data_size));
}
}
void LocalPrebuiltGraph::OutputPixelStreamCallbackFunction(void* cookie, int streamIndex,
int64_t timestamp, const uint8_t* pixels,
int width, int height, int step,
int format) {
LocalPrebuiltGraph* graph = reinterpret_cast<LocalPrebuiltGraph*>(cookie);
CHECK(graph);
std::shared_ptr<PrebuiltEngineInterface> engineInterface = graph->mEngineInterface.lock();
if (engineInterface) {
runner::InputFrame frame(height, width, static_cast<PixelFormat>(format), step, pixels);
engineInterface->DispatchPixelData(streamIndex, timestamp, frame);
}
}
void LocalPrebuiltGraph::GraphTerminationCallbackFunction(void* cookie,
const unsigned char* termination_message,
size_t termination_message_size) {
LocalPrebuiltGraph* graph = reinterpret_cast<LocalPrebuiltGraph*>(cookie);
CHECK(graph);
std::shared_ptr<PrebuiltEngineInterface> engineInterface = graph->mEngineInterface.lock();
if (engineInterface) {
std::string errorMessage = "";
if (termination_message != nullptr && termination_message_size > 0) {
std::string(termination_message, termination_message + termination_message_size);
}
graph->mGraphState.store(PrebuiltGraphState::STOPPED);
engineInterface->DispatchGraphTerminationMessage(graph->GetStatus(),
std::move(errorMessage));
}
}
PrebuiltGraph* GetLocalGraphFromLibrary(const std::string& prebuilt_library,
std::weak_ptr<PrebuiltEngineInterface> engineInterface) {
return LocalPrebuiltGraph::GetPrebuiltGraphFromLibrary(prebuilt_library, engineInterface);
}
} // namespace graph
} // namespace computepipe
} // namespace automotive
} // namespace android