Internal change.
PiperOrigin-RevId: 494166776
This commit is contained in:
parent
5bc1baf96a
commit
db3cb68d91
71
mediapipe/framework/formats/tensor_hardware_buffer.h
Normal file
71
mediapipe/framework/formats/tensor_hardware_buffer.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#ifndef MEDIAPIPE_FRAMEWORK_FORMATS_TENSOR_HARDWARE_BUFFER_H_
|
||||
#define MEDIAPIPE_FRAMEWORK_FORMATS_TENSOR_HARDWARE_BUFFER_H_
|
||||
|
||||
#if !defined(MEDIAPIPE_NO_JNI) && \
|
||||
(__ANDROID_API__ >= 26 || \
|
||||
defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "mediapipe/framework/formats/tensor_buffer.h"
|
||||
#include "mediapipe/framework/formats/tensor_internal.h"
|
||||
#include "mediapipe/framework/formats/tensor_v2.h"
|
||||
|
||||
namespace mediapipe {
|
||||
|
||||
// Supports:
|
||||
// - float 16 and 32 bits
|
||||
// - signed / unsigned integers 8,16,32 bits
|
||||
class TensorHardwareBufferView;
|
||||
struct TensorHardwareBufferViewDescriptor : public Tensor::ViewDescriptor {
|
||||
using ViewT = TensorHardwareBufferView;
|
||||
TensorBufferDescriptor buffer;
|
||||
};
|
||||
|
||||
class TensorHardwareBufferView : public Tensor::View {
|
||||
public:
|
||||
TENSOR_UNIQUE_VIEW_TYPE_ID();
|
||||
~TensorHardwareBufferView() = default;
|
||||
|
||||
const TensorHardwareBufferViewDescriptor& descriptor() const override {
|
||||
return descriptor_;
|
||||
}
|
||||
AHardwareBuffer* handle() const { return ahwb_handle_; }
|
||||
|
||||
protected:
|
||||
TensorHardwareBufferView(int access_capability, Tensor::View::Access access,
|
||||
Tensor::View::State state,
|
||||
const TensorHardwareBufferViewDescriptor& desc,
|
||||
AHardwareBuffer* ahwb_handle)
|
||||
: Tensor::View(kId, access_capability, access, state),
|
||||
descriptor_(desc),
|
||||
ahwb_handle_(ahwb_handle) {}
|
||||
|
||||
private:
|
||||
bool MatchDescriptor(
|
||||
uint64_t view_type_id,
|
||||
const Tensor::ViewDescriptor& base_descriptor) const override {
|
||||
if (!Tensor::View::MatchDescriptor(view_type_id, base_descriptor))
|
||||
return false;
|
||||
auto descriptor =
|
||||
static_cast<const TensorHardwareBufferViewDescriptor&>(base_descriptor);
|
||||
return descriptor.buffer.format == descriptor_.buffer.format &&
|
||||
descriptor.buffer.size_alignment <=
|
||||
descriptor_.buffer.size_alignment &&
|
||||
descriptor_.buffer.size_alignment %
|
||||
descriptor.buffer.size_alignment ==
|
||||
0;
|
||||
}
|
||||
const TensorHardwareBufferViewDescriptor& descriptor_;
|
||||
AHardwareBuffer* ahwb_handle_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace mediapipe
|
||||
|
||||
#endif // !defined(MEDIAPIPE_NO_JNI) && \
|
||||
(__ANDROID_API__ >= 26 || \
|
||||
defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))
|
||||
|
||||
#endif // MEDIAPIPE_FRAMEWORK_FORMATS_TENSOR_HARDWARE_BUFFER_H_
|
|
@ -0,0 +1,216 @@
|
|||
#if !defined(MEDIAPIPE_NO_JNI) && \
|
||||
(__ANDROID_API__ >= 26 || \
|
||||
defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "mediapipe/framework/formats/tensor_backend.h"
|
||||
#include "mediapipe/framework/formats/tensor_cpu_buffer.h"
|
||||
#include "mediapipe/framework/formats/tensor_hardware_buffer.h"
|
||||
#include "mediapipe/framework/formats/tensor_v2.h"
|
||||
#include "util/task/status_macros.h"
|
||||
|
||||
namespace mediapipe {
|
||||
namespace {
|
||||
|
||||
class TensorCpuViewImpl : public TensorCpuView {
|
||||
public:
|
||||
TensorCpuViewImpl(int access_capabilities, Tensor::View::Access access,
|
||||
Tensor::View::State state,
|
||||
const TensorCpuViewDescriptor& descriptor, void* pointer,
|
||||
AHardwareBuffer* ahwb_handle)
|
||||
: TensorCpuView(access_capabilities, access, state, descriptor, pointer),
|
||||
ahwb_handle_(ahwb_handle) {}
|
||||
~TensorCpuViewImpl() {
|
||||
// If handle_ is null then this view is constructed in GetViews with no
|
||||
// access.
|
||||
if (ahwb_handle_) {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_unlock(ahwb_handle_, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AHardwareBuffer* ahwb_handle_;
|
||||
};
|
||||
|
||||
class TensorHardwareBufferViewImpl : public TensorHardwareBufferView {
|
||||
public:
|
||||
TensorHardwareBufferViewImpl(
|
||||
int access_capability, Tensor::View::Access access,
|
||||
Tensor::View::State state,
|
||||
const TensorHardwareBufferViewDescriptor& descriptor,
|
||||
AHardwareBuffer* handle)
|
||||
: TensorHardwareBufferView(access_capability, access, state, descriptor,
|
||||
handle) {}
|
||||
~TensorHardwareBufferViewImpl() = default;
|
||||
};
|
||||
|
||||
class HardwareBufferCpuStorage : public TensorStorage {
|
||||
public:
|
||||
~HardwareBufferCpuStorage() {
|
||||
if (!ahwb_handle_) return;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_release(ahwb_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
static absl::Status CanProvide(
|
||||
int access_capability, const Tensor::Shape& shape, uint64_t view_type_id,
|
||||
const Tensor::ViewDescriptor& base_descriptor) {
|
||||
// TODO: use AHardwareBuffer_isSupported for API >= 29.
|
||||
static const bool is_ahwb_supported = [] {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc desc = {};
|
||||
// Aligned to the largest possible virtual memory page size.
|
||||
constexpr uint32_t kPageSize = 16384;
|
||||
desc.width = kPageSize;
|
||||
desc.height = 1;
|
||||
desc.layers = 1;
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
|
||||
desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
|
||||
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
|
||||
AHardwareBuffer* handle;
|
||||
if (AHardwareBuffer_allocate(&desc, &handle) != 0) return false;
|
||||
AHardwareBuffer_release(handle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (!is_ahwb_supported) {
|
||||
return absl::UnavailableError(
|
||||
"AHardwareBuffer is not supported on the platform.");
|
||||
}
|
||||
|
||||
if (view_type_id != TensorCpuView::kId &&
|
||||
view_type_id != TensorHardwareBufferView::kId) {
|
||||
return absl::InvalidArgumentError(
|
||||
"A view type is not supported by this storage.");
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Tensor::View>> GetViews(uint64_t latest_version) {
|
||||
std::vector<std::unique_ptr<Tensor::View>> result;
|
||||
auto update_state = latest_version == version_
|
||||
? Tensor::View::State::kUpToDate
|
||||
: Tensor::View::State::kOutdated;
|
||||
if (ahwb_handle_) {
|
||||
result.push_back(
|
||||
std::unique_ptr<Tensor::View>(new TensorHardwareBufferViewImpl(
|
||||
kAccessCapability, Tensor::View::Access::kNoAccess, update_state,
|
||||
hw_descriptor_, ahwb_handle_)));
|
||||
|
||||
result.push_back(std::unique_ptr<Tensor::View>(new TensorCpuViewImpl(
|
||||
kAccessCapability, Tensor::View::Access::kNoAccess, update_state,
|
||||
cpu_descriptor_, nullptr, nullptr)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
absl::StatusOr<std::unique_ptr<Tensor::View>> GetView(
|
||||
Tensor::View::Access access, const Tensor::Shape& shape,
|
||||
uint64_t latest_version, uint64_t view_type_id,
|
||||
const Tensor::ViewDescriptor& base_descriptor, int access_capability) {
|
||||
MP_RETURN_IF_ERROR(
|
||||
CanProvide(access_capability, shape, view_type_id, base_descriptor));
|
||||
const auto& buffer_descriptor =
|
||||
view_type_id == TensorHardwareBufferView::kId
|
||||
? static_cast<const TensorHardwareBufferViewDescriptor&>(
|
||||
base_descriptor)
|
||||
.buffer
|
||||
: static_cast<const TensorCpuViewDescriptor&>(base_descriptor)
|
||||
.buffer;
|
||||
if (!ahwb_handle_) {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc desc = {};
|
||||
desc.width = TensorBufferSize(buffer_descriptor, shape);
|
||||
desc.height = 1;
|
||||
desc.layers = 1;
|
||||
desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
|
||||
// TODO: Use access capabilities to set hints.
|
||||
desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
|
||||
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
|
||||
auto error = AHardwareBuffer_allocate(&desc, &ahwb_handle_);
|
||||
if (error != 0) {
|
||||
return absl::UnknownError(
|
||||
absl::StrCat("Error allocating hardware buffer: ", error));
|
||||
}
|
||||
// Fill all possible views to provide it as proto views.
|
||||
hw_descriptor_.buffer = buffer_descriptor;
|
||||
cpu_descriptor_.buffer = buffer_descriptor;
|
||||
}
|
||||
}
|
||||
if (buffer_descriptor.format != hw_descriptor_.buffer.format ||
|
||||
buffer_descriptor.size_alignment >
|
||||
hw_descriptor_.buffer.size_alignment ||
|
||||
hw_descriptor_.buffer.size_alignment %
|
||||
buffer_descriptor.size_alignment >
|
||||
0) {
|
||||
return absl::AlreadyExistsError(
|
||||
"A view with different params is already allocated with this "
|
||||
"storage");
|
||||
}
|
||||
|
||||
absl::StatusOr<std::unique_ptr<Tensor::View>> result;
|
||||
if (view_type_id == TensorHardwareBufferView::kId) {
|
||||
result = GetAhwbView(access, shape, base_descriptor);
|
||||
} else {
|
||||
result = GetCpuView(access, shape, base_descriptor);
|
||||
}
|
||||
if (result.ok()) version_ = latest_version;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
absl::StatusOr<std::unique_ptr<Tensor::View>> GetAhwbView(
|
||||
Tensor::View::Access access, const Tensor::Shape& shape,
|
||||
const Tensor::ViewDescriptor& base_descriptor) {
|
||||
return std::unique_ptr<Tensor::View>(new TensorHardwareBufferViewImpl(
|
||||
kAccessCapability, access, Tensor::View::State::kUpToDate,
|
||||
hw_descriptor_, ahwb_handle_));
|
||||
}
|
||||
|
||||
absl::StatusOr<std::unique_ptr<Tensor::View>> GetCpuView(
|
||||
Tensor::View::Access access, const Tensor::Shape& shape,
|
||||
const Tensor::ViewDescriptor& base_descriptor) {
|
||||
void* pointer = nullptr;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
int error =
|
||||
AHardwareBuffer_lock(ahwb_handle_,
|
||||
access == Tensor::View::Access::kWriteOnly
|
||||
? AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN
|
||||
: AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
|
||||
-1, nullptr, &pointer);
|
||||
if (error != 0) {
|
||||
return absl::UnknownError(
|
||||
absl::StrCat("Error locking hardware buffer: ", error));
|
||||
}
|
||||
}
|
||||
return std::unique_ptr<Tensor::View>(
|
||||
new TensorCpuViewImpl(access == Tensor::View::Access::kWriteOnly
|
||||
? Tensor::View::AccessCapability::kWrite
|
||||
: Tensor::View::AccessCapability::kRead,
|
||||
access, Tensor::View::State::kUpToDate,
|
||||
cpu_descriptor_, pointer, ahwb_handle_));
|
||||
}
|
||||
|
||||
static constexpr int kAccessCapability =
|
||||
Tensor::View::AccessCapability::kRead |
|
||||
Tensor::View::AccessCapability::kWrite;
|
||||
TensorHardwareBufferViewDescriptor hw_descriptor_;
|
||||
AHardwareBuffer* ahwb_handle_ = nullptr;
|
||||
|
||||
TensorCpuViewDescriptor cpu_descriptor_;
|
||||
uint64_t version_ = 0;
|
||||
};
|
||||
TENSOR_REGISTER_STORAGE(HardwareBufferCpuStorage);
|
||||
|
||||
} // namespace
|
||||
} // namespace mediapipe
|
||||
|
||||
#endif // !defined(MEDIAPIPE_NO_JNI) && (__ANDROID_API__ >= 26 ||
|
||||
// defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
#if !defined(MEDIAPIPE_NO_JNI) && \
|
||||
(__ANDROID_API__ >= 26 || \
|
||||
defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "mediapipe/framework/formats/tensor_cpu_buffer.h"
|
||||
#include "mediapipe/framework/formats/tensor_hardware_buffer.h"
|
||||
#include "mediapipe/framework/formats/tensor_v2.h"
|
||||
#include "testing/base/public/gmock.h"
|
||||
#include "testing/base/public/gunit.h"
|
||||
|
||||
namespace mediapipe {
|
||||
|
||||
namespace {
|
||||
|
||||
class TensorHardwareBufferTest : public ::testing::Test {
|
||||
public:
|
||||
TensorHardwareBufferTest() {}
|
||||
~TensorHardwareBufferTest() override {}
|
||||
};
|
||||
|
||||
TEST_F(TensorHardwareBufferTest, TestFloat32) {
|
||||
Tensor tensor{Tensor::Shape({1})};
|
||||
{
|
||||
MP_ASSERT_OK_AND_ASSIGN(
|
||||
auto view,
|
||||
tensor.GetView<Tensor::View::Access::kWriteOnly>(
|
||||
TensorHardwareBufferViewDescriptor{
|
||||
.buffer = {.format =
|
||||
TensorBufferDescriptor::Format::kFloat32}}));
|
||||
EXPECT_NE(view->handle(), nullptr);
|
||||
}
|
||||
{
|
||||
const auto& const_tensor = tensor;
|
||||
MP_ASSERT_OK_AND_ASSIGN(
|
||||
auto view,
|
||||
const_tensor.GetView<Tensor::View::Access::kReadOnly>(
|
||||
TensorCpuViewDescriptor{
|
||||
.buffer = {.format =
|
||||
TensorBufferDescriptor::Format::kFloat32}}));
|
||||
EXPECT_NE(view->data<void>(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TensorHardwareBufferTest, TestInt8Padding) {
|
||||
Tensor tensor{Tensor::Shape({1})};
|
||||
|
||||
{
|
||||
MP_ASSERT_OK_AND_ASSIGN(
|
||||
auto view,
|
||||
tensor.GetView<Tensor::View::Access::kWriteOnly>(
|
||||
TensorHardwareBufferViewDescriptor{
|
||||
.buffer = {.format = TensorBufferDescriptor::Format::kInt8,
|
||||
.size_alignment = 4}}));
|
||||
EXPECT_NE(view->handle(), nullptr);
|
||||
}
|
||||
{
|
||||
const auto& const_tensor = tensor;
|
||||
MP_ASSERT_OK_AND_ASSIGN(
|
||||
auto view,
|
||||
const_tensor.GetView<Tensor::View::Access::kReadOnly>(
|
||||
TensorCpuViewDescriptor{
|
||||
.buffer = {.format = TensorBufferDescriptor::Format::kInt8}}));
|
||||
EXPECT_NE(view->data<void>(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace mediapipe
|
||||
|
||||
#endif // !defined(MEDIAPIPE_NO_JNI) && (__ANDROID_API__ >= 26 ||
|
||||
// defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__))
|
Loading…
Reference in New Issue
Block a user