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 <optional>
8 #include <utility>
9 
10 #include "flutter/fml/logging.h"
11 #include "flutter/fml/mapping.h"
12 #include "flutter/fml/trace_event.h"
14 #include "impeller/base/strings.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;
43  return false;
44  }
45  FML_UNREACHABLE();
46 }
47 
48 static 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()
62 }
63 
64 struct 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_RGBA;
92  external_format = GL_RGBA;
93  type = GL_FLOAT;
94  break;
96  internal_format = GL_RGBA;
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 
136  switch (type) {
139  return HandleType::kTexture;
143  }
144  FML_UNREACHABLE();
145 }
146 
147 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
148  std::shared_ptr<ReactorGLES> reactor,
149  TextureDescriptor desc,
150  GLuint fbo) {
151  auto texture = std::shared_ptr<TextureGLES>(
152  new TextureGLES(std::move(reactor), desc, fbo, std::nullopt));
153  if (!texture->IsValid()) {
154  return nullptr;
155  }
156  return texture;
157 }
158 
159 std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
160  std::shared_ptr<ReactorGLES> reactor,
161  TextureDescriptor desc,
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>(
172  new TextureGLES(std::move(reactor), desc, std::nullopt, external_handle));
173  if (!texture->IsValid()) {
174  return nullptr;
175  }
176  return texture;
177 }
178 
179 std::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 
185 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
186  TextureDescriptor desc)
187  : TextureGLES(std::move(reactor), //
188  desc, //
189  std::nullopt, //
190  std::nullopt //
191  ) {}
192 
193 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
194  TextureDescriptor desc,
195  std::optional<GLuint> fbo,
196  std::optional<HandleGLES> external_handle)
197  : Texture(desc),
198  reactor_(std::move(reactor)),
199  type_(GetTextureTypeFromDescriptor(
200  GetTextureDescriptor(),
201  reactor_->GetProcTable().GetCapabilities())),
202  handle_(external_handle.has_value()
203  ? external_handle.value()
204  : reactor_->CreateUntrackedHandle(ToHandleType(type_))),
205  is_wrapped_(fbo.has_value() || external_handle.has_value()),
206  wrapped_fbo_(fbo) {
207  // Ensure the texture descriptor itself is valid.
208  if (!GetTextureDescriptor().IsValid()) {
209  VALIDATION_LOG << "Invalid texture descriptor.";
210  return;
211  }
212  // Ensure the texture doesn't exceed device capabilities.
213  const auto tex_size = GetTextureDescriptor().size;
214  const auto max_size =
215  reactor_->GetProcTable().GetCapabilities()->max_texture_size;
216  if (tex_size.Max(max_size) != max_size) {
217  VALIDATION_LOG << "Texture of size " << tex_size
218  << " would exceed max supported size of " << max_size << ".";
219  return;
220  }
221 
222  is_valid_ = true;
223 }
224 
225 // |Texture|
227  reactor_->CollectHandle(handle_);
228  if (!cached_fbo_.IsDead()) {
229  reactor_->CollectHandle(cached_fbo_);
230  }
231 }
232 
234  handle_ = HandleGLES::DeadHandle();
235 }
236 
237 // |Texture|
238 bool TextureGLES::IsValid() const {
239  return is_valid_;
240 }
241 
242 // |Texture|
243 void TextureGLES::SetLabel(std::string_view label) {
244 #ifdef IMPELLER_DEBUG
245  reactor_->SetDebugLabel(handle_, label);
246 #endif // IMPELLER_DEBUG
247 }
248 
249 // |Texture|
250 void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
251 #ifdef IMPELLER_DEBUG
252  if (reactor_->CanSetDebugLabels()) {
253  reactor_->SetDebugLabel(handle_,
254  SPrintF("%s %s", label.data(), trailing.data()));
255  }
256 #endif // IMPELLER_DEBUG
257 }
258 
259 // |Texture|
260 bool TextureGLES::OnSetContents(const uint8_t* contents,
261  size_t length,
262  size_t slice) {
263  return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
264 }
265 
266 // |Texture|
267 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
268  size_t slice) {
269  if (!mapping) {
270  return false;
271  }
272 
273  if (mapping->GetSize() == 0u) {
274  return true;
275  }
276 
277  if (mapping->GetMapping() == nullptr) {
278  return false;
279  }
280 
281  if (GetType() != Type::kTexture) {
282  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
283  "this texture object.";
284  return false;
285  }
286 
287  if (is_wrapped_) {
288  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
289  return false;
290  }
291 
292  const auto& tex_descriptor = GetTextureDescriptor();
293 
294  if (tex_descriptor.size.IsEmpty()) {
295  return true;
296  }
297 
298  if (!tex_descriptor.IsValid() ||
299  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
300  return false;
301  }
302 
303  GLenum texture_type;
304  GLenum texture_target;
305  switch (tex_descriptor.type) {
307  texture_type = GL_TEXTURE_2D;
308  texture_target = GL_TEXTURE_2D;
309  break;
311  VALIDATION_LOG << "Multisample texture uploading is not supported for "
312  "the OpenGLES backend.";
313  return false;
315  texture_type = GL_TEXTURE_CUBE_MAP;
316  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
317  break;
319  texture_type = GL_TEXTURE_EXTERNAL_OES;
320  texture_target = GL_TEXTURE_EXTERNAL_OES;
321  break;
322  }
323 
324  auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
325  std::move(mapping));
326  if (!data || !data->IsValid()) {
327  VALIDATION_LOG << "Invalid texture format.";
328  return false;
329  }
330 
331  ReactorGLES::Operation texture_upload = [handle = handle_, //
332  data, //
333  size = tex_descriptor.size, //
334  texture_type, //
335  texture_target //
336  ](const auto& reactor) {
337  auto gl_handle = reactor.GetGLHandle(handle);
338  if (!gl_handle.has_value()) {
340  << "Texture was collected before it could be uploaded to the GPU.";
341  return;
342  }
343  const auto& gl = reactor.GetProcTable();
344  gl.BindTexture(texture_type, gl_handle.value());
345  const GLvoid* tex_data = nullptr;
346  if (data->data) {
347  tex_data = data->data->GetMapping();
348  }
349 
350  {
351  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
352  std::to_string(data->data->GetSize()).c_str());
353  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
354  gl.TexImage2D(texture_target, // target
355  0u, // LOD level
356  data->internal_format, // internal format
357  size.width, // width
358  size.height, // height
359  0u, // border
360  data->external_format, // external format
361  data->type, // type
362  tex_data // data
363  );
364  }
365  };
366 
367  slices_initialized_ = reactor_->AddOperation(texture_upload);
368  return slices_initialized_[0];
369 }
370 
371 // |Texture|
372 ISize TextureGLES::GetSize() const {
373  return GetTextureDescriptor().size;
374 }
375 
376 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
377  switch (format) {
380  return GL_RGBA8;
382  return GL_RGBA32F;
384  return GL_RGBA16F;
386  return GL_STENCIL_INDEX8;
388  return GL_DEPTH24_STENCIL8;
390  return GL_DEPTH32F_STENCIL8;
400  return std::nullopt;
401  }
402  FML_UNREACHABLE();
403 }
404 
406  // When binding to a GL_READ_FRAMEBUFFER, any multisampled
407  // textures must be bound as single sampled.
408  if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
409  return Type::kTexture;
410  }
411  return type_;
412 }
413 
414 void TextureGLES::InitializeContentsIfNecessary() const {
415  if (!IsValid() || slices_initialized_[0]) {
416  return;
417  }
418  slices_initialized_[0] = true;
419 
420  if (is_wrapped_) {
421  return;
422  }
423 
424  auto size = GetSize();
425 
426  if (size.IsEmpty()) {
427  return;
428  }
429 
430  const auto& gl = reactor_->GetProcTable();
431  std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
432  if (!handle.has_value()) {
433  VALIDATION_LOG << "Could not initialize the contents of texture.";
434  return;
435  }
436 
437  switch (type_) {
438  case Type::kTexture:
440  TexImage2DData tex_data(GetTextureDescriptor().format);
441  if (!tex_data.IsValid()) {
442  VALIDATION_LOG << "Invalid format for texture image.";
443  return;
444  }
445  gl.BindTexture(GL_TEXTURE_2D, handle.value());
446  {
447  TRACE_EVENT0("impeller", "TexImage2DInitialization");
448  gl.TexImage2D(GL_TEXTURE_2D, // target
449  0u, // LOD level (base mip level size checked)
450  tex_data.internal_format, // internal format
451  size.width, // width
452  size.height, // height
453  0u, // border
454  tex_data.external_format, // format
455  tex_data.type, // type
456  nullptr // data
457  );
458  }
459  } break;
460  case Type::kRenderBuffer:
462  auto render_buffer_format =
464  if (!render_buffer_format.has_value()) {
465  VALIDATION_LOG << "Invalid format for render-buffer image.";
466  return;
467  }
468  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
469  {
470  if (type_ == Type::kRenderBufferMultisampled) {
471  // BEWARE: these functions are not at all equivalent! the extensions
472  // are from EXT_multisampled_render_to_texture and cannot be used
473  // with regular GLES 3.0 multisampled renderbuffers/textures.
474  if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
475  gl.RenderbufferStorageMultisampleEXT(
476  /*target=*/GL_RENDERBUFFER, //
477  /*samples=*/4, //
478  /*internal_format=*/render_buffer_format.value(), //
479  /*width=*/size.width, //
480  /*height=*/size.height //
481  );
482  } else {
483  gl.RenderbufferStorageMultisample(
484  /*target=*/GL_RENDERBUFFER, //
485  /*samples=*/4, //
486  /*internal_format=*/render_buffer_format.value(), //
487  /*width=*/size.width, //
488  /*height=*/size.height //
489  );
490  }
491  } else {
492  gl.RenderbufferStorage(
493  /*target=*/GL_RENDERBUFFER, //
494  /*internal_format=*/render_buffer_format.value(), //
495  /*width=*/size.width, //
496  /*height=*/size.height //
497  );
498  }
499  }
500  } break;
501  }
502 }
503 
504 std::optional<GLuint> TextureGLES::GetGLHandle() const {
505  if (!IsValid()) {
506  return std::nullopt;
507  }
508  return reactor_->GetGLHandle(handle_);
509 }
510 
511 bool TextureGLES::Bind() const {
512  auto handle = GetGLHandle();
513  if (!handle.has_value()) {
514  return false;
515  }
516  const auto& gl = reactor_->GetProcTable();
517 
518  if (fence_.has_value()) {
519  std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
520  if (fence.has_value()) {
521  gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
522  }
523  reactor_->CollectHandle(fence_.value());
524  fence_ = std::nullopt;
525  }
526 
527  switch (type_) {
528  case Type::kTexture:
530  const auto target = ToTextureTarget(GetTextureDescriptor().type);
531  if (!target.has_value()) {
532  VALIDATION_LOG << "Could not bind texture of this type.";
533  return false;
534  }
535  gl.BindTexture(target.value(), handle.value());
536  } break;
537  case Type::kRenderBuffer:
539  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
540  break;
541  }
542  InitializeContentsIfNecessary();
543  return true;
544 }
545 
547  for (size_t i = 0; i < slices_initialized_.size(); i++) {
548  slices_initialized_[i] = true;
549  }
550 }
551 
552 void TextureGLES::MarkSliceInitialized(size_t slice) const {
553  slices_initialized_[slice] = true;
554 }
555 
556 bool TextureGLES::IsSliceInitialized(size_t slice) const {
557  return slices_initialized_[slice];
558 }
559 
561  if (!IsValid()) {
562  return false;
563  }
564 
565  auto type = GetTextureDescriptor().type;
566  switch (type) {
568  break;
570  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
571  "supported in the GLES backend.";
572  return false;
574  break;
576  break;
577  }
578 
579  if (!Bind()) {
580  return false;
581  }
582 
583  auto handle = GetGLHandle();
584  if (!handle.has_value()) {
585  return false;
586  }
587 
588  const auto& gl = reactor_->GetProcTable();
589  gl.GenerateMipmap(ToTextureType(type));
590  mipmap_generated_ = true;
591  return true;
592 }
593 
595  return type_;
596 }
597 
599  switch (point) {
601  return GL_COLOR_ATTACHMENT0;
603  return GL_DEPTH_ATTACHMENT;
605  return GL_STENCIL_ATTACHMENT;
606  }
607 }
608 
610  GLenum target,
611  AttachmentType attachment_type) const {
612  if (!IsValid()) {
613  return false;
614  }
615  InitializeContentsIfNecessary();
616  auto handle = GetGLHandle();
617  if (!handle.has_value()) {
618  return false;
619  }
620  const auto& gl = reactor_->GetProcTable();
621 
622  switch (ComputeTypeForBinding(target)) {
623  case Type::kTexture:
624  gl.FramebufferTexture2D(target, // target
625  ToAttachmentType(attachment_type), // attachment
626  GL_TEXTURE_2D, // textarget
627  handle.value(), // texture
628  0 // level
629  );
630  break;
632  gl.FramebufferTexture2DMultisampleEXT(
633  target, // target
634  ToAttachmentType(attachment_type), // attachment
635  GL_TEXTURE_2D, // textarget
636  handle.value(), // texture
637  0, // level
638  4 // samples
639  );
640  break;
641  case Type::kRenderBuffer:
643  gl.FramebufferRenderbuffer(
644  target, // target
645  ToAttachmentType(attachment_type), // attachment
646  GL_RENDERBUFFER, // render-buffer target
647  handle.value() // render-buffer
648  );
649  break;
650  }
651 
652  return true;
653 }
654 
655 // |Texture|
656 Scalar TextureGLES::GetYCoordScale() const {
657  switch (GetCoordinateSystem()) {
659  return 1.0;
661  return -1.0;
662  }
663  FML_UNREACHABLE();
664 }
665 
667  return is_wrapped_;
668 }
669 
670 std::optional<GLuint> TextureGLES::GetFBO() const {
671  return wrapped_fbo_;
672 }
673 
675  FML_DCHECK(!fence_.has_value());
676  fence_ = fence;
677 }
678 
679 // Visible for testing.
680 std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
681  return fence_;
682 }
683 
685  cached_fbo_ = fbo;
686 }
687 
689  return cached_fbo_;
690 }
691 
692 } // namespace impeller
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
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)
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....
TextureGLES(std::shared_ptr< ReactorGLES > reactor, TextureDescriptor desc)
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
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
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:308
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:67
GLenum external_format
Definition: texture_gles.cc:66
GLint internal_format
Definition: texture_gles.cc:65
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:68
#define VALIDATION_LOG
Definition: validation.h:91