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 <format>
8 #include <sstream>
9 
15 
16 namespace impeller {
17 
18 std::string_view 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.data())) { \
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.data())) { \
119  proc_ivar.function = \
120  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
121  proc_ivar.error_fn = error_fn; \
122  }
123 
124  if (description_->GetGlVersion().IsAtLeast(Version(3))) {
126  }
127 
129 
130 #undef IMPELLER_PROC
131 
132  if (!IP_ENABLE_GLES_LABELING || !description_->HasDebugExtension()) {
133  PushDebugGroupKHR.Reset();
134  PopDebugGroupKHR.Reset();
135  ObjectLabelKHR.Reset();
136  } else {
137  GetIntegerv(GL_MAX_LABEL_LENGTH_KHR, &debug_label_max_length_);
138  }
139 
140  if (!description_->HasExtension("GL_EXT_discard_framebuffer")) {
141  DiscardFramebufferEXT.Reset();
142  }
143 
144  if (!description_->HasExtension("GL_ANGLE_framebuffer_blit")) {
145  BlitFramebufferANGLE.Reset();
146  }
147 
148  capabilities_ = std::make_shared<CapabilitiesGLES>(*this);
149 
150  is_valid_ = true;
151 }
152 
154 
156  return is_valid_;
157 }
158 
160  GLuint shader,
161  const fml::Mapping& mapping,
162  const std::vector<Scalar>& defines) const {
163  if (defines.empty()) {
164  const GLchar* sources[] = {
165  reinterpret_cast<const GLchar*>(mapping.GetMapping())};
166  const GLint lengths[] = {static_cast<GLint>(mapping.GetSize())};
167  ShaderSource(shader, 1u, sources, lengths);
168  return;
169  }
170  const auto& shader_source = ComputeShaderWithDefines(mapping, defines);
171  if (!shader_source.has_value()) {
172  VALIDATION_LOG << "Failed to append constant data to shader";
173  return;
174  }
175 
176  const GLchar* sources[] = {
177  reinterpret_cast<const GLchar*>(shader_source->c_str())};
178  const GLint lengths[] = {static_cast<GLint>(shader_source->size())};
179  ShaderSource(shader, 1u, sources, lengths);
180 }
181 
182 // Visible For testing.
183 std::optional<std::string> ProcTableGLES::ComputeShaderWithDefines(
184  const fml::Mapping& mapping,
185  const std::vector<Scalar>& defines) const {
186  std::string shader_source = std::string{
187  reinterpret_cast<const char*>(mapping.GetMapping()), mapping.GetSize()};
188 
189  // Look for the first newline after the '#version' header, which impellerc
190  // will always emit as the first line of a compiled shader.
191  size_t index = shader_source.find('\n');
192  if (index == std::string::npos) {
193  VALIDATION_LOG << "Failed to append constant data to shader";
194  return std::nullopt;
195  }
196 
197  std::stringstream ss;
198  ss << std::fixed;
199  for (auto i = 0u; i < defines.size(); i++) {
200  ss << "#define SPIRV_CROSS_CONSTANT_ID_" << i << " " << defines[i] << '\n';
201  }
202  auto define_string = ss.str();
203  shader_source.insert(index + 1, define_string);
204  return shader_source;
205 }
206 
208  return description_.get();
209 }
210 
211 const std::shared_ptr<const CapabilitiesGLES>& ProcTableGLES::GetCapabilities()
212  const {
213  return capabilities_;
214 }
215 
216 static const char* FramebufferStatusToString(GLenum status) {
217  switch (status) {
218  case GL_FRAMEBUFFER_COMPLETE:
219  return "GL_FRAMEBUFFER_COMPLETE";
220  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
221  return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
222 #if GL_ES_VERSION_2_0
223  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
224  return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
225 #endif
226  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
227  return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
228  case GL_FRAMEBUFFER_UNSUPPORTED:
229  return "GL_FRAMEBUFFER_UNSUPPORTED";
230  case GL_INVALID_ENUM:
231  return "GL_INVALID_ENUM";
232  }
233 
234  return "Unknown FBO Error Status";
235 }
236 
237 static const char* AttachmentTypeString(GLint type) {
238  switch (type) {
239  case GL_RENDERBUFFER:
240  return "GL_RENDERBUFFER";
241  case GL_TEXTURE:
242  return "GL_TEXTURE";
243  case GL_NONE:
244  return "GL_NONE";
245  }
246 
247  return "Unknown Type";
248 }
249 
250 static std::string DescribeFramebufferAttachment(const ProcTableGLES& gl,
251  GLenum attachment) {
252  GLint type = GL_NONE;
253  gl.GetFramebufferAttachmentParameteriv(
254  GL_FRAMEBUFFER, // target
255  attachment, // attachment
256  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, // parameter name
257  &type // parameter
258  );
259 
260  if (type != GL_NONE) {
261  GLint object = GL_NONE;
262  gl.GetFramebufferAttachmentParameteriv(
263  GL_FRAMEBUFFER, // target
264  attachment, // attachment
265  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, // parameter name
266  &object // parameter
267  );
268  std::stringstream stream;
269  stream << AttachmentTypeString(type) << "(" << object << ")";
270  return stream.str();
271  }
272 
273  return "No Attachment";
274 }
275 
277  GLint framebuffer = GL_NONE;
278  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
279  if (framebuffer == GL_NONE) {
280  return "The default framebuffer (FBO0) was bound.";
281  }
282  if (IsFramebuffer(framebuffer) == GL_FALSE) {
283  return std::format(
284  "The framebuffer binding ({}) was not a valid framebuffer.",
285  framebuffer);
286  }
287 
288  GLenum status = CheckFramebufferStatus(GL_FRAMEBUFFER);
289  std::stringstream stream;
290  stream << "FBO "
291  << ((framebuffer == GL_NONE) ? "(Default)"
292  : std::to_string(framebuffer))
293  << ": " << FramebufferStatusToString(status) << std::endl;
295  stream << "Framebuffer is complete." << std::endl;
296  } else {
297  stream << "Framebuffer is incomplete." << std::endl;
298  }
299  stream << "Description: " << std::endl;
300  stream << "Color Attachment: "
301  << DescribeFramebufferAttachment(*this, GL_COLOR_ATTACHMENT0)
302  << std::endl;
303  stream << "Depth Attachment: "
304  << DescribeFramebufferAttachment(*this, GL_DEPTH_ATTACHMENT)
305  << std::endl;
306  stream << "Stencil Attachment: "
307  << DescribeFramebufferAttachment(*this, GL_STENCIL_ATTACHMENT)
308  << std::endl;
309  return stream.str();
310 }
311 
313  GLint framebuffer = GL_NONE;
314  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
315  if (IsFramebuffer(framebuffer) == GL_FALSE) {
316  // The default framebuffer is always complete.
317  return true;
318  }
319  GLenum status = CheckFramebufferStatus(GL_FRAMEBUFFER);
320  return status == GL_FRAMEBUFFER_COMPLETE;
321 }
322 
323 static std::optional<GLenum> ToDebugIdentifier(DebugResourceType type) {
324  switch (type) {
326  return GL_TEXTURE;
328  return GL_BUFFER_KHR;
330  return GL_PROGRAM_KHR;
332  return GL_SHADER_KHR;
334  return GL_RENDERBUFFER;
336  return GL_FRAMEBUFFER;
338  return GL_SYNC_FENCE;
339  }
340  FML_UNREACHABLE();
341 }
342 
343 static bool ResourceIsLive(const ProcTableGLES& gl,
345  GLint name) {
346  switch (type) {
348  return gl.IsTexture(name);
350  return gl.IsBuffer(name);
352  return gl.IsProgram(name);
354  return gl.IsShader(name);
356  return gl.IsRenderbuffer(name);
358  return gl.IsFramebuffer(name);
360  return true;
361  }
362  FML_UNREACHABLE();
363 }
364 
366  if (debug_label_max_length_ <= 0) {
367  return false;
368  }
369  if (!ObjectLabelKHR.IsAvailable()) {
370  return false;
371  }
372  return true;
373 }
374 
376  GLint name,
377  std::string_view label) const {
378  if (!SupportsDebugLabels()) {
379  return true;
380  }
381  if (!ResourceIsLive(*this, type, name)) {
382  return false;
383  }
384  const auto identifier = ToDebugIdentifier(type);
385  const auto label_length =
386  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
387  if (!identifier.has_value()) {
388  return true;
389  }
390  ObjectLabelKHR(identifier.value(), // identifier
391  name, // name
392  label_length, // length
393  label.data() // label
394  );
395  return true;
396 }
397 
398 void ProcTableGLES::PushDebugGroup(const std::string& label) const {
399 #ifdef IMPELLER_DEBUG
400  if (debug_label_max_length_ <= 0) {
401  return;
402  }
403 
404  UniqueID id;
405  const auto label_length =
406  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
407  PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, // source
408  static_cast<GLuint>(id.id), // id
409  label_length, // length
410  label.data() // message
411  );
412 #endif // IMPELLER_DEBUG
413 }
414 
416 #ifdef IMPELLER_DEBUG
417  if (debug_label_max_length_ <= 0) {
418  return;
419  }
420 
421  PopDebugGroupKHR();
422 #endif // IMPELLER_DEBUG
423 }
424 
425 std::string ProcTableGLES::GetProgramInfoLogString(GLuint program) const {
426  GLint length = 0;
427  GetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
428  if (length <= 0) {
429  return "";
430  }
431 
432  length = std::min<GLint>(length, 1024);
433  Allocation allocation;
434  if (!allocation.Truncate(Bytes{length}, false)) {
435  return "";
436  }
437  GetProgramInfoLog(program, // program
438  length, // max length
439  &length, // length written (excluding NULL terminator)
440  reinterpret_cast<GLchar*>(allocation.GetBuffer()) // buffer
441  );
442  if (length <= 0) {
443  return "";
444  }
445  return std::string{reinterpret_cast<const char*>(allocation.GetBuffer()),
446  static_cast<size_t>(length)};
447 }
448 
449 } // namespace impeller
GLenum type
Describes an allocation on the heap.
Definition: allocation.h:22
uint8_t * GetBuffer() const
Gets the pointer to the start of the allocation.
Definition: allocation.cc:20
bool Truncate(Bytes length, bool npot=true)
Resize the underlying allocation to at least given number of bytes.
Definition: allocation.cc:32
FOR_EACH_IMPELLER_ES_ONLY_PROC(IMPELLER_PROC)
std::optional< std::string > ComputeShaderWithDefines(const fml::Mapping &mapping, const std::vector< Scalar > &defines) const
bool SetDebugLabel(DebugResourceType type, GLint name, std::string_view label) const
FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC)
std::function< void *(const char *function_name)> Resolver
void ShaderSourceMapping(GLuint shader, const fml::Mapping &mapping, const std::vector< Scalar > &defines={}) const
Set the source for the attached [shader].
FOR_EACH_IMPELLER_DESKTOP_ONLY_PROC(IMPELLER_PROC)
std::string GetProgramInfoLogString(GLuint program) const
bool SupportsDebugLabels() const
std::string DescribeCurrentFramebuffer() const
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
bool IsCurrentFramebufferComplete() const
ProcTableGLES(Resolver resolver)
FOR_EACH_IMPELLER_PROC(IMPELLER_PROC)
FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC)
void PushDebugGroup(const std::string &string) const
const DescriptionGLES * GetDescription() const
int32_t value
ProcTableGLES::Resolver WrappedResolver(const ProcTableGLES::Resolver &resolver)
static const char * AttachmentTypeString(GLint type)
std::string_view GLErrorToString(GLenum value)
bool GLErrorIsFatal(GLenum value)
static const char * FramebufferStatusToString(GLenum status)
static std::optional< GLenum > ToDebugIdentifier(DebugResourceType type)
static bool ResourceIsLive(const ProcTableGLES &gl, DebugResourceType type, GLint name)
static std::string DescribeFramebufferAttachment(const ProcTableGLES &gl, GLenum attachment)
#define IMPELLER_PROC(proc_ivar)
#define IP_ENABLE_GLES_LABELING
Enable to allow GLES to push/pop labels for usage in GPU traces.
#define VALIDATION_LOG
Definition: validation.h:91