Flutter Engine
 
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;
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
64struct TexImage2DData {
65 GLint internal_format = 0;
66 GLenum external_format = GL_NONE;
67 GLenum type = GL_NONE;
68 std::shared_ptr<const fml::Mapping> data;
69
70 explicit TexImage2DData(PixelFormat pixel_format) {
71 switch (pixel_format) {
73 internal_format = GL_ALPHA;
74 external_format = GL_ALPHA;
75 type = GL_UNSIGNED_BYTE;
76 break;
78 internal_format = GL_RED;
79 external_format = GL_RED;
80 type = GL_UNSIGNED_BYTE;
81 break;
86 internal_format = GL_RGBA;
87 external_format = GL_RGBA;
88 type = GL_UNSIGNED_BYTE;
89 break;
91 internal_format = GL_RGBA32F;
92 external_format = GL_RGBA;
93 type = GL_FLOAT;
94 break;
96 internal_format = GL_RGBA16F;
97 external_format = GL_RGBA;
98 type = GL_HALF_FLOAT;
99 break;
101 // Pure stencil textures are only available in OpenGL 4.4+, which is
102 // ~0% of mobile devices. Instead, we use a depth-stencil texture and
103 // only use the stencil component.
104 //
105 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
107 internal_format = GL_DEPTH_STENCIL;
108 external_format = GL_DEPTH_STENCIL;
109 type = GL_UNSIGNED_INT_24_8;
110 break;
117 return;
118 }
119 is_valid_ = true;
120 }
121
122 TexImage2DData(PixelFormat pixel_format,
123 std::shared_ptr<const fml::Mapping> mapping)
124 : TexImage2DData(pixel_format) {
125 data = std::move(mapping);
126 }
127
128 bool IsValid() const { return is_valid_; }
129
130 private:
131 bool is_valid_ = false;
132};
133} // namespace
134
146
147std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
148 std::shared_ptr<ReactorGLES> reactor,
150 GLuint fbo) {
151 auto texture = std::shared_ptr<TextureGLES>(
152 new TextureGLES(std::move(reactor), desc, false, fbo, std::nullopt));
153 if (!texture->IsValid()) {
154 return nullptr;
155 }
156 return texture;
157}
158
159std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
160 std::shared_ptr<ReactorGLES> reactor,
162 HandleGLES external_handle) {
163 if (external_handle.IsDead()) {
164 VALIDATION_LOG << "Cannot wrap a dead handle.";
165 return nullptr;
166 }
167 if (external_handle.GetType() != HandleType::kTexture) {
168 VALIDATION_LOG << "Cannot wrap a non-texture handle.";
169 return nullptr;
170 }
171 auto texture = std::shared_ptr<TextureGLES>(new TextureGLES(
172 std::move(reactor), desc, false, std::nullopt, external_handle));
173 if (!texture->IsValid()) {
174 return nullptr;
175 }
176 return texture;
177}
178
179std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
180 std::shared_ptr<ReactorGLES> reactor,
181 TextureDescriptor desc) {
182 return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
183}
184
185TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
187 bool threadsafe)
188 : TextureGLES(std::move(reactor), //
189 desc, //
190 threadsafe, //
191 std::nullopt, //
192 std::nullopt //
193 ) {}
194
195TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
197 bool threadsafe,
198 std::optional<GLuint> fbo,
199 std::optional<HandleGLES> external_handle)
200 : Texture(desc),
201 reactor_(std::move(reactor)),
202 type_(GetTextureTypeFromDescriptor(
203 GetTextureDescriptor(),
204 reactor_->GetProcTable().GetCapabilities())),
205 handle_(external_handle.has_value()
206 ? external_handle.value()
207 : (threadsafe ? reactor_->CreateHandle(ToHandleType(type_))
208 : reactor_->CreateUntrackedHandle(
209 ToHandleType(type_)))),
210 is_wrapped_(fbo.has_value() || external_handle.has_value()),
211 wrapped_fbo_(fbo) {
212 // Ensure the texture descriptor itself is valid.
213 if (!GetTextureDescriptor().IsValid()) {
214 VALIDATION_LOG << "Invalid texture descriptor.";
215 return;
216 }
217 // Ensure the texture doesn't exceed device capabilities.
218 const auto tex_size = GetTextureDescriptor().size;
219 const auto max_size =
220 reactor_->GetProcTable().GetCapabilities()->max_texture_size;
221 if (tex_size.Max(max_size) != max_size) {
222 VALIDATION_LOG << "Texture of size " << tex_size
223 << " would exceed max supported size of " << max_size << ".";
224 return;
225 }
226
227 is_valid_ = true;
228}
229
230// |Texture|
232 reactor_->CollectHandle(handle_);
233 if (!cached_fbo_.IsDead()) {
234 reactor_->CollectHandle(cached_fbo_);
235 }
236}
237
239 handle_ = HandleGLES::DeadHandle();
240}
241
242// |Texture|
244 return is_valid_;
245}
246
247// |Texture|
248void TextureGLES::SetLabel(std::string_view label) {
249#ifdef IMPELLER_DEBUG
250 reactor_->SetDebugLabel(handle_, label);
251#endif // IMPELLER_DEBUG
252}
253
254// |Texture|
255void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
256#ifdef IMPELLER_DEBUG
257 if (reactor_->CanSetDebugLabels()) {
258 reactor_->SetDebugLabel(handle_, std::format("{} {}", label, trailing));
259 }
260#endif // IMPELLER_DEBUG
261}
262
263// |Texture|
264bool TextureGLES::OnSetContents(const uint8_t* contents,
265 size_t length,
266 size_t slice) {
267 return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
268}
269
270// |Texture|
271bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
272 size_t slice) {
273 if (!mapping) {
274 return false;
275 }
276
277 if (mapping->GetSize() == 0u) {
278 return true;
279 }
280
281 if (mapping->GetMapping() == nullptr) {
282 return false;
283 }
284
285 if (GetType() != Type::kTexture) {
286 VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
287 "this texture object.";
288 return false;
289 }
290
291 if (is_wrapped_) {
292 VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
293 return false;
294 }
295
296 const auto& tex_descriptor = GetTextureDescriptor();
297
298 if (tex_descriptor.size.IsEmpty()) {
299 return true;
300 }
301
302 if (!tex_descriptor.IsValid() ||
303 mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
304 return false;
305 }
306
307 GLenum texture_type;
308 GLenum texture_target;
309 switch (tex_descriptor.type) {
311 texture_type = GL_TEXTURE_2D;
312 texture_target = GL_TEXTURE_2D;
313 break;
315 VALIDATION_LOG << "Multisample texture uploading is not supported for "
316 "the OpenGLES backend.";
317 return false;
319 texture_type = GL_TEXTURE_CUBE_MAP;
320 texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
321 break;
323 texture_type = GL_TEXTURE_EXTERNAL_OES;
324 texture_target = GL_TEXTURE_EXTERNAL_OES;
325 break;
326 }
327
328 auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
329 std::move(mapping));
330 if (!data || !data->IsValid()) {
331 VALIDATION_LOG << "Invalid texture format.";
332 return false;
333 }
334
335 ReactorGLES::Operation texture_upload = [handle = handle_, //
336 data, //
337 size = tex_descriptor.size, //
338 texture_type, //
339 texture_target //
340 ](const auto& reactor) {
341 auto gl_handle = reactor.GetGLHandle(handle);
342 if (!gl_handle.has_value()) {
344 << "Texture was collected before it could be uploaded to the GPU.";
345 return;
346 }
347 const auto& gl = reactor.GetProcTable();
348 gl.BindTexture(texture_type, gl_handle.value());
349 const GLvoid* tex_data = nullptr;
350 if (data->data) {
351 tex_data = data->data->GetMapping();
352 }
353
354 {
355 TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
356 std::to_string(data->data->GetSize()).c_str());
357 gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
358 gl.TexImage2D(texture_target, // target
359 0u, // LOD level
360 data->internal_format, // internal format
361 size.width, // width
362 size.height, // height
363 0u, // border
364 data->external_format, // external format
365 data->type, // type
366 tex_data // data
367 );
368 }
369 };
370
371 slices_initialized_ = reactor_->AddOperation(texture_upload);
372 return slices_initialized_[0];
373}
374
375// |Texture|
376ISize TextureGLES::GetSize() const {
377 return GetTextureDescriptor().size;
378}
379
380static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
381 switch (format) {
384 return GL_RGBA8;
386 return GL_RGBA32F;
388 return GL_RGBA16F;
390 return GL_STENCIL_INDEX8;
392 return GL_DEPTH24_STENCIL8;
394 return GL_DEPTH32F_STENCIL8;
404 return std::nullopt;
405 }
407}
408
410 // When binding to a GL_READ_FRAMEBUFFER, any multisampled
411 // textures must be bound as single sampled.
412 if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
413 return Type::kTexture;
414 }
415 return type_;
416}
417
418void TextureGLES::InitializeContentsIfNecessary() const {
419 if (!IsValid() || slices_initialized_[0]) {
420 return;
421 }
422 slices_initialized_[0] = true;
423
424 if (is_wrapped_) {
425 return;
426 }
427
428 auto size = GetSize();
429
430 if (size.IsEmpty()) {
431 return;
432 }
433
434 const auto& gl = reactor_->GetProcTable();
435 std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
436 if (!handle.has_value()) {
437 VALIDATION_LOG << "Could not initialize the contents of texture.";
438 return;
439 }
440
441 switch (type_) {
442 case Type::kTexture:
444 TexImage2DData tex_data(GetTextureDescriptor().format);
445 if (!tex_data.IsValid()) {
446 VALIDATION_LOG << "Invalid format for texture image.";
447 return;
448 }
449 gl.BindTexture(GL_TEXTURE_2D, handle.value());
450 {
451 TRACE_EVENT0("impeller", "TexImage2DInitialization");
452 gl.TexImage2D(GL_TEXTURE_2D, // target
453 0u, // LOD level (base mip level size checked)
454 tex_data.internal_format, // internal format
455 size.width, // width
456 size.height, // height
457 0u, // border
458 tex_data.external_format, // format
459 tex_data.type, // type
460 nullptr // data
461 );
462 }
463 } break;
466 auto render_buffer_format =
468 if (!render_buffer_format.has_value()) {
469 VALIDATION_LOG << "Invalid format for render-buffer image.";
470 return;
471 }
472 gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
473 {
474 if (type_ == Type::kRenderBufferMultisampled) {
475 // BEWARE: these functions are not at all equivalent! the extensions
476 // are from EXT_multisampled_render_to_texture and cannot be used
477 // with regular GLES 3.0 multisampled renderbuffers/textures.
478 if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
479 gl.RenderbufferStorageMultisampleEXT(
480 /*target=*/GL_RENDERBUFFER, //
481 /*samples=*/4, //
482 /*internal_format=*/render_buffer_format.value(), //
483 /*width=*/size.width, //
484 /*height=*/size.height //
485 );
486 } else {
487 gl.RenderbufferStorageMultisample(
488 /*target=*/GL_RENDERBUFFER, //
489 /*samples=*/4, //
490 /*internal_format=*/render_buffer_format.value(), //
491 /*width=*/size.width, //
492 /*height=*/size.height //
493 );
494 }
495 } else {
496 gl.RenderbufferStorage(
497 /*target=*/GL_RENDERBUFFER, //
498 /*internal_format=*/render_buffer_format.value(), //
499 /*width=*/size.width, //
500 /*height=*/size.height //
501 );
502 }
503 }
504 } break;
505 }
506}
507
508std::optional<GLuint> TextureGLES::GetGLHandle() const {
509 if (!IsValid()) {
510 return std::nullopt;
511 }
512 return reactor_->GetGLHandle(handle_);
513}
514
515bool TextureGLES::Bind() const {
516 auto handle = GetGLHandle();
517 if (!handle.has_value()) {
518 return false;
519 }
520 const auto& gl = reactor_->GetProcTable();
521
522 if (fence_.has_value()) {
523 std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
524 if (fence.has_value()) {
525 gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
526 }
527 reactor_->CollectHandle(fence_.value());
528 fence_ = std::nullopt;
529 }
530
531 switch (type_) {
532 case Type::kTexture:
535 if (!target.has_value()) {
536 VALIDATION_LOG << "Could not bind texture of this type.";
537 return false;
538 }
539 gl.BindTexture(target.value(), handle.value());
540 } break;
543 gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
544 break;
545 }
546 InitializeContentsIfNecessary();
547 return true;
548}
549
551 for (size_t i = 0; i < slices_initialized_.size(); i++) {
552 slices_initialized_[i] = true;
553 }
554}
555
556void TextureGLES::MarkSliceInitialized(size_t slice) const {
557 slices_initialized_[slice] = true;
558}
559
560bool TextureGLES::IsSliceInitialized(size_t slice) const {
561 return slices_initialized_[slice];
562}
563
565 if (!IsValid()) {
566 return false;
567 }
568
570 switch (type) {
572 break;
574 VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
575 "supported in the GLES backend.";
576 return false;
578 break;
580 break;
581 }
582
583 if (!Bind()) {
584 return false;
585 }
586
587 auto handle = GetGLHandle();
588 if (!handle.has_value()) {
589 return false;
590 }
591
592 const auto& gl = reactor_->GetProcTable();
593 gl.GenerateMipmap(ToTextureType(type));
594 mipmap_generated_ = true;
595 return true;
596}
597
599 return type_;
600}
601
603 switch (point) {
605 return GL_COLOR_ATTACHMENT0;
607 return GL_DEPTH_ATTACHMENT;
609 return GL_STENCIL_ATTACHMENT;
610 }
611}
612
614 GLenum target,
615 AttachmentType attachment_type) const {
616 if (!IsValid()) {
617 return false;
618 }
619 InitializeContentsIfNecessary();
620 auto handle = GetGLHandle();
621 if (!handle.has_value()) {
622 return false;
623 }
624 const auto& gl = reactor_->GetProcTable();
625
626 switch (ComputeTypeForBinding(target)) {
627 case Type::kTexture:
628 gl.FramebufferTexture2D(target, // target
629 ToAttachmentType(attachment_type), // attachment
630 GL_TEXTURE_2D, // textarget
631 handle.value(), // texture
632 0 // level
633 );
634 break;
636 gl.FramebufferTexture2DMultisampleEXT(
637 target, // target
638 ToAttachmentType(attachment_type), // attachment
639 GL_TEXTURE_2D, // textarget
640 handle.value(), // texture
641 0, // level
642 4 // samples
643 );
644 break;
647 gl.FramebufferRenderbuffer(
648 target, // target
649 ToAttachmentType(attachment_type), // attachment
650 GL_RENDERBUFFER, // render-buffer target
651 handle.value() // render-buffer
652 );
653 break;
654 }
655
656 return true;
657}
658
659// |Texture|
660Scalar TextureGLES::GetYCoordScale() const {
661 switch (GetCoordinateSystem()) {
663 return 1.0;
665 return -1.0;
666 }
668}
669
671 return is_wrapped_;
672}
673
674std::optional<GLuint> TextureGLES::GetFBO() const {
675 return wrapped_fbo_;
676}
677
679 FML_DCHECK(!fence_.has_value());
680 fence_ = fence;
681}
682
683// Visible for testing.
684std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
685 return fence_;
686}
687
689 cached_fbo_ = fbo;
690}
691
693 return cached_fbo_;
694}
695
696} // 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:308
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