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