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"
19
20namespace impeller {
21
22namespace {
23static bool IsDepthStencilFormat(PixelFormat format) {
24 switch (format) {
28 return true;
43 return false;
44 }
46}
47
48static TextureGLES::Type GetTextureTypeFromDescriptor(
49 const TextureDescriptor& desc,
50 const std::shared_ptr<const CapabilitiesGLES>& capabilities) {
51 const auto usage = static_cast<TextureUsageMask>(desc.usage);
52 const auto render_target = TextureUsage::kRenderTarget;
53 const auto is_msaa = desc.sample_count == SampleCount::kCount4;
54 if (usage == render_target && IsDepthStencilFormat(desc.format)) {
57 }
58 return is_msaa ? (capabilities->SupportsImplicitResolvingMSAA()
61 : TextureGLES::Type::kTexture;
62}
63
64} // namespace
65
77
78std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
79 std::shared_ptr<ReactorGLES> reactor,
81 GLuint fbo) {
82 auto texture = std::shared_ptr<TextureGLES>(
83 new TextureGLES(std::move(reactor), desc, false, fbo, std::nullopt));
84 if (!texture->IsValid()) {
85 return nullptr;
86 }
87 return texture;
88}
89
90std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
91 std::shared_ptr<ReactorGLES> reactor,
93 HandleGLES external_handle) {
94 if (external_handle.IsDead()) {
95 VALIDATION_LOG << "Cannot wrap a dead handle.";
96 return nullptr;
97 }
98 if (external_handle.GetType() != HandleType::kTexture) {
99 VALIDATION_LOG << "Cannot wrap a non-texture handle.";
100 return nullptr;
101 }
102 auto texture = std::shared_ptr<TextureGLES>(new TextureGLES(
103 std::move(reactor), desc, false, std::nullopt, external_handle));
104 if (!texture->IsValid()) {
105 return nullptr;
106 }
107 return texture;
108}
109
110std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
111 std::shared_ptr<ReactorGLES> reactor,
112 TextureDescriptor desc) {
113 return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
114}
115
116TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
118 bool threadsafe)
119 : TextureGLES(std::move(reactor), //
120 desc, //
121 threadsafe, //
122 std::nullopt, //
123 std::nullopt //
124 ) {}
125
126TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
128 bool threadsafe,
129 std::optional<GLuint> fbo,
130 std::optional<HandleGLES> external_handle)
131 : Texture(desc),
132 reactor_(std::move(reactor)),
133 type_(GetTextureTypeFromDescriptor(
134 GetTextureDescriptor(),
135 reactor_->GetProcTable().GetCapabilities())),
136 handle_(external_handle.has_value()
137 ? external_handle.value()
138 : (threadsafe ? reactor_->CreateHandle(ToHandleType(type_))
139 : reactor_->CreateUntrackedHandle(
140 ToHandleType(type_)))),
141 is_wrapped_(fbo.has_value() || external_handle.has_value()),
142 wrapped_fbo_(fbo) {
143 // Ensure the texture descriptor itself is valid.
144 if (!GetTextureDescriptor().IsValid()) {
145 return;
146 }
147 // Ensure the texture doesn't exceed device capabilities.
148 const auto tex_size = GetTextureDescriptor().size;
149 const auto max_size =
150 reactor_->GetProcTable().GetCapabilities()->max_texture_size;
151 if (tex_size.Max(max_size) != max_size) {
152 VALIDATION_LOG << "Texture of size " << tex_size
153 << " would exceed max supported size of " << max_size << ".";
154 return;
155 }
156
157 is_valid_ = true;
158}
159
160// |Texture|
162 reactor_->CollectHandle(handle_);
163 if (!cached_fbo_.IsDead()) {
164 reactor_->CollectHandle(cached_fbo_);
165 }
166}
167
169 handle_ = HandleGLES::DeadHandle();
170}
171
172// |Texture|
174 return is_valid_;
175}
176
177// |Texture|
178void TextureGLES::SetLabel(std::string_view label) {
179#ifdef IMPELLER_DEBUG
180 reactor_->SetDebugLabel(handle_, label);
181#endif // IMPELLER_DEBUG
182}
183
184// |Texture|
185void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
186#ifdef IMPELLER_DEBUG
187 if (reactor_->CanSetDebugLabels()) {
188 reactor_->SetDebugLabel(handle_, std::format("{} {}", label, trailing));
189 }
190#endif // IMPELLER_DEBUG
191}
192
193// |Texture|
194bool TextureGLES::OnSetContents(const uint8_t* contents,
195 size_t length,
196 size_t slice) {
197 return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
198}
199
200// |Texture|
201bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
202 size_t slice) {
203 if (!mapping) {
204 return false;
205 }
206
207 if (mapping->GetSize() == 0u) {
208 return true;
209 }
210
211 if (mapping->GetMapping() == nullptr) {
212 return false;
213 }
214
215 if (GetType() != Type::kTexture) {
216 VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
217 "this texture object.";
218 return false;
219 }
220
221 if (is_wrapped_) {
222 VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
223 return false;
224 }
225
226 const auto& tex_descriptor = GetTextureDescriptor();
227
228 if (tex_descriptor.size.IsEmpty()) {
229 return true;
230 }
231
232 if (!tex_descriptor.IsValid() ||
233 mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
234 return false;
235 }
236
237 GLenum texture_type;
238 GLenum texture_target;
239 switch (tex_descriptor.type) {
241 texture_type = GL_TEXTURE_2D;
242 texture_target = GL_TEXTURE_2D;
243 break;
245 VALIDATION_LOG << "Multisample texture uploading is not supported for "
246 "the OpenGLES backend.";
247 return false;
249 texture_type = GL_TEXTURE_CUBE_MAP;
250 texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
251 break;
253 texture_type = GL_TEXTURE_EXTERNAL_OES;
254 texture_target = GL_TEXTURE_EXTERNAL_OES;
255 break;
256 }
257
258 std::optional<PixelFormatGLES> gles_format =
259 ToPixelFormatGLES(tex_descriptor.format,
260 /*supports_bgra=*/
261 reactor_->GetProcTable().GetDescription()->HasExtension(
262 "GL_EXT_texture_format_BGRA8888"));
263 if (!gles_format.has_value()) {
264 VALIDATION_LOG << "Invalid texture format.";
265 return false;
266 }
267
268 ReactorGLES::Operation texture_upload = [handle = handle_, //
269 mapping, //
270 format = gles_format.value(), //
271 size = tex_descriptor.size, //
272 texture_type, //
273 texture_target //
274 ](const auto& reactor) {
275 auto gl_handle = reactor.GetGLHandle(handle);
276 if (!gl_handle.has_value()) {
278 << "Texture was collected before it could be uploaded to the GPU.";
279 return;
280 }
281 const auto& gl = reactor.GetProcTable();
282 gl.BindTexture(texture_type, gl_handle.value());
283 const GLvoid* tex_data = nullptr;
284 if (mapping) {
285 tex_data = mapping->GetMapping();
286 }
287
288 {
289 TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
290 std::to_string(mapping->GetSize()).c_str());
291 gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
292 gl.TexImage2D(texture_target, // target
293 0u, // LOD level
294 format.internal_format, // internal format
295 size.width, // width
296 size.height, // height
297 0u, // border
298 format.external_format, // format
299 format.type, // type
300 tex_data); // data
301 }
302 };
303
304 const bool added = reactor_->AddOperation(texture_upload);
305 if (added) {
307 }
308 return added;
309}
310
311// |Texture|
312ISize TextureGLES::GetSize() const {
313 return GetTextureDescriptor().size;
314}
315
316static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
317 switch (format) {
320 return GL_RGBA8;
322 return GL_RGBA32F;
324 return GL_RGBA16F;
326 return GL_STENCIL_INDEX8;
328 return GL_DEPTH24_STENCIL8;
330 return GL_DEPTH32F_STENCIL8;
341 return std::nullopt;
342 }
344}
345
347 // When binding to a GL_READ_FRAMEBUFFER, any multisampled
348 // textures must be bound as single sampled.
349 if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
350 return Type::kTexture;
351 }
352 return type_;
353}
354
355void TextureGLES::InitializeContentsIfNecessary() {
356 if (!IsValid() || IsSliceMipLevelInitialized(0, 0)) {
357 return;
358 }
359
360 if (is_wrapped_) {
361 // Storage is owned externally; mark it covered so we don't re-enter.
363 return;
364 }
365
366 auto size = GetSize();
367
368 if (size.IsEmpty()) {
370 return;
371 }
372
373 const auto& gl = reactor_->GetProcTable();
374 std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
375 if (!handle.has_value()) {
376 VALIDATION_LOG << "Could not initialize the contents of texture.";
377 return;
378 }
379
380 switch (type_) {
381 case Type::kTexture:
383 const auto& desc = GetTextureDescriptor();
384 std::optional<PixelFormatGLES> gles_format = ToPixelFormatGLES(
385 desc.format,
386 /*supports_bgra=*/
387 reactor_->GetProcTable().GetDescription()->HasExtension(
388 "GL_EXT_texture_format_BGRA8888"));
389 if (!gles_format.has_value()) {
390 VALIDATION_LOG << "Invalid format for texture image.";
391 return;
392 }
393 TRACE_EVENT0("impeller", "TexImage2DInitialization");
394 if (desc.type == TextureType::kTextureCube) {
395 // Cubemap handles must be bound to GL_TEXTURE_CUBE_MAP and each face
396 // target must be defined independently before sampling. Allocate the
397 // base mip level for every face here so the cubemap is sample-ready
398 // even before any face has been uploaded; non-zero mip levels are
399 // still allocated lazily on first per-level write in the blit path.
400 gl.BindTexture(GL_TEXTURE_CUBE_MAP, handle.value());
401 for (size_t face = 0; face < 6; ++face) {
402 gl.TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, // target
403 0u, // LOD level
404 gles_format->internal_format, // internal
405 size.width, // width
406 size.height, // height
407 0u, // border
408 gles_format->external_format, // format
409 gles_format->type, // type
410 nullptr // data
411 );
413 }
414 } else {
415 // 2D / multisampled. External-OES textures are always wrapped, so
416 // they returned at the is_wrapped_ check above. Only the base mip
417 // is allocated here. `glGenerateMipmap` (used by the snapshot
418 // pipeline) implicitly allocates and fills the rest, and per-level
419 // uploads from the blit path allocate non-zero levels lazily on
420 // first write.
421 gl.BindTexture(GL_TEXTURE_2D, handle.value());
422 gl.TexImage2D(GL_TEXTURE_2D, // target
423 0u, // LOD level
424 gles_format->internal_format, // internal
425 size.width, // width
426 size.height, // height
427 0u, // border
428 gles_format->external_format, // format
429 gles_format->type, // type
430 nullptr // data
431 );
433 }
434 } break;
437 auto render_buffer_format =
439 if (!render_buffer_format.has_value()) {
440 VALIDATION_LOG << "Invalid format for render-buffer image.";
441 return;
442 }
443 gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
444 {
445 if (type_ == Type::kRenderBufferMultisampled) {
446 // BEWARE: these functions are not at all equivalent! the extensions
447 // are from EXT_multisampled_render_to_texture and cannot be used
448 // with regular GLES 3.0 multisampled renderbuffers/textures.
449 if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
450 gl.RenderbufferStorageMultisampleEXT(
451 /*target=*/GL_RENDERBUFFER, //
452 /*samples=*/4, //
453 /*internal_format=*/render_buffer_format.value(), //
454 /*width=*/size.width, //
455 /*height=*/size.height //
456 );
457 } else {
458 gl.RenderbufferStorageMultisample(
459 /*target=*/GL_RENDERBUFFER, //
460 /*samples=*/4, //
461 /*internal_format=*/render_buffer_format.value(), //
462 /*width=*/size.width, //
463 /*height=*/size.height //
464 );
465 }
466 } else {
467 gl.RenderbufferStorage(
468 /*target=*/GL_RENDERBUFFER, //
469 /*internal_format=*/render_buffer_format.value(), //
470 /*width=*/size.width, //
471 /*height=*/size.height //
472 );
473 }
474 }
475 // Renderbuffers don't have mip levels, but we still mark slot (0, 0)
476 // so the early-out guard at the top of this function fires on subsequent
477 // calls.
479 } break;
480 }
481}
482
483std::optional<GLuint> TextureGLES::GetGLHandle() const {
484 if (!IsValid()) {
485 return std::nullopt;
486 }
487 return reactor_->GetGLHandle(handle_);
488}
489
491 auto handle = GetGLHandle();
492 if (!handle.has_value()) {
493 return false;
494 }
495 const auto& gl = reactor_->GetProcTable();
496
497 if (fence_.has_value()) {
498 std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
499 if (fence.has_value()) {
500 gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
501 }
502 reactor_->CollectHandle(fence_.value());
503 fence_ = std::nullopt;
504 }
505
506 switch (type_) {
507 case Type::kTexture:
510 if (!target.has_value()) {
511 VALIDATION_LOG << "Could not bind texture of this type.";
512 return false;
513 }
514 gl.BindTexture(target.value(), handle.value());
515 } break;
518 gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
519 break;
520 }
521 InitializeContentsIfNecessary();
522 return true;
523}
524
526 for (auto& slice_mips : slice_mip_initialized_) {
527 slice_mips.set();
528 }
529}
530
534
535bool TextureGLES::IsSliceInitialized(size_t slice) const {
536 return IsSliceMipLevelInitialized(slice, 0);
537}
538
539void TextureGLES::MarkSliceMipLevelInitialized(size_t slice, size_t mip_level) {
540 if (slice >= slice_mip_initialized_.size() ||
541 mip_level >= kMaxTrackedMipLevels) {
542 return;
543 }
544 slice_mip_initialized_[slice].set(mip_level);
545}
546
548 size_t mip_level) const {
549 if (slice >= slice_mip_initialized_.size() ||
550 mip_level >= kMaxTrackedMipLevels) {
551 return false;
552 }
553 return slice_mip_initialized_[slice].test(mip_level);
554}
555
557 if (!IsValid()) {
558 return false;
559 }
560
562 switch (type) {
564 break;
566 VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
567 "supported in the GLES backend.";
568 return false;
570 break;
572 break;
573 }
574
575 if (!Bind()) {
576 return false;
577 }
578
579 auto handle = GetGLHandle();
580 if (!handle.has_value()) {
581 return false;
582 }
583
584 const auto& gl = reactor_->GetProcTable();
585 gl.GenerateMipmap(ToTextureType(type));
586 mipmap_generated_ = true;
587 return true;
588}
589
591 return type_;
592}
593
595 switch (point) {
597 return GL_COLOR_ATTACHMENT0;
599 return GL_DEPTH_ATTACHMENT;
601 return GL_STENCIL_ATTACHMENT;
602 }
603}
604
606 AttachmentType attachment_type) {
607 if (!IsValid()) {
608 return false;
609 }
610 InitializeContentsIfNecessary();
611 auto handle = GetGLHandle();
612 if (!handle.has_value()) {
613 return false;
614 }
615 const auto& gl = reactor_->GetProcTable();
616
617 switch (ComputeTypeForBinding(target)) {
618 case Type::kTexture:
619 gl.FramebufferTexture2D(target, // target
620 ToAttachmentType(attachment_type), // attachment
621 GL_TEXTURE_2D, // textarget
622 handle.value(), // texture
623 0 // level
624 );
625 break;
627 gl.FramebufferTexture2DMultisampleEXT(
628 target, // target
629 ToAttachmentType(attachment_type), // attachment
630 GL_TEXTURE_2D, // textarget
631 handle.value(), // texture
632 0, // level
633 4 // samples
634 );
635 break;
638 gl.FramebufferRenderbuffer(
639 target, // target
640 ToAttachmentType(attachment_type), // attachment
641 GL_RENDERBUFFER, // render-buffer target
642 handle.value() // render-buffer
643 );
644 break;
645 }
646
647 return true;
648}
649
650// |Texture|
651Scalar TextureGLES::GetYCoordScale() const {
652 switch (GetCoordinateSystem()) {
654 return 1.0;
656 return -1.0;
657 }
659}
660
662 return is_wrapped_;
663}
664
665std::optional<GLuint> TextureGLES::GetFBO() const {
666 return wrapped_fbo_;
667}
668
670 FML_DCHECK(!fence_.has_value());
671 fence_ = fence;
672}
673
674// Visible for testing.
675std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
676 return fence_;
677}
678
680 cached_fbo_ = fbo;
681}
682
684 return cached_fbo_;
685}
686
687} // namespace impeller
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition handle_gles.h:42
constexpr bool IsDead() const
Determines if the handle is dead.
Definition handle_gles.h:58
HandleType GetType() const
Definition handle_gles.h:79
static HandleGLES DeadHandle()
Creates a dead handle.
Definition handle_gles.h:49
std::function< void(const ReactorGLES &reactor)> Operation
void MarkSliceInitialized(size_t slice)
Indicates that a specific texture slice has been initialized.
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.
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type)
std::optional< HandleGLES > GetSyncFence() const
bool IsSliceMipLevelInitialized(size_t slice, size_t mip_level) const
bool IsSliceInitialized(size_t slice) const
void MarkSliceMipLevelInitialized(size_t slice, size_t mip_level)
Indicates that storage for mip_level of slice has been allocated by a glTexImage2D call (or equivalen...
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)
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
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
std::optional< PixelFormatGLES > ToPixelFormatGLES(PixelFormat pixel_format, bool supports_bgra)
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_
impeller::ShaderType type
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define TRACE_EVENT0(category_group, name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
#define VALIDATION_LOG
Definition validation.h:91