Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
texture_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 <optional>
9#include <utility>
10
11#include "flutter/fml/logging.h"
12#include "flutter/fml/mapping.h"
20
21namespace impeller {
22
23namespace {
24static bool IsDepthStencilFormat(PixelFormat format) {
25 switch (format) {
29 return true;
44 return false;
45 }
47}
48
49static TextureGLES::Type GetTextureTypeFromDescriptor(
50 const TextureDescriptor& desc,
51 const std::shared_ptr<const CapabilitiesGLES>& capabilities) {
52 const auto usage = static_cast<TextureUsageMask>(desc.usage);
53 const auto render_target = TextureUsage::kRenderTarget;
54 const auto is_msaa = desc.sample_count == SampleCount::kCount4;
55 if (usage == render_target && IsDepthStencilFormat(desc.format)) {
58 }
59 return is_msaa ? (capabilities->SupportsImplicitResolvingMSAA()
62 : TextureGLES::Type::kTexture;
63}
64
65struct TexImage2DData {
66 GLint internal_format = 0;
67 GLenum external_format = GL_NONE;
68 GLenum type = GL_NONE;
69 std::shared_ptr<const fml::Mapping> data;
70
71 explicit TexImage2DData(PixelFormat pixel_format) {
72 switch (pixel_format) {
74 internal_format = GL_ALPHA;
75 external_format = GL_ALPHA;
76 type = GL_UNSIGNED_BYTE;
77 break;
79 internal_format = GL_RED;
80 external_format = GL_RED;
81 type = GL_UNSIGNED_BYTE;
82 break;
87 internal_format = GL_RGBA;
88 external_format = GL_RGBA;
89 type = GL_UNSIGNED_BYTE;
90 break;
92 internal_format = GL_R32F;
93 external_format = GL_RGBA;
94 type = GL_FLOAT;
95 break;
97 internal_format = GL_RGBA32F;
98 external_format = GL_RGBA;
99 type = GL_FLOAT;
100 break;
102 internal_format = GL_RGBA16F;
103 external_format = GL_RGBA;
104 type = GL_HALF_FLOAT;
105 break;
107 // Pure stencil textures are only available in OpenGL 4.4+, which is
108 // ~0% of mobile devices. Instead, we use a depth-stencil texture and
109 // only use the stencil component.
110 //
111 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
113 internal_format = GL_DEPTH_STENCIL;
114 external_format = GL_DEPTH_STENCIL;
115 type = GL_UNSIGNED_INT_24_8;
116 break;
123 return;
124 }
125 is_valid_ = true;
126 }
127
128 TexImage2DData(PixelFormat pixel_format,
129 std::shared_ptr<const fml::Mapping> mapping)
130 : TexImage2DData(pixel_format) {
131 data = std::move(mapping);
132 }
133
134 bool IsValid() const { return is_valid_; }
135
136 private:
137 bool is_valid_ = false;
138};
139} // namespace
140
152
153std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
154 std::shared_ptr<ReactorGLES> reactor,
156 GLuint fbo) {
157 auto texture = std::shared_ptr<TextureGLES>(
158 new TextureGLES(std::move(reactor), desc, false, fbo, std::nullopt));
159 if (!texture->IsValid()) {
160 return nullptr;
161 }
162 return texture;
163}
164
165std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
166 std::shared_ptr<ReactorGLES> reactor,
168 HandleGLES external_handle) {
169 if (external_handle.IsDead()) {
170 VALIDATION_LOG << "Cannot wrap a dead handle.";
171 return nullptr;
172 }
173 if (external_handle.GetType() != HandleType::kTexture) {
174 VALIDATION_LOG << "Cannot wrap a non-texture handle.";
175 return nullptr;
176 }
177 auto texture = std::shared_ptr<TextureGLES>(new TextureGLES(
178 std::move(reactor), desc, false, std::nullopt, external_handle));
179 if (!texture->IsValid()) {
180 return nullptr;
181 }
182 return texture;
183}
184
185std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
186 std::shared_ptr<ReactorGLES> reactor,
187 TextureDescriptor desc) {
188 return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
189}
190
191TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
193 bool threadsafe)
194 : TextureGLES(std::move(reactor), //
195 desc, //
196 threadsafe, //
197 std::nullopt, //
198 std::nullopt //
199 ) {}
200
201TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
203 bool threadsafe,
204 std::optional<GLuint> fbo,
205 std::optional<HandleGLES> external_handle)
206 : Texture(desc),
207 reactor_(std::move(reactor)),
208 type_(GetTextureTypeFromDescriptor(
209 GetTextureDescriptor(),
210 reactor_->GetProcTable().GetCapabilities())),
211 handle_(external_handle.has_value()
212 ? external_handle.value()
213 : (threadsafe ? reactor_->CreateHandle(ToHandleType(type_))
214 : reactor_->CreateUntrackedHandle(
215 ToHandleType(type_)))),
216 is_wrapped_(fbo.has_value() || external_handle.has_value()),
217 wrapped_fbo_(fbo) {
218 // Ensure the texture descriptor itself is valid.
219 if (!GetTextureDescriptor().IsValid()) {
220 VALIDATION_LOG << "Invalid texture descriptor.";
221 return;
222 }
223 // Ensure the texture doesn't exceed device capabilities.
224 const auto tex_size = GetTextureDescriptor().size;
225 const auto max_size =
226 reactor_->GetProcTable().GetCapabilities()->max_texture_size;
227 if (tex_size.Max(max_size) != max_size) {
228 VALIDATION_LOG << "Texture of size " << tex_size
229 << " would exceed max supported size of " << max_size << ".";
230 return;
231 }
232
233 is_valid_ = true;
234}
235
236// |Texture|
238 reactor_->CollectHandle(handle_);
239 if (!cached_fbo_.IsDead()) {
240 reactor_->CollectHandle(cached_fbo_);
241 }
242}
243
245 handle_ = HandleGLES::DeadHandle();
246}
247
248// |Texture|
250 return is_valid_;
251}
252
253// |Texture|
254void TextureGLES::SetLabel(std::string_view label) {
255#ifdef IMPELLER_DEBUG
256 reactor_->SetDebugLabel(handle_, label);
257#endif // IMPELLER_DEBUG
258}
259
260// |Texture|
261void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
262#ifdef IMPELLER_DEBUG
263 if (reactor_->CanSetDebugLabels()) {
264 reactor_->SetDebugLabel(handle_, std::format("{} {}", label, trailing));
265 }
266#endif // IMPELLER_DEBUG
267}
268
269// |Texture|
270bool TextureGLES::OnSetContents(const uint8_t* contents,
271 size_t length,
272 size_t slice) {
273 return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
274}
275
276// |Texture|
277bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
278 size_t slice) {
279 if (!mapping) {
280 return false;
281 }
282
283 if (mapping->GetSize() == 0u) {
284 return true;
285 }
286
287 if (mapping->GetMapping() == nullptr) {
288 return false;
289 }
290
291 if (GetType() != Type::kTexture) {
292 VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
293 "this texture object.";
294 return false;
295 }
296
297 if (is_wrapped_) {
298 VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
299 return false;
300 }
301
302 const auto& tex_descriptor = GetTextureDescriptor();
303
304 if (tex_descriptor.size.IsEmpty()) {
305 return true;
306 }
307
308 if (!tex_descriptor.IsValid() ||
309 mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
310 return false;
311 }
312
313 GLenum texture_type;
314 GLenum texture_target;
315 switch (tex_descriptor.type) {
317 texture_type = GL_TEXTURE_2D;
318 texture_target = GL_TEXTURE_2D;
319 break;
321 VALIDATION_LOG << "Multisample texture uploading is not supported for "
322 "the OpenGLES backend.";
323 return false;
325 texture_type = GL_TEXTURE_CUBE_MAP;
326 texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
327 break;
329 texture_type = GL_TEXTURE_EXTERNAL_OES;
330 texture_target = GL_TEXTURE_EXTERNAL_OES;
331 break;
332 }
333
334 auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
335 std::move(mapping));
336 if (!data || !data->IsValid()) {
337 VALIDATION_LOG << "Invalid texture format.";
338 return false;
339 }
340
341 ReactorGLES::Operation texture_upload = [handle = handle_, //
342 data, //
343 size = tex_descriptor.size, //
344 texture_type, //
345 texture_target //
346 ](const auto& reactor) {
347 auto gl_handle = reactor.GetGLHandle(handle);
348 if (!gl_handle.has_value()) {
350 << "Texture was collected before it could be uploaded to the GPU.";
351 return;
352 }
353 const auto& gl = reactor.GetProcTable();
354 gl.BindTexture(texture_type, gl_handle.value());
355 const GLvoid* tex_data = nullptr;
356 if (data->data) {
357 tex_data = data->data->GetMapping();
358 }
359
360 {
361 TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
362 std::to_string(data->data->GetSize()).c_str());
363 gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
364 gl.TexImage2D(texture_target, // target
365 0u, // LOD level
366 data->internal_format, // internal format
367 size.width, // width
368 size.height, // height
369 0u, // border
370 data->external_format, // external format
371 data->type, // type
372 tex_data // data
373 );
374 }
375 };
376
377 slices_initialized_ = reactor_->AddOperation(texture_upload);
378 return slices_initialized_[0];
379}
380
381// |Texture|
382ISize TextureGLES::GetSize() const {
383 return GetTextureDescriptor().size;
384}
385
386static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
387 switch (format) {
390 return GL_RGBA8;
392 return GL_RGBA32F;
394 return GL_RGBA16F;
396 return GL_STENCIL_INDEX8;
398 return GL_DEPTH24_STENCIL8;
400 return GL_DEPTH32F_STENCIL8;
411 return std::nullopt;
412 }
414}
415
417 // When binding to a GL_READ_FRAMEBUFFER, any multisampled
418 // textures must be bound as single sampled.
419 if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
420 return Type::kTexture;
421 }
422 return type_;
423}
424
425void TextureGLES::InitializeContentsIfNecessary() const {
426 if (!IsValid() || slices_initialized_[0]) {
427 return;
428 }
429 slices_initialized_[0] = true;
430
431 if (is_wrapped_) {
432 return;
433 }
434
435 auto size = GetSize();
436
437 if (size.IsEmpty()) {
438 return;
439 }
440
441 const auto& gl = reactor_->GetProcTable();
442 std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
443 if (!handle.has_value()) {
444 VALIDATION_LOG << "Could not initialize the contents of texture.";
445 return;
446 }
447
448 switch (type_) {
449 case Type::kTexture:
451 TexImage2DData tex_data(GetTextureDescriptor().format);
452 if (!tex_data.IsValid()) {
453 VALIDATION_LOG << "Invalid format for texture image.";
454 return;
455 }
456 gl.BindTexture(GL_TEXTURE_2D, handle.value());
457 {
458 TRACE_EVENT0("impeller", "TexImage2DInitialization");
459 gl.TexImage2D(GL_TEXTURE_2D, // target
460 0u, // LOD level (base mip level size checked)
461 tex_data.internal_format, // internal format
462 size.width, // width
463 size.height, // height
464 0u, // border
465 tex_data.external_format, // format
466 tex_data.type, // type
467 nullptr // data
468 );
469 }
470 } break;
473 auto render_buffer_format =
475 if (!render_buffer_format.has_value()) {
476 VALIDATION_LOG << "Invalid format for render-buffer image.";
477 return;
478 }
479 gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
480 {
481 if (type_ == Type::kRenderBufferMultisampled) {
482 // BEWARE: these functions are not at all equivalent! the extensions
483 // are from EXT_multisampled_render_to_texture and cannot be used
484 // with regular GLES 3.0 multisampled renderbuffers/textures.
485 if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
486 gl.RenderbufferStorageMultisampleEXT(
487 /*target=*/GL_RENDERBUFFER, //
488 /*samples=*/4, //
489 /*internal_format=*/render_buffer_format.value(), //
490 /*width=*/size.width, //
491 /*height=*/size.height //
492 );
493 } else {
494 gl.RenderbufferStorageMultisample(
495 /*target=*/GL_RENDERBUFFER, //
496 /*samples=*/4, //
497 /*internal_format=*/render_buffer_format.value(), //
498 /*width=*/size.width, //
499 /*height=*/size.height //
500 );
501 }
502 } else {
503 gl.RenderbufferStorage(
504 /*target=*/GL_RENDERBUFFER, //
505 /*internal_format=*/render_buffer_format.value(), //
506 /*width=*/size.width, //
507 /*height=*/size.height //
508 );
509 }
510 }
511 } break;
512 }
513}
514
515std::optional<GLuint> TextureGLES::GetGLHandle() const {
516 if (!IsValid()) {
517 return std::nullopt;
518 }
519 return reactor_->GetGLHandle(handle_);
520}
521
522bool TextureGLES::Bind() const {
523 auto handle = GetGLHandle();
524 if (!handle.has_value()) {
525 return false;
526 }
527 const auto& gl = reactor_->GetProcTable();
528
529 if (fence_.has_value()) {
530 std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
531 if (fence.has_value()) {
532 gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
533 }
534 reactor_->CollectHandle(fence_.value());
535 fence_ = std::nullopt;
536 }
537
538 switch (type_) {
539 case Type::kTexture:
542 if (!target.has_value()) {
543 VALIDATION_LOG << "Could not bind texture of this type.";
544 return false;
545 }
546 gl.BindTexture(target.value(), handle.value());
547 } break;
550 gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
551 break;
552 }
553 InitializeContentsIfNecessary();
554 return true;
555}
556
558 for (size_t i = 0; i < slices_initialized_.size(); i++) {
559 slices_initialized_[i] = true;
560 }
561}
562
563void TextureGLES::MarkSliceInitialized(size_t slice) const {
564 slices_initialized_[slice] = true;
565}
566
567bool TextureGLES::IsSliceInitialized(size_t slice) const {
568 return slices_initialized_[slice];
569}
570
572 if (!IsValid()) {
573 return false;
574 }
575
577 switch (type) {
579 break;
581 VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
582 "supported in the GLES backend.";
583 return false;
585 break;
587 break;
588 }
589
590 if (!Bind()) {
591 return false;
592 }
593
594 auto handle = GetGLHandle();
595 if (!handle.has_value()) {
596 return false;
597 }
598
599 const auto& gl = reactor_->GetProcTable();
600 gl.GenerateMipmap(ToTextureType(type));
601 mipmap_generated_ = true;
602 return true;
603}
604
606 return type_;
607}
608
610 switch (point) {
612 return GL_COLOR_ATTACHMENT0;
614 return GL_DEPTH_ATTACHMENT;
616 return GL_STENCIL_ATTACHMENT;
617 }
618}
619
621 GLenum target,
622 AttachmentType attachment_type) const {
623 if (!IsValid()) {
624 return false;
625 }
626 InitializeContentsIfNecessary();
627 auto handle = GetGLHandle();
628 if (!handle.has_value()) {
629 return false;
630 }
631 const auto& gl = reactor_->GetProcTable();
632
633 switch (ComputeTypeForBinding(target)) {
634 case Type::kTexture:
635 gl.FramebufferTexture2D(target, // target
636 ToAttachmentType(attachment_type), // attachment
637 GL_TEXTURE_2D, // textarget
638 handle.value(), // texture
639 0 // level
640 );
641 break;
643 gl.FramebufferTexture2DMultisampleEXT(
644 target, // target
645 ToAttachmentType(attachment_type), // attachment
646 GL_TEXTURE_2D, // textarget
647 handle.value(), // texture
648 0, // level
649 4 // samples
650 );
651 break;
654 gl.FramebufferRenderbuffer(
655 target, // target
656 ToAttachmentType(attachment_type), // attachment
657 GL_RENDERBUFFER, // render-buffer target
658 handle.value() // render-buffer
659 );
660 break;
661 }
662
663 return true;
664}
665
666// |Texture|
667Scalar TextureGLES::GetYCoordScale() const {
668 switch (GetCoordinateSystem()) {
670 return 1.0;
672 return -1.0;
673 }
675}
676
678 return is_wrapped_;
679}
680
681std::optional<GLuint> TextureGLES::GetFBO() const {
682 return wrapped_fbo_;
683}
684
686 FML_DCHECK(!fence_.has_value());
687 fence_ = fence;
688}
689
690// Visible for testing.
691std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
692 return fence_;
693}
694
696 cached_fbo_ = fbo;
697}
698
700 return cached_fbo_;
701}
702
703} // namespace impeller
GLenum type
GLenum external_format
GLint internal_format
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition handle_gles.h:37
constexpr bool IsDead() const
Determines if the handle is dead.
Definition handle_gles.h:53
HandleType GetType() const
Definition handle_gles.h:74
static HandleGLES DeadHandle()
Creates a dead handle.
Definition handle_gles.h:44
std::function< void(const ReactorGLES &reactor)> Operation
static std::shared_ptr< TextureGLES > WrapFBO(std::shared_ptr< ReactorGLES > reactor, TextureDescriptor desc, GLuint fbo)
Create a texture by wrapping an external framebuffer object whose lifecycle is owned by the caller.
void MarkContentsInitialized()
Indicates that all texture storage has already been allocated and contents initialized.
const HandleGLES & GetCachedFBO() const
Retrieve the cached FBO object, or a dead handle if there is no object.
std::optional< HandleGLES > GetSyncFence() const
bool IsSliceInitialized(size_t slice) const
bool IsValid() const override
void SetFence(HandleGLES fence)
Attach a sync fence to this texture that will be waited on before encoding a rendering operation that...
void Leak()
Reset the internal texture state so that the reactor will not free the associated handle.
void SetCachedFBO(HandleGLES fbo)
TextureGLES(std::shared_ptr< ReactorGLES > reactor, TextureDescriptor desc, bool threadsafe=false)
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type) const
static std::shared_ptr< TextureGLES > CreatePlaceholder(std::shared_ptr< ReactorGLES > reactor, TextureDescriptor desc)
Create a "texture" that is never expected to be bound/unbound explicitly or initialized in any way....
std::optional< GLuint > GetFBO() const
Type ComputeTypeForBinding(GLenum target) const
void MarkSliceInitialized(size_t slice) const
Indicates that a specific texture slice has been initialized.
std::optional< GLuint > GetGLHandle() const
static std::shared_ptr< TextureGLES > WrapTexture(std::shared_ptr< ReactorGLES > reactor, TextureDescriptor desc, HandleGLES external_handle)
Create a texture by wrapping an external OpenGL texture handle. Ownership of the texture handle is as...
const TextureDescriptor & GetTextureDescriptor() const
Definition texture.cc:57
TextureCoordinateSystem GetCoordinateSystem() const
Definition texture.cc:77
bool mipmap_generated_
Definition texture.h:79
int32_t value
uint32_t uint32_t * format
uint32_t * target
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DCHECK(condition)
Definition logging.h:122
size_t length
FlTexture * texture
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
constexpr std::optional< GLenum > ToTextureTarget(TextureType type)
float Scalar
Definition scalar.h:19
std::shared_ptr< fml::Mapping > CreateMappingWithCopy(const uint8_t *contents, Bytes length)
Creates a mapping with copy of the bytes.
Definition allocation.cc:83
AllocationSize< 1u > Bytes
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
constexpr GLenum ToTextureType(TextureType type)
static std::optional< GLenum > ToRenderBufferFormat(PixelFormat format)
ISize64 ISize
Definition size.h:162
static GLenum ToAttachmentType(TextureGLES::AttachmentType point)
Mask< TextureUsage > TextureUsageMask
Definition formats.h:311
HandleType ToHandleType(TextureGLES::Type type)
Definition ref_ptr.h:261
fuchsia::ui::composition::ParentViewportWatcherHandle handle_
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
std::shared_ptr< const fml::Mapping > data
#define TRACE_EVENT0(category_group, name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
#define VALIDATION_LOG
Definition validation.h:91