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_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_,
259  SPrintF("%s %s", label.data(), trailing.data()));
260  }
261 #endif // IMPELLER_DEBUG
262 }
263 
264 // |Texture|
265 bool TextureGLES::OnSetContents(const uint8_t* contents,
266  size_t length,
267  size_t slice) {
268  return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
269 }
270 
271 // |Texture|
272 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
273  size_t slice) {
274  if (!mapping) {
275  return false;
276  }
277 
278  if (mapping->GetSize() == 0u) {
279  return true;
280  }
281 
282  if (mapping->GetMapping() == nullptr) {
283  return false;
284  }
285 
286  if (GetType() != Type::kTexture) {
287  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
288  "this texture object.";
289  return false;
290  }
291 
292  if (is_wrapped_) {
293  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
294  return false;
295  }
296 
297  const auto& tex_descriptor = GetTextureDescriptor();
298 
299  if (tex_descriptor.size.IsEmpty()) {
300  return true;
301  }
302 
303  if (!tex_descriptor.IsValid() ||
304  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
305  return false;
306  }
307 
308  GLenum texture_type;
309  GLenum texture_target;
310  switch (tex_descriptor.type) {
312  texture_type = GL_TEXTURE_2D;
313  texture_target = GL_TEXTURE_2D;
314  break;
316  VALIDATION_LOG << "Multisample texture uploading is not supported for "
317  "the OpenGLES backend.";
318  return false;
320  texture_type = GL_TEXTURE_CUBE_MAP;
321  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
322  break;
324  texture_type = GL_TEXTURE_EXTERNAL_OES;
325  texture_target = GL_TEXTURE_EXTERNAL_OES;
326  break;
327  }
328 
329  auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
330  std::move(mapping));
331  if (!data || !data->IsValid()) {
332  VALIDATION_LOG << "Invalid texture format.";
333  return false;
334  }
335 
336  ReactorGLES::Operation texture_upload = [handle = handle_, //
337  data, //
338  size = tex_descriptor.size, //
339  texture_type, //
340  texture_target //
341  ](const auto& reactor) {
342  auto gl_handle = reactor.GetGLHandle(handle);
343  if (!gl_handle.has_value()) {
345  << "Texture was collected before it could be uploaded to the GPU.";
346  return;
347  }
348  const auto& gl = reactor.GetProcTable();
349  gl.BindTexture(texture_type, gl_handle.value());
350  const GLvoid* tex_data = nullptr;
351  if (data->data) {
352  tex_data = data->data->GetMapping();
353  }
354 
355  {
356  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
357  std::to_string(data->data->GetSize()).c_str());
358  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
359  gl.TexImage2D(texture_target, // target
360  0u, // LOD level
361  data->internal_format, // internal format
362  size.width, // width
363  size.height, // height
364  0u, // border
365  data->external_format, // external format
366  data->type, // type
367  tex_data // data
368  );
369  }
370  };
371 
372  slices_initialized_ = reactor_->AddOperation(texture_upload);
373  return slices_initialized_[0];
374 }
375 
376 // |Texture|
377 ISize TextureGLES::GetSize() const {
378  return GetTextureDescriptor().size;
379 }
380 
381 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
382  switch (format) {
385  return GL_RGBA8;
387  return GL_RGBA32F;
389  return GL_RGBA16F;
391  return GL_STENCIL_INDEX8;
393  return GL_DEPTH24_STENCIL8;
395  return GL_DEPTH32F_STENCIL8;
405  return std::nullopt;
406  }
407  FML_UNREACHABLE();
408 }
409 
411  // When binding to a GL_READ_FRAMEBUFFER, any multisampled
412  // textures must be bound as single sampled.
413  if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
414  return Type::kTexture;
415  }
416  return type_;
417 }
418 
419 void TextureGLES::InitializeContentsIfNecessary() const {
420  if (!IsValid() || slices_initialized_[0]) {
421  return;
422  }
423  slices_initialized_[0] = true;
424 
425  if (is_wrapped_) {
426  return;
427  }
428 
429  auto size = GetSize();
430 
431  if (size.IsEmpty()) {
432  return;
433  }
434 
435  const auto& gl = reactor_->GetProcTable();
436  std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
437  if (!handle.has_value()) {
438  VALIDATION_LOG << "Could not initialize the contents of texture.";
439  return;
440  }
441 
442  switch (type_) {
443  case Type::kTexture:
445  TexImage2DData tex_data(GetTextureDescriptor().format);
446  if (!tex_data.IsValid()) {
447  VALIDATION_LOG << "Invalid format for texture image.";
448  return;
449  }
450  gl.BindTexture(GL_TEXTURE_2D, handle.value());
451  {
452  TRACE_EVENT0("impeller", "TexImage2DInitialization");
453  gl.TexImage2D(GL_TEXTURE_2D, // target
454  0u, // LOD level (base mip level size checked)
455  tex_data.internal_format, // internal format
456  size.width, // width
457  size.height, // height
458  0u, // border
459  tex_data.external_format, // format
460  tex_data.type, // type
461  nullptr // data
462  );
463  }
464  } break;
465  case Type::kRenderBuffer:
467  auto render_buffer_format =
469  if (!render_buffer_format.has_value()) {
470  VALIDATION_LOG << "Invalid format for render-buffer image.";
471  return;
472  }
473  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
474  {
475  if (type_ == Type::kRenderBufferMultisampled) {
476  // BEWARE: these functions are not at all equivalent! the extensions
477  // are from EXT_multisampled_render_to_texture and cannot be used
478  // with regular GLES 3.0 multisampled renderbuffers/textures.
479  if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
480  gl.RenderbufferStorageMultisampleEXT(
481  /*target=*/GL_RENDERBUFFER, //
482  /*samples=*/4, //
483  /*internal_format=*/render_buffer_format.value(), //
484  /*width=*/size.width, //
485  /*height=*/size.height //
486  );
487  } else {
488  gl.RenderbufferStorageMultisample(
489  /*target=*/GL_RENDERBUFFER, //
490  /*samples=*/4, //
491  /*internal_format=*/render_buffer_format.value(), //
492  /*width=*/size.width, //
493  /*height=*/size.height //
494  );
495  }
496  } else {
497  gl.RenderbufferStorage(
498  /*target=*/GL_RENDERBUFFER, //
499  /*internal_format=*/render_buffer_format.value(), //
500  /*width=*/size.width, //
501  /*height=*/size.height //
502  );
503  }
504  }
505  } break;
506  }
507 }
508 
509 std::optional<GLuint> TextureGLES::GetGLHandle() const {
510  if (!IsValid()) {
511  return std::nullopt;
512  }
513  return reactor_->GetGLHandle(handle_);
514 }
515 
516 bool TextureGLES::Bind() const {
517  auto handle = GetGLHandle();
518  if (!handle.has_value()) {
519  return false;
520  }
521  const auto& gl = reactor_->GetProcTable();
522 
523  if (fence_.has_value()) {
524  std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
525  if (fence.has_value()) {
526  gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
527  }
528  reactor_->CollectHandle(fence_.value());
529  fence_ = std::nullopt;
530  }
531 
532  switch (type_) {
533  case Type::kTexture:
535  const auto target = ToTextureTarget(GetTextureDescriptor().type);
536  if (!target.has_value()) {
537  VALIDATION_LOG << "Could not bind texture of this type.";
538  return false;
539  }
540  gl.BindTexture(target.value(), handle.value());
541  } break;
542  case Type::kRenderBuffer:
544  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
545  break;
546  }
547  InitializeContentsIfNecessary();
548  return true;
549 }
550 
552  for (size_t i = 0; i < slices_initialized_.size(); i++) {
553  slices_initialized_[i] = true;
554  }
555 }
556 
557 void TextureGLES::MarkSliceInitialized(size_t slice) const {
558  slices_initialized_[slice] = true;
559 }
560 
561 bool TextureGLES::IsSliceInitialized(size_t slice) const {
562  return slices_initialized_[slice];
563 }
564 
566  if (!IsValid()) {
567  return false;
568  }
569 
570  auto type = GetTextureDescriptor().type;
571  switch (type) {
573  break;
575  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
576  "supported in the GLES backend.";
577  return false;
579  break;
581  break;
582  }
583 
584  if (!Bind()) {
585  return false;
586  }
587 
588  auto handle = GetGLHandle();
589  if (!handle.has_value()) {
590  return false;
591  }
592 
593  const auto& gl = reactor_->GetProcTable();
594  gl.GenerateMipmap(ToTextureType(type));
595  mipmap_generated_ = true;
596  return true;
597 }
598 
600  return type_;
601 }
602 
604  switch (point) {
606  return GL_COLOR_ATTACHMENT0;
608  return GL_DEPTH_ATTACHMENT;
610  return GL_STENCIL_ATTACHMENT;
611  }
612 }
613 
615  GLenum target,
616  AttachmentType attachment_type) const {
617  if (!IsValid()) {
618  return false;
619  }
620  InitializeContentsIfNecessary();
621  auto handle = GetGLHandle();
622  if (!handle.has_value()) {
623  return false;
624  }
625  const auto& gl = reactor_->GetProcTable();
626 
627  switch (ComputeTypeForBinding(target)) {
628  case Type::kTexture:
629  gl.FramebufferTexture2D(target, // target
630  ToAttachmentType(attachment_type), // attachment
631  GL_TEXTURE_2D, // textarget
632  handle.value(), // texture
633  0 // level
634  );
635  break;
637  gl.FramebufferTexture2DMultisampleEXT(
638  target, // target
639  ToAttachmentType(attachment_type), // attachment
640  GL_TEXTURE_2D, // textarget
641  handle.value(), // texture
642  0, // level
643  4 // samples
644  );
645  break;
646  case Type::kRenderBuffer:
648  gl.FramebufferRenderbuffer(
649  target, // target
650  ToAttachmentType(attachment_type), // attachment
651  GL_RENDERBUFFER, // render-buffer target
652  handle.value() // render-buffer
653  );
654  break;
655  }
656 
657  return true;
658 }
659 
660 // |Texture|
661 Scalar TextureGLES::GetYCoordScale() const {
662  switch (GetCoordinateSystem()) {
664  return 1.0;
666  return -1.0;
667  }
668  FML_UNREACHABLE();
669 }
670 
672  return is_wrapped_;
673 }
674 
675 std::optional<GLuint> TextureGLES::GetFBO() const {
676  return wrapped_fbo_;
677 }
678 
680  FML_DCHECK(!fence_.has_value());
681  fence_ = fence;
682 }
683 
684 // Visible for testing.
685 std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
686  return fence_;
687 }
688 
690  cached_fbo_ = fbo;
691 }
692 
694  return cached_fbo_;
695 }
696 
697 } // 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
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