1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "LocalPrebuiltGraph.h"
16 
17 #include <android-base/logging.h>
18 #include <dlfcn.h>
19 
20 #include <functional>
21 #include <iostream>
22 #include <mutex>
23 #include <shared_mutex>
24 #include <string>
25 #include <vector>
26 
27 #include "ClientConfig.pb.h"
28 #include "InputFrame.h"
29 #include "PrebuiltGraph.h"
30 #include "RunnerComponent.h"
31 #include "prebuilt_interface.h"
32 #include "types/Status.h"
33 
34 namespace android {
35 namespace automotive {
36 namespace computepipe {
37 namespace graph {
38 
39 #define LOAD_FUNCTION(name)                                                        \
40     {                                                                              \
41         std::string func_name = std::string("PrebuiltComputepipeRunner_") + #name; \
42         mPrebuiltGraphInstance->mFn##name =                                        \
43                 dlsym(mPrebuiltGraphInstance->mHandle, func_name.c_str());         \
44         if (mPrebuiltGraphInstance->mFn##name == nullptr) {                        \
45             initialized = false;                                                   \
46             LOG(ERROR) << std::string(dlerror()) << std::endl;                     \
47         }                                                                          \
48     }
49 
50 std::mutex LocalPrebuiltGraph::mCreationMutex;
51 LocalPrebuiltGraph* LocalPrebuiltGraph::mPrebuiltGraphInstance = nullptr;
52 
53 // Function to confirm that there would be no further changes to the graph configuration. This
54 // needs to be called before starting the graph.
handleConfigPhase(const runner::ClientConfig & e)55 Status LocalPrebuiltGraph::handleConfigPhase(const runner::ClientConfig& e) {
56     if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
57         return Status::ILLEGAL_STATE;
58     }
59 
60     // handleConfigPhase is a blocking call, so abort call is pointless for this RunnerEvent.
61     if (e.isAborted()) {
62         return Status::INVALID_ARGUMENT;
63     } else if (e.isTransitionComplete()) {
64         return Status::SUCCESS;
65     }
66 
67     std::string config = e.getSerializedClientConfig();
68     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(const unsigned char*,
69                                                             size_t))mFnUpdateGraphConfig;
70     PrebuiltComputepipeRunner_ErrorCode errorCode =
71             mappedFn(reinterpret_cast<const unsigned char*>(config.c_str()), config.length());
72     if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
73         return static_cast<Status>(static_cast<int>(errorCode));
74     }
75 
76     // Set the pixel stream callback function. The same function will be called for all requested
77     // pixel output streams.
78     if (mEngineInterface.lock() != nullptr) {
79         auto pixelCallbackFn = (PrebuiltComputepipeRunner_ErrorCode(*)(
80                 void (*)(void* cookie, int, int64_t, const uint8_t* pixels, int width, int height,
81                          int step, int format)))mFnSetOutputPixelStreamCallback;
82         PrebuiltComputepipeRunner_ErrorCode errorCode =
83                 pixelCallbackFn(LocalPrebuiltGraph::OutputPixelStreamCallbackFunction);
84         if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
85             return static_cast<Status>(static_cast<int>(errorCode));
86         }
87 
88         // Set the serialized stream callback function. The same callback function will be invoked
89         // for all requested serialized output streams.
90         auto streamCallbackFn = (PrebuiltComputepipeRunner_ErrorCode(*)(
91                 void (*)(void* cookie, int, int64_t, const unsigned char*,
92                          size_t)))mFnSetOutputStreamCallback;
93         errorCode = streamCallbackFn(LocalPrebuiltGraph::OutputStreamCallbackFunction);
94         if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
95             return static_cast<Status>(static_cast<int>(errorCode));
96         }
97 
98         // Set the callback function for when the graph terminates.
99         auto terminationCallback = (PrebuiltComputepipeRunner_ErrorCode(*)(
100                 void (*)(void* cookie, const unsigned char*,
101                          size_t)))mFnSetGraphTerminationCallback;
102         errorCode = terminationCallback(LocalPrebuiltGraph::GraphTerminationCallbackFunction);
103         if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
104             return static_cast<Status>(static_cast<int>(errorCode));
105         }
106     }
107 
108     return Status::SUCCESS;
109 }
110 
111 // Starts the graph.
handleExecutionPhase(const runner::RunnerEvent & e)112 Status LocalPrebuiltGraph::handleExecutionPhase(const runner::RunnerEvent& e) {
113     if (mGraphState.load() != PrebuiltGraphState::STOPPED) {
114         return Status::ILLEGAL_STATE;
115     }
116 
117     if (e.isAborted()) {
118         // Starting the graph is a blocking call and cannot be aborted in between.
119         return Status::INVALID_ARGUMENT;
120     } else if (e.isTransitionComplete()) {
121         return Status::SUCCESS;
122     }
123 
124     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(void*))mFnStartGraphExecution;
125     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(reinterpret_cast<void*>(this));
126     if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
127         mGraphState.store(PrebuiltGraphState::RUNNING);
128     }
129     return static_cast<Status>(static_cast<int>(errorCode));
130 }
131 
132 // Stops the graph while letting the graph flush output packets in flight.
handleStopWithFlushPhase(const runner::RunnerEvent & e)133 Status LocalPrebuiltGraph::handleStopWithFlushPhase(const runner::RunnerEvent& e) {
134     if (mGraphState.load() != PrebuiltGraphState::RUNNING) {
135         return Status::ILLEGAL_STATE;
136     }
137 
138     if (e.isAborted()) {
139         return Status::INVALID_ARGUMENT;
140     } else if (e.isTransitionComplete()) {
141         return Status::SUCCESS;
142     }
143 
144     return StopGraphExecution(/* flushOutputFrames = */ true);
145 }
146 
147 // Stops the graph and cancels all the output packets.
handleStopImmediatePhase(const runner::RunnerEvent & e)148 Status LocalPrebuiltGraph::handleStopImmediatePhase(const runner::RunnerEvent& e) {
149     if (mGraphState.load() != PrebuiltGraphState::RUNNING) {
150         return Status::ILLEGAL_STATE;
151     }
152 
153     if (e.isAborted()) {
154         return Status::INVALID_ARGUMENT;
155     } else if (e.isTransitionComplete()) {
156         return Status::SUCCESS;
157     }
158 
159     return StopGraphExecution(/* flushOutputFrames = */ false);
160 }
161 
handleResetPhase(const runner::RunnerEvent & e)162 Status LocalPrebuiltGraph::handleResetPhase(const runner::RunnerEvent& e) {
163     if (mGraphState.load() != PrebuiltGraphState::STOPPED) {
164         return Status::ILLEGAL_STATE;
165     }
166 
167     if (e.isAborted()) {
168         return Status::INVALID_ARGUMENT;
169     } else if (e.isTransitionComplete()) {
170         return Status::SUCCESS;
171     }
172 
173     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnResetGraph;
174     mappedFn();
175     return Status::SUCCESS;
176 }
177 
GetPrebuiltGraphFromLibrary(const std::string & prebuilt_library,std::weak_ptr<PrebuiltEngineInterface> engineInterface)178 LocalPrebuiltGraph* LocalPrebuiltGraph::GetPrebuiltGraphFromLibrary(
179         const std::string& prebuilt_library,
180         std::weak_ptr<PrebuiltEngineInterface> engineInterface) {
181     std::unique_lock<std::mutex> lock(LocalPrebuiltGraph::mCreationMutex);
182     if (mPrebuiltGraphInstance != nullptr) {
183         mPrebuiltGraphInstance = new LocalPrebuiltGraph();
184     }
185     if (mPrebuiltGraphInstance->mGraphState.load() != PrebuiltGraphState::UNINITIALIZED) {
186         return mPrebuiltGraphInstance;
187     }
188     mPrebuiltGraphInstance->mHandle = dlopen(prebuilt_library.c_str(), RTLD_NOW);
189 
190     if (mPrebuiltGraphInstance->mHandle) {
191         bool initialized = true;
192 
193         // Load config and version number first.
194         const unsigned char* (*getVersionFn)() =
195                 (const unsigned char* (*)())dlsym(mPrebuiltGraphInstance->mHandle,
196                                                   "PrebuiltComputepipeRunner_GetVersion");
197         if (getVersionFn != nullptr) {
198             mPrebuiltGraphInstance->mGraphVersion =
199                     std::string(reinterpret_cast<const char*>(getVersionFn()));
200         } else {
201             LOG(ERROR) << std::string(dlerror());
202             initialized = false;
203         }
204 
205         void (*getSupportedGraphConfigsFn)(const void**, size_t*) =
206                 (void (*)(const void**,
207                           size_t*))dlsym(mPrebuiltGraphInstance->mHandle,
208                                          "PrebuiltComputepipeRunner_GetSupportedGraphConfigs");
209         if (getSupportedGraphConfigsFn != nullptr) {
210             size_t graphConfigSize;
211             const void* graphConfig;
212 
213             getSupportedGraphConfigsFn(&graphConfig, &graphConfigSize);
214 
215             if (graphConfigSize > 0) {
216                 initialized &= mPrebuiltGraphInstance->mGraphConfig.ParseFromString(
217                         std::string(reinterpret_cast<const char*>(graphConfig), graphConfigSize));
218             }
219         } else {
220             LOG(ERROR) << std::string(dlerror());
221             initialized = false;
222         }
223 
224         // Null callback interface is not acceptable.
225         if (initialized && engineInterface.lock() == nullptr) {
226             initialized = false;
227         }
228 
229         LOAD_FUNCTION(GetErrorCode);
230         LOAD_FUNCTION(GetErrorMessage);
231         LOAD_FUNCTION(ResetGraph);
232         LOAD_FUNCTION(UpdateGraphConfig);
233         LOAD_FUNCTION(SetInputStreamData);
234         LOAD_FUNCTION(SetInputStreamPixelData);
235         LOAD_FUNCTION(SetOutputStreamCallback);
236         LOAD_FUNCTION(SetOutputPixelStreamCallback);
237         LOAD_FUNCTION(SetGraphTerminationCallback);
238         LOAD_FUNCTION(StartGraphExecution);
239         LOAD_FUNCTION(StopGraphExecution);
240         LOAD_FUNCTION(StartGraphProfiling);
241         LOAD_FUNCTION(StopGraphProfiling);
242         LOAD_FUNCTION(GetDebugInfo);
243 
244         // This is the only way to create this object and there is already a
245         // lock around object creation, so no need to hold the graphState lock
246         // here.
247         if (initialized) {
248             mPrebuiltGraphInstance->mGraphState.store(PrebuiltGraphState::STOPPED);
249             mPrebuiltGraphInstance->mEngineInterface = engineInterface;
250         }
251     }
252 
253     return mPrebuiltGraphInstance;
254 }
255 
~LocalPrebuiltGraph()256 LocalPrebuiltGraph::~LocalPrebuiltGraph() {
257     if (mHandle) {
258         dlclose(mHandle);
259     }
260 }
261 
GetStatus() const262 Status LocalPrebuiltGraph::GetStatus() const {
263     if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
264         return Status::ILLEGAL_STATE;
265     }
266 
267     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnGetErrorCode;
268     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
269     return static_cast<Status>(static_cast<int>(errorCode));
270 }
271 
GetErrorMessage() const272 std::string LocalPrebuiltGraph::GetErrorMessage() const {
273     if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
274         return "Graph has not been initialized";
275     }
276     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(unsigned char*, size_t,
277                                                             size_t*))mFnGetErrorMessage;
278     size_t errorMessageSize = 0;
279 
280     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(nullptr, 0, &errorMessageSize);
281     std::vector<unsigned char> errorMessage(errorMessageSize);
282 
283     errorCode = mappedFn(&errorMessage[0], errorMessage.size(), &errorMessageSize);
284     if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
285         return "Unable to get error message from the graph.";
286     }
287 
288     return std::string(reinterpret_cast<char*>(&errorMessage[0]),
289                        reinterpret_cast<char*>(&errorMessage[0]) + errorMessage.size());
290 }
291 
SetInputStreamData(int streamIndex,int64_t timestamp,const std::string & streamData)292 Status LocalPrebuiltGraph::SetInputStreamData(int streamIndex, int64_t timestamp,
293                                               const std::string& streamData) {
294     if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
295         return Status::ILLEGAL_STATE;
296     }
297     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(int, int64_t, const unsigned char*,
298                                                             size_t))mFnSetInputStreamData;
299     PrebuiltComputepipeRunner_ErrorCode errorCode =
300             mappedFn(streamIndex, timestamp,
301                      reinterpret_cast<const unsigned char*>(streamData.c_str()),
302                      streamData.length());
303     return static_cast<Status>(static_cast<int>(errorCode));
304 }
305 
SetInputStreamPixelData(int streamIndex,int64_t timestamp,const runner::InputFrame & inputFrame)306 Status LocalPrebuiltGraph::SetInputStreamPixelData(int streamIndex, int64_t timestamp,
307                                                    const runner::InputFrame& inputFrame) {
308     if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
309         return Status::ILLEGAL_STATE;
310     }
311 
312     auto mappedFn =
313             (PrebuiltComputepipeRunner_ErrorCode(*)(int, int64_t, const uint8_t*, int, int, int,
314                                                     PrebuiltComputepipeRunner_PixelDataFormat))
315                     mFnSetInputStreamPixelData;
316     PrebuiltComputepipeRunner_ErrorCode errorCode =
317             mappedFn(streamIndex, timestamp, inputFrame.getFramePtr(),
318                      inputFrame.getFrameInfo().width, inputFrame.getFrameInfo().height,
319                      inputFrame.getFrameInfo().stride,
320                      static_cast<PrebuiltComputepipeRunner_PixelDataFormat>(
321                              static_cast<int>(inputFrame.getFrameInfo().format)));
322     return static_cast<Status>(static_cast<int>(errorCode));
323 }
324 
StopGraphExecution(bool flushOutputFrames)325 Status LocalPrebuiltGraph::StopGraphExecution(bool flushOutputFrames) {
326     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(bool))mFnStopGraphExecution;
327     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(flushOutputFrames);
328     if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
329         mGraphState.store(flushOutputFrames ? PrebuiltGraphState::FLUSHING
330                                             : PrebuiltGraphState::STOPPED);
331     }
332     return static_cast<Status>(static_cast<int>(errorCode));
333 }
334 
StartGraphProfiling()335 Status LocalPrebuiltGraph::StartGraphProfiling() {
336     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnStartGraphProfiling;
337     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
338     return static_cast<Status>(static_cast<int>(errorCode));
339 }
340 
StopGraphProfiling()341 Status LocalPrebuiltGraph::StopGraphProfiling() {
342     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnStopGraphProfiling;
343     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
344     return static_cast<Status>(static_cast<int>(errorCode));
345 }
346 
GetDebugInfo()347 std::string LocalPrebuiltGraph::GetDebugInfo() {
348     if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
349         return "";
350     }
351     auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(unsigned char*, size_t,
352                                                             size_t*))mFnGetDebugInfo;
353 
354     size_t debugInfoSize = 0;
355     PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(nullptr, 0, &debugInfoSize);
356     std::vector<unsigned char> debugInfo(debugInfoSize);
357 
358     errorCode = mappedFn(&debugInfo[0], debugInfo.size(), &debugInfoSize);
359     if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
360         return "";
361     }
362 
363     return std::string(reinterpret_cast<char*>(&debugInfo[0]),
364                        reinterpret_cast<char*>(&debugInfo[0]) + debugInfo.size());
365 }
366 
OutputStreamCallbackFunction(void * cookie,int streamIndex,int64_t timestamp,const unsigned char * data,size_t data_size)367 void LocalPrebuiltGraph::OutputStreamCallbackFunction(void* cookie, int streamIndex,
368                                                       int64_t timestamp, const unsigned char* data,
369                                                       size_t data_size) {
370     LocalPrebuiltGraph* graph = reinterpret_cast<LocalPrebuiltGraph*>(cookie);
371     CHECK(graph);
372     std::shared_ptr<PrebuiltEngineInterface> engineInterface = graph->mEngineInterface.lock();
373     if (engineInterface != nullptr) {
374         engineInterface->DispatchSerializedData(streamIndex, timestamp,
375                                                 std::string(data, data + data_size));
376     }
377 }
378 
OutputPixelStreamCallbackFunction(void * cookie,int streamIndex,int64_t timestamp,const uint8_t * pixels,int width,int height,int step,int format)379 void LocalPrebuiltGraph::OutputPixelStreamCallbackFunction(void* cookie, int streamIndex,
380                                                            int64_t timestamp, const uint8_t* pixels,
381                                                            int width, int height, int step,
382                                                            int format) {
383     LocalPrebuiltGraph* graph = reinterpret_cast<LocalPrebuiltGraph*>(cookie);
384     CHECK(graph);
385     std::shared_ptr<PrebuiltEngineInterface> engineInterface = graph->mEngineInterface.lock();
386 
387     if (engineInterface) {
388         runner::InputFrame frame(height, width, static_cast<PixelFormat>(format), step, pixels);
389         engineInterface->DispatchPixelData(streamIndex, timestamp, frame);
390     }
391 }
392 
GraphTerminationCallbackFunction(void * cookie,const unsigned char * termination_message,size_t termination_message_size)393 void LocalPrebuiltGraph::GraphTerminationCallbackFunction(void* cookie,
394                                                           const unsigned char* termination_message,
395                                                           size_t termination_message_size) {
396     LocalPrebuiltGraph* graph = reinterpret_cast<LocalPrebuiltGraph*>(cookie);
397     CHECK(graph);
398     std::shared_ptr<PrebuiltEngineInterface> engineInterface = graph->mEngineInterface.lock();
399 
400     if (engineInterface) {
401         std::string errorMessage = "";
402         if (termination_message != nullptr && termination_message_size > 0) {
403             std::string(termination_message, termination_message + termination_message_size);
404         }
405         graph->mGraphState.store(PrebuiltGraphState::STOPPED);
406         engineInterface->DispatchGraphTerminationMessage(graph->GetStatus(),
407                                                          std::move(errorMessage));
408     }
409 }
410 
GetLocalGraphFromLibrary(const std::string & prebuilt_library,std::weak_ptr<PrebuiltEngineInterface> engineInterface)411 PrebuiltGraph* GetLocalGraphFromLibrary(const std::string& prebuilt_library,
412                                         std::weak_ptr<PrebuiltEngineInterface> engineInterface) {
413     return LocalPrebuiltGraph::GetPrebuiltGraphFromLibrary(prebuilt_library, engineInterface);
414 }
415 
416 }  // namespace graph
417 }  // namespace computepipe
418 }  // namespace automotive
419 }  // namespace android
420