Flutter Engine
 
Loading...
Searching...
No Matches
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
16namespace impeller {
17
18std::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
38bool 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
74ProcTableGLES::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.
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
211const std::shared_ptr<const CapabilitiesGLES>& ProcTableGLES::GetCapabilities()
212 const {
213 return capabilities_;
214}
215
216static 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
237static 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
250static 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
323static 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 }
341}
342
343static 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 }
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
398void 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
425std::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
virtual const uint8_t * GetMapping() const =0
virtual size_t GetSize() const =0
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
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
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].
std::string GetProgramInfoLogString(GLuint program) const
std::string DescribeCurrentFramebuffer() const
const std::shared_ptr< const CapabilitiesGLES > & GetCapabilities() const
bool IsCurrentFramebufferComplete() const
ProcTableGLES(Resolver resolver)
void PushDebugGroup(const std::string &string) const
const DescriptionGLES * GetDescription() const
int32_t value
#define FML_UNREACHABLE()
Definition logging.h:128
Dart_NativeFunction function
Definition fuchsia.cc:50
const char * name
Definition fuchsia.cc:49
size_t length
ProcTableGLES::Resolver WrappedResolver(const ProcTableGLES::Resolver &resolver)
std::string_view GLErrorToString(GLenum value)
bool GLErrorIsFatal(GLenum value)
static std::optional< GLenum > ToDebugIdentifier(DebugResourceType type)
static const char * FramebufferStatusToString(GLenum status)
static const char * AttachmentTypeString(GLint 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 FOR_EACH_IMPELLER_EXT_PROC(PROC)
#define FOR_EACH_IMPELLER_GLES3_PROC(PROC)
#define FOR_EACH_IMPELLER_DESKTOP_ONLY_PROC(PROC)
#define IP_ENABLE_GLES_LABELING
Enable to allow GLES to push/pop labels for usage in GPU traces.
#define FOR_EACH_IMPELLER_ES_ONLY_PROC(PROC)
#define FOR_EACH_IMPELLER_PROC(PROC)
const uintptr_t id
#define VALIDATION_LOG
Definition validation.h:91