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/gl_program.h"
17
18 #include <string>
19 #include <utility>
20 #include <variant>
21
22 #include "absl/types/variant.h"
23 #include "tensorflow/lite/delegates/gpu/common/status.h"
24 #include "tensorflow/lite/delegates/gpu/common/types.h"
25 #include "tensorflow/lite/delegates/gpu/gl/gl_call.h"
26 #include "tensorflow/lite/delegates/gpu/gl/gl_errors.h"
27 #include "tensorflow/lite/delegates/gpu/gl/variable.h"
28
29 namespace tflite {
30 namespace gpu {
31 namespace gl {
32 namespace {
33
CreateNewProgramId(GLuint * program_id)34 absl::Status CreateNewProgramId(GLuint* program_id) {
35 RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glCreateProgram, program_id));
36 if (!*program_id) {
37 return absl::UnknownError("Can't create opengl program: 0 program_id");
38 }
39 return absl::OkStatus();
40 }
41
CheckProgramLinked(GLuint program_id)42 absl::Status CheckProgramLinked(GLuint program_id) {
43 GLint linked;
44 glGetProgramiv(program_id, GL_LINK_STATUS, &linked);
45 if (linked == GL_TRUE) {
46 return absl::OkStatus();
47 }
48 GLint info_size;
49 glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_size);
50 std::string errors;
51 errors.resize(info_size + 1 /* plus \0 */);
52 glGetProgramInfoLog(program_id, info_size + 1, nullptr, &errors[0]);
53 // TODO(akulik): use glValidateProgram to gather more info.
54 return absl::UnavailableError("Program is not properly linked: " + errors);
55 }
56
57 struct ParameterSetter {
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter58 absl::Status operator()(int value) {
59 return TFLITE_GPU_CALL_GL(glProgramUniform1i, program_id, uniform_id,
60 value);
61 }
62
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter63 absl::Status operator()(const int2& value) {
64 return TFLITE_GPU_CALL_GL(glProgramUniform2i, program_id, uniform_id,
65 value.x, value.y);
66 }
67
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter68 absl::Status operator()(const int4& value) {
69 return TFLITE_GPU_CALL_GL(glProgramUniform4i, program_id, uniform_id,
70 value.x, value.y, value.z, value.w);
71 }
72
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter73 absl::Status operator()(const std::vector<int2>& value) {
74 std::vector<GLint> ints(value.size() * 2, 0);
75 for (int i = 0; i < value.size(); ++i) {
76 ints[i * 2] = value[i].x;
77 ints[i * 2 + 1] = value[i].y;
78 }
79 return TFLITE_GPU_CALL_GL(glProgramUniform2iv, program_id, uniform_id,
80 ints.size(), ints.data());
81 }
82
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter83 absl::Status operator()(unsigned int value) {
84 return TFLITE_GPU_CALL_GL(glProgramUniform1ui, program_id, uniform_id,
85 value);
86 }
87
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter88 absl::Status operator()(const uint4& value) {
89 return TFLITE_GPU_CALL_GL(glProgramUniform4ui, program_id, uniform_id,
90 value.x, value.y, value.z, value.w);
91 }
92
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter93 absl::Status operator()(float value) {
94 return TFLITE_GPU_CALL_GL(glProgramUniform1f, program_id, uniform_id,
95 value);
96 }
97
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter98 absl::Status operator()(const float2& value) {
99 return TFLITE_GPU_CALL_GL(glProgramUniform2f, program_id, uniform_id,
100 value.x, value.y);
101 }
102
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter103 absl::Status operator()(const float4& value) {
104 return TFLITE_GPU_CALL_GL(glProgramUniform4f, program_id, uniform_id,
105 value.x, value.y, value.z, value.w);
106 }
107
operator ()tflite::gpu::gl::__anoncdde1b600111::ParameterSetter108 absl::Status operator()(const std::vector<float4>& value) {
109 std::vector<GLfloat> floats(value.size() * 4, 0);
110 for (int i = 0; i < value.size(); ++i) {
111 floats[i * 4] = value[i].x;
112 floats[i * 4 + 1] = value[i].y;
113 floats[i * 4 + 2] = value[i].z;
114 floats[i * 4 + 3] = value[i].w;
115 }
116 return TFLITE_GPU_CALL_GL(glProgramUniform4fv, program_id, uniform_id,
117 floats.size(), floats.data());
118 }
119
120 const GLuint program_id;
121 const GLint uniform_id;
122 };
123
124 } // namespace
125
CreateWithShader(const GlShader & shader,GlProgram * gl_program)126 absl::Status GlProgram::CreateWithShader(const GlShader& shader,
127 GlProgram* gl_program) {
128 GLuint program_id;
129 RETURN_IF_ERROR(CreateNewProgramId(&program_id));
130
131 // program_id needs to be properly deleted if there will be an error, hense
132 // wrap program_id into Program.
133 GlProgram program(program_id);
134
135 RETURN_IF_ERROR(
136 TFLITE_GPU_CALL_GL(glAttachShader, program.id(), shader.id()));
137 RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glLinkProgram, program.id()));
138 RETURN_IF_ERROR(CheckProgramLinked(program.id()));
139
140 *gl_program = std::move(program);
141 return absl::OkStatus();
142 }
143
CreateWithBinaryShader(const BinaryShader & shader,GlProgram * gl_program)144 absl::Status GlProgram::CreateWithBinaryShader(const BinaryShader& shader,
145 GlProgram* gl_program) {
146 GLuint program_id;
147 RETURN_IF_ERROR(CreateNewProgramId(&program_id));
148
149 // program_id needs to be properly deleted if there will be an error, hense
150 // wrap program_id into Program.
151 GlProgram program(program_id);
152
153 RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glProgramBinary, program.id(),
154 shader.format(), shader.binary().data(),
155 shader.binary().size()));
156 RETURN_IF_ERROR(CheckProgramLinked(program.id()));
157
158 *gl_program = std::move(program);
159 return absl::OkStatus();
160 }
161
GetBinary(BinaryShader * binary_shader)162 absl::Status GlProgram::GetBinary(BinaryShader* binary_shader) {
163 GLint size = 0;
164 RETURN_IF_ERROR(
165 TFLITE_GPU_CALL_GL(glGetProgramiv, id_, GL_PROGRAM_BINARY_LENGTH, &size));
166 if (!size) {
167 return absl::InternalError("Getting binary size failed.");
168 }
169 // TODO(akulik): call
170 // glProgramParameteri(id_, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE)
171 // before linking a program to increase chances of retrieving a binary.
172 std::vector<uint8_t> binary(size);
173 GLsizei returned_size;
174 GLenum format;
175 RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glGetProgramBinary, id_, size,
176 &returned_size, &format,
177 reinterpret_cast<void*>(&binary[0])));
178 if (size != returned_size) {
179 return absl::InternalError("Getting binary is failed.");
180 }
181 *binary_shader = BinaryShader(format, std::move(binary));
182 return absl::OkStatus();
183 }
184
GlProgram(GlProgram && program)185 GlProgram::GlProgram(GlProgram&& program) : id_(program.id_) {
186 program.id_ = 0;
187 }
188
Invalidate()189 void GlProgram::Invalidate() {
190 if (id_) {
191 glDeleteProgram(id_);
192 id_ = 0;
193 }
194 }
195
operator =(GlProgram && program)196 GlProgram& GlProgram::operator=(GlProgram&& program) {
197 if (this != &program) {
198 Invalidate();
199 std::swap(id_, program.id_);
200 }
201 return *this;
202 }
203
~GlProgram()204 GlProgram::~GlProgram() { Invalidate(); }
205
SetParameter(const Variable & param)206 absl::Status GlProgram::SetParameter(const Variable& param) {
207 GLint uniform_location;
208 RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glGetUniformLocation, &uniform_location,
209 id_, param.name.c_str()));
210 return std::visit(ParameterSetter{id_, uniform_location}, param.value);
211 }
212
Dispatch(const uint3 & workgroups) const213 absl::Status GlProgram::Dispatch(const uint3& workgroups) const {
214 if (workgroups.x == 0 || workgroups.y == 0 || workgroups.z == 0) {
215 return absl::InvalidArgumentError("Invalid workgroups");
216 }
217 RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glUseProgram, id_));
218 return TFLITE_GPU_CALL_GL(glDispatchCompute, workgroups.x, workgroups.y,
219 workgroups.z);
220 }
221
222 } // namespace gl
223 } // namespace gpu
224 } // namespace tflite
225