xref: /aosp_15_r20/external/tensorflow/tensorflow/lite/delegates/gpu/gl/gl_program.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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