1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "SampleDriver"
18
19 #include "SampleDriver.h"
20
21 #include <android-base/logging.h>
22 #include <android-base/properties.h>
23 #include <android-base/scopeguard.h>
24 #include <android/binder_auto_utils.h>
25 #include <android/binder_interface_utils.h>
26 #include <android/binder_manager.h>
27 #include <android/binder_process.h>
28 #include <nnapi/Result.h>
29 #include <nnapi/Types.h>
30 #include <nnapi/Validation.h>
31 #include <nnapi/hal/aidl/Conversions.h>
32 #include <nnapi/hal/aidl/Utils.h>
33
34 #include <algorithm>
35 #include <chrono>
36 #include <map>
37 #include <memory>
38 #include <optional>
39 #include <set>
40 #include <string>
41 #include <thread>
42 #include <tuple>
43 #include <utility>
44 #include <variant>
45 #include <vector>
46
47 #include "AidlBufferTracker.h"
48 #include "AidlHalUtils.h"
49 #include "CpuExecutor.h"
50 #include "SampleDriverUtils.h"
51 #include "Tracing.h"
52 #include "Utils.h"
53
54 namespace android {
55 namespace nn {
56 namespace sample_driver {
57
58 namespace {
59
nanosecondsDuration(TimePoint end,TimePoint start)60 int64_t nanosecondsDuration(TimePoint end, TimePoint start) {
61 return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
62 };
63
64 constexpr aidl_hal::Timing kNoTiming = {.timeOnDeviceNs = -1, .timeInDriverNs = -1};
65
66 } // namespace
67
getVersionString(std::string * versionString)68 ndk::ScopedAStatus SampleDriver::getVersionString(std::string* versionString) {
69 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
70 "SampleDriver::getVersionString");
71 *versionString = "JUST_AN_EXAMPLE";
72 return ndk::ScopedAStatus::ok();
73 }
74
getType(aidl_hal::DeviceType * deviceType)75 ndk::ScopedAStatus SampleDriver::getType(aidl_hal::DeviceType* deviceType) {
76 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getType");
77 *deviceType = aidl_hal::DeviceType::CPU;
78 return ndk::ScopedAStatus::ok();
79 }
80
getSupportedExtensions(std::vector<aidl_hal::Extension> * supportedExtensions)81 ndk::ScopedAStatus SampleDriver::getSupportedExtensions(
82 std::vector<aidl_hal::Extension>* supportedExtensions) {
83 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
84 "SampleDriver::getSupportedExtensions");
85 *supportedExtensions = {/* No extensions. */};
86 return ndk::ScopedAStatus::ok();
87 }
88
getNumberOfCacheFilesNeeded(aidl_hal::NumberOfCacheFiles * numberOfCacheFiles)89 ndk::ScopedAStatus SampleDriver::getNumberOfCacheFilesNeeded(
90 aidl_hal::NumberOfCacheFiles* numberOfCacheFiles) {
91 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION,
92 "SampleDriver::getNumberOfCacheFilesNeeded");
93 // Set both numbers to be 0 for cache not supported.
94 numberOfCacheFiles->numDataCache = 0;
95 numberOfCacheFiles->numModelCache = 0;
96 return ndk::ScopedAStatus::ok();
97 }
98
prepareModel(const aidl_hal::Model & model,aidl_hal::ExecutionPreference preference,aidl_hal::Priority priority,int64_t deadlineNs,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<uint8_t> &,const std::shared_ptr<aidl_hal::IPreparedModelCallback> & callback)99 ndk::ScopedAStatus SampleDriver::prepareModel(
100 const aidl_hal::Model& model, aidl_hal::ExecutionPreference preference,
101 aidl_hal::Priority priority, int64_t deadlineNs,
102 const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
103 const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
104 const std::vector<uint8_t>& /*token*/,
105 const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
106 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION, "SampleDriver::prepareModel");
107 auto copiedModel = aidl_hal::utils::clone(model);
108 if (!copiedModel.has_value()) {
109 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, copiedModel.error().message);
110 }
111 return prepareModelBase(std::move(copiedModel).value(), this, preference, priority, deadlineNs,
112 callback);
113 }
114
prepareModelFromCache(int64_t,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<ndk::ScopedFileDescriptor> &,const std::vector<uint8_t> &,const std::shared_ptr<aidl_hal::IPreparedModelCallback> & callback)115 ndk::ScopedAStatus SampleDriver::prepareModelFromCache(
116 int64_t /*deadlineNs*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
117 const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
118 const std::vector<uint8_t>& /*token*/,
119 const std::shared_ptr<aidl_hal::IPreparedModelCallback>& callback) {
120 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION,
121 "SampleDriver::prepareModelFromCache");
122 notify(callback, aidl_hal::ErrorStatus::GENERAL_FAILURE, nullptr);
123 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
124 "Caching is not supported in the sample driver.");
125 }
126
127 // Safely downcast an IPreparedModel object to SamplePreparedModel.
128 // This function will return nullptr if the IPreparedModel object is not originated from the sample
129 // driver process.
castToSamplePreparedModel(const std::shared_ptr<aidl_hal::IPreparedModel> & preparedModel)130 static const SamplePreparedModel* castToSamplePreparedModel(
131 const std::shared_ptr<aidl_hal::IPreparedModel>& preparedModel) {
132 if (preparedModel->isRemote()) {
133 return nullptr;
134 } else {
135 // This static_cast is safe because SamplePreparedModel is the only class that implements
136 // the IPreparedModel interface in the sample driver process.
137 return static_cast<const SamplePreparedModel*>(preparedModel.get());
138 }
139 }
140
allocate(const aidl_hal::BufferDesc & desc,const std::vector<aidl_hal::IPreparedModelParcel> & halPreparedModels,const std::vector<aidl_hal::BufferRole> & inputRoles,const std::vector<aidl_hal::BufferRole> & outputRoles,aidl_hal::DeviceBuffer * buffer)141 ndk::ScopedAStatus SampleDriver::allocate(
142 const aidl_hal::BufferDesc& desc,
143 const std::vector<aidl_hal::IPreparedModelParcel>& halPreparedModels,
144 const std::vector<aidl_hal::BufferRole>& inputRoles,
145 const std::vector<aidl_hal::BufferRole>& outputRoles, aidl_hal::DeviceBuffer* buffer) {
146 VLOG(DRIVER) << "SampleDriver::allocate";
147 constexpr auto getModel = [](const std::shared_ptr<aidl_hal::IPreparedModel>& preparedModel)
148 -> const aidl_hal::Model* {
149 const auto* samplePreparedModel = castToSamplePreparedModel(preparedModel);
150 if (samplePreparedModel == nullptr) {
151 LOG(ERROR) << "SampleDriver::allocate -- unknown remote IPreparedModel.";
152 return nullptr;
153 }
154 return samplePreparedModel->getModel();
155 };
156
157 std::vector<std::shared_ptr<aidl_hal::IPreparedModel>> preparedModels;
158 preparedModels.reserve(halPreparedModels.size());
159 for (const auto& halPreparedModelParcel : halPreparedModels) {
160 preparedModels.push_back(halPreparedModelParcel.preparedModel);
161 }
162 std::set<AidlHalPreparedModelRole> roles;
163 aidl_hal::Operand operand;
164 if (!validateMemoryDesc(desc, preparedModels, inputRoles, outputRoles, getModel, &roles,
165 &operand)) {
166 LOG(ERROR) << "SampleDriver::allocate -- validation failed.";
167 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
168 "SampleDriver::allocate -- validation failed.");
169 }
170
171 if (isExtensionOperandType(operand.type)) {
172 LOG(ERROR) << "SampleDriver::allocate -- does not support extension type.";
173 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
174 "SampleDriver::allocate -- does not support extension type.");
175 }
176
177 // TODO(xusongw): Support allocating buffers with unknown dimensions or rank.
178
179 // An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical
180 // types.
181 uint32_t size = nonExtensionOperandSizeOfData(convert(operand.type).value(),
182 toUnsigned(operand.dimensions).value());
183 VLOG(DRIVER) << "SampleDriver::allocate -- type = " << toString(operand.type)
184 << ", dimensions = " << toString(operand.dimensions) << ", size = " << size;
185 if (size == 0) {
186 LOG(ERROR) << "SampleDriver::allocate -- does not support dynamic output shape.";
187 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
188 "SampleDriver::allocate -- does not support dynamic output shape.");
189 }
190
191 // An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical
192 // types, so it safe to do an unvalidated conversion here.
193 auto bufferWrapper =
194 AidlManagedBuffer::create(size, std::move(roles), unvalidatedConvert(operand).value());
195 if (bufferWrapper == nullptr) {
196 LOG(ERROR) << "SampleDriver::allocate -- not enough memory.";
197 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
198 "SampleDriver::allocate -- not enough memory.");
199 }
200
201 auto token = mBufferTracker->add(bufferWrapper);
202 if (token == nullptr) {
203 LOG(ERROR) << "SampleDriver::allocate -- AidlBufferTracker returned invalid token.";
204 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
205 "SampleDriver::allocate -- AidlBufferTracker returned invalid token.");
206 }
207
208 const uint32_t tokenValue = token->get();
209 std::shared_ptr<SampleBuffer> sampleBuffer =
210 ndk::SharedRefBase::make<SampleBuffer>(std::move(bufferWrapper), std::move(token));
211 VLOG(DRIVER) << "SampleDriver::allocate -- successfully allocates the requested memory";
212 buffer->buffer = std::move(sampleBuffer);
213 buffer->token = tokenValue;
214 return ndk::ScopedAStatus::ok();
215 }
216
run()217 int SampleDriver::run() {
218 ABinderProcess_setThreadPoolMaxThreadCount(4);
219 const std::string name = std::string(SampleDriver::descriptor) + "/" + mName;
220 const binder_status_t status = AServiceManager_addService(this->asBinder().get(), name.c_str());
221 if (status != STATUS_OK) {
222 return 1;
223 }
224 ABinderProcess_joinThreadPool();
225 return 1;
226 }
227
copyRunTimePoolInfos(const RunTimePoolInfo & srcPool,const RunTimePoolInfo & dstPool)228 static void copyRunTimePoolInfos(const RunTimePoolInfo& srcPool, const RunTimePoolInfo& dstPool) {
229 CHECK(srcPool.getBuffer() != nullptr);
230 CHECK(dstPool.getBuffer() != nullptr);
231 CHECK(srcPool.getSize() == dstPool.getSize());
232 std::copy(srcPool.getBuffer(), srcPool.getBuffer() + srcPool.getSize(), dstPool.getBuffer());
233 dstPool.flush();
234 }
235
copyTo(const aidl_hal::Memory & dst)236 ndk::ScopedAStatus SampleBuffer::copyTo(const aidl_hal::Memory& dst) {
237 const auto canonicalMemory = convert(dst);
238 if (!canonicalMemory.has_value()) {
239 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, canonicalMemory.error().message);
240 }
241 const auto dstPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value());
242 if (!dstPool.has_value()) {
243 LOG(ERROR) << "SampleBuffer::copyTo -- unable to map dst memory.";
244 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
245 "SampleBuffer::copyTo -- unable to map dst memory.");
246 }
247 const auto validationStatus =
248 aidl_hal::utils::convert(kBuffer->validateCopyTo(dstPool->getSize())).value();
249 if (validationStatus != aidl_hal::ErrorStatus::NONE) {
250 return toAStatus(validationStatus);
251 }
252 const auto srcPool = kBuffer->createRunTimePoolInfo();
253 copyRunTimePoolInfos(srcPool, dstPool.value());
254 return ndk::ScopedAStatus::ok();
255 }
256
copyFromInternal(const aidl_hal::Memory & src,const std::vector<uint32_t> & dimensions,const std::shared_ptr<AidlManagedBuffer> & bufferWrapper)257 static aidl_hal::ErrorStatus copyFromInternal(
258 const aidl_hal::Memory& src, const std::vector<uint32_t>& dimensions,
259 const std::shared_ptr<AidlManagedBuffer>& bufferWrapper) {
260 CHECK(bufferWrapper != nullptr);
261 const auto canonicalMemory = convert(src);
262 if (!canonicalMemory.has_value()) {
263 return aidl_hal::ErrorStatus::INVALID_ARGUMENT;
264 }
265 const auto srcPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value());
266 if (!srcPool.has_value()) {
267 LOG(ERROR) << "SampleBuffer::copyFrom -- unable to map src memory.";
268 return aidl_hal::ErrorStatus::GENERAL_FAILURE;
269 }
270 const auto validationStatus = aidl_hal::utils::convert(bufferWrapper->validateCopyFrom(
271 dimensions, srcPool->getSize()))
272 .value();
273 if (validationStatus != aidl_hal::ErrorStatus::NONE) {
274 return validationStatus;
275 }
276 const auto dstPool = bufferWrapper->createRunTimePoolInfo();
277 copyRunTimePoolInfos(srcPool.value(), dstPool);
278 return aidl_hal::ErrorStatus::NONE;
279 }
280
copyFrom(const aidl_hal::Memory & src,const std::vector<int32_t> & dimensions)281 ndk::ScopedAStatus SampleBuffer::copyFrom(const aidl_hal::Memory& src,
282 const std::vector<int32_t>& dimensions) {
283 const auto unsignedDimensions = toUnsigned(dimensions);
284 if (!unsignedDimensions.has_value()) {
285 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
286 unsignedDimensions.error().message);
287 }
288 const auto status = copyFromInternal(src, unsignedDimensions.value(), kBuffer);
289 if (status != aidl_hal::ErrorStatus::NONE) {
290 kBuffer->setInitialized(false);
291 return toAStatus(status);
292 }
293 kBuffer->updateDimensions(unsignedDimensions.value());
294 kBuffer->setInitialized(true);
295 return ndk::ScopedAStatus::ok();
296 }
297
initialize()298 bool SamplePreparedModel::initialize() {
299 const auto canonicalPools = convert(mModel.pools);
300 if (!canonicalPools.has_value()) {
301 return false;
302 }
303 return setRunTimePoolInfosFromCanonicalMemories(&mPoolInfos, canonicalPools.value());
304 }
305
306 static std::tuple<aidl_hal::ErrorStatus, std::vector<RunTimePoolInfo>,
307 std::vector<std::shared_ptr<AidlManagedBuffer>>>
createRunTimePoolInfos(const Request & request,const SampleDriver & driver,const SamplePreparedModel * preparedModel)308 createRunTimePoolInfos(const Request& request, const SampleDriver& driver,
309 const SamplePreparedModel* preparedModel) {
310 std::vector<RunTimePoolInfo> requestPoolInfos;
311 std::vector<std::shared_ptr<AidlManagedBuffer>> bufferWrappers;
312 requestPoolInfos.reserve(request.pools.size());
313 bufferWrappers.reserve(request.pools.size());
314 for (uint32_t i = 0; i < request.pools.size(); i++) {
315 const auto& pool = request.pools[i];
316 if (const auto* memory = std::get_if<SharedMemory>(&pool)) {
317 auto buffer = RunTimePoolInfo::createFromMemory(*memory);
318 if (!buffer.has_value()) {
319 LOG(ERROR) << "createRuntimeMemoriesFromMemoryPools -- could not map pools";
320 return {aidl_hal::ErrorStatus::GENERAL_FAILURE, {}, {}};
321 }
322 requestPoolInfos.push_back(std::move(*buffer));
323 bufferWrappers.push_back(nullptr);
324 } else if (const auto* token = std::get_if<Request::MemoryDomainToken>(&pool)) {
325 auto bufferWrapper = driver.getBufferTracker()->get(static_cast<uint32_t>(*token));
326 if (bufferWrapper == nullptr) {
327 return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}};
328 }
329 const auto validationStatus =
330 aidl_hal::utils::convert(
331 bufferWrapper->validateRequest(i, request, preparedModel))
332 .value();
333 if (validationStatus != aidl_hal::ErrorStatus::NONE) {
334 return {validationStatus, {}, {}};
335 }
336 requestPoolInfos.push_back(bufferWrapper->createRunTimePoolInfo());
337 bufferWrappers.push_back(std::move(bufferWrapper));
338 } else {
339 // If the pool is not a Memory or a token, the input is invalid.
340 return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}};
341 }
342 }
343 return {aidl_hal::ErrorStatus::NONE, std::move(requestPoolInfos), std::move(bufferWrappers)};
344 }
345
updateDeviceMemories(aidl_hal::ErrorStatus status,const Request & request,const std::vector<std::shared_ptr<AidlManagedBuffer>> & bufferWrappers,const std::vector<aidl_hal::OutputShape> & outputShapes)346 static aidl_hal::ErrorStatus updateDeviceMemories(
347 aidl_hal::ErrorStatus status, const Request& request,
348 const std::vector<std::shared_ptr<AidlManagedBuffer>>& bufferWrappers,
349 const std::vector<aidl_hal::OutputShape>& outputShapes) {
350 if (status == aidl_hal::ErrorStatus::NONE) {
351 for (uint32_t i = 0; i < request.outputs.size(); i++) {
352 const uint32_t poolIndex = request.outputs[i].location.poolIndex;
353 const auto& pool = request.pools[poolIndex];
354 if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
355 const auto unsignedDimensions = toUnsigned(outputShapes[i].dimensions).value();
356 if (!bufferWrappers[poolIndex]->updateDimensions(unsignedDimensions)) {
357 return aidl_hal::ErrorStatus::GENERAL_FAILURE;
358 }
359 }
360 }
361 for (uint32_t i = 0; i < request.outputs.size(); i++) {
362 const uint32_t poolIndex = request.outputs[i].location.poolIndex;
363 const auto& pool = request.pools[poolIndex];
364 if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
365 bufferWrappers[poolIndex]->setInitialized(true);
366 }
367 }
368 } else if (status == aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
369 // If CpuExecutor reports OUTPUT_INSUFFCIENT_SIZE on a device memory, this is because the
370 // dimensions of the device memory are incorrectly specified. The driver should return
371 // GENERAL_FAILURE instead in this case.
372 for (uint32_t i = 0; i < request.outputs.size(); i++) {
373 const uint32_t poolIndex = request.outputs[i].location.poolIndex;
374 const auto& pool = request.pools[poolIndex];
375 if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
376 if (!outputShapes[i].isSufficient) {
377 LOG(ERROR) << "Invalid dimensions for output " << i
378 << ": actual shape = " << toString(outputShapes[i].dimensions);
379 return aidl_hal::ErrorStatus::GENERAL_FAILURE;
380 }
381 }
382 }
383 }
384 return aidl_hal::ErrorStatus::NONE;
385 }
386
executeSynchronously(const aidl_hal::Request & halRequest,bool measureTiming,int64_t halDeadlineNs,int64_t loopTimeoutDurationNs,aidl_hal::ExecutionResult * executionResult)387 ndk::ScopedAStatus SamplePreparedModel::executeSynchronously(
388 const aidl_hal::Request& halRequest, bool measureTiming, int64_t halDeadlineNs,
389 int64_t loopTimeoutDurationNs, aidl_hal::ExecutionResult* executionResult) {
390 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
391 "SampleDriver::executeSynchronously");
392 VLOG(DRIVER) << "executeSynchronously(" << SHOW_IF_DEBUG(halRequest.toString()) << ")";
393
394 TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
395 if (measureTiming) driverStart = Clock::now();
396
397 const auto model = convert(mModel).value();
398
399 auto maybeRequest = convert(halRequest);
400 if (!maybeRequest.has_value()) {
401 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message);
402 }
403 const auto request = std::move(maybeRequest).value();
404
405 const auto validationResult = validateRequestForModel(request, model);
406 if (!validationResult.ok()) {
407 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error());
408 }
409
410 if (halDeadlineNs < -1) {
411 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
412 "Invalid deadline: " + toString(halDeadlineNs));
413 }
414 if (loopTimeoutDurationNs < -1) {
415 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
416 "Invalid loop timeout duration: " + toString(loopTimeoutDurationNs));
417 }
418
419 const auto deadline = makeDeadline(halDeadlineNs);
420 if (hasDeadlinePassed(deadline)) {
421 return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT);
422 }
423
424 NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS,
425 "SampleDriver::executeSynchronouslyBase");
426 const auto [poolStatus, requestPoolInfos, bufferWrappers] =
427 createRunTimePoolInfos(request, *mDriver, this);
428 if (poolStatus != aidl_hal::ErrorStatus::NONE) {
429 return toAStatus(poolStatus);
430 }
431
432 NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
433 "SampleDriver::executeSynchronouslyBase");
434 CpuExecutor executor = mDriver->getExecutor();
435 if (loopTimeoutDurationNs >= 0) {
436 executor.setLoopTimeout(loopTimeoutDurationNs);
437 }
438 if (deadline.has_value()) {
439 executor.setDeadline(*deadline);
440 }
441 if (measureTiming) deviceStart = Clock::now();
442 int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
443 if (measureTiming) deviceEnd = Clock::now();
444 VLOG(DRIVER) << "executor.run returned " << n;
445 aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n);
446 if (executionStatus != aidl_hal::ErrorStatus::NONE &&
447 executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
448 return toAStatus(executionStatus);
449 }
450 auto outputShapes = aidl_hal::utils::convert(executor.getOutputShapes()).value();
451
452 // Update device memory metadata.
453 const aidl_hal::ErrorStatus updateStatus =
454 updateDeviceMemories(executionStatus, request, bufferWrappers, outputShapes);
455 if (updateStatus != aidl_hal::ErrorStatus::NONE) {
456 return toAStatus(updateStatus);
457 }
458
459 executionResult->outputSufficientSize =
460 executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
461 executionResult->outputShapes = std::move(outputShapes);
462 executionResult->timing = kNoTiming;
463 if (measureTiming && executionStatus == aidl_hal::ErrorStatus::NONE) {
464 driverEnd = Clock::now();
465 aidl_hal::Timing timing = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
466 .timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)};
467 VLOG(DRIVER) << "executeSynchronously timing = " << timing.toString();
468
469 executionResult->timing = timing;
470 }
471 return ndk::ScopedAStatus::ok();
472 }
473
474 // The sample driver will finish the execution and then return.
executeFenced(const aidl_hal::Request & halRequest,const std::vector<ndk::ScopedFileDescriptor> & waitFor,bool measureTiming,int64_t halDeadlineNs,int64_t loopTimeoutDurationNs,int64_t durationNs,aidl_hal::FencedExecutionResult * executionResult)475 ndk::ScopedAStatus SamplePreparedModel::executeFenced(
476 const aidl_hal::Request& halRequest, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
477 bool measureTiming, int64_t halDeadlineNs, int64_t loopTimeoutDurationNs,
478 int64_t durationNs, aidl_hal::FencedExecutionResult* executionResult) {
479 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
480 "SamplePreparedModel::executeFenced");
481 VLOG(DRIVER) << "executeFenced(" << SHOW_IF_DEBUG(halRequest.toString()) << ")";
482
483 TimePoint driverStart, driverEnd, deviceStart, deviceEnd;
484 if (measureTiming) driverStart = Clock::now();
485
486 const auto model = convert(mModel).value();
487
488 auto maybeRequest = convert(halRequest);
489 if (!maybeRequest.has_value()) {
490 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message);
491 }
492 const auto request = std::move(maybeRequest).value();
493
494 const auto validationResult =
495 validateRequestForModel(request, model, /*allowUnspecifiedOutput=*/false);
496 if (!validationResult.ok()) {
497 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error());
498 }
499
500 if (halDeadlineNs < -1) {
501 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
502 "Invalid deadline: " + toString(halDeadlineNs));
503 }
504 if (loopTimeoutDurationNs < -1) {
505 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
506 "Invalid loop timeout duration: " + toString(loopTimeoutDurationNs));
507 }
508 if (durationNs < -1) {
509 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
510 "Invalid fenced execution duration: " + toString(durationNs));
511 }
512
513 const auto deadline = makeDeadline(halDeadlineNs);
514 if (hasDeadlinePassed(deadline)) {
515 return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT);
516 }
517
518 // Wait for the dependent events to signal
519 for (const auto& fenceHandle : waitFor) {
520 int syncFenceFd = fenceHandle.get();
521 if (syncWait(syncFenceFd, -1) != FenceState::SIGNALED) {
522 LOG(ERROR) << "syncWait failed";
523 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "syncWait failed");
524 }
525 }
526
527 // Update deadline if the timeout duration is closer than the deadline.
528 auto closestDeadline = deadline;
529 if (durationNs >= 0) {
530 const auto timeoutDurationDeadline = makeDeadline(durationNs);
531 if (!closestDeadline.has_value() || *closestDeadline > timeoutDurationDeadline) {
532 closestDeadline = timeoutDurationDeadline;
533 }
534 }
535
536 TimePoint driverStartAfterFence;
537 if (measureTiming) driverStartAfterFence = Clock::now();
538
539 NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS,
540 "SamplePreparedModel::executeFenced");
541 const auto [poolStatus, requestPoolInfos, bufferWrappers] =
542 createRunTimePoolInfos(request, *mDriver, this);
543 if (poolStatus != aidl_hal::ErrorStatus::NONE) {
544 return toAStatus(poolStatus);
545 }
546
547 NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
548 "SamplePreparedModel::executeFenced");
549 CpuExecutor executor = mDriver->getExecutor();
550 if (loopTimeoutDurationNs >= 0) {
551 executor.setLoopTimeout(loopTimeoutDurationNs);
552 }
553 if (closestDeadline.has_value()) {
554 executor.setDeadline(*closestDeadline);
555 }
556 if (measureTiming) deviceStart = Clock::now();
557 int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
558 if (measureTiming) deviceEnd = Clock::now();
559 VLOG(DRIVER) << "executor.run returned " << n;
560 aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n);
561 if (executionStatus != aidl_hal::ErrorStatus::NONE) {
562 return toAStatus(executionStatus);
563 }
564
565 // Set output memories to the initialized state.
566 if (executionStatus == aidl_hal::ErrorStatus::NONE) {
567 for (const auto& output : request.outputs) {
568 const uint32_t poolIndex = output.location.poolIndex;
569 const auto& pool = request.pools[poolIndex];
570 if (std::holds_alternative<Request::MemoryDomainToken>(pool)) {
571 bufferWrappers[poolIndex]->setInitialized(true);
572 }
573 }
574 }
575
576 aidl_hal::Timing timingSinceLaunch = kNoTiming;
577 aidl_hal::Timing timingAfterFence = kNoTiming;
578 if (measureTiming) {
579 driverEnd = Clock::now();
580 timingSinceLaunch = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
581 .timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)};
582 timingAfterFence = {
583 .timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart),
584 .timeInDriverNs = nanosecondsDuration(driverEnd, driverStartAfterFence)};
585 VLOG(DRIVER) << "executeFenced timingSinceLaunch = " << timingSinceLaunch.toString();
586 VLOG(DRIVER) << "executeFenced timingAfterFence = " << timingAfterFence.toString();
587 }
588
589 executionResult->callback = ndk::SharedRefBase::make<SampleFencedExecutionCallback>(
590 timingSinceLaunch, timingAfterFence, executionStatus);
591 executionResult->syncFence = ndk::ScopedFileDescriptor();
592 return ndk::ScopedAStatus::ok();
593 }
594
configureExecutionBurst(std::shared_ptr<aidl_hal::IBurst> * burst)595 ndk::ScopedAStatus SamplePreparedModel::configureExecutionBurst(
596 std::shared_ptr<aidl_hal::IBurst>* burst) {
597 std::shared_ptr<SamplePreparedModel> self = this->template ref<SamplePreparedModel>();
598 *burst = ndk::SharedRefBase::make<SampleBurst>(std::move(self));
599 return ndk::ScopedAStatus::ok();
600 }
601
SampleBurst(std::shared_ptr<SamplePreparedModel> preparedModel)602 SampleBurst::SampleBurst(std::shared_ptr<SamplePreparedModel> preparedModel)
603 : kPreparedModel(std::move(preparedModel)) {
604 CHECK(kPreparedModel != nullptr);
605 }
606
executeSynchronously(const aidl_hal::Request & request,const std::vector<int64_t> & memoryIdentifierTokens,bool measureTiming,int64_t deadlineNs,int64_t loopTimeoutDurationNs,aidl_hal::ExecutionResult * executionResult)607 ndk::ScopedAStatus SampleBurst::executeSynchronously(
608 const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
609 bool measureTiming, int64_t deadlineNs, int64_t loopTimeoutDurationNs,
610 aidl_hal::ExecutionResult* executionResult) {
611 if (request.pools.size() != memoryIdentifierTokens.size()) {
612 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT,
613 "request.pools.size() != memoryIdentifierTokens.size()");
614 }
615 if (!std::all_of(memoryIdentifierTokens.begin(), memoryIdentifierTokens.end(),
616 [](int64_t token) { return token >= -1; })) {
617 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierTokens");
618 }
619
620 // Ensure at most one execution is in flight at a time.
621 const bool executionAlreadyInFlight = mExecutionInFlight.test_and_set();
622 if (executionAlreadyInFlight) {
623 return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE,
624 "Burst object supports at most one execution at a time");
625 }
626 const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
627
628 return kPreparedModel->executeSynchronously(request, measureTiming, deadlineNs,
629 loopTimeoutDurationNs, executionResult);
630 }
631
releaseMemoryResource(int64_t memoryIdentifierToken)632 ndk::ScopedAStatus SampleBurst::releaseMemoryResource(int64_t memoryIdentifierToken) {
633 if (memoryIdentifierToken < -1) {
634 return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierToken");
635 }
636 return ndk::ScopedAStatus::ok();
637 }
638
639 } // namespace sample_driver
640 } // namespace nn
641 } // namespace android
642