xref: /aosp_15_r20/external/mesa3d/src/gallium/targets/teflon/test_executor.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright (c) 2023-2024 Tomeu Vizoso <[email protected]>
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include <dlfcn.h>
7 #include <stdio.h>
8 #include <vector>
9 #include <gtest/gtest.h>
10 #include <xtensor/xrandom.hpp>
11 
12 #include "util/macros.h"
13 
14 #include "tensorflow/lite/c/c_api.h"
15 #include "tensorflow/lite/c/common.h"
16 
17 #include <fcntl.h>
18 #include "test_executor.h"
19 #include "tflite-schema-v2.15.0_generated.h"
20 
21 static float
randf(float min,float max)22 randf(float min, float max)
23 {
24    return ((max - min) * ((float)rand() / (float)RAND_MAX)) + min;
25 }
26 
27 static void
read_model(const char * file_name,tflite::ModelT & model)28 read_model(const char *file_name, tflite::ModelT &model)
29 {
30    std::ostringstream file_path;
31    assert(getenv("TEFLON_TEST_DATA"));
32    file_path << getenv("TEFLON_TEST_DATA") << "/" << file_name;
33 
34    FILE *f = fopen(file_path.str().c_str(), "rb");
35    assert(f);
36    fseek(f, 0, SEEK_END);
37    long fsize = ftell(f);
38    fseek(f, 0, SEEK_SET);
39    void *buf = malloc(fsize);
40    fread(buf, fsize, 1, f);
41    fclose(f);
42 
43    tflite::GetModel(buf)->UnPackTo(&model);
44 }
45 
46 static void
patch_conv2d(unsigned operation_index,tflite::ModelT * model,int input_size,int weight_size,int input_channels,int output_channels,int stride,bool padding_same,bool is_signed,bool depthwise)47 patch_conv2d(unsigned operation_index,
48              tflite::ModelT *model,
49              int input_size,
50              int weight_size,
51              int input_channels,
52              int output_channels,
53              int stride,
54              bool padding_same,
55              bool is_signed,
56              bool depthwise)
57 {
58    unsigned output_size = 0;
59    unsigned input_index;
60    unsigned weights_index;
61    unsigned bias_index;
62    unsigned output_index;
63    unsigned weights_buffer_index;
64    unsigned bias_buffer_index;
65 
66    auto subgraph = model->subgraphs[0];
67 
68    /* Operation */
69    if (depthwise) {
70       auto value = new tflite::DepthwiseConv2DOptionsT();
71       value->depth_multiplier = 1;
72       value->padding = padding_same ? tflite::Padding_SAME : tflite::Padding_VALID;
73       value->stride_w = stride;
74       value->stride_h = stride;
75       value->dilation_w_factor = 1;
76       value->dilation_h_factor = 1;
77       subgraph->operators[operation_index]->builtin_options.value = value;
78       subgraph->operators[operation_index]->builtin_options.type = tflite::BuiltinOptions_DepthwiseConv2DOptions;
79 
80       model->operator_codes[0]->deprecated_builtin_code = 4;
81       model->operator_codes[0]->builtin_code = tflite::BuiltinOperator_DEPTHWISE_CONV_2D;
82    } else {
83       auto value = new tflite::Conv2DOptionsT();
84       value->padding = padding_same ? tflite::Padding_SAME : tflite::Padding_VALID;
85       value->stride_w = stride;
86       value->stride_h = stride;
87       subgraph->operators[operation_index]->builtin_options.value = value;
88    }
89 
90    input_index = subgraph->operators[operation_index]->inputs.data()[0];
91    weights_index = subgraph->operators[operation_index]->inputs.data()[1];
92    bias_index = subgraph->operators[operation_index]->inputs.data()[2];
93    output_index = subgraph->operators[operation_index]->outputs.data()[0];
94 
95    /* Input */
96    auto input_tensor = subgraph->tensors[input_index];
97    input_tensor->shape.data()[0] = 1;
98    input_tensor->shape.data()[1] = input_size;
99    input_tensor->shape.data()[2] = input_size;
100    input_tensor->shape.data()[3] = input_channels;
101    input_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
102 
103    /* Bias */
104    auto bias_tensor = subgraph->tensors[bias_index];
105    bias_buffer_index = bias_tensor->buffer;
106    bias_tensor->shape.data()[0] = output_channels;
107 
108    auto bias_data = &model->buffers[bias_buffer_index]->data;
109    xt::xarray<int32_t> bias_array = xt::random::randint<int32_t>({output_channels}, -20000, 20000);
110    bias_data->resize(bias_array.size() * sizeof(int32_t));
111    memcpy(bias_data->data(), bias_array.data(), bias_array.size() * sizeof(int32_t));
112 
113    /* Weight */
114    auto weight_tensor = subgraph->tensors[weights_index];
115    weights_buffer_index = weight_tensor->buffer;
116    if (depthwise) {
117       weight_tensor->shape.data()[0] = 1;
118       weight_tensor->shape.data()[1] = weight_size;
119       weight_tensor->shape.data()[2] = weight_size;
120       weight_tensor->shape.data()[3] = output_channels;
121    } else {
122       weight_tensor->shape.data()[0] = output_channels;
123       weight_tensor->shape.data()[1] = weight_size;
124       weight_tensor->shape.data()[2] = weight_size;
125       weight_tensor->shape.data()[3] = input_channels;
126    }
127    weight_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
128 
129    auto weights_data = &model->buffers[weights_buffer_index]->data;
130    std::vector<int> weight_shape;
131    if (depthwise)
132       weight_shape = {1, weight_size, weight_size, output_channels};
133    else
134       weight_shape = {output_channels, weight_size, weight_size, input_channels};
135 
136    xt::xarray<uint8_t> weights_array = xt::random::randint<uint8_t>(weight_shape, 0, 255);
137    weights_data->resize(weights_array.size());
138    memcpy(weights_data->data(), weights_array.data(), weights_array.size());
139 
140    /* Output */
141    if (padding_same)
142       output_size = (input_size + stride - 1) / stride;
143    else
144       output_size = (input_size + stride - weight_size) / stride;
145 
146    auto output_tensor = subgraph->tensors[output_index];
147    output_tensor->shape.data()[0] = 1;
148    output_tensor->shape.data()[1] = output_size;
149    output_tensor->shape.data()[2] = output_size;
150    output_tensor->shape.data()[3] = output_channels;
151    output_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
152 }
153 
154 std::vector<uint8_t>
conv2d_generate_model(int input_size,int weight_size,int input_channels,int output_channels,int stride,bool padding_same,bool is_signed,bool depthwise)155 conv2d_generate_model(int input_size,
156                       int weight_size,
157                       int input_channels,
158                       int output_channels,
159                       int stride,
160                       bool padding_same,
161                       bool is_signed,
162                       bool depthwise)
163 {
164    tflite::ModelT model;
165    read_model("conv2d.tflite", model);
166 
167    patch_conv2d(0, &model, input_size, weight_size, input_channels, output_channels, stride, padding_same, is_signed, depthwise);
168 
169    flatbuffers::FlatBufferBuilder builder;
170    builder.Finish(tflite::Model::Pack(builder, &model), "TFL3");
171 
172    return {builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()};
173 }
174 
175 static void
patch_quant_for_add(tflite::ModelT * model)176 patch_quant_for_add(tflite::ModelT *model)
177 {
178    auto subgraph = model->subgraphs[0];
179    auto add_op = subgraph->operators[2];
180 
181    auto input_index = add_op->inputs.data()[0];
182    auto input_tensor = subgraph->tensors[input_index];
183    input_tensor->quantization->scale[0] = randf(0.0078125, 0.4386410117149353);
184    input_tensor->quantization->zero_point[0] = rand() % 255;
185 
186    input_index = add_op->inputs.data()[1];
187    input_tensor = subgraph->tensors[input_index];
188    input_tensor->quantization->scale[0] = randf(0.0078125, 0.4386410117149353);
189    input_tensor->quantization->zero_point[0] = rand() % 255;
190 }
191 
192 std::vector<uint8_t>
add_generate_model(int input_size,int weight_size,int input_channels,int output_channels,int stride,bool padding_same,bool is_signed,bool depthwise)193 add_generate_model(int input_size,
194                    int weight_size,
195                    int input_channels,
196                    int output_channels,
197                    int stride,
198                    bool padding_same,
199                    bool is_signed,
200                    bool depthwise)
201 {
202    tflite::ModelT model;
203    read_model("add.tflite", model);
204 
205    patch_conv2d(0, &model, input_size, weight_size, input_channels, output_channels, stride, padding_same, is_signed, depthwise);
206    patch_conv2d(1, &model, input_size, weight_size, input_channels, output_channels, stride, padding_same, is_signed, depthwise);
207    patch_quant_for_add(&model);
208 
209    /* Output */
210    auto subgraph = model.subgraphs[0];
211    unsigned input_index = subgraph->operators[2]->inputs.data()[0];
212    unsigned output_index = subgraph->operators[2]->outputs.data()[0];
213 
214    auto input_tensor = subgraph->tensors[input_index];
215    auto output_tensor = subgraph->tensors[output_index];
216    output_tensor->shape.data()[0] = input_tensor->shape.data()[0];
217    output_tensor->shape.data()[1] = input_tensor->shape.data()[1];
218    output_tensor->shape.data()[2] = input_tensor->shape.data()[2];
219    output_tensor->shape.data()[3] = input_tensor->shape.data()[3];
220    output_tensor->type = is_signed ? tflite::TensorType_INT8 : tflite::TensorType_UINT8;
221 
222    flatbuffers::FlatBufferBuilder builder;
223    builder.Finish(tflite::Model::Pack(builder, &model), "TFL3");
224 
225    return {builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()};
226 }
227 
228 static void
tflite_error_cb(void * user_data,const char * format,va_list args)229 tflite_error_cb(void *user_data, const char *format, va_list args)
230 {
231    vfprintf(stderr, format, args);
232 }
233 
234 TfLiteDelegate *(*tflite_plugin_create_delegate)(char **options_keys,
235                                                  char **options_values,
236                                                  size_t num_options,
237                                                  void (*report_error)(const char *));
238 
239 void (*tflite_plugin_destroy_delegate)(TfLiteDelegate *delegate);
240 
241 static void
load_delegate()242 load_delegate()
243 {
244    const char *delegate_path = getenv("TEFLON_TEST_DELEGATE");
245    assert(delegate_path);
246 
247    void *delegate_lib = dlopen(delegate_path, RTLD_LAZY | RTLD_LOCAL);
248    assert(delegate_lib);
249 
250    tflite_plugin_create_delegate = reinterpret_cast<TfLiteDelegate *(*)(char **options_keys,
251                                                                         char **options_values,
252                                                                         size_t num_options,
253                                                                         void (*report_error)(const char *))>(
254       dlsym(delegate_lib, "tflite_plugin_create_delegate"));
255    assert(tflite_plugin_create_delegate);
256 
257    tflite_plugin_destroy_delegate = reinterpret_cast<void (*)(TfLiteDelegate *delegate)>(
258       dlsym(delegate_lib, "tflite_plugin_destroy_delegate"));
259    assert(tflite_plugin_destroy_delegate);
260 }
261 
262 std::vector<std::vector<uint8_t>>
run_model(TfLiteModel * model,enum executor executor,std::vector<std::vector<uint8_t>> & input)263 run_model(TfLiteModel *model, enum executor executor, std::vector<std::vector<uint8_t>> &input)
264 {
265    TfLiteDelegate *delegate = NULL;
266    TfLiteInterpreterOptions *options = TfLiteInterpreterOptionsCreate();
267    bool generate_random_input = input.empty();
268    std::vector<std::vector<uint8_t>> output;
269 
270    if (executor == EXECUTOR_NPU) {
271       load_delegate();
272       delegate = tflite_plugin_create_delegate(NULL, NULL, 0, NULL);
273       TfLiteInterpreterOptionsAddDelegate(options, delegate);
274    }
275 
276    TfLiteInterpreterOptionsSetErrorReporter(options, tflite_error_cb, NULL);
277 
278    TfLiteInterpreter *interpreter = TfLiteInterpreterCreate(model, options);
279    assert(interpreter);
280 
281    TfLiteInterpreterAllocateTensors(interpreter);
282 
283    unsigned input_tensors = TfLiteInterpreterGetInputTensorCount(interpreter);
284    for (unsigned i = 0; i < input_tensors; i++) {
285       TfLiteTensor *input_tensor = TfLiteInterpreterGetInputTensor(interpreter, i);
286 
287       if (generate_random_input) {
288          int shape[4] = {input_tensor->dims->data[0],
289                          input_tensor->dims->data[1],
290                          input_tensor->dims->data[2],
291                          input_tensor->dims->data[3]};
292          xt::xarray<uint8_t> a = xt::random::randint<uint8_t>(shape, 0, 255);
293          input.push_back({a.begin(), a.end()});
294       }
295 
296       TfLiteTensorCopyFromBuffer(input_tensor, input[i].data(), input_tensor->bytes);
297    }
298 
299    EXPECT_EQ(TfLiteInterpreterInvoke(interpreter), kTfLiteOk);
300 
301    unsigned output_tensors = TfLiteInterpreterGetOutputTensorCount(interpreter);
302    for (unsigned i = 0; i < output_tensors; i++) {
303       const TfLiteTensor *output_tensor = TfLiteInterpreterGetOutputTensor(interpreter, i);
304 
305       std::vector<uint8_t> out;
306       out.resize(output_tensor->bytes);
307       EXPECT_EQ(TfLiteTensorCopyToBuffer(output_tensor, out.data(), output_tensor->bytes), kTfLiteOk);
308 
309       output.push_back(out);
310    }
311 
312    TfLiteInterpreterDelete(interpreter);
313    if (executor == EXECUTOR_NPU)
314       tflite_plugin_destroy_delegate(delegate);
315    TfLiteInterpreterOptionsDelete(options);
316 
317    return output;
318 }
319