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;
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_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 
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, false, 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>(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 
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  bool threadsafe)
188  : TextureGLES(std::move(reactor), //
189  desc, //
190  threadsafe, //
191  std::nullopt, //
192  std::nullopt //
193  ) {}
194 
195 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
196  TextureDescriptor desc,
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|
243 bool TextureGLES::IsValid() const {
244  return is_valid_;
245 }
246 
247 // |Texture|
248 void TextureGLES::SetLabel(std::string_view label) {
249 #ifdef IMPELLER_DEBUG
250  reactor_->SetDebugLabel(handle_, label);
251 #endif // IMPELLER_DEBUG
252 }
253 
254 // |Texture|
255 void 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|
264 bool 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|
271 bool 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|
376 ISize TextureGLES::GetSize() const {
377  return GetTextureDescriptor().size;
378 }
379 
380 static 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  }
406  FML_UNREACHABLE();
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 
418 void 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;
464  case Type::kRenderBuffer:
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 
508 std::optional<GLuint> TextureGLES::GetGLHandle() const {
509  if (!IsValid()) {
510  return std::nullopt;
511  }
512  return reactor_->GetGLHandle(handle_);
513 }
514 
515 bool 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:
534  const auto target = ToTextureTarget(GetTextureDescriptor().type);
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;
541  case Type::kRenderBuffer:
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 
556 void TextureGLES::MarkSliceInitialized(size_t slice) const {
557  slices_initialized_[slice] = true;
558 }
559 
560 bool TextureGLES::IsSliceInitialized(size_t slice) const {
561  return slices_initialized_[slice];
562 }
563 
565  if (!IsValid()) {
566  return false;
567  }
568 
569  auto type = GetTextureDescriptor().type;
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;
645  case Type::kRenderBuffer:
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|
660 Scalar TextureGLES::GetYCoordScale() const {
661  switch (GetCoordinateSystem()) {
663  return 1.0;
665  return -1.0;
666  }
667  FML_UNREACHABLE();
668 }
669 
671  return is_wrapped_;
672 }
673 
674 std::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.
684 std::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
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)
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: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