1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/lite/delegates/gpu/gl/kernels/converter.h"
17
18 #include <algorithm>
19 #include <vector>
20
21 #include <gmock/gmock.h>
22 #include <gtest/gtest.h>
23 #include "absl/types/span.h"
24 #include "tensorflow/lite/delegates/gpu/common/convert.h"
25 #include "tensorflow/lite/delegates/gpu/common/shape.h"
26 #include "tensorflow/lite/delegates/gpu/common/status.h"
27 #include "tensorflow/lite/delegates/gpu/gl/egl_environment.h"
28 #include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
29 #include "tensorflow/lite/delegates/gpu/gl/portable_gl31.h"
30
31 namespace tflite {
32 namespace gpu {
33 namespace gl {
34 namespace {
35
GenerateFloats(float multiplier,int size)36 inline std::vector<float> GenerateFloats(float multiplier, int size) {
37 std::vector<float> v(size);
38 for (int i = 0; i < size; ++i) {
39 v[i] = multiplier * i * (i % 2 == 0 ? -1 : 1);
40 }
41 return v;
42 }
43
ToDimensions(const BHWC & shape)44 Dimensions ToDimensions(const BHWC& shape) {
45 return Dimensions(shape.b, shape.h, shape.w, shape.c);
46 }
47
RunFromTensorTest(const BHWC & shape)48 absl::Status RunFromTensorTest(const BHWC& shape) {
49 // Create random input and calculate expected output for it.
50 std::vector<float> input =
51 GenerateFloats(0.01, GetElementsSizeForPHWC4(shape));
52 std::vector<float> output(shape.DimensionsProduct(), 0);
53 RETURN_IF_ERROR(
54 ConvertFromPHWC4(absl::MakeConstSpan(input.data(), input.size()), shape,
55 absl::MakeSpan(output.data(), output.size())));
56
57 std::unique_ptr<EglEnvironment> env;
58 RETURN_IF_ERROR(EglEnvironment::NewEglEnvironment(&env));
59
60 // Create input and output buffers
61 GlBuffer input_buffer;
62 RETURN_IF_ERROR(CreateReadOnlyShaderStorageBuffer(
63 absl::MakeConstSpan(input.data(), input.size()), &input_buffer));
64
65 GlBuffer output_buffer;
66 RETURN_IF_ERROR(CreateReadWriteShaderStorageBuffer<float>(
67 shape.DimensionsProduct(), &output_buffer));
68
69 // Create converter and run it.
70 auto builder = NewConverterBuilder(nullptr);
71 TensorObjectDef input_def;
72 input_def.object_def.data_type = DataType::FLOAT32;
73 input_def.object_def.data_layout = DataLayout::DHWC4;
74 input_def.object_def.object_type = ObjectType::OPENGL_SSBO;
75 input_def.dimensions = ToDimensions(shape);
76 TensorObjectDef output_def = input_def;
77 output_def.object_def.data_layout = DataLayout::BHWC;
78 std::unique_ptr<TensorObjectConverter> converter;
79 RETURN_IF_ERROR(builder->MakeConverter(input_def, output_def, &converter));
80 RETURN_IF_ERROR(converter->Convert(OpenGlBuffer{input_buffer.id()},
81 OpenGlBuffer{output_buffer.id()}));
82
83 // Compare outputs.
84 std::vector<float> converted_output(output.size(), 0);
85 RETURN_IF_ERROR(output_buffer.Read(
86 absl::MakeSpan(converted_output.data(), converted_output.size())));
87 if (output != converted_output) {
88 return absl::InternalError("Outputs don't match");
89 }
90 return absl::OkStatus();
91 }
92
TEST(FromTensor,Smoke)93 TEST(FromTensor, Smoke) {
94 for (int32_t h : {1, 2, 3, 7, 20}) {
95 for (int32_t w : {1, 2, 4, 5, 11}) {
96 for (int32_t c : {1, 2, 4, 5, 8, 9}) {
97 BHWC shape(1, h, w, c);
98 auto status = RunFromTensorTest(shape);
99 EXPECT_TRUE(status.ok()) << status << ", shape = " << shape.h << " "
100 << shape.w << " " << shape.c;
101 }
102 }
103 }
104 }
105
RunToTensorTest(const BHWC & shape)106 absl::Status RunToTensorTest(const BHWC& shape) {
107 // Create random input and calculate expected output for it.
108 std::vector<float> input = GenerateFloats(0.01, shape.DimensionsProduct());
109 std::vector<float> output(GetElementsSizeForPHWC4(shape), 0);
110 RETURN_IF_ERROR(
111 ConvertToPHWC4(absl::MakeConstSpan(input.data(), input.size()), shape,
112 absl::MakeSpan(output.data(), output.size())));
113
114 std::unique_ptr<EglEnvironment> env;
115 RETURN_IF_ERROR(EglEnvironment::NewEglEnvironment(&env));
116
117 // Create input and output buffers
118 GlBuffer input_buffer;
119 RETURN_IF_ERROR(CreateReadOnlyShaderStorageBuffer(
120 absl::MakeConstSpan(input.data(), input.size()), &input_buffer));
121
122 GlBuffer output_buffer;
123 RETURN_IF_ERROR(CreateReadWriteShaderStorageBuffer<float>(
124 GetElementsSizeForPHWC4(shape), &output_buffer));
125
126 // Create converter and run it.
127 auto builder = NewConverterBuilder(nullptr);
128 TensorObjectDef input_def;
129 input_def.object_def.data_type = DataType::FLOAT32;
130 input_def.object_def.data_layout = DataLayout::BHWC;
131 input_def.object_def.object_type = ObjectType::OPENGL_SSBO;
132 input_def.dimensions = ToDimensions(shape);
133 TensorObjectDef output_def = input_def;
134 output_def.object_def.data_layout = DataLayout::DHWC4;
135 std::unique_ptr<TensorObjectConverter> converter;
136 RETURN_IF_ERROR(builder->MakeConverter(input_def, output_def, &converter));
137 RETURN_IF_ERROR(converter->Convert(OpenGlBuffer{input_buffer.id()},
138 OpenGlBuffer{output_buffer.id()}));
139
140 // Compare outputs.
141 std::vector<float> converted_output(output.size(), 0);
142 RETURN_IF_ERROR(output_buffer.Read(
143 absl::MakeSpan(converted_output.data(), converted_output.size())));
144 if (output != converted_output) {
145 return absl::InternalError("Outputs don't match");
146 }
147 return absl::OkStatus();
148 }
149
TEST(ToTensor,Smoke)150 TEST(ToTensor, Smoke) {
151 for (int32_t h : {1, 2, 3, 7, 20}) {
152 for (int32_t w : {1, 2, 4, 5, 11}) {
153 for (int32_t c : {1, 2, 4, 5, 8, 9}) {
154 BHWC shape(1, h, w, c);
155 auto status = RunToTensorTest(shape);
156 EXPECT_TRUE(status.ok()) << status << ", shape = " << shape.h << " "
157 << shape.w << " " << shape.c;
158 }
159 }
160 }
161 }
162
163 } // namespace
164 } // namespace gl
165 } // namespace gpu
166 } // namespace tflite
167