// 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 #include #include #include #include #include #include #include #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(config.c_str()), config.length()); if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) { return static_cast(static_cast(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(static_cast(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(static_cast(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(static_cast(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(this)); if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) { mGraphState.store(PrebuiltGraphState::RUNNING); } return static_cast(static_cast(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 engineInterface) { std::unique_lock 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(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(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(static_cast(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 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(&errorMessage[0]), reinterpret_cast(&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(streamData.c_str()), streamData.length()); return static_cast(static_cast(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( static_cast(inputFrame.getFrameInfo().format))); return static_cast(static_cast(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(static_cast(errorCode)); } Status LocalPrebuiltGraph::StartGraphProfiling() { auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnStartGraphProfiling; PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(); return static_cast(static_cast(errorCode)); } Status LocalPrebuiltGraph::StopGraphProfiling() { auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnStopGraphProfiling; PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(); return static_cast(static_cast(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 debugInfo(debugInfoSize); errorCode = mappedFn(&debugInfo[0], debugInfo.size(), &debugInfoSize); if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) { return ""; } return std::string(reinterpret_cast(&debugInfo[0]), reinterpret_cast(&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(cookie); CHECK(graph); std::shared_ptr 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(cookie); CHECK(graph); std::shared_ptr engineInterface = graph->mEngineInterface.lock(); if (engineInterface) { runner::InputFrame frame(height, width, static_cast(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(cookie); CHECK(graph); std::shared_ptr 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 engineInterface) { return LocalPrebuiltGraph::GetPrebuiltGraphFromLibrary(prebuilt_library, engineInterface); } } // namespace graph } // namespace computepipe } // namespace automotive } // namespace android