Flutter Impeller
proc_table_gles.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <sstream>
8 
9 #include "fml/closure.h"
15 
16 namespace impeller {
17 
18 const char* GLErrorToString(GLenum value) {
19  switch (value) {
20  case GL_NO_ERROR:
21  return "GL_NO_ERROR";
22  case GL_INVALID_ENUM:
23  return "GL_INVALID_ENUM";
24  case GL_INVALID_VALUE:
25  return "GL_INVALID_VALUE";
26  case GL_INVALID_OPERATION:
27  return "GL_INVALID_OPERATION";
28  case GL_INVALID_FRAMEBUFFER_OPERATION:
29  return "GL_INVALID_FRAMEBUFFER_OPERATION";
30  case GL_FRAMEBUFFER_COMPLETE:
31  return "GL_FRAMEBUFFER_COMPLETE";
32  case GL_OUT_OF_MEMORY:
33  return "GL_OUT_OF_MEMORY";
34  }
35  return "Unknown.";
36 }
37 
38 bool GLErrorIsFatal(GLenum value) {
39  switch (value) {
40  case GL_NO_ERROR:
41  return false;
42  case GL_INVALID_ENUM:
43  case GL_INVALID_VALUE:
44  case GL_INVALID_OPERATION:
45  case GL_INVALID_FRAMEBUFFER_OPERATION:
46  case GL_OUT_OF_MEMORY:
47  return true;
48  }
49  return false;
50 }
51 
53  const ProcTableGLES::Resolver& resolver) {
54  return [resolver](const char* function_name) -> void* {
55  auto resolved = resolver(function_name);
56  if (resolved) {
57  return resolved;
58  }
59  // If there are certain known suffixes (usually for extensions), strip them
60  // out and try to resolve the same proc addresses again.
61  auto function = std::string{function_name};
62  if (function.find("KHR", function.size() - 3) != std::string::npos) {
63  auto truncated = function.substr(0u, function.size() - 3);
64  return resolver(truncated.c_str());
65  }
66  if (function.find("EXT", function.size() - 3) != std::string::npos) {
67  auto truncated = function.substr(0u, function.size() - 3);
68  return resolver(truncated.c_str());
69  }
70  return nullptr;
71  };
72 }
73 
74 ProcTableGLES::ProcTableGLES( // NOLINT(google-readability-function-size)
75  Resolver resolver) {
76  // The reason this constructor has anywhere near enough code to tip off
77  // `google-readability-function-size` is the proc macros, so ignore the lint.
78 
79  if (!resolver) {
80  return;
81  }
82 
83  resolver = WrappedResolver(resolver);
84 
85  auto error_fn = reinterpret_cast<PFNGLGETERRORPROC>(resolver("glGetError"));
86  if (!error_fn) {
87  VALIDATION_LOG << "Could not resolve " << "glGetError";
88  return;
89  }
90 
91 #define IMPELLER_PROC(proc_ivar) \
92  if (auto fn_ptr = resolver(proc_ivar.name)) { \
93  proc_ivar.function = \
94  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
95  proc_ivar.error_fn = error_fn; \
96  } else { \
97  VALIDATION_LOG << "Could not resolve " << proc_ivar.name; \
98  return; \
99  }
100 
102 
103  description_ = std::make_unique<DescriptionGLES>(*this);
104 
105  if (!description_->IsValid()) {
106  return;
107  }
108 
109  if (description_->IsES()) {
111  } else {
113  }
114 
115 #undef IMPELLER_PROC
116 
117 #define IMPELLER_PROC(proc_ivar) \
118  if (auto fn_ptr = resolver(proc_ivar.name)) { \
119  proc_ivar.function = \
120  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
121  proc_ivar.error_fn = error_fn; \
122  }
125 
126 #undef IMPELLER_PROC
127 
128  if (!description_->HasDebugExtension()) {
129  PushDebugGroupKHR.Reset();
130  PopDebugGroupKHR.Reset();
131  ObjectLabelKHR.Reset();
132  } else {
133  GetIntegerv(GL_MAX_LABEL_LENGTH_KHR, &debug_label_max_length_);
134  }
135 
136  if (!description_->HasExtension("GL_EXT_discard_framebuffer")) {
137  DiscardFramebufferEXT.Reset();
138  }
139 
140  capabilities_ = std::make_shared<CapabilitiesGLES>(*this);
141 
142  is_valid_ = true;
143 }
144 
146 
148  return is_valid_;
149 }
150 
152  GLuint shader,
153  const fml::Mapping& mapping,
154  const std::vector<Scalar>& defines) const {
155  if (defines.empty()) {
156  const GLchar* sources[] = {
157  reinterpret_cast<const GLchar*>(mapping.GetMapping())};
158  const GLint lengths[] = {static_cast<GLint>(mapping.GetSize())};
159  ShaderSource(shader, 1u, sources, lengths);
160  return;
161  }
162  const auto& shader_source = ComputeShaderWithDefines(mapping, defines);
163  if (!shader_source.has_value()) {
164  VALIDATION_LOG << "Failed to append constant data to shader";
165  return;
166  }
167 
168  const GLchar* sources[] = {
169  reinterpret_cast<const GLchar*>(shader_source->c_str())};
170  const GLint lengths[] = {static_cast<GLint>(shader_source->size())};
171  ShaderSource(shader, 1u, sources, lengths);
172 }
173 
174 // Visible For testing.
175 std::optional<std::string> ProcTableGLES::ComputeShaderWithDefines(
176  const fml::Mapping& mapping,
177  const std::vector<Scalar>& defines) const {
178  auto shader_source = std::string{
179  reinterpret_cast<const char*>(mapping.GetMapping()), mapping.GetSize()};
180 
181  // Look for the first newline after the '#version' header, which impellerc
182  // will always emit as the first line of a compiled shader.
183  auto index = shader_source.find('\n');
184  if (index == std::string::npos) {
185  VALIDATION_LOG << "Failed to append constant data to shader";
186  return std::nullopt;
187  }
188 
189  std::stringstream ss;
190  ss << std::fixed;
191  for (auto i = 0u; i < defines.size(); i++) {
192  ss << "#define SPIRV_CROSS_CONSTANT_ID_" << i << " " << defines[i] << '\n';
193  }
194  auto define_string = ss.str();
195  shader_source.insert(index + 1, define_string);
196  return shader_source;
197 }
198 
200  return description_.get();
201 }
202 
203 const std::shared_ptr<const CapabilitiesGLES>& ProcTableGLES::GetCapabilities()
204  const {
205  return capabilities_;
206 }
207 
208 static const char* FramebufferStatusToString(GLenum status) {
209  switch (status) {
210  case GL_FRAMEBUFFER_COMPLETE:
211  return "GL_FRAMEBUFFER_COMPLETE";
212  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
213  return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
214 #if GL_ES_VERSION_2_0
215  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
216  return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
217 #endif
218  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
219  return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
220  case GL_FRAMEBUFFER_UNSUPPORTED:
221  return "GL_FRAMEBUFFER_UNSUPPORTED";
222  case GL_INVALID_ENUM:
223  return "GL_INVALID_ENUM";
224  }
225 
226  return "Unknown FBO Error Status";
227 }
228 
229 static const char* AttachmentTypeString(GLint type) {
230  switch (type) {
231  case GL_RENDERBUFFER:
232  return "GL_RENDERBUFFER";
233  case GL_TEXTURE:
234  return "GL_TEXTURE";
235  case GL_NONE:
236  return "GL_NONE";
237  }
238 
239  return "Unknown Type";
240 }
241 
242 static std::string DescribeFramebufferAttachment(const ProcTableGLES& gl,
243  GLenum attachment) {
244  GLint param = GL_NONE;
245  gl.GetFramebufferAttachmentParameteriv(
246  GL_FRAMEBUFFER, // target
247  attachment, // attachment
248  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, // parameter name
249  &param // parameter
250  );
251 
252  if (param != GL_NONE) {
253  param = GL_NONE;
254  gl.GetFramebufferAttachmentParameteriv(
255  GL_FRAMEBUFFER, // target
256  attachment, // attachment
257  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, // parameter name
258  &param // parameter
259  );
260  std::stringstream stream;
261  stream << AttachmentTypeString(param) << "(" << param << ")";
262  return stream.str();
263  }
264 
265  return "No Attachment";
266 }
267 
269  GLint framebuffer = GL_NONE;
270  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
271  if (IsFramebuffer(framebuffer) == GL_FALSE) {
272  return "No framebuffer or the default window framebuffer is bound.";
273  }
274 
275  GLenum status = CheckFramebufferStatus(framebuffer);
276  std::stringstream stream;
277  stream << "FBO "
278  << ((framebuffer == GL_NONE) ? "(Default)"
279  : std::to_string(framebuffer))
280  << ": " << FramebufferStatusToString(status) << std::endl;
282  stream << "Framebuffer is complete." << std::endl;
283  } else {
284  stream << "Framebuffer is incomplete." << std::endl;
285  }
286  stream << "Description: " << std::endl;
287  stream << "Color Attachment: "
288  << DescribeFramebufferAttachment(*this, GL_COLOR_ATTACHMENT0)
289  << std::endl;
290  stream << "Color Attachment: "
291  << DescribeFramebufferAttachment(*this, GL_DEPTH_ATTACHMENT)
292  << std::endl;
293  stream << "Color Attachment: "
294  << DescribeFramebufferAttachment(*this, GL_STENCIL_ATTACHMENT)
295  << std::endl;
296  return stream.str();
297 }
298 
300  GLint framebuffer = GL_NONE;
301  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
302  if (IsFramebuffer(framebuffer) == GL_FALSE) {
303  // The default framebuffer is always complete.
304  return true;
305  }
306  GLenum status = CheckFramebufferStatus(framebuffer);
307  return status == GL_FRAMEBUFFER_COMPLETE;
308 }
309 
310 static std::optional<GLenum> ToDebugIdentifier(DebugResourceType type) {
311  switch (type) {
313  return GL_TEXTURE;
315  return GL_BUFFER_KHR;
317  return GL_PROGRAM_KHR;
319  return GL_SHADER_KHR;
321  return GL_RENDERBUFFER;
323  return GL_FRAMEBUFFER;
324  }
325  FML_UNREACHABLE();
326 }
327 
328 static bool ResourceIsLive(const ProcTableGLES& gl,
329  DebugResourceType type,
330  GLint name) {
331  switch (type) {
333  return gl.IsTexture(name);
335  return gl.IsBuffer(name);
337  return gl.IsProgram(name);
339  return gl.IsShader(name);
341  return gl.IsRenderbuffer(name);
343  return gl.IsFramebuffer(name);
344  }
345  FML_UNREACHABLE();
346 }
347 
349  GLint name,
350  const std::string& label) const {
351  if (debug_label_max_length_ <= 0) {
352  return true;
353  }
354  if (!ObjectLabelKHR.IsAvailable()) {
355  return true;
356  }
357  if (!ResourceIsLive(*this, type, name)) {
358  return false;
359  }
360  const auto identifier = ToDebugIdentifier(type);
361  const auto label_length =
362  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
363  if (!identifier.has_value()) {
364  return true;
365  }
366  ObjectLabelKHR(identifier.value(), // identifier
367  name, // name
368  label_length, // length
369  label.data() // label
370  );
371  return true;
372 }
373 
374 void ProcTableGLES::PushDebugGroup(const std::string& label) const {
375 #ifdef IMPELLER_DEBUG
376  if (debug_label_max_length_ <= 0) {
377  return;
378  }
379 
380  UniqueID id;
381  const auto label_length =
382  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
383  PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, // source
384  static_cast<GLuint>(id.id), // id
385  label_length, // length
386  label.data() // message
387  );
388 #endif // IMPELLER_DEBUG
389 }
390 
392 #ifdef IMPELLER_DEBUG
393  if (debug_label_max_length_ <= 0) {
394  return;
395  }
396 
397  PopDebugGroupKHR();
398 #endif // IMPELLER_DEBUG
399 }
400 
401 std::string ProcTableGLES::GetProgramInfoLogString(GLuint program) const {
402  GLint length = 0;
403  GetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
404  if (length <= 0) {
405  return "";
406  }
407 
408  length = std::min<GLint>(length, 1024);
409  Allocation allocation;
410  if (!allocation.Truncate(length, false)) {
411  return "";
412  }
413  GetProgramInfoLog(program, // program
414  length, // max length
415  &length, // length written (excluding NULL terminator)
416  reinterpret_cast<GLchar*>(allocation.GetBuffer()) // buffer
417  );
418  if (length <= 0) {
419  return "";
420  }
421  return std::string{reinterpret_cast<const char*>(allocation.GetBuffer()),
422  static_cast<size_t>(length)};
423 }
424 
425 } // namespace impeller
impeller::ProcTableGLES::ShaderSourceMapping
void ShaderSourceMapping(GLuint shader, const fml::Mapping &mapping, const std::vector< Scalar > &defines={}) const
Set the source for the attached [shader].
Definition: proc_table_gles.cc:151
impeller::DescribeFramebufferAttachment
static std::string DescribeFramebufferAttachment(const ProcTableGLES &gl, GLenum attachment)
Definition: proc_table_gles.cc:242
impeller::ProcTableGLES::FOR_EACH_IMPELLER_PROC
FOR_EACH_IMPELLER_PROC(IMPELLER_PROC)
impeller::ProcTableGLES::ProcTableGLES
ProcTableGLES(Resolver resolver)
Definition: proc_table_gles.cc:74
allocation.h
impeller::ProcTableGLES::FOR_EACH_IMPELLER_GLES3_PROC
FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC)
impeller::ProcTableGLES::PushDebugGroup
void PushDebugGroup(const std::string &string) const
Definition: proc_table_gles.cc:374
IMPELLER_PROC
#define IMPELLER_PROC(proc_ivar)
impeller::DebugResourceType::kBuffer
@ kBuffer
impeller::ResourceIsLive
static bool ResourceIsLive(const ProcTableGLES &gl, DebugResourceType type, GLint name)
Definition: proc_table_gles.cc:328
impeller::ProcTableGLES::SetDebugLabel
bool SetDebugLabel(DebugResourceType type, GLint name, const std::string &label) const
Definition: proc_table_gles.cc:348
impeller::WrappedResolver
ProcTableGLES::Resolver WrappedResolver(const ProcTableGLES::Resolver &resolver)
Definition: proc_table_gles.cc:52
impeller::ProcTableGLES::IsValid
bool IsValid() const
Definition: proc_table_gles.cc:147
impeller::DebugResourceType::kProgram
@ kProgram
validation.h
impeller::ProcTableGLES::FOR_EACH_IMPELLER_EXT_PROC
FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC)
impeller::DescriptionGLES
Definition: description_gles.h:18
impeller::ProcTableGLES::FOR_EACH_IMPELLER_ES_ONLY_PROC
FOR_EACH_IMPELLER_ES_ONLY_PROC(IMPELLER_PROC)
impeller::ProcTableGLES::Resolver
std::function< void *(const char *function_name)> Resolver
Definition: proc_table_gles.h:231
impeller::DebugResourceType::kTexture
@ kTexture
capabilities.h
impeller::ProcTableGLES::GetCapabilities
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
Definition: proc_table_gles.cc:203
impeller::Allocation::GetBuffer
uint8_t * GetBuffer() const
Definition: allocation.cc:20
impeller::ProcTableGLES
Definition: proc_table_gles.h:229
impeller::FramebufferStatusToString
static const char * FramebufferStatusToString(GLenum status)
Definition: proc_table_gles.cc:208
impeller::ProcTableGLES::ComputeShaderWithDefines
std::optional< std::string > ComputeShaderWithDefines(const fml::Mapping &mapping, const std::vector< Scalar > &defines) const
Definition: proc_table_gles.cc:175
impeller::ProcTableGLES::GetProgramInfoLogString
std::string GetProgramInfoLogString(GLuint program) const
Definition: proc_table_gles.cc:401
proc_table_gles.h
impeller::DebugResourceType::kShader
@ kShader
impeller::Allocation
Definition: allocation.h:15
impeller::DebugResourceType::kRenderBuffer
@ kRenderBuffer
impeller::ProcTableGLES::DescribeCurrentFramebuffer
std::string DescribeCurrentFramebuffer() const
Definition: proc_table_gles.cc:268
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::ProcTableGLES::FOR_EACH_IMPELLER_DESKTOP_ONLY_PROC
FOR_EACH_IMPELLER_DESKTOP_ONLY_PROC(IMPELLER_PROC)
impeller::DebugResourceType
DebugResourceType
Definition: proc_table_gles.h:220
comparable.h
impeller::ProcTableGLES::IsCurrentFramebufferComplete
bool IsCurrentFramebufferComplete() const
Definition: proc_table_gles.cc:299
impeller::DebugResourceType::kFrameBuffer
@ kFrameBuffer
impeller::GLErrorToString
const char * GLErrorToString(GLenum value)
Definition: proc_table_gles.cc:18
impeller::UniqueID
Definition: comparable.h:16
impeller::AttachmentTypeString
static const char * AttachmentTypeString(GLint type)
Definition: proc_table_gles.cc:229
capabilities_gles.h
impeller
Definition: aiks_blur_unittests.cc:20
impeller::ProcTableGLES::PopDebugGroup
void PopDebugGroup() const
Definition: proc_table_gles.cc:391
impeller::Allocation::Truncate
bool Truncate(size_t length, bool npot=true)
Definition: allocation.cc:32
impeller::GLErrorIsFatal
bool GLErrorIsFatal(GLenum value)
Definition: proc_table_gles.cc:38
impeller::ToDebugIdentifier
static std::optional< GLenum > ToDebugIdentifier(DebugResourceType type)
Definition: proc_table_gles.cc:310
impeller::ProcTableGLES::GetDescription
const DescriptionGLES * GetDescription() const
Definition: proc_table_gles.cc:199
impeller::ProcTableGLES::~ProcTableGLES
~ProcTableGLES()