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 auto usage = static_cast<TextureUsageMask>(desc.usage);
50  const auto render_target = TextureUsage::kRenderTarget;
51  const auto is_msaa = desc.sample_count == SampleCount::kCount4;
52  if (usage == render_target && IsDepthStencilFormat(desc.format)) {
55  }
58 }
59 
60 struct TexImage2DData {
61  GLint internal_format = 0;
62  GLenum external_format = GL_NONE;
63  GLenum type = GL_NONE;
64  std::shared_ptr<const fml::Mapping> data;
65 
66  explicit TexImage2DData(PixelFormat pixel_format) {
67  switch (pixel_format) {
69  internal_format = GL_ALPHA;
70  external_format = GL_ALPHA;
71  type = GL_UNSIGNED_BYTE;
72  break;
74  internal_format = GL_RED;
75  external_format = GL_RED;
76  type = GL_UNSIGNED_BYTE;
77  break;
82  internal_format = GL_RGBA;
83  external_format = GL_RGBA;
84  type = GL_UNSIGNED_BYTE;
85  break;
87  internal_format = GL_RGBA;
88  external_format = GL_RGBA;
89  type = GL_FLOAT;
90  break;
92  internal_format = GL_RGBA;
93  external_format = GL_RGBA;
94  type = GL_HALF_FLOAT;
95  break;
97  // Pure stencil textures are only available in OpenGL 4.4+, which is
98  // ~0% of mobile devices. Instead, we use a depth-stencil texture and
99  // only use the stencil component.
100  //
101  // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
103  internal_format = GL_DEPTH_STENCIL;
104  external_format = GL_DEPTH_STENCIL;
105  type = GL_UNSIGNED_INT_24_8;
106  break;
113  return;
114  }
115  is_valid_ = true;
116  }
117 
118  TexImage2DData(PixelFormat pixel_format,
119  std::shared_ptr<const fml::Mapping> mapping)
120  : TexImage2DData(pixel_format) {
121  data = std::move(mapping);
122  }
123 
124  bool IsValid() const { return is_valid_; }
125 
126  private:
127  bool is_valid_ = false;
128 };
129 } // namespace
130 
132  switch (type) {
135  return HandleType::kTexture;
139  }
140  FML_UNREACHABLE();
141 }
142 
143 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
144  std::shared_ptr<ReactorGLES> reactor,
145  TextureDescriptor desc,
146  GLuint fbo) {
147  auto texture = std::shared_ptr<TextureGLES>(
148  new TextureGLES(std::move(reactor), desc, fbo, std::nullopt));
149  if (!texture->IsValid()) {
150  return nullptr;
151  }
152  return texture;
153 }
154 
155 std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
156  std::shared_ptr<ReactorGLES> reactor,
157  TextureDescriptor desc,
158  HandleGLES external_handle) {
159  if (external_handle.IsDead()) {
160  VALIDATION_LOG << "Cannot wrap a dead handle.";
161  return nullptr;
162  }
163  if (external_handle.GetType() != HandleType::kTexture) {
164  VALIDATION_LOG << "Cannot wrap a non-texture handle.";
165  return nullptr;
166  }
167  auto texture = std::shared_ptr<TextureGLES>(
168  new TextureGLES(std::move(reactor), desc, std::nullopt, external_handle));
169  if (!texture->IsValid()) {
170  return nullptr;
171  }
172  return texture;
173 }
174 
175 std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
176  std::shared_ptr<ReactorGLES> reactor,
177  TextureDescriptor desc) {
178  return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
179 }
180 
181 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
182  TextureDescriptor desc)
183  : TextureGLES(std::move(reactor), //
184  desc, //
185  std::nullopt, //
186  std::nullopt //
187  ) {}
188 
189 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
190  TextureDescriptor desc,
191  std::optional<GLuint> fbo,
192  std::optional<HandleGLES> external_handle)
193  : Texture(desc),
194  reactor_(std::move(reactor)),
195  type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
196  handle_(external_handle.has_value()
197  ? external_handle.value()
198  : reactor_->CreateUntrackedHandle(ToHandleType(type_))),
199  is_wrapped_(fbo.has_value() || external_handle.has_value()),
200  wrapped_fbo_(fbo) {
201  // Ensure the texture descriptor itself is valid.
202  if (!GetTextureDescriptor().IsValid()) {
203  VALIDATION_LOG << "Invalid texture descriptor.";
204  return;
205  }
206  // Ensure the texture doesn't exceed device capabilities.
207  const auto tex_size = GetTextureDescriptor().size;
208  const auto max_size =
209  reactor_->GetProcTable().GetCapabilities()->max_texture_size;
210  if (tex_size.Max(max_size) != max_size) {
211  VALIDATION_LOG << "Texture of size " << tex_size
212  << " would exceed max supported size of " << max_size << ".";
213  return;
214  }
215 
216  is_valid_ = true;
217 }
218 
219 // |Texture|
221  reactor_->CollectHandle(handle_);
222  if (cached_fbo_ != GL_NONE) {
223  reactor_->GetProcTable().DeleteFramebuffers(1, &cached_fbo_);
224  }
225 }
226 
227 // |Texture|
228 bool TextureGLES::IsValid() const {
229  return is_valid_;
230 }
231 
232 // |Texture|
233 void TextureGLES::SetLabel(std::string_view label) {
234 #ifdef IMPELLER_DEBUG
235  reactor_->SetDebugLabel(handle_, label);
236 #endif // IMPELLER_DEBUG
237 }
238 
239 // |Texture|
240 void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
241 #ifdef IMPELLER_DEBUG
242  if (reactor_->CanSetDebugLabels()) {
243  reactor_->SetDebugLabel(handle_,
244  SPrintF("%s %s", label.data(), trailing.data()));
245  }
246 #endif // IMPELLER_DEBUG
247 }
248 
249 // |Texture|
250 bool TextureGLES::OnSetContents(const uint8_t* contents,
251  size_t length,
252  size_t slice) {
253  return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
254 }
255 
256 // |Texture|
257 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
258  size_t slice) {
259  if (!mapping) {
260  return false;
261  }
262 
263  if (mapping->GetSize() == 0u) {
264  return true;
265  }
266 
267  if (mapping->GetMapping() == nullptr) {
268  return false;
269  }
270 
271  if (GetType() != Type::kTexture) {
272  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
273  "this texture object.";
274  return false;
275  }
276 
277  if (is_wrapped_) {
278  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
279  return false;
280  }
281 
282  const auto& tex_descriptor = GetTextureDescriptor();
283 
284  if (tex_descriptor.size.IsEmpty()) {
285  return true;
286  }
287 
288  if (!tex_descriptor.IsValid() ||
289  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
290  return false;
291  }
292 
293  GLenum texture_type;
294  GLenum texture_target;
295  switch (tex_descriptor.type) {
297  texture_type = GL_TEXTURE_2D;
298  texture_target = GL_TEXTURE_2D;
299  break;
301  VALIDATION_LOG << "Multisample texture uploading is not supported for "
302  "the OpenGLES backend.";
303  return false;
305  texture_type = GL_TEXTURE_CUBE_MAP;
306  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
307  break;
309  texture_type = GL_TEXTURE_EXTERNAL_OES;
310  texture_target = GL_TEXTURE_EXTERNAL_OES;
311  break;
312  }
313 
314  auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
315  std::move(mapping));
316  if (!data || !data->IsValid()) {
317  VALIDATION_LOG << "Invalid texture format.";
318  return false;
319  }
320 
321  ReactorGLES::Operation texture_upload = [handle = handle_, //
322  data, //
323  size = tex_descriptor.size, //
324  texture_type, //
325  texture_target //
326  ](const auto& reactor) {
327  auto gl_handle = reactor.GetGLHandle(handle);
328  if (!gl_handle.has_value()) {
330  << "Texture was collected before it could be uploaded to the GPU.";
331  return;
332  }
333  const auto& gl = reactor.GetProcTable();
334  gl.BindTexture(texture_type, gl_handle.value());
335  const GLvoid* tex_data = nullptr;
336  if (data->data) {
337  tex_data = data->data->GetMapping();
338  }
339 
340  {
341  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
342  std::to_string(data->data->GetSize()).c_str());
343  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
344  gl.TexImage2D(texture_target, // target
345  0u, // LOD level
346  data->internal_format, // internal format
347  size.width, // width
348  size.height, // height
349  0u, // border
350  data->external_format, // external format
351  data->type, // type
352  tex_data // data
353  );
354  }
355  };
356 
357  slices_initialized_ = reactor_->AddOperation(texture_upload);
358  return slices_initialized_[0];
359 }
360 
361 // |Texture|
362 ISize TextureGLES::GetSize() const {
363  return GetTextureDescriptor().size;
364 }
365 
366 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
367  switch (format) {
370  return GL_RGBA4;
372  return GL_RGBA32F;
374  return GL_RGBA16F;
376  return GL_STENCIL_INDEX8;
378  return GL_DEPTH24_STENCIL8;
380  return GL_DEPTH32F_STENCIL8;
390  return std::nullopt;
391  }
392  FML_UNREACHABLE();
393 }
394 
396  // When binding to a GL_READ_FRAMEBUFFER, any multisampled
397  // textures must be bound as single sampled.
398  if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
399  return Type::kTexture;
400  }
401  return type_;
402 }
403 
404 void TextureGLES::InitializeContentsIfNecessary() const {
405  if (!IsValid() || slices_initialized_[0]) {
406  return;
407  }
408  slices_initialized_[0] = true;
409 
410  if (is_wrapped_) {
411  return;
412  }
413 
414  auto size = GetSize();
415 
416  if (size.IsEmpty()) {
417  return;
418  }
419 
420  const auto& gl = reactor_->GetProcTable();
421  std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
422  if (!handle.has_value()) {
423  VALIDATION_LOG << "Could not initialize the contents of texture.";
424  return;
425  }
426 
427  switch (type_) {
428  case Type::kTexture:
430  TexImage2DData tex_data(GetTextureDescriptor().format);
431  if (!tex_data.IsValid()) {
432  VALIDATION_LOG << "Invalid format for texture image.";
433  return;
434  }
435  gl.BindTexture(GL_TEXTURE_2D, handle.value());
436  {
437  TRACE_EVENT0("impeller", "TexImage2DInitialization");
438  gl.TexImage2D(GL_TEXTURE_2D, // target
439  0u, // LOD level (base mip level size checked)
440  tex_data.internal_format, // internal format
441  size.width, // width
442  size.height, // height
443  0u, // border
444  tex_data.external_format, // format
445  tex_data.type, // type
446  nullptr // data
447  );
448  }
449  } break;
450  case Type::kRenderBuffer:
452  auto render_buffer_format =
454  if (!render_buffer_format.has_value()) {
455  VALIDATION_LOG << "Invalid format for render-buffer image.";
456  return;
457  }
458  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
459  {
460  TRACE_EVENT0("impeller", "RenderBufferStorageInitialization");
461  if (type_ == Type::kRenderBufferMultisampled) {
462  gl.RenderbufferStorageMultisampleEXT(
463  GL_RENDERBUFFER, // target
464  4, // samples
465  render_buffer_format.value(), // internal format
466  size.width, // width
467  size.height // height
468  );
469  } else {
470  gl.RenderbufferStorage(
471  GL_RENDERBUFFER, // target
472  render_buffer_format.value(), // internal format
473  size.width, // width
474  size.height // height
475  );
476  }
477  }
478  } break;
479  }
480 }
481 
482 std::optional<GLuint> TextureGLES::GetGLHandle() const {
483  if (!IsValid()) {
484  return std::nullopt;
485  }
486  return reactor_->GetGLHandle(handle_);
487 }
488 
489 bool TextureGLES::Bind() const {
490  auto handle = GetGLHandle();
491  if (!handle.has_value()) {
492  return false;
493  }
494  const auto& gl = reactor_->GetProcTable();
495 
496  if (fence_.has_value()) {
497  std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
498  if (fence.has_value()) {
499  gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
500  }
501  reactor_->CollectHandle(fence_.value());
502  fence_ = std::nullopt;
503  }
504 
505  switch (type_) {
506  case Type::kTexture:
508  const auto target = ToTextureTarget(GetTextureDescriptor().type);
509  if (!target.has_value()) {
510  VALIDATION_LOG << "Could not bind texture of this type.";
511  return false;
512  }
513  gl.BindTexture(target.value(), handle.value());
514  } break;
515  case Type::kRenderBuffer:
517  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
518  break;
519  }
520  InitializeContentsIfNecessary();
521  return true;
522 }
523 
525  for (size_t i = 0; i < slices_initialized_.size(); i++) {
526  slices_initialized_[i] = true;
527  }
528 }
529 
530 void TextureGLES::MarkSliceInitialized(size_t slice) const {
531  slices_initialized_[slice] = true;
532 }
533 
534 bool TextureGLES::IsSliceInitialized(size_t slice) const {
535  return slices_initialized_[slice];
536 }
537 
539  if (!IsValid()) {
540  return false;
541  }
542 
543  auto type = GetTextureDescriptor().type;
544  switch (type) {
546  break;
548  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
549  "supported in the GLES backend.";
550  return false;
552  break;
554  break;
555  }
556 
557  if (!Bind()) {
558  return false;
559  }
560 
561  auto handle = GetGLHandle();
562  if (!handle.has_value()) {
563  return false;
564  }
565 
566  const auto& gl = reactor_->GetProcTable();
567  gl.GenerateMipmap(ToTextureType(type));
568  mipmap_generated_ = true;
569  return true;
570 }
571 
573  return type_;
574 }
575 
577  switch (point) {
579  return GL_COLOR_ATTACHMENT0;
581  return GL_DEPTH_ATTACHMENT;
583  return GL_STENCIL_ATTACHMENT;
584  }
585 }
586 
588  GLenum target,
589  AttachmentType attachment_type) const {
590  if (!IsValid()) {
591  return false;
592  }
593  InitializeContentsIfNecessary();
594  auto handle = GetGLHandle();
595  if (!handle.has_value()) {
596  return false;
597  }
598  const auto& gl = reactor_->GetProcTable();
599 
600  switch (ComputeTypeForBinding(target)) {
601  case Type::kTexture:
602  gl.FramebufferTexture2D(target, // target
603  ToAttachmentType(attachment_type), // attachment
604  GL_TEXTURE_2D, // textarget
605  handle.value(), // texture
606  0 // level
607  );
608  break;
610  gl.FramebufferTexture2DMultisampleEXT(
611  target, // target
612  ToAttachmentType(attachment_type), // attachment
613  GL_TEXTURE_2D, // textarget
614  handle.value(), // texture
615  0, // level
616  4 // samples
617  );
618  break;
619  case Type::kRenderBuffer:
621  gl.FramebufferRenderbuffer(
622  target, // target
623  ToAttachmentType(attachment_type), // attachment
624  GL_RENDERBUFFER, // render-buffer target
625  handle.value() // render-buffer
626  );
627  break;
628  }
629 
630  return true;
631 }
632 
633 // |Texture|
634 Scalar TextureGLES::GetYCoordScale() const {
635  switch (GetCoordinateSystem()) {
637  return 1.0;
639  return -1.0;
640  }
641  FML_UNREACHABLE();
642 }
643 
645  return is_wrapped_;
646 }
647 
648 std::optional<GLuint> TextureGLES::GetFBO() const {
649  return wrapped_fbo_;
650 }
651 
653  FML_DCHECK(!fence_.has_value());
654  fence_ = fence;
655 }
656 
657 // Visible for testing.
658 std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
659  return fence_;
660 }
661 
662 void TextureGLES::SetCachedFBO(GLuint fbo) {
663  cached_fbo_ = fbo;
664 }
665 
667  return cached_fbo_;
668 }
669 
670 } // 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.
std::optional< HandleGLES > GetSyncFence() const
bool IsSliceInitialized(size_t slice) const
bool IsValid() const override
void SetCachedFBO(GLuint fbo)
void SetFence(HandleGLES fence)
Attach a sync fence to this texture that will be waited on before encoding a rendering operation that...
GLuint GetCachedFBO() const
Retrieve the cached FBO object, or GL_NONE if there is no object.
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
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:174
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:63
GLenum external_format
Definition: texture_gles.cc:62
GLint internal_format
Definition: texture_gles.cc:61
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:64
#define VALIDATION_LOG
Definition: validation.h:91