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