#include "compilation.h"
#include "execution.h"
+#include "internal/nnapi/feature/Reader.h"
+#include "internal/nnapi/feature/View.h"
+
+#include "internal/arm_compute/feature/View.h"
+
+#include "util/feature/IndexIterator.h"
+
#include <cassert>
+//
+// FeatureSource
+//
+class FeatureSource final : public Source
+{
+public:
+ FeatureSource(const nnfw::util::feature::Shape &shape, const uint8_t *base, const size_t size)
+ : _shape{shape}, _base{base}, _size{size}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void push(::arm_compute::ITensor &tensor) const override
+ {
+ const ::internal::nnapi::feature::Reader<float> from{_shape, _base, _size};
+ ::internal::arm_compute::feature::View<float> into{&tensor};
+
+ ::nnfw::util::feature::iterate(_shape) << [&] (uint32_t ch, uint32_t row, uint32_t col)
+ {
+ const auto value = from.at(ch, row, col);
+ into.at(ch, row, col) = value;
+ };
+ }
+
+private:
+ const nnfw::util::feature::Shape _shape;
+ const uint8_t * const _base;
+ const size_t _size;
+};
+
+//
+// FeatureSink
+//
+class FeatureSink final : public Sink
+{
+public:
+ FeatureSink(const nnfw::util::feature::Shape &shape, uint8_t *base, const size_t size)
+ : _shape{shape}, _base{base}, _size{size}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void pull(::arm_compute::ITensor &tensor) const override
+ {
+ const ::internal::arm_compute::feature::View<float> from{&tensor};
+ ::internal::nnapi::feature::View<float> into{_shape, _base, _size};
+
+ ::nnfw::util::feature::iterate(_shape) << [&] (uint32_t ch, uint32_t row, uint32_t col)
+ {
+ const auto value = from.at(ch, row, col);
+ into.at(ch, row, col) = value;
+ };
+ }
+
+private:
+ const nnfw::util::feature::Shape _shape;
+ uint8_t * const _base;
+ const size_t _size;
+};
+
+//
+// NNAPI Implementation
+//
ResultCode
ANeuralNetworksExecution_create(ANeuralNetworksCompilation* compilation, ANeuralNetworksExecution** execution)
{
int32_t index, const ANeuralNetworksOperandType* type,
const void* buffer, size_t length)
{
+ const auto &operands = execution->plan().model().operands();
+
+ // TODO Check type conflicts
+
+ // NOTE The current implemenation assumes that every input is a feature map.
+ // TODO Remove this assumption
+ const auto operand_index = execution->plan().model().inputs.at(index);
+ const auto &operand_shape = operands.at(operand_index).shape().asFeature();
+
+ execution->source<FeatureSource>(index,
+ operand_shape, reinterpret_cast<const uint8_t *>(buffer), length);
+
return ANEURALNETWORKS_NO_ERROR;
}
int32_t index, const ANeuralNetworksOperandType* type,
void* buffer, size_t length)
{
+ const auto &operands = execution->plan().model().operands();
+
+ // TODO Check type conflicts
+
+ // NOTE The current implemenation assumes that every output is a feature map.
+ // TODO Remove this assumption
+ const auto operand_index = execution->plan().model().outputs.at(index);
+ const auto &operand_shape = operands.at(operand_index).shape().asFeature();
+
+ execution->sink<FeatureSink>(index,
+ operand_shape, reinterpret_cast<uint8_t *>(buffer), length);
+
return ANEURALNETWORKS_NO_ERROR;
}
{
assert(execution != nullptr);
+ const auto &plan = execution->plan();
+ const auto &model = plan.model();
+
+ // Set input(s)
+ for (uint32_t n = 0; n < model.inputs.size(); ++n)
+ {
+ auto setter = [&] (::arm_compute::ITensor &tensor)
+ {
+ execution->source(n).push(tensor);
+ };
+
+ plan.operands().at(model.inputs.at(n)).access(setter);
+ }
+
const auto &operations = execution->plan().operations();
for (uint32_t n = 0; n < operations.size(); ++n)
operations.at(n).run();
}
+ // Get output(s)
+ for (uint32_t n = 0; n < model.outputs.size(); ++n)
+ {
+ auto getter = [&] (::arm_compute::ITensor &tensor)
+ {
+ execution->sink(n).pull(tensor);
+ };
+
+ plan.operands().at(model.outputs.at(n)).access(getter);
+ }
+
return ANEURALNETWORKS_NO_ERROR;
}
#include "internal/arm_compute.h"
+struct Source
+{
+ virtual ~Source() = default;
+
+ virtual void push(::arm_compute::ITensor &tensor) const = 0;
+};
+
+struct Sink
+{
+ virtual ~Sink() = default;
+
+ virtual void pull(::arm_compute::ITensor &tensor) const = 0;
+};
+
struct ANeuralNetworksExecution
{
public:
ANeuralNetworksExecution(const std::shared_ptr<const internal::arm_compute::Plan> &plan)
: _plan{plan}
{
- // DO NOTHING
+ _sources.resize(_plan->model().inputs.size());
+ _sinks.resize(_plan->model().outputs.size());
}
public:
private:
std::shared_ptr<const internal::arm_compute::Plan> _plan;
+
+public:
+ // TODO Use InputIndex instead of int
+ void source(int n, std::unique_ptr<Source> &&source) { _sources.at(n) = std::move(source); }
+ template<typename T, typename... Args> void source(int n, Args&&... args)
+ {
+ source(n, std::unique_ptr<T>{new T{std::forward<Args>(args)...}});
+ }
+
+public:
+ const Source &source(int n) const { return *(_sources.at(n)); }
+
+public:
+ // TODO Use OutputIndex instead of int
+ void sink(int n, std::unique_ptr<Sink> &&sink) { _sinks.at(n) = std::move(sink); }
+ template<typename T, typename... Args> void sink(int n, Args&&... args)
+ {
+ sink(n, std::unique_ptr<T>{new T{std::forward<Args>(args)...}});
+ }
+
+public:
+ const Sink &sink(int n) const { return *(_sinks.at(n)); }
+
+private:
+ std::vector<std::unique_ptr<Source>> _sources;
+ std::vector<std::unique_ptr<Sink>> _sinks;
};
#endif
#include "internal/arm_compute.h"
+#include <arm_compute/runtime/CL/CLScheduler.h>
+
#include <cassert>
namespace internal
namespace operand
{
+void Object::access(const std::function<void (::arm_compute::ITensor &tensor)> &fn) const
+{
+ auto &queue = ::arm_compute::CLScheduler::get().queue();
+
+ _tensor->map(queue);
+ fn(*_tensor);
+ _tensor->unmap(queue);
+}
+
+} // namespace operand
+} // namepsace arm_compute
+} // namespace internal
+
+namespace internal
+{
+namespace arm_compute
+{
+namespace operand
+{
+
Context &Context::set(const ::internal::tflite::operand::Index &id,
const std::shared_ptr<::arm_compute::ICLTensor> &tensor)
{
private:
std::shared_ptr<::arm_compute::ICLTensor> _tensor;
+
+public:
+ void access(const std::function<void (::arm_compute::ITensor &tensor)> &fn) const;
};
} // namespace operand
--- /dev/null
+#ifndef __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
+#define __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
+
+#include "util/feature/Reader.h"
+
+#include <arm_compute/core/ITensor.h>
+
+#include <cassert>
+
+namespace internal
+{
+namespace arm_compute
+{
+namespace feature
+{
+
+template<typename T> class View;
+
+template<> class View<float> final : public nnfw::util::feature::Reader<float>
+{
+public:
+ View(::arm_compute::ITensor *tensor) : _tensor{tensor}
+ {
+ assert(tensor->info()->data_type() == ::arm_compute::DataType::F32);
+
+ // TODO Validate whether tensor is a feature map, or not
+
+ _shape.C = tensor->info()->dimension(2);
+ _shape.H = tensor->info()->dimension(1);
+ _shape.W = tensor->info()->dimension(0);
+ }
+
+public:
+ const ::nnfw::util::feature::Shape &shape(void) const { return _shape; }
+
+public:
+ float at(uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ const auto offset = feature_index_to_byte_offset(ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+
+public:
+ float &at(uint32_t ch, uint32_t row, uint32_t col)
+ {
+ const auto offset = feature_index_to_byte_offset(ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+
+private:
+ size_t feature_index_to_byte_offset(uint32_t ch, uint32_t row, uint32_t col) const
+ {
+ // ARM Compute uses CHW ordering
+ return _tensor->info()->offset_element_in_bytes(::arm_compute::Coordinates{col, row, ch});
+ }
+
+private:
+ ::nnfw::util::feature::Shape _shape;
+ ::arm_compute::ITensor *_tensor;
+};
+
+} // namespace feature
+} // namespace arm_compute
+} // namespace internal
+
+#endif // __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
--- /dev/null
+#ifndef __INTERNAL_NNAPI_FEATURE_READER_H__
+#define __INTERNAL_NNAPI_FEATURE_READER_H__
+
+#include "internal/nnapi/feature/Utils.h"
+
+#include "util/feature/Reader.h"
+
+namespace internal
+{
+namespace nnapi
+{
+namespace feature
+{
+
+template<typename T> class Reader;
+
+template<> class Reader<float> final : public nnfw::util::feature::Reader<float>
+{
+public:
+ Reader(const ::nnfw::util::feature::Shape &shape, const uint8_t *ptr, size_t len)
+ : _shape{shape}, _ptr{ptr}, _len{len}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const nnfw::util::feature::Shape &shape(void) const { return _shape; }
+
+public:
+ float at(uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ uint32_t index = index_of(_shape, ch, row, col);
+
+ const auto arr = reinterpret_cast<const float *>(_ptr);
+
+ return arr[index];
+ }
+
+private:
+ nnfw::util::feature::Shape _shape;
+
+private:
+ const uint8_t *_ptr;
+ const size_t _len;
+};
+
+} // namespace feature
+} // namespace nnapi
+} // namespace internal
+
+#endif // __INTERNAL_NNAPI_FEATURE_READER_H__
--- /dev/null
+#ifndef __INTERNAL_NNAPI_FEATURE_UTILS_H__
+#define __INTERNAL_NNAPI_FEATURE_UTILS_H__
+
+#include "util/feature/Shape.h"
+
+namespace internal
+{
+namespace nnapi
+{
+namespace feature
+{
+
+inline uint32_t index_of(const ::nnfw::util::feature::Shape &shape,
+ uint32_t ch, uint32_t row, uint32_t col)
+{
+ uint32_t res = 0;
+
+ // NNAPI uses NHWC ordering
+ res += row * shape.W * shape.C;
+ res += col * shape.C;
+ res += ch;
+
+ return res;
+}
+
+} // namespace feature
+} // namespace nnapi
+} // namespace internal
+
+#endif // __INTERNAL_NNAPI_FEATURE_UTILS_H__
--- /dev/null
+#ifndef __INTERNAL_NNAPI_FEATURE_VIEW_H__
+#define __INTERNAL_NNAPI_FEATURE_VIEW_H__
+
+#include "internal/nnapi/feature/Utils.h"
+
+#include "util/feature/Reader.h"
+
+namespace internal
+{
+namespace nnapi
+{
+namespace feature
+{
+
+template<typename T> class View final : public nnfw::util::feature::Reader<float>
+{
+public:
+ View(const ::nnfw::util::feature::Shape &shape, uint8_t *ptr, size_t len)
+ : _shape{shape}, _ptr{ptr}, _len{len}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const nnfw::util::feature::Shape &shape(void) const { return _shape; }
+
+public:
+ T at(uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ uint32_t index = index_of(_shape, ch, row, col);
+
+ T *arr = reinterpret_cast<T *>(_ptr);
+
+ return arr[index];
+ }
+
+ T &at(uint32_t ch, uint32_t row, uint32_t col)
+ {
+ uint32_t index = index_of(_shape, ch, row, col);
+
+ T *arr = reinterpret_cast<T *>(_ptr);
+
+ return arr[index];
+ }
+
+private:
+ nnfw::util::feature::Shape _shape;
+
+private:
+ uint8_t *_ptr;
+ const size_t _len;
+};
+
+} // namespace feature
+} // namespace nnapi
+} // namespace internal
+
+#endif // __INTERNAL_NNAPI_FEATURE_VIEW_H__