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 
10 #include "GLES3/gl3.h"
16 
17 namespace impeller {
18 
19 std::string_view GLErrorToString(GLenum value) {
20  switch (value) {
21  case GL_NO_ERROR:
22  return "GL_NO_ERROR";
23  case GL_INVALID_ENUM:
24  return "GL_INVALID_ENUM";
25  case GL_INVALID_VALUE:
26  return "GL_INVALID_VALUE";
27  case GL_INVALID_OPERATION:
28  return "GL_INVALID_OPERATION";
29  case GL_INVALID_FRAMEBUFFER_OPERATION:
30  return "GL_INVALID_FRAMEBUFFER_OPERATION";
31  case GL_FRAMEBUFFER_COMPLETE:
32  return "GL_FRAMEBUFFER_COMPLETE";
33  case GL_OUT_OF_MEMORY:
34  return "GL_OUT_OF_MEMORY";
35  }
36  return "Unknown.";
37 }
38 
39 bool GLErrorIsFatal(GLenum value) {
40  switch (value) {
41  case GL_NO_ERROR:
42  return false;
43  case GL_INVALID_ENUM:
44  case GL_INVALID_VALUE:
45  case GL_INVALID_OPERATION:
46  case GL_INVALID_FRAMEBUFFER_OPERATION:
47  case GL_OUT_OF_MEMORY:
48  return true;
49  }
50  return false;
51 }
52 
54  const ProcTableGLES::Resolver& resolver) {
55  return [resolver](const char* function_name) -> void* {
56  auto resolved = resolver(function_name);
57  if (resolved) {
58  return resolved;
59  }
60  // If there are certain known suffixes (usually for extensions), strip them
61  // out and try to resolve the same proc addresses again.
62  auto function = std::string{function_name};
63  if (function.find("KHR", function.size() - 3) != std::string::npos) {
64  auto truncated = function.substr(0u, function.size() - 3);
65  return resolver(truncated.c_str());
66  }
67  if (function.find("EXT", function.size() - 3) != std::string::npos) {
68  auto truncated = function.substr(0u, function.size() - 3);
69  return resolver(truncated.c_str());
70  }
71  return nullptr;
72  };
73 }
74 
75 ProcTableGLES::ProcTableGLES( // NOLINT(google-readability-function-size)
76  Resolver resolver) {
77  // The reason this constructor has anywhere near enough code to tip off
78  // `google-readability-function-size` is the proc macros, so ignore the lint.
79 
80  if (!resolver) {
81  return;
82  }
83 
84  resolver = WrappedResolver(resolver);
85 
86  auto error_fn = reinterpret_cast<PFNGLGETERRORPROC>(resolver("glGetError"));
87  if (!error_fn) {
88  VALIDATION_LOG << "Could not resolve " << "glGetError";
89  return;
90  }
91 
92 #define IMPELLER_PROC(proc_ivar) \
93  if (auto fn_ptr = resolver(proc_ivar.name.data())) { \
94  proc_ivar.function = \
95  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
96  proc_ivar.error_fn = error_fn; \
97  } else { \
98  VALIDATION_LOG << "Could not resolve " << proc_ivar.name; \
99  return; \
100  }
101 
103 
104  description_ = std::make_unique<DescriptionGLES>(*this);
105 
106  if (!description_->IsValid()) {
107  return;
108  }
109 
110  if (description_->IsES()) {
112  } else {
114  }
115 
116 #undef IMPELLER_PROC
117 
118 #define IMPELLER_PROC(proc_ivar) \
119  if (auto fn_ptr = resolver(proc_ivar.name.data())) { \
120  proc_ivar.function = \
121  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
122  proc_ivar.error_fn = error_fn; \
123  }
124 
125  if (description_->GetGlVersion().IsAtLeast(Version(3))) {
127  }
128 
130 
131 #undef IMPELLER_PROC
132 
133  if (!IP_ENABLE_GLES_LABELING || !description_->HasDebugExtension()) {
134  PushDebugGroupKHR.Reset();
135  PopDebugGroupKHR.Reset();
136  ObjectLabelKHR.Reset();
137  } else {
138  GetIntegerv(GL_MAX_LABEL_LENGTH_KHR, &debug_label_max_length_);
139  }
140 
141  if (!description_->HasExtension("GL_EXT_discard_framebuffer")) {
142  DiscardFramebufferEXT.Reset();
143  }
144 
145  if (!description_->HasExtension("GL_ANGLE_framebuffer_blit")) {
146  BlitFramebufferANGLE.Reset();
147  }
148 
149  if (!description_->HasExtension("GL_EXT_instanced_arrays")) {
150  VertexAttribDivisorEXT.Reset();
151  }
152 
153  if (!description_->HasExtension("GL_EXT_draw_instanced")) {
154  DrawArraysInstancedEXT.Reset();
155  DrawElementsInstancedEXT.Reset();
156  }
157 
158  capabilities_ = std::make_shared<CapabilitiesGLES>(*this);
159 
160  is_valid_ = true;
161 }
162 
164 
166  return is_valid_;
167 }
168 
170  GLuint shader,
171  const fml::Mapping& mapping,
172  const std::vector<Scalar>& defines) const {
173  if (defines.empty()) {
174  const GLchar* sources[] = {
175  reinterpret_cast<const GLchar*>(mapping.GetMapping())};
176  const GLint lengths[] = {static_cast<GLint>(mapping.GetSize())};
177  ShaderSource(shader, 1u, sources, lengths);
178  return;
179  }
180  const auto& shader_source = ComputeShaderWithDefines(mapping, defines);
181  if (!shader_source.has_value()) {
182  VALIDATION_LOG << "Failed to append constant data to shader";
183  return;
184  }
185 
186  const GLchar* sources[] = {
187  reinterpret_cast<const GLchar*>(shader_source->c_str())};
188  const GLint lengths[] = {static_cast<GLint>(shader_source->size())};
189  ShaderSource(shader, 1u, sources, lengths);
190 }
191 
192 // Visible For testing.
193 std::optional<std::string> ProcTableGLES::ComputeShaderWithDefines(
194  const fml::Mapping& mapping,
195  const std::vector<Scalar>& defines) const {
196  std::string shader_source = std::string{
197  reinterpret_cast<const char*>(mapping.GetMapping()), mapping.GetSize()};
198 
199  // Look for the first newline after the '#version' header, which impellerc
200  // will always emit as the first line of a compiled shader.
201  size_t index = shader_source.find('\n');
202  if (index == std::string::npos) {
203  VALIDATION_LOG << "Failed to append constant data to shader";
204  return std::nullopt;
205  }
206 
207  std::stringstream ss;
208  ss << std::fixed;
209  for (auto i = 0u; i < defines.size(); i++) {
210  ss << "#define SPIRV_CROSS_CONSTANT_ID_" << i << " " << defines[i] << '\n';
211  }
212  auto define_string = ss.str();
213  shader_source.insert(index + 1, define_string);
214  return shader_source;
215 }
216 
218  return description_.get();
219 }
220 
221 const std::shared_ptr<const CapabilitiesGLES>& ProcTableGLES::GetCapabilities()
222  const {
223  return capabilities_;
224 }
225 
226 static const char* FramebufferStatusToString(GLenum status) {
227  switch (status) {
228  case GL_FRAMEBUFFER_COMPLETE:
229  return "GL_FRAMEBUFFER_COMPLETE";
230  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
231  return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
232 #if GL_ES_VERSION_2_0
233  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
234  return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
235 #endif
236  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
237  return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
238  case GL_FRAMEBUFFER_UNSUPPORTED:
239  return "GL_FRAMEBUFFER_UNSUPPORTED";
240  case GL_INVALID_ENUM:
241  return "GL_INVALID_ENUM";
242  }
243 
244  return "Unknown FBO Error Status";
245 }
246 
247 static const char* AttachmentTypeString(GLint type) {
248  switch (type) {
249  case GL_RENDERBUFFER:
250  return "GL_RENDERBUFFER";
251  case GL_TEXTURE:
252  return "GL_TEXTURE";
253  case GL_NONE:
254  return "GL_NONE";
255  }
256 
257  return "Unknown Type";
258 }
259 
260 static std::string DescribeFramebufferAttachment(const ProcTableGLES& gl,
261  GLenum attachment) {
262  GLint type = GL_NONE;
263  gl.GetFramebufferAttachmentParameteriv(
264  GL_FRAMEBUFFER, // target
265  attachment, // attachment
266  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, // parameter name
267  &type // parameter
268  );
269 
270  if (type != GL_NONE) {
271  GLint object = GL_NONE;
272  gl.GetFramebufferAttachmentParameteriv(
273  GL_FRAMEBUFFER, // target
274  attachment, // attachment
275  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, // parameter name
276  &object // parameter
277  );
278  std::stringstream stream;
279  stream << AttachmentTypeString(type) << "(" << object << ")";
280  return stream.str();
281  }
282 
283  return "No Attachment";
284 }
285 
287  GLint framebuffer = GL_NONE;
288  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
289  if (framebuffer == GL_NONE) {
290  return "The default framebuffer (FBO0) was bound.";
291  }
292  if (IsFramebuffer(framebuffer) == GL_FALSE) {
293  return std::format(
294  "The framebuffer binding ({}) was not a valid framebuffer.",
295  framebuffer);
296  }
297 
298  GLenum status = CheckFramebufferStatus(GL_FRAMEBUFFER);
299  std::stringstream stream;
300  stream << "FBO "
301  << ((framebuffer == GL_NONE) ? "(Default)"
302  : std::to_string(framebuffer))
303  << ": " << FramebufferStatusToString(status) << std::endl;
305  stream << "Framebuffer is complete." << std::endl;
306  } else {
307  stream << "Framebuffer is incomplete." << std::endl;
308  }
309  stream << "Description: " << std::endl;
310  stream << "Color Attachment: "
311  << DescribeFramebufferAttachment(*this, GL_COLOR_ATTACHMENT0)
312  << std::endl;
313  stream << "Depth Attachment: "
314  << DescribeFramebufferAttachment(*this, GL_DEPTH_ATTACHMENT)
315  << std::endl;
316  stream << "Stencil Attachment: "
317  << DescribeFramebufferAttachment(*this, GL_STENCIL_ATTACHMENT)
318  << std::endl;
319  return stream.str();
320 }
321 
323  GLint framebuffer = GL_NONE;
324  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
325  if (IsFramebuffer(framebuffer) == GL_FALSE) {
326  // The default framebuffer is always complete.
327  return true;
328  }
329  GLenum status = CheckFramebufferStatus(GL_FRAMEBUFFER);
330  return status == GL_FRAMEBUFFER_COMPLETE;
331 }
332 
333 static std::optional<GLenum> ToDebugIdentifier(DebugResourceType type) {
334  switch (type) {
336  return GL_TEXTURE;
338  return GL_BUFFER_KHR;
340  return GL_PROGRAM_KHR;
342  return GL_SHADER_KHR;
344  return GL_RENDERBUFFER;
346  return GL_FRAMEBUFFER;
348  return GL_SYNC_FENCE;
349  }
350  FML_UNREACHABLE();
351 }
352 
353 static bool ResourceIsLive(const ProcTableGLES& gl,
354  DebugResourceType type,
355  GLint name) {
356  switch (type) {
358  return gl.IsTexture(name);
360  return gl.IsBuffer(name);
362  return gl.IsProgram(name);
364  return gl.IsShader(name);
366  return gl.IsRenderbuffer(name);
368  return gl.IsFramebuffer(name);
370  return true;
371  }
372  FML_UNREACHABLE();
373 }
374 
376  if (debug_label_max_length_ <= 0) {
377  return false;
378  }
379  if (!ObjectLabelKHR.IsAvailable()) {
380  return false;
381  }
382  return true;
383 }
384 
386  GLint name,
387  std::string_view label) const {
388  if (!SupportsDebugLabels()) {
389  return true;
390  }
391  if (!ResourceIsLive(*this, type, name)) {
392  return false;
393  }
394  const auto identifier = ToDebugIdentifier(type);
395  const auto label_length =
396  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
397  if (!identifier.has_value()) {
398  return true;
399  }
400  ObjectLabelKHR(identifier.value(), // identifier
401  name, // name
402  label_length, // length
403  label.data() // label
404  );
405  return true;
406 }
407 
408 void ProcTableGLES::PushDebugGroup(const std::string& label) const {
409 #ifdef IMPELLER_DEBUG
410  if (debug_label_max_length_ <= 0) {
411  return;
412  }
413 
414  UniqueID id;
415  const auto label_length =
416  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
417  PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, // source
418  static_cast<GLuint>(id.id), // id
419  label_length, // length
420  label.data() // message
421  );
422 #endif // IMPELLER_DEBUG
423 }
424 
426 #ifdef IMPELLER_DEBUG
427  if (debug_label_max_length_ <= 0) {
428  return;
429  }
430 
431  PopDebugGroupKHR();
432 #endif // IMPELLER_DEBUG
433 }
434 
435 std::string ProcTableGLES::GetProgramInfoLogString(GLuint program) const {
436  GLint length = 0;
437  GetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
438  if (length <= 0) {
439  return "";
440  }
441 
442  length = std::min<GLint>(length, 1024);
443  Allocation allocation;
444  if (!allocation.Truncate(Bytes(length), false)) {
445  return "";
446  }
447  GetProgramInfoLog(program, // program
448  length, // max length
449  &length, // length written (excluding NULL terminator)
450  reinterpret_cast<GLchar*>(allocation.GetBuffer()) // buffer
451  );
452  if (length <= 0) {
453  return "";
454  }
455  return std::string{reinterpret_cast<const char*>(allocation.GetBuffer()),
456  static_cast<size_t>(length)};
457 }
458 
459 GLenum ProcTableGLES::CheckFramebufferStatusDebug(GLenum target) const {
460 #ifdef IMPELLER_DEBUG
461  return CheckFramebufferStatus(target);
462 #endif
463  return GL_FRAMEBUFFER_COMPLETE;
464 };
465 
466 } // namespace impeller
Describes an allocation on the heap.
Definition: allocation.h:23
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
GLenum CheckFramebufferStatusDebug(GLenum target) 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