blob: 8015a9766c6cf401c3ca84376f15259c15266259 [file] [edit]
/*
* Copyright 2024 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <vector>
#include "absl/random/random.h"
#include "android/hardware_buffer.h"
#include "common/async_driver.h"
#include "common/scoped_ahwb.h"
#include "common/simple_model_builder.h"
#include "common/test_util/fp16_compare.h"
#include "common/test_util/stable_delegate_env.h"
#include "tensorflow/lite/core/c/builtin_op_data.h"
namespace tflite::cros::tests {
StableDelegateEnvironment* g_env = nullptr;
class AsyncDelegateTest : public testing::TestWithParam<int> {};
TEST_P(AsyncDelegateTest, Inference) {
auto ahwb_desc_with_size = [](uint32_t size) {
return AHardwareBuffer_Desc{
.width = size,
.height = 1,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_BLOB,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
.stride = size,
};
};
using TensorArgs = SimpleModelBuilder::TensorArgs;
const TensorArgs base_args = {
.type = kTfLiteFloat32,
.shape = {2, 3},
};
auto arg_with_name = [&](const char* name) {
TensorArgs args = base_args;
args.name = name;
return args;
};
SimpleModelBuilder mb;
int a = mb.AddInput(arg_with_name("a"));
int b = mb.AddInput(arg_with_name("b"));
int c = mb.AddInput(arg_with_name("c"));
int d = mb.AddOutput(arg_with_name("d"));
int a_plus_b = mb.AddInternalTensor(arg_with_name("a_plus_b"));
mb.AddOperator<TfLiteAddParams>({
.op = kTfLiteBuiltinAdd,
.inputs = {a, b},
.outputs = {a_plus_b},
});
mb.AddOperator<TfLiteSubParams>({
.op = kTfLiteBuiltinSub,
.inputs = {a_plus_b, c},
.outputs = {d},
});
auto model = mb.Build();
ASSERT_NE(model, nullptr);
TfLiteDelegatePtr delegate = g_env->CreateDelegate();
ASSERT_NE(delegate, nullptr);
auto driver = AsyncDriver::Create(std::move(delegate), std::move(model));
ASSERT_NE(driver, nullptr);
ASSERT_EQ(driver->Prepare(), kTfLiteOk);
int n = 1;
for (int dim : base_args.shape) {
n *= dim;
}
const int num_iterations = GetParam();
for (int iteration = 0; iteration < num_iterations; ++iteration) {
auto a_attrs = driver->GetInputBufferAttributes("a");
auto b_attrs = driver->GetInputBufferAttributes("b");
auto c_attrs = driver->GetInputBufferAttributes("c");
auto d_attrs = driver->GetOutputBufferAttributes("d");
EXPECT_TRUE(a_attrs.ok());
EXPECT_TRUE(b_attrs.ok());
EXPECT_TRUE(c_attrs.ok());
EXPECT_TRUE(d_attrs.ok());
EXPECT_EQ(*a_attrs->size, n * sizeof(float));
EXPECT_EQ(*b_attrs->size, n * sizeof(float));
EXPECT_EQ(*c_attrs->size, n * sizeof(float));
EXPECT_EQ(*d_attrs->size, n * sizeof(float));
ScopedAHardwareBuffer a_buffer(ahwb_desc_with_size(*a_attrs->size));
ScopedAHardwareBuffer b_buffer(ahwb_desc_with_size(*b_attrs->size));
ScopedAHardwareBuffer c_buffer(ahwb_desc_with_size(*c_attrs->size));
ScopedAHardwareBuffer d_buffer(ahwb_desc_with_size(*d_attrs->size));
driver->SetInputTensorBuffer("a", std::move(a_buffer));
driver->SetInputTensorBuffer("b", std::move(b_buffer));
driver->SetInputTensorBuffer("c", std::move(c_buffer));
driver->SetOutputTensorBuffer("d", std::move(d_buffer));
absl::BitGen gen;
std::vector<float> a_data(n);
std::vector<float> b_data(n);
std::vector<float> c_data(n);
std::vector<float> d_data(n);
for (int i = 0; i < n; ++i) {
// Limit the input precision to float16 since we are comparing the
// computation in fp16.
const _Float16 a = absl::Uniform(gen, 0.0, 1.0);
const _Float16 b = absl::Uniform(gen, 0.0, 1.0);
const _Float16 c = absl::Uniform(gen, 0.0, 1.0);
// If we do not cast back to float16 first, the result will be casted back
// to float16 only after the full computation.
const _Float16 d = static_cast<_Float16>(a + b) - c;
a_data[i] = a;
b_data[i] = b;
c_data[i] = c;
d_data[i] = d;
}
EXPECT_EQ(driver->SetInputTensorData("a", a_data), kTfLiteOk);
EXPECT_EQ(driver->SetInputTensorData("b", b_data), kTfLiteOk);
EXPECT_EQ(driver->SetInputTensorData("c", c_data), kTfLiteOk);
ASSERT_EQ(driver->AllocateBuffers(), kTfLiteOk);
ASSERT_EQ(driver->Invoke(), kTfLiteOk);
auto output = driver->GetOutputTensorData<float>("d");
EXPECT_THAT(output, testing::ElementsAreArray(ArrayFp16Eq(d_data)));
}
}
INSTANTIATE_TEST_SUITE_P(DifferentIterations,
AsyncDelegateTest,
testing::Values(1, 10));
} // namespace tflite::cros::tests
using tflite::cros::tests::g_env;
int main(int argc, char** argv) {
g_env = new StableDelegateEnvironment();
if (!g_env->InitFromCommandLine(&argc, argv)) {
delete g_env;
return EXIT_FAILURE;
}
testing::InitGoogleTest(&argc, argv);
// GoogleTest takes the ownership of g_env.
::testing::AddGlobalTestEnvironment(g_env);
return RUN_ALL_TESTS();
}