1 /*
2 * Copyright (C) 2021 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 #include "CanonicalDevice.h"
18
19 #include <Tracing.h>
20 #include <android-base/logging.h>
21 #include <nnapi/IBuffer.h>
22 #include <nnapi/IDevice.h>
23 #include <nnapi/IPreparedModel.h>
24 #include <nnapi/OperandTypes.h>
25 #include <nnapi/Result.h>
26 #include <nnapi/Types.h>
27 #include <nnapi/Validation.h>
28
29 #include <algorithm>
30 #include <any>
31 #include <functional>
32 #include <iterator>
33 #include <memory>
34 #include <optional>
35 #include <set>
36 #include <string>
37 #include <utility>
38 #include <vector>
39
40 #include "CanonicalBuffer.h"
41 #include "CanonicalPreparedModel.h"
42
43 namespace android::nn::sample {
44 namespace {
45
makeCapabilities()46 Capabilities makeCapabilities() {
47 constexpr float kPerf = 1.0f;
48 const Capabilities::PerformanceInfo kPerfInfo = {.execTime = kPerf, .powerUsage = kPerf};
49
50 constexpr OperandType kOperandsTypes[] = {
51 OperandType::FLOAT32,
52 OperandType::INT32,
53 OperandType::UINT32,
54 OperandType::TENSOR_FLOAT32,
55 OperandType::TENSOR_INT32,
56 OperandType::TENSOR_QUANT8_ASYMM,
57 OperandType::BOOL,
58 OperandType::TENSOR_QUANT16_SYMM,
59 OperandType::TENSOR_FLOAT16,
60 OperandType::TENSOR_BOOL8,
61 OperandType::FLOAT16,
62 OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL,
63 OperandType::TENSOR_QUANT16_ASYMM,
64 OperandType::TENSOR_QUANT8_SYMM,
65 OperandType::TENSOR_QUANT8_ASYMM_SIGNED,
66 };
67
68 std::vector<Capabilities::OperandPerformance> operandPerformance;
69 operandPerformance.reserve(std::size(kOperandsTypes));
70 std::transform(std::begin(kOperandsTypes), std::end(kOperandsTypes),
71 std::back_inserter(operandPerformance), [kPerfInfo](OperandType op) {
72 return Capabilities::OperandPerformance{.type = op, .info = kPerfInfo};
73 });
74 auto table =
75 Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)).value();
76
77 return {.relaxedFloat32toFloat16PerformanceScalar = kPerfInfo,
78 .relaxedFloat32toFloat16PerformanceTensor = kPerfInfo,
79 .operandPerformance = std::move(table),
80 .ifPerformance = kPerfInfo,
81 .whilePerformance = kPerfInfo};
82 }
83
toString(const Dimensions & dimensions)84 std::string toString(const Dimensions& dimensions) {
85 std::ostringstream oss;
86 oss << "[";
87 for (size_t i = 0; i < dimensions.size(); ++i) {
88 if (i != 0) oss << ", ";
89 oss << dimensions[i];
90 }
91 oss << "]";
92 return oss.str();
93 }
94
95 } // namespace
96
Device(std::string name,const IOperationResolver * operationResolver)97 Device::Device(std::string name, const IOperationResolver* operationResolver)
98 : kName(std::move(name)), kOperationResolver(*operationResolver) {
99 CHECK(operationResolver != nullptr);
100 initVLogMask();
101 }
102
getName() const103 const std::string& Device::getName() const {
104 return kName;
105 }
106
getVersionString() const107 const std::string& Device::getVersionString() const {
108 static const std::string kVersionString = "JUST_AN_EXAMPLE";
109 return kVersionString;
110 }
111
getFeatureLevel() const112 Version Device::getFeatureLevel() const {
113 return kVersionFeatureLevel8;
114 }
115
getType() const116 DeviceType Device::getType() const {
117 return DeviceType::CPU;
118 }
119
getSupportedExtensions() const120 const std::vector<Extension>& Device::getSupportedExtensions() const {
121 static const std::vector<Extension> kExtensions = {/* No extensions. */};
122 return kExtensions;
123 }
124
getCapabilities() const125 const Capabilities& Device::getCapabilities() const {
126 static const Capabilities kCapabilities = makeCapabilities();
127 return kCapabilities;
128 }
129
getNumberOfCacheFilesNeeded() const130 std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
131 return std::make_pair(/*numModelCache=*/0, /*numDataCache=*/0);
132 }
133
wait() const134 GeneralResult<void> Device::wait() const {
135 return {};
136 }
137
getSupportedOperations(const Model & model) const138 GeneralResult<std::vector<bool>> Device::getSupportedOperations(const Model& model) const {
139 VLOG(DRIVER) << "sample::Device::getSupportedOperations";
140
141 // Validate arguments.
142 if (const auto result = validate(model); !result.ok()) {
143 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << result.error();
144 }
145
146 // Mark all operations except extension operations as supported.
147 std::vector<bool> supported;
148 supported.reserve(model.main.operations.size());
149 std::transform(model.main.operations.begin(), model.main.operations.end(),
150 std::back_inserter(supported), [](const Operation& operation) {
151 return !isExtensionOperationType(operation.type) &&
152 operation.type != OperationType::OEM_OPERATION;
153 });
154
155 return supported;
156 }
157
prepareModel(const Model & model,ExecutionPreference preference,Priority priority,OptionalTimePoint deadline,const std::vector<SharedHandle> &,const std::vector<SharedHandle> &,const CacheToken &,const std::vector<TokenValuePair> &,const std::vector<ExtensionNameAndPrefix> &) const158 GeneralResult<SharedPreparedModel> Device::prepareModel(
159 const Model& model, ExecutionPreference preference, Priority priority,
160 OptionalTimePoint deadline, const std::vector<SharedHandle>& /*modelCache*/,
161 const std::vector<SharedHandle>& /*dataCache*/, const CacheToken& /*token*/,
162 const std::vector<TokenValuePair>& /*hints*/,
163 const std::vector<ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const {
164 if (VLOG_IS_ON(DRIVER)) {
165 VLOG(DRIVER) << "sample::Device::prepareModel";
166 logModelToInfo(model);
167 }
168
169 // Validate arguments.
170 if (const auto result = validate(model); !result.ok()) {
171 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid Model: " << result.error();
172 }
173 if (const auto result = validate(preference); !result.ok()) {
174 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT)
175 << "Invalid ExecutionPreference: " << result.error();
176 }
177 if (const auto result = validate(priority); !result.ok()) {
178 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid Priority: " << result.error();
179 }
180
181 // Check if deadline has passed.
182 if (hasDeadlinePassed(deadline)) {
183 return NN_ERROR(ErrorStatus::MISSED_DEADLINE_PERSISTENT);
184 }
185
186 std::vector<RunTimePoolInfo> poolInfos;
187 if (!setRunTimePoolInfosFromCanonicalMemories(&poolInfos, model.pools)) {
188 return NN_ERROR() << "setRunTimePoolInfosFromCanonicalMemories failed";
189 }
190
191 // Create the prepared model.
192 return std::make_shared<const PreparedModel>(model, preference, priority, &kOperationResolver,
193 kBufferTracker, std::move(poolInfos));
194 }
195
prepareModelFromCache(OptionalTimePoint,const std::vector<SharedHandle> &,const std::vector<SharedHandle> &,const CacheToken &) const196 GeneralResult<SharedPreparedModel> Device::prepareModelFromCache(
197 OptionalTimePoint /*deadline*/, const std::vector<SharedHandle>& /*modelCache*/,
198 const std::vector<SharedHandle>& /*dataCache*/, const CacheToken& /*token*/) const {
199 NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION,
200 "sample::Device::prepareModelFromCache");
201 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
202 << "prepareModelFromCache not supported on sample::Device::prepareModelFromCache("
203 << kName << ")";
204 }
205
allocate(const BufferDesc & desc,const std::vector<SharedPreparedModel> & preparedModels,const std::vector<BufferRole> & inputRoles,const std::vector<BufferRole> & outputRoles) const206 GeneralResult<SharedBuffer> Device::allocate(const BufferDesc& desc,
207 const std::vector<SharedPreparedModel>& preparedModels,
208 const std::vector<BufferRole>& inputRoles,
209 const std::vector<BufferRole>& outputRoles) const {
210 VLOG(DRIVER) << "sample::Device::allocate";
211 std::set<PreparedModelRole> roles;
212 Operand operand;
213 auto getModel = [](const SharedPreparedModel& preparedModel) -> const Model* {
214 std::any resource = preparedModel->getUnderlyingResource();
215 const Model** maybeModel = std::any_cast<const Model*>(&resource);
216 if (maybeModel == nullptr) {
217 LOG(ERROR) << "sample::Device::allocate -- unknown remote IPreparedModel.";
218 return nullptr;
219 }
220 return *maybeModel;
221 };
222 if (const auto result = validateMemoryDesc(desc, preparedModels, inputRoles, outputRoles,
223 getModel, &roles, &operand);
224 !result.ok()) {
225 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT)
226 << "sample::Device::allocate -- validation failed: " << result.error();
227 }
228
229 if (isExtensionOperandType(operand.type)) {
230 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
231 << "sample::Device::allocate -- does not support extension type.";
232 }
233
234 // TODO(xusongw): Support allocating buffers with unknown dimensions or rank.
235 uint32_t size = nonExtensionOperandSizeOfData(operand.type, operand.dimensions);
236 VLOG(DRIVER) << "sample::Device::allocate -- type = " << operand.type
237 << ", dimensions = " << toString(operand.dimensions) << ", size = " << size;
238 if (size == 0) {
239 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
240 << "sample::Device::allocate -- does not support dynamic output shape.";
241 }
242
243 auto bufferWrapper = ManagedBuffer::create(size, std::move(roles), operand);
244 if (bufferWrapper == nullptr) {
245 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
246 << "sample::Device::allocate -- not enough memory.";
247 }
248
249 auto token = kBufferTracker->add(bufferWrapper);
250 if (token == nullptr) {
251 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
252 << "sample::Device::allocate -- BufferTracker returned invalid token.";
253 }
254
255 auto sampleBuffer = std::make_shared<const Buffer>(std::move(bufferWrapper), std::move(token));
256 VLOG(DRIVER) << "sample::Device::allocate -- successfully allocates the requested memory";
257 return sampleBuffer;
258 }
259
260 } // namespace android::nn::sample
261