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 
75  if (!resolver) {
76  return;
77  }
78 
79  resolver = WrappedResolver(resolver);
80 
81  auto error_fn = reinterpret_cast<PFNGLGETERRORPROC>(resolver("glGetError"));
82  if (!error_fn) {
83  VALIDATION_LOG << "Could not resolve "
84  << "glGetError";
85  return;
86  }
87 
88 #define IMPELLER_PROC(proc_ivar) \
89  if (auto fn_ptr = resolver(proc_ivar.name)) { \
90  proc_ivar.function = \
91  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
92  proc_ivar.error_fn = error_fn; \
93  } else { \
94  VALIDATION_LOG << "Could not resolve " << proc_ivar.name; \
95  return; \
96  }
97 
99 
100 #undef IMPELLER_PROC
101 
102 #define IMPELLER_PROC(proc_ivar) \
103  if (auto fn_ptr = resolver(proc_ivar.name)) { \
104  proc_ivar.function = \
105  reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
106  proc_ivar.error_fn = error_fn; \
107  }
110 
111 #undef IMPELLER_PROC
112 
113  description_ = std::make_unique<DescriptionGLES>(*this);
114 
115  if (!description_->IsValid()) {
116  return;
117  }
118 
119  if (!description_->HasDebugExtension()) {
120  PushDebugGroupKHR.Reset();
121  PopDebugGroupKHR.Reset();
122  ObjectLabelKHR.Reset();
123  } else {
124  GetIntegerv(GL_MAX_LABEL_LENGTH_KHR, &debug_label_max_length_);
125  }
126 
127  if (!description_->HasExtension("GL_EXT_discard_framebuffer")) {
128  DiscardFramebufferEXT.Reset();
129  }
130 
131  capabilities_ = std::make_shared<CapabilitiesGLES>(*this);
132 
133  is_valid_ = true;
134 }
135 
137 
139  return is_valid_;
140 }
141 
143  GLuint shader,
144  const fml::Mapping& mapping,
145  const std::vector<Scalar>& defines) const {
146  if (defines.empty()) {
147  const GLchar* sources[] = {
148  reinterpret_cast<const GLchar*>(mapping.GetMapping())};
149  const GLint lengths[] = {static_cast<GLint>(mapping.GetSize())};
150  ShaderSource(shader, 1u, sources, lengths);
151  return;
152  }
153  const auto& shader_source = ComputeShaderWithDefines(mapping, defines);
154  if (!shader_source.has_value()) {
155  VALIDATION_LOG << "Failed to append constant data to shader";
156  return;
157  }
158 
159  const GLchar* sources[] = {
160  reinterpret_cast<const GLchar*>(shader_source->c_str())};
161  const GLint lengths[] = {static_cast<GLint>(shader_source->size())};
162  ShaderSource(shader, 1u, sources, lengths);
163 }
164 
165 // Visible For testing.
166 std::optional<std::string> ProcTableGLES::ComputeShaderWithDefines(
167  const fml::Mapping& mapping,
168  const std::vector<Scalar>& defines) const {
169  auto shader_source = std::string{
170  reinterpret_cast<const char*>(mapping.GetMapping()), mapping.GetSize()};
171 
172  // Look for the first newline after the '#version' header, which impellerc
173  // will always emit as the first line of a compiled shader.
174  auto index = shader_source.find('\n');
175  if (index == std::string::npos) {
176  VALIDATION_LOG << "Failed to append constant data to shader";
177  return std::nullopt;
178  }
179 
180  std::stringstream ss;
181  ss << std::fixed;
182  for (auto i = 0u; i < defines.size(); i++) {
183  ss << "#define SPIRV_CROSS_CONSTANT_ID_" << i << " " << defines[i] << '\n';
184  }
185  auto define_string = ss.str();
186  shader_source.insert(index + 1, define_string);
187  return shader_source;
188 }
189 
191  return description_.get();
192 }
193 
194 const std::shared_ptr<const CapabilitiesGLES>& ProcTableGLES::GetCapabilities()
195  const {
196  return capabilities_;
197 }
198 
199 static const char* FramebufferStatusToString(GLenum status) {
200  switch (status) {
201  case GL_FRAMEBUFFER_COMPLETE:
202  return "GL_FRAMEBUFFER_COMPLETE";
203  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
204  return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
205 #if GL_ES_VERSION_2_0
206  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
207  return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
208 #endif
209  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
210  return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
211  case GL_FRAMEBUFFER_UNSUPPORTED:
212  return "GL_FRAMEBUFFER_UNSUPPORTED";
213  case GL_INVALID_ENUM:
214  return "GL_INVALID_ENUM";
215  }
216 
217  return "Unknown FBO Error Status";
218 }
219 
220 static const char* AttachmentTypeString(GLint type) {
221  switch (type) {
222  case GL_RENDERBUFFER:
223  return "GL_RENDERBUFFER";
224  case GL_TEXTURE:
225  return "GL_TEXTURE";
226  case GL_NONE:
227  return "GL_NONE";
228  }
229 
230  return "Unknown Type";
231 }
232 
233 static std::string DescribeFramebufferAttachment(const ProcTableGLES& gl,
234  GLenum attachment) {
235  GLint param = GL_NONE;
236  gl.GetFramebufferAttachmentParameteriv(
237  GL_FRAMEBUFFER, // target
238  attachment, // attachment
239  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, // parameter name
240  &param // parameter
241  );
242 
243  if (param != GL_NONE) {
244  param = GL_NONE;
245  gl.GetFramebufferAttachmentParameteriv(
246  GL_FRAMEBUFFER, // target
247  attachment, // attachment
248  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, // parameter name
249  &param // parameter
250  );
251  std::stringstream stream;
252  stream << AttachmentTypeString(param) << "(" << param << ")";
253  return stream.str();
254  }
255 
256  return "No Attachment";
257 }
258 
260  GLint framebuffer = GL_NONE;
261  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
262  if (IsFramebuffer(framebuffer) == GL_FALSE) {
263  return "No framebuffer or the default window framebuffer is bound.";
264  }
265 
266  GLenum status = CheckFramebufferStatus(framebuffer);
267  std::stringstream stream;
268  stream << "FBO "
269  << ((framebuffer == GL_NONE) ? "(Default)"
270  : std::to_string(framebuffer))
271  << ": " << FramebufferStatusToString(status) << std::endl;
273  stream << "Framebuffer is complete." << std::endl;
274  } else {
275  stream << "Framebuffer is incomplete." << std::endl;
276  }
277  stream << "Description: " << std::endl;
278  stream << "Color Attachment: "
279  << DescribeFramebufferAttachment(*this, GL_COLOR_ATTACHMENT0)
280  << std::endl;
281  stream << "Color Attachment: "
282  << DescribeFramebufferAttachment(*this, GL_DEPTH_ATTACHMENT)
283  << std::endl;
284  stream << "Color Attachment: "
285  << DescribeFramebufferAttachment(*this, GL_STENCIL_ATTACHMENT)
286  << std::endl;
287  return stream.str();
288 }
289 
291  GLint framebuffer = GL_NONE;
292  GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer);
293  if (IsFramebuffer(framebuffer) == GL_FALSE) {
294  // The default framebuffer is always complete.
295  return true;
296  }
297  GLenum status = CheckFramebufferStatus(framebuffer);
298  return status == GL_FRAMEBUFFER_COMPLETE;
299 }
300 
301 static std::optional<GLenum> ToDebugIdentifier(DebugResourceType type) {
302  switch (type) {
304  return GL_TEXTURE;
306  return GL_BUFFER_KHR;
308  return GL_PROGRAM_KHR;
310  return GL_SHADER_KHR;
312  return GL_RENDERBUFFER;
314  return GL_FRAMEBUFFER;
315  }
316  FML_UNREACHABLE();
317 }
318 
319 static bool ResourceIsLive(const ProcTableGLES& gl,
320  DebugResourceType type,
321  GLint name) {
322  switch (type) {
324  return gl.IsTexture(name);
326  return gl.IsBuffer(name);
328  return gl.IsProgram(name);
330  return gl.IsShader(name);
332  return gl.IsRenderbuffer(name);
334  return gl.IsFramebuffer(name);
335  }
336  FML_UNREACHABLE();
337 }
338 
340  GLint name,
341  const std::string& label) const {
342  if (debug_label_max_length_ <= 0) {
343  return true;
344  }
345  if (!ObjectLabelKHR.IsAvailable()) {
346  return true;
347  }
348  if (!ResourceIsLive(*this, type, name)) {
349  return false;
350  }
351  const auto identifier = ToDebugIdentifier(type);
352  const auto label_length =
353  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
354  if (!identifier.has_value()) {
355  return true;
356  }
357  ObjectLabelKHR(identifier.value(), // identifier
358  name, // name
359  label_length, // length
360  label.data() // label
361  );
362  return true;
363 }
364 
365 void ProcTableGLES::PushDebugGroup(const std::string& label) const {
366 #ifdef IMPELLER_DEBUG
367  if (debug_label_max_length_ <= 0) {
368  return;
369  }
370 
371  UniqueID id;
372  const auto label_length =
373  std::min<GLsizei>(debug_label_max_length_ - 1, label.size());
374  PushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, // source
375  static_cast<GLuint>(id.id), // id
376  label_length, // length
377  label.data() // message
378  );
379 #endif // IMPELLER_DEBUG
380 }
381 
383 #ifdef IMPELLER_DEBUG
384  if (debug_label_max_length_ <= 0) {
385  return;
386  }
387 
388  PopDebugGroupKHR();
389 #endif // IMPELLER_DEBUG
390 }
391 
392 std::string ProcTableGLES::GetProgramInfoLogString(GLuint program) const {
393  GLint length = 0;
394  GetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
395  if (length <= 0) {
396  return "";
397  }
398 
399  length = std::min<GLint>(length, 1024);
400  Allocation allocation;
401  if (!allocation.Truncate(length, false)) {
402  return "";
403  }
404  GetProgramInfoLog(program, // program
405  length, // max length
406  &length, // length written (excluding NULL terminator)
407  reinterpret_cast<GLchar*>(allocation.GetBuffer()) // buffer
408  );
409  if (length <= 0) {
410  return "";
411  }
412  return std::string{reinterpret_cast<const char*>(allocation.GetBuffer()),
413  static_cast<size_t>(length)};
414 }
415 
416 } // 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:142
impeller::DescribeFramebufferAttachment
static std::string DescribeFramebufferAttachment(const ProcTableGLES &gl, GLenum attachment)
Definition: proc_table_gles.cc:233
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:365
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:319
impeller::ProcTableGLES::SetDebugLabel
bool SetDebugLabel(DebugResourceType type, GLint name, const std::string &label) const
Definition: proc_table_gles.cc:339
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:138
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::Resolver
std::function< void *(const char *function_name)> Resolver
Definition: proc_table_gles.h:219
impeller::DebugResourceType::kTexture
@ kTexture
capabilities.h
impeller::ProcTableGLES::GetCapabilities
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
Definition: proc_table_gles.cc:194
impeller::Allocation::GetBuffer
uint8_t * GetBuffer() const
Definition: allocation.cc:20
impeller::ProcTableGLES
Definition: proc_table_gles.h:217
impeller::FramebufferStatusToString
static const char * FramebufferStatusToString(GLenum status)
Definition: proc_table_gles.cc:199
impeller::ProcTableGLES::ComputeShaderWithDefines
std::optional< std::string > ComputeShaderWithDefines(const fml::Mapping &mapping, const std::vector< Scalar > &defines) const
Definition: proc_table_gles.cc:166
impeller::ProcTableGLES::GetProgramInfoLogString
std::string GetProgramInfoLogString(GLuint program) const
Definition: proc_table_gles.cc:392
proc_table_gles.h
impeller::DebugResourceType::kShader
@ kShader
impeller::Allocation
Definition: allocation.h:16
impeller::DebugResourceType::kRenderBuffer
@ kRenderBuffer
impeller::ProcTableGLES::DescribeCurrentFramebuffer
std::string DescribeCurrentFramebuffer() const
Definition: proc_table_gles.cc:259
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:67
impeller::DebugResourceType
DebugResourceType
Definition: proc_table_gles.h:208
comparable.h
impeller::ProcTableGLES::IsCurrentFramebufferComplete
bool IsCurrentFramebufferComplete() const
Definition: proc_table_gles.cc:290
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:220
capabilities_gles.h
impeller
Definition: aiks_context.cc:10
impeller::ProcTableGLES::PopDebugGroup
void PopDebugGroup() const
Definition: proc_table_gles.cc:382
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:301
impeller::ProcTableGLES::GetDescription
const DescriptionGLES * GetDescription() const
Definition: proc_table_gles.cc:190
impeller::ProcTableGLES::~ProcTableGLES
~ProcTableGLES()