If possible use BLOB mode AHWB instead of ashmem for a memory object created by ANNMemory_createFromDesc
Bug: 147777318
Test: NNT_static
Change-Id: I11d3afea445149c07fd78f2d211d28e323b9c2ae
Merged-In: I11d3afea445149c07fd78f2d211d28e323b9c2ae
Merged-In: I11d3afea445149c07fd78f2d211d28e323b9c2ae
(cherry picked from commit f3a2684855562719a5c181af4b12e46c51668a12)
(cherry picked from commit 603346b6b80cd4227d070162f1b16bb5838b2102)
diff --git a/runtime/ExecutionBuilder.cpp b/runtime/ExecutionBuilder.cpp
index 5905ea5..4ab401e 100644
--- a/runtime/ExecutionBuilder.cpp
+++ b/runtime/ExecutionBuilder.cpp
@@ -152,7 +152,7 @@
length)) {
return ANEURALNETWORKS_BAD_DATA;
}
- // For some types of memory, e.g. MemoryAshmem allocated from ANNMemory_createFromDesc, we
+ // For some types of memory, e.g. MemoryRuntimeAHWB allocated from ANNMemory_createFromDesc, we
// allow the client to specify offset == 0 && length == 0 indicating that the entire memory
// region is used. We update the length here because the drivers are still expecting a real
// length. For other memories that do not allow this semantic, it is checked in
@@ -213,7 +213,7 @@
length)) {
return ANEURALNETWORKS_BAD_DATA;
}
- // For some types of memory, e.g. MemoryAshmem allocated from ANNMemory_createFromDesc, we
+ // For some types of memory, e.g. MemoryRuntimeAHWB allocated from ANNMemory_createFromDesc, we
// allow the client to specify offset == 0 && length == 0 indicating that the entire memory
// region is used. We update the length here because the drivers are still expecting a real
// length. For other memories that do not allow this semantic, it is checked in
diff --git a/runtime/Memory.cpp b/runtime/Memory.cpp
index 9bddcd9..1f16a28 100644
--- a/runtime/Memory.cpp
+++ b/runtime/Memory.cpp
@@ -18,6 +18,10 @@
#include "Memory.h"
+#include <android-base/scopeguard.h>
+#include <android/hardware_buffer.h>
+#include <vndk/hardware_buffer.h>
+
#include <algorithm>
#include <memory>
#include <set>
@@ -256,8 +260,9 @@
static int copyIBuffers(const sp<IBuffer>& src, const sp<IBuffer>& dst,
const MemoryValidatorBase::Metadata& srcMetadata) {
- // TODO(xusongw): Use BLOB mode AHardwareBuffer.
- hidl_memory hidlMemory = allocateSharedMemory(srcMetadata.logicalSize);
+ const auto [n, memory] = MemoryRuntimeAHWB::create(srcMetadata.logicalSize);
+ NN_RETURN_IF_ERROR(n);
+ const hidl_memory& hidlMemory = memory->getHidlMemory();
if (!hidlMemory.valid()) return ANEURALNETWORKS_OUT_OF_MEMORY;
NN_RETURN_IF_ERROR(copyIBufferToHidlMemory(src, hidlMemory));
NN_RETURN_IF_ERROR(copyHidlMemoryToIBuffer(hidlMemory, dst, srcMetadata.dimensions));
@@ -429,20 +434,13 @@
LOG(INFO) << "MemoryDescriptor end";
}
-static const Device* selectDeviceMemoryAllocator(const MemoryDescriptor& desc) {
- const Device* allocator = nullptr;
+static std::set<const Device*> getDevices(const MemoryDescriptor& desc) {
+ std::set<const Device*> devices;
for (const auto* preparedModel : desc.preparedModels) {
const auto* device = preparedModel->getDevice();
- if (allocator == nullptr) {
- allocator = device;
- } else if (allocator != device) {
- LOG(INFO) << "selectDeviceMemoryAllocator -- cannot handle multiple devices.";
- return nullptr;
- }
+ devices.insert(device);
}
- CHECK(allocator != nullptr);
- VLOG(MEMORY) << "Using " << allocator->getName() << " as allocator.";
- return allocator;
+ return devices;
}
int MemoryBuilder::finish() {
@@ -455,7 +453,17 @@
if (VLOG_IS_ON(MEMORY)) {
logMemoryDescriptorToInfo(mDesc, mOperand.value());
}
- mAllocator = selectDeviceMemoryAllocator(mDesc);
+ std::set<const Device*> devices = getDevices(mDesc);
+ if (devices.size() == 1) {
+ mAllocator = *devices.begin();
+ VLOG(MEMORY) << "Using " << mAllocator->getName() << " as allocator.";
+ } else {
+ LOG(INFO) << "MemoryBuilder::finish -- cannot handle multiple devices.";
+ mAllocator = nullptr;
+ }
+ mSupportsAhwb = std::all_of(devices.begin(), devices.end(), [](const auto* device) {
+ return device->getFeatureLevel() >= __ANDROID_API_Q__;
+ });
mShouldFallback = std::none_of(mRoles.begin(), mRoles.end(), [](const auto& role) {
const auto* cb = std::get<const CompilationBuilder*>(role);
return cb->createdWithExplicitDeviceList();
@@ -487,11 +495,15 @@
std::tie(n, memory) = mAllocator->allocate(mDesc, mOperand->type);
}
- // If failed, fallback to ashmem.
- // TODO(xusongw): Use BLOB mode hardware buffer when possible.
+ // If failed, fallback to ashmem or BLOB mode AHWB.
if (n != ANEURALNETWORKS_NO_ERROR && mShouldFallback) {
- VLOG(MEMORY) << "MemoryBuilder::allocate -- fallback to ashmem.";
- std::tie(n, memory) = MemoryAshmem::create(size);
+ if (mSupportsAhwb) {
+ VLOG(MEMORY) << "MemoryBuilder::allocate -- fallback to BLOB mode AHWB.";
+ std::tie(n, memory) = MemoryRuntimeAHWB::create(size);
+ } else {
+ VLOG(MEMORY) << "MemoryBuilder::allocate -- fallback to ashmem.";
+ std::tie(n, memory) = MemoryAshmem::create(size);
+ }
}
if (n == ANEURALNETWORKS_NO_ERROR) {
@@ -570,7 +582,6 @@
AHardwareBuffer_describe(&ahwb, &bufferDesc);
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(&ahwb);
hidl_memory hidlMemory;
- std::unique_ptr<MemoryAHWB> memory;
std::unique_ptr<MemoryValidatorBase> validator;
if (bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB) {
hidlMemory = hidl_memory("hardware_buffer_blob", handle, bufferDesc.width);
@@ -580,10 +591,62 @@
hidlMemory = hidl_memory("hardware_buffer", handle, 0);
validator = std::make_unique<AHardwareBufferNonBlobValidator>();
}
- memory = std::make_unique<MemoryAHWB>(std::move(hidlMemory), std::move(validator));
+ auto memory = std::make_unique<MemoryAHWB>(std::move(hidlMemory), std::move(validator));
return {ANEURALNETWORKS_NO_ERROR, std::move(memory)};
};
+std::pair<int, std::unique_ptr<MemoryRuntimeAHWB>> MemoryRuntimeAHWB::create(uint32_t size) {
+ AHardwareBuffer* ahwb = nullptr;
+ const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ const AHardwareBuffer_Desc desc = {
+ .width = size,
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = usage,
+ .stride = size,
+ };
+ int err = AHardwareBuffer_allocate(&desc, &ahwb);
+ if (err != 0 || ahwb == nullptr) {
+ LOG(ERROR) << "Failed to allocate BLOB mode AHWB.";
+ return {ANEURALNETWORKS_OP_FAILED, nullptr};
+ }
+ auto allocateGuard = base::make_scope_guard([&ahwb]() { AHardwareBuffer_release(ahwb); });
+
+ void* buffer = nullptr;
+ err = AHardwareBuffer_lock(ahwb, usage, -1, nullptr, &buffer);
+ if (err != 0 || buffer == nullptr) {
+ LOG(ERROR) << "Failed to lock BLOB mode AHWB.";
+ return {ANEURALNETWORKS_OP_FAILED, nullptr};
+ }
+ auto lockGuard = base::make_scope_guard([&ahwb]() { AHardwareBuffer_unlock(ahwb, nullptr); });
+
+ const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahwb);
+ if (handle == nullptr) {
+ LOG(ERROR) << "Failed to retrieve the native handle from the AHWB.";
+ return {ANEURALNETWORKS_OP_FAILED, nullptr};
+ }
+
+ hidl_memory hidlMemory = hidl_memory("hardware_buffer_blob", handle, desc.width);
+ auto memory = std::make_unique<MemoryRuntimeAHWB>(std::move(hidlMemory), ahwb,
+ static_cast<uint8_t*>(buffer));
+ allocateGuard.Disable();
+ lockGuard.Disable();
+ return {ANEURALNETWORKS_NO_ERROR, std::move(memory)};
+}
+
+MemoryRuntimeAHWB::MemoryRuntimeAHWB(hal::hidl_memory memory, AHardwareBuffer* ahwb,
+ uint8_t* buffer)
+ : Memory(std::move(memory)), mAhwb(ahwb), mBuffer(buffer) {
+ CHECK(mAhwb != nullptr);
+ CHECK(mBuffer != nullptr);
+}
+
+MemoryRuntimeAHWB::~MemoryRuntimeAHWB() {
+ AHardwareBuffer_unlock(mAhwb, nullptr);
+ AHardwareBuffer_release(mAhwb);
+}
+
std::pair<int, std::unique_ptr<MemoryFromDevice>> MemoryFromDevice::create(sp<hal::IBuffer> buffer,
uint32_t token) {
if (buffer == nullptr) {
diff --git a/runtime/Memory.h b/runtime/Memory.h
index 80c5251..a73201a 100644
--- a/runtime/Memory.h
+++ b/runtime/Memory.h
@@ -252,6 +252,9 @@
// The chosen device to allocate the memory. Set to nullptr if there are multiple devices.
const Device* mAllocator = nullptr;
+ // Whether BLOB mode AHWB is supported on all of the relevant devices of the roles.
+ bool mSupportsAhwb = false;
+
// If set to true, allocate() will fallback to Ashmem or AHardwareBuffer if the memory
// allocation fails on the chosen device, or if there is no device chosen.
bool mShouldFallback = true;
@@ -313,6 +316,30 @@
: Memory(std::move(memory), std::move(validator)) {}
};
+class MemoryRuntimeAHWB : public Memory {
+ public:
+ // Create a memory object containing a new BLOB-mode AHardwareBuffer memory
+ // object of the size specified in bytes. The created memory is managed and
+ // owned by the NNAPI runtime.
+ //
+ // On success, returns ANEURALNETWORKS_NO_ERROR and a memory object.
+ // On error, returns the appropriate NNAPI error code and nullptr.
+ static std::pair<int, std::unique_ptr<MemoryRuntimeAHWB>> create(uint32_t size);
+
+ // Get a pointer to the content of the memory. The returned pointer is
+ // valid for the lifetime of the MemoryRuntimeAHWB object. This call always
+ // returns non-null because it was validated during MemoryRuntimeAHWB::create.
+ uint8_t* getPointer() const { return mBuffer; }
+
+ // prefer using MemoryRuntimeAHWB::create
+ MemoryRuntimeAHWB(hal::hidl_memory memory, AHardwareBuffer* ahwb, uint8_t* buffer);
+ ~MemoryRuntimeAHWB();
+
+ private:
+ AHardwareBuffer* const mAhwb;
+ uint8_t* const mBuffer;
+};
+
class MemoryFromDevice : public Memory {
public:
// Create a memory object to keep track of a driver-allocated device memory.