Flutter Impeller
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"
13 #include "flutter/fml/trace_event.h"
16 #include "impeller/core/formats.h"
20 
21 namespace impeller {
22 
23 namespace {
24 static bool IsDepthStencilFormat(PixelFormat format) {
25  switch (format) {
29  return true;
44  return false;
45  }
46  FML_UNREACHABLE();
47 }
48 
49 static 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()
63 }
64 
65 struct 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 
142  switch (type) {
145  return HandleType::kTexture;
149  }
150  FML_UNREACHABLE();
151 }
152 
153 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
154  std::shared_ptr<ReactorGLES> reactor,
155  TextureDescriptor desc,
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 
165 std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
166  std::shared_ptr<ReactorGLES> reactor,
167  TextureDescriptor desc,
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 
185 std::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 
191 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
192  TextureDescriptor desc,
193  bool threadsafe)
194  : TextureGLES(std::move(reactor), //
195  desc, //
196  threadsafe, //
197  std::nullopt, //
198  std::nullopt //
199  ) {}
200 
201 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
202  TextureDescriptor desc,
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|
249 bool TextureGLES::IsValid() const {
250  return is_valid_;
251 }
252 
253 // |Texture|
254 void TextureGLES::SetLabel(std::string_view label) {
255 #ifdef IMPELLER_DEBUG
256  reactor_->SetDebugLabel(handle_, label);
257 #endif // IMPELLER_DEBUG
258 }
259 
260 // |Texture|
261 void 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|
270 bool 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|
277 bool 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|
382 ISize TextureGLES::GetSize() const {
383  return GetTextureDescriptor().size;
384 }
385 
386 static 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  }
413  FML_UNREACHABLE();
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 
425 void 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;
471  case Type::kRenderBuffer:
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 
515 std::optional<GLuint> TextureGLES::GetGLHandle() const {
516  if (!IsValid()) {
517  return std::nullopt;
518  }
519  return reactor_->GetGLHandle(handle_);
520 }
521 
522 bool 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:
541  const auto target = ToTextureTarget(GetTextureDescriptor().type);
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;
548  case Type::kRenderBuffer:
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 
563 void TextureGLES::MarkSliceInitialized(size_t slice) const {
564  slices_initialized_[slice] = true;
565 }
566 
567 bool TextureGLES::IsSliceInitialized(size_t slice) const {
568  return slices_initialized_[slice];
569 }
570 
572  if (!IsValid()) {
573  return false;
574  }
575 
576  auto type = GetTextureDescriptor().type;
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;
652  case Type::kRenderBuffer:
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|
667 Scalar TextureGLES::GetYCoordScale() const {
668  switch (GetCoordinateSystem()) {
670  return 1.0;
672  return -1.0;
673  }
674  FML_UNREACHABLE();
675 }
676 
678  return is_wrapped_;
679 }
680 
681 std::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.
691 std::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
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
Definition: reactor_gles.h:214
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...
bool IsWrapped() const
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
const ProcTable & GetProcTable()
Definition: proc_table.cc:12
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)
Definition: formats_gles.h:171
static std::optional< GLenum > ToRenderBufferFormat(PixelFormat format)
constexpr std::optional< GLenum > ToTextureTarget(TextureType type)
Definition: formats_gles.h:185
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: comparable.h:95
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
GLenum type
Definition: texture_gles.cc:68
GLenum external_format
Definition: texture_gles.cc:67
GLint internal_format
Definition: texture_gles.cc:66
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:69
#define VALIDATION_LOG
Definition: validation.h:91