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