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