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"
19 
20 namespace impeller {
21 
22 namespace {
23 static bool IsDepthStencilFormat(PixelFormat format) {
24  switch (format) {
28  return true;
42  return false;
43  }
44  FML_UNREACHABLE();
45 }
46 
47 static TextureGLES::Type GetTextureTypeFromDescriptor(
48  const TextureDescriptor& desc,
49  const std::shared_ptr<const CapabilitiesGLES>& capabilities) {
50  const auto usage = static_cast<TextureUsageMask>(desc.usage);
51  const auto render_target = TextureUsage::kRenderTarget;
52  const auto is_msaa = desc.sample_count == SampleCount::kCount4;
53  if (usage == render_target && IsDepthStencilFormat(desc.format)) {
56  }
57  return is_msaa ? (capabilities->SupportsImplicitResolvingMSAA()
61 }
62 
63 struct TexImage2DData {
64  GLint internal_format = 0;
65  GLenum external_format = GL_NONE;
66  GLenum type = GL_NONE;
67  std::shared_ptr<const fml::Mapping> data;
68 
69  explicit TexImage2DData(PixelFormat pixel_format) {
70  switch (pixel_format) {
72  internal_format = GL_ALPHA;
73  external_format = GL_ALPHA;
74  type = GL_UNSIGNED_BYTE;
75  break;
77  internal_format = GL_RED;
78  external_format = GL_RED;
79  type = GL_UNSIGNED_BYTE;
80  break;
85  internal_format = GL_RGBA;
86  external_format = GL_RGBA;
87  type = GL_UNSIGNED_BYTE;
88  break;
90  internal_format = GL_RGBA;
91  external_format = GL_RGBA;
92  type = GL_FLOAT;
93  break;
95  internal_format = GL_RGBA;
96  external_format = GL_RGBA;
97  type = GL_HALF_FLOAT;
98  break;
100  // Pure stencil textures are only available in OpenGL 4.4+, which is
101  // ~0% of mobile devices. Instead, we use a depth-stencil texture and
102  // only use the stencil component.
103  //
104  // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
106  internal_format = GL_DEPTH_STENCIL;
107  external_format = GL_DEPTH_STENCIL;
108  type = GL_UNSIGNED_INT_24_8;
109  break;
116  return;
117  }
118  is_valid_ = true;
119  }
120 
121  TexImage2DData(PixelFormat pixel_format,
122  std::shared_ptr<const fml::Mapping> mapping)
123  : TexImage2DData(pixel_format) {
124  data = std::move(mapping);
125  }
126 
127  bool IsValid() const { return is_valid_; }
128 
129  private:
130  bool is_valid_ = false;
131 };
132 } // namespace
133 
135  switch (type) {
138  return HandleType::kTexture;
142  }
143  FML_UNREACHABLE();
144 }
145 
146 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
147  std::shared_ptr<ReactorGLES> reactor,
148  TextureDescriptor desc,
149  GLuint fbo) {
150  auto texture = std::shared_ptr<TextureGLES>(
151  new TextureGLES(std::move(reactor), desc, fbo, std::nullopt));
152  if (!texture->IsValid()) {
153  return nullptr;
154  }
155  return texture;
156 }
157 
158 std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
159  std::shared_ptr<ReactorGLES> reactor,
160  TextureDescriptor desc,
161  HandleGLES external_handle) {
162  if (external_handle.IsDead()) {
163  VALIDATION_LOG << "Cannot wrap a dead handle.";
164  return nullptr;
165  }
166  if (external_handle.GetType() != HandleType::kTexture) {
167  VALIDATION_LOG << "Cannot wrap a non-texture handle.";
168  return nullptr;
169  }
170  auto texture = std::shared_ptr<TextureGLES>(
171  new TextureGLES(std::move(reactor), desc, std::nullopt, external_handle));
172  if (!texture->IsValid()) {
173  return nullptr;
174  }
175  return texture;
176 }
177 
178 std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
179  std::shared_ptr<ReactorGLES> reactor,
180  TextureDescriptor desc) {
181  return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
182 }
183 
184 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
185  TextureDescriptor desc)
186  : TextureGLES(std::move(reactor), //
187  desc, //
188  std::nullopt, //
189  std::nullopt //
190  ) {}
191 
192 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
193  TextureDescriptor desc,
194  std::optional<GLuint> fbo,
195  std::optional<HandleGLES> external_handle)
196  : Texture(desc),
197  reactor_(std::move(reactor)),
198  type_(GetTextureTypeFromDescriptor(
199  GetTextureDescriptor(),
200  reactor_->GetProcTable().GetCapabilities())),
201  handle_(external_handle.has_value()
202  ? external_handle.value()
203  : reactor_->CreateUntrackedHandle(ToHandleType(type_))),
204  is_wrapped_(fbo.has_value() || external_handle.has_value()),
205  wrapped_fbo_(fbo) {
206  // Ensure the texture descriptor itself is valid.
207  if (!GetTextureDescriptor().IsValid()) {
208  VALIDATION_LOG << "Invalid texture descriptor.";
209  return;
210  }
211  // Ensure the texture doesn't exceed device capabilities.
212  const auto tex_size = GetTextureDescriptor().size;
213  const auto max_size =
214  reactor_->GetProcTable().GetCapabilities()->max_texture_size;
215  if (tex_size.Max(max_size) != max_size) {
216  VALIDATION_LOG << "Texture of size " << tex_size
217  << " would exceed max supported size of " << max_size << ".";
218  return;
219  }
220 
221  is_valid_ = true;
222 }
223 
224 // |Texture|
226  reactor_->CollectHandle(handle_);
227  if (!cached_fbo_.IsDead()) {
228  reactor_->CollectHandle(cached_fbo_);
229  }
230 }
231 
232 // |Texture|
233 bool TextureGLES::IsValid() const {
234  return is_valid_;
235 }
236 
237 // |Texture|
238 void TextureGLES::SetLabel(std::string_view label) {
239 #ifdef IMPELLER_DEBUG
240  reactor_->SetDebugLabel(handle_, label);
241 #endif // IMPELLER_DEBUG
242 }
243 
244 // |Texture|
245 void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
246 #ifdef IMPELLER_DEBUG
247  if (reactor_->CanSetDebugLabels()) {
248  reactor_->SetDebugLabel(handle_,
249  SPrintF("%s %s", label.data(), trailing.data()));
250  }
251 #endif // IMPELLER_DEBUG
252 }
253 
254 // |Texture|
255 bool TextureGLES::OnSetContents(const uint8_t* contents,
256  size_t length,
257  size_t slice) {
258  return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
259 }
260 
261 // |Texture|
262 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
263  size_t slice) {
264  if (!mapping) {
265  return false;
266  }
267 
268  if (mapping->GetSize() == 0u) {
269  return true;
270  }
271 
272  if (mapping->GetMapping() == nullptr) {
273  return false;
274  }
275 
276  if (GetType() != Type::kTexture) {
277  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
278  "this texture object.";
279  return false;
280  }
281 
282  if (is_wrapped_) {
283  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
284  return false;
285  }
286 
287  const auto& tex_descriptor = GetTextureDescriptor();
288 
289  if (tex_descriptor.size.IsEmpty()) {
290  return true;
291  }
292 
293  if (!tex_descriptor.IsValid() ||
294  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
295  return false;
296  }
297 
298  GLenum texture_type;
299  GLenum texture_target;
300  switch (tex_descriptor.type) {
302  texture_type = GL_TEXTURE_2D;
303  texture_target = GL_TEXTURE_2D;
304  break;
306  VALIDATION_LOG << "Multisample texture uploading is not supported for "
307  "the OpenGLES backend.";
308  return false;
310  texture_type = GL_TEXTURE_CUBE_MAP;
311  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
312  break;
314  texture_type = GL_TEXTURE_EXTERNAL_OES;
315  texture_target = GL_TEXTURE_EXTERNAL_OES;
316  break;
317  }
318 
319  auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
320  std::move(mapping));
321  if (!data || !data->IsValid()) {
322  VALIDATION_LOG << "Invalid texture format.";
323  return false;
324  }
325 
326  ReactorGLES::Operation texture_upload = [handle = handle_, //
327  data, //
328  size = tex_descriptor.size, //
329  texture_type, //
330  texture_target //
331  ](const auto& reactor) {
332  auto gl_handle = reactor.GetGLHandle(handle);
333  if (!gl_handle.has_value()) {
335  << "Texture was collected before it could be uploaded to the GPU.";
336  return;
337  }
338  const auto& gl = reactor.GetProcTable();
339  gl.BindTexture(texture_type, gl_handle.value());
340  const GLvoid* tex_data = nullptr;
341  if (data->data) {
342  tex_data = data->data->GetMapping();
343  }
344 
345  {
346  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
347  std::to_string(data->data->GetSize()).c_str());
348  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
349  gl.TexImage2D(texture_target, // target
350  0u, // LOD level
351  data->internal_format, // internal format
352  size.width, // width
353  size.height, // height
354  0u, // border
355  data->external_format, // external format
356  data->type, // type
357  tex_data // data
358  );
359  }
360  };
361 
362  slices_initialized_ = reactor_->AddOperation(texture_upload);
363  return slices_initialized_[0];
364 }
365 
366 // |Texture|
367 ISize TextureGLES::GetSize() const {
368  return GetTextureDescriptor().size;
369 }
370 
371 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
372  switch (format) {
375  return GL_RGBA8;
377  return GL_RGBA32F;
379  return GL_RGBA16F;
381  return GL_STENCIL_INDEX8;
383  return GL_DEPTH24_STENCIL8;
385  return GL_DEPTH32F_STENCIL8;
395  return std::nullopt;
396  }
397  FML_UNREACHABLE();
398 }
399 
401  // When binding to a GL_READ_FRAMEBUFFER, any multisampled
402  // textures must be bound as single sampled.
403  if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
404  return Type::kTexture;
405  }
406  return type_;
407 }
408 
409 void TextureGLES::InitializeContentsIfNecessary() const {
410  if (!IsValid() || slices_initialized_[0]) {
411  return;
412  }
413  slices_initialized_[0] = true;
414 
415  if (is_wrapped_) {
416  return;
417  }
418 
419  auto size = GetSize();
420 
421  if (size.IsEmpty()) {
422  return;
423  }
424 
425  const auto& gl = reactor_->GetProcTable();
426  std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
427  if (!handle.has_value()) {
428  VALIDATION_LOG << "Could not initialize the contents of texture.";
429  return;
430  }
431 
432  switch (type_) {
433  case Type::kTexture:
435  TexImage2DData tex_data(GetTextureDescriptor().format);
436  if (!tex_data.IsValid()) {
437  VALIDATION_LOG << "Invalid format for texture image.";
438  return;
439  }
440  gl.BindTexture(GL_TEXTURE_2D, handle.value());
441  {
442  TRACE_EVENT0("impeller", "TexImage2DInitialization");
443  gl.TexImage2D(GL_TEXTURE_2D, // target
444  0u, // LOD level (base mip level size checked)
445  tex_data.internal_format, // internal format
446  size.width, // width
447  size.height, // height
448  0u, // border
449  tex_data.external_format, // format
450  tex_data.type, // type
451  nullptr // data
452  );
453  }
454  } break;
455  case Type::kRenderBuffer:
457  auto render_buffer_format =
459  if (!render_buffer_format.has_value()) {
460  VALIDATION_LOG << "Invalid format for render-buffer image.";
461  return;
462  }
463  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
464  {
465  if (type_ == Type::kRenderBufferMultisampled) {
466  // BEWARE: these functions are not at all equivalent! the extensions
467  // are from EXT_multisampled_render_to_texture and cannot be used
468  // with regular GLES 3.0 multisampled renderbuffers/textures.
469  if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
470  gl.RenderbufferStorageMultisampleEXT(
471  /*target=*/GL_RENDERBUFFER, //
472  /*samples=*/4, //
473  /*internal_format=*/render_buffer_format.value(), //
474  /*width=*/size.width, //
475  /*height=*/size.height //
476  );
477  } else {
478  gl.RenderbufferStorageMultisample(
479  /*target=*/GL_RENDERBUFFER, //
480  /*samples=*/4, //
481  /*internal_format=*/render_buffer_format.value(), //
482  /*width=*/size.width, //
483  /*height=*/size.height //
484  );
485  }
486  } else {
487  gl.RenderbufferStorage(
488  /*target=*/GL_RENDERBUFFER, //
489  /*internal_format=*/render_buffer_format.value(), //
490  /*width=*/size.width, //
491  /*height=*/size.height //
492  );
493  }
494  }
495  } break;
496  }
497 }
498 
499 std::optional<GLuint> TextureGLES::GetGLHandle() const {
500  if (!IsValid()) {
501  return std::nullopt;
502  }
503  return reactor_->GetGLHandle(handle_);
504 }
505 
506 bool TextureGLES::Bind() const {
507  auto handle = GetGLHandle();
508  if (!handle.has_value()) {
509  return false;
510  }
511  const auto& gl = reactor_->GetProcTable();
512 
513  if (fence_.has_value()) {
514  std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
515  if (fence.has_value()) {
516  gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
517  }
518  reactor_->CollectHandle(fence_.value());
519  fence_ = std::nullopt;
520  }
521 
522  switch (type_) {
523  case Type::kTexture:
525  const auto target = ToTextureTarget(GetTextureDescriptor().type);
526  if (!target.has_value()) {
527  VALIDATION_LOG << "Could not bind texture of this type.";
528  return false;
529  }
530  gl.BindTexture(target.value(), handle.value());
531  } break;
532  case Type::kRenderBuffer:
534  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
535  break;
536  }
537  InitializeContentsIfNecessary();
538  return true;
539 }
540 
542  for (size_t i = 0; i < slices_initialized_.size(); i++) {
543  slices_initialized_[i] = true;
544  }
545 }
546 
547 void TextureGLES::MarkSliceInitialized(size_t slice) const {
548  slices_initialized_[slice] = true;
549 }
550 
551 bool TextureGLES::IsSliceInitialized(size_t slice) const {
552  return slices_initialized_[slice];
553 }
554 
556  if (!IsValid()) {
557  return false;
558  }
559 
560  auto type = GetTextureDescriptor().type;
561  switch (type) {
563  break;
565  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
566  "supported in the GLES backend.";
567  return false;
569  break;
571  break;
572  }
573 
574  if (!Bind()) {
575  return false;
576  }
577 
578  auto handle = GetGLHandle();
579  if (!handle.has_value()) {
580  return false;
581  }
582 
583  const auto& gl = reactor_->GetProcTable();
584  gl.GenerateMipmap(ToTextureType(type));
585  mipmap_generated_ = true;
586  return true;
587 }
588 
590  return type_;
591 }
592 
594  switch (point) {
596  return GL_COLOR_ATTACHMENT0;
598  return GL_DEPTH_ATTACHMENT;
600  return GL_STENCIL_ATTACHMENT;
601  }
602 }
603 
605  GLenum target,
606  AttachmentType attachment_type) const {
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;
636  case Type::kRenderBuffer:
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|
651 Scalar TextureGLES::GetYCoordScale() const {
652  switch (GetCoordinateSystem()) {
654  return 1.0;
656  return -1.0;
657  }
658  FML_UNREACHABLE();
659 }
660 
662  return is_wrapped_;
663 }
664 
665 std::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.
675 std::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:37
constexpr bool IsDead() const
Determines if the handle is dead.
Definition: handle_gles.h:53
HandleType GetType() const
Definition: handle_gles.h:74
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 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:18
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:66
GLenum external_format
Definition: texture_gles.cc:65
GLint internal_format
Definition: texture_gles.cc:64
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:67
#define VALIDATION_LOG
Definition: validation.h:91