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"
19 
20 namespace impeller {
21 
22 namespace {
23 static bool IsDepthStencilFormat(PixelFormat format) {
24  switch (format) {
28  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 } // namespace
65 
67  switch (type) {
70  return HandleType::kTexture;
74  }
75  FML_UNREACHABLE();
76 }
77 
78 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
79  std::shared_ptr<ReactorGLES> reactor,
80  TextureDescriptor desc,
81  GLuint fbo) {
82  auto texture = std::shared_ptr<TextureGLES>(
83  new TextureGLES(std::move(reactor), desc, false, fbo, std::nullopt));
84  if (!texture->IsValid()) {
85  return nullptr;
86  }
87  return texture;
88 }
89 
90 std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
91  std::shared_ptr<ReactorGLES> reactor,
92  TextureDescriptor desc,
93  HandleGLES external_handle) {
94  if (external_handle.IsDead()) {
95  VALIDATION_LOG << "Cannot wrap a dead handle.";
96  return nullptr;
97  }
98  if (external_handle.GetType() != HandleType::kTexture) {
99  VALIDATION_LOG << "Cannot wrap a non-texture handle.";
100  return nullptr;
101  }
102  auto texture = std::shared_ptr<TextureGLES>(new TextureGLES(
103  std::move(reactor), desc, false, std::nullopt, external_handle));
104  if (!texture->IsValid()) {
105  return nullptr;
106  }
107  return texture;
108 }
109 
110 std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
111  std::shared_ptr<ReactorGLES> reactor,
112  TextureDescriptor desc) {
113  return TextureGLES::WrapFBO(std::move(reactor), desc, 0u);
114 }
115 
116 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
117  TextureDescriptor desc,
118  bool threadsafe)
119  : TextureGLES(std::move(reactor), //
120  desc, //
121  threadsafe, //
122  std::nullopt, //
123  std::nullopt //
124  ) {}
125 
126 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
127  TextureDescriptor desc,
128  bool threadsafe,
129  std::optional<GLuint> fbo,
130  std::optional<HandleGLES> external_handle)
131  : Texture(desc),
132  reactor_(std::move(reactor)),
133  type_(GetTextureTypeFromDescriptor(
134  GetTextureDescriptor(),
135  reactor_->GetProcTable().GetCapabilities())),
136  handle_(external_handle.has_value()
137  ? external_handle.value()
138  : (threadsafe ? reactor_->CreateHandle(ToHandleType(type_))
139  : reactor_->CreateUntrackedHandle(
140  ToHandleType(type_)))),
141  is_wrapped_(fbo.has_value() || external_handle.has_value()),
142  wrapped_fbo_(fbo) {
143  // Ensure the texture descriptor itself is valid.
144  if (!GetTextureDescriptor().IsValid()) {
145  return;
146  }
147  // Ensure the texture doesn't exceed device capabilities.
148  const auto tex_size = GetTextureDescriptor().size;
149  const auto max_size =
150  reactor_->GetProcTable().GetCapabilities()->max_texture_size;
151  if (tex_size.Max(max_size) != max_size) {
152  VALIDATION_LOG << "Texture of size " << tex_size
153  << " would exceed max supported size of " << max_size << ".";
154  return;
155  }
156 
157  is_valid_ = true;
158 }
159 
160 // |Texture|
162  reactor_->CollectHandle(handle_);
163  if (!cached_fbo_.IsDead()) {
164  reactor_->CollectHandle(cached_fbo_);
165  }
166 }
167 
169  handle_ = HandleGLES::DeadHandle();
170 }
171 
172 // |Texture|
173 bool TextureGLES::IsValid() const {
174  return is_valid_;
175 }
176 
177 // |Texture|
178 void TextureGLES::SetLabel(std::string_view label) {
179 #ifdef IMPELLER_DEBUG
180  reactor_->SetDebugLabel(handle_, label);
181 #endif // IMPELLER_DEBUG
182 }
183 
184 // |Texture|
185 void TextureGLES::SetLabel(std::string_view label, std::string_view trailing) {
186 #ifdef IMPELLER_DEBUG
187  if (reactor_->CanSetDebugLabels()) {
188  reactor_->SetDebugLabel(handle_, std::format("{} {}", label, trailing));
189  }
190 #endif // IMPELLER_DEBUG
191 }
192 
193 // |Texture|
194 bool TextureGLES::OnSetContents(const uint8_t* contents,
195  size_t length,
196  size_t slice) {
197  return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
198 }
199 
200 // |Texture|
201 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
202  size_t slice) {
203  if (!mapping) {
204  return false;
205  }
206 
207  if (mapping->GetSize() == 0u) {
208  return true;
209  }
210 
211  if (mapping->GetMapping() == nullptr) {
212  return false;
213  }
214 
215  if (GetType() != Type::kTexture) {
216  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
217  "this texture object.";
218  return false;
219  }
220 
221  if (is_wrapped_) {
222  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
223  return false;
224  }
225 
226  const auto& tex_descriptor = GetTextureDescriptor();
227 
228  if (tex_descriptor.size.IsEmpty()) {
229  return true;
230  }
231 
232  if (!tex_descriptor.IsValid() ||
233  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
234  return false;
235  }
236 
237  GLenum texture_type;
238  GLenum texture_target;
239  switch (tex_descriptor.type) {
241  texture_type = GL_TEXTURE_2D;
242  texture_target = GL_TEXTURE_2D;
243  break;
245  VALIDATION_LOG << "Multisample texture uploading is not supported for "
246  "the OpenGLES backend.";
247  return false;
249  texture_type = GL_TEXTURE_CUBE_MAP;
250  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
251  break;
253  texture_type = GL_TEXTURE_EXTERNAL_OES;
254  texture_target = GL_TEXTURE_EXTERNAL_OES;
255  break;
256  }
257 
258  std::optional<PixelFormatGLES> gles_format =
259  ToPixelFormatGLES(tex_descriptor.format,
260  /*supports_bgra=*/
261  reactor_->GetProcTable().GetDescription()->HasExtension(
262  "GL_EXT_texture_format_BGRA8888"));
263  if (!gles_format.has_value()) {
264  VALIDATION_LOG << "Invalid texture format.";
265  return false;
266  }
267 
268  ReactorGLES::Operation texture_upload = [handle = handle_, //
269  mapping, //
270  format = gles_format.value(), //
271  size = tex_descriptor.size, //
272  texture_type, //
273  texture_target //
274  ](const auto& reactor) {
275  auto gl_handle = reactor.GetGLHandle(handle);
276  if (!gl_handle.has_value()) {
278  << "Texture was collected before it could be uploaded to the GPU.";
279  return;
280  }
281  const auto& gl = reactor.GetProcTable();
282  gl.BindTexture(texture_type, gl_handle.value());
283  const GLvoid* tex_data = nullptr;
284  if (mapping) {
285  tex_data = mapping->GetMapping();
286  }
287 
288  {
289  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
290  std::to_string(mapping->GetSize()).c_str());
291  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
292  gl.TexImage2D(texture_target, // target
293  0u, // LOD level
294  format.internal_format, // internal format
295  size.width, // width
296  size.height, // height
297  0u, // border
298  format.external_format, // format
299  format.type, // type
300  tex_data); // data
301  }
302  };
303 
304  slices_initialized_ = reactor_->AddOperation(texture_upload);
305  return slices_initialized_[0];
306 }
307 
308 // |Texture|
309 ISize TextureGLES::GetSize() const {
310  return GetTextureDescriptor().size;
311 }
312 
313 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
314  switch (format) {
317  return GL_RGBA8;
319  return GL_RGBA32F;
321  return GL_RGBA16F;
323  return GL_STENCIL_INDEX8;
325  return GL_DEPTH24_STENCIL8;
327  return GL_DEPTH32F_STENCIL8;
338  return std::nullopt;
339  }
340  FML_UNREACHABLE();
341 }
342 
344  // When binding to a GL_READ_FRAMEBUFFER, any multisampled
345  // textures must be bound as single sampled.
346  if (target == GL_READ_FRAMEBUFFER && type_ == Type::kTextureMultisampled) {
347  return Type::kTexture;
348  }
349  return type_;
350 }
351 
352 void TextureGLES::InitializeContentsIfNecessary() const {
353  if (!IsValid() || slices_initialized_[0]) {
354  return;
355  }
356  slices_initialized_[0] = true;
357 
358  if (is_wrapped_) {
359  return;
360  }
361 
362  auto size = GetSize();
363 
364  if (size.IsEmpty()) {
365  return;
366  }
367 
368  const auto& gl = reactor_->GetProcTable();
369  std::optional<GLuint> handle = reactor_->GetGLHandle(handle_);
370  if (!handle.has_value()) {
371  VALIDATION_LOG << "Could not initialize the contents of texture.";
372  return;
373  }
374 
375  switch (type_) {
376  case Type::kTexture:
378  std::optional<PixelFormatGLES> gles_format = ToPixelFormatGLES(
379  GetTextureDescriptor().format,
380  /*supports_bgra=*/
381  reactor_->GetProcTable().GetDescription()->HasExtension(
382  "GL_EXT_texture_format_BGRA8888"));
383  if (!gles_format.has_value()) {
384  VALIDATION_LOG << "Invalid format for texture image.";
385  return;
386  }
387  gl.BindTexture(GL_TEXTURE_2D, handle.value());
388  {
389  TRACE_EVENT0("impeller", "TexImage2DInitialization");
390  gl.TexImage2D(GL_TEXTURE_2D, // target
391  0u, // LOD level (base mip level size checked)
392  gles_format->internal_format, // internal format
393  size.width, // width
394  size.height, // height
395  0u, // border
396  gles_format->external_format, // format
397  gles_format->type, // type
398  nullptr // data
399  );
400  }
401  } break;
402  case Type::kRenderBuffer:
404  auto render_buffer_format =
406  if (!render_buffer_format.has_value()) {
407  VALIDATION_LOG << "Invalid format for render-buffer image.";
408  return;
409  }
410  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
411  {
412  if (type_ == Type::kRenderBufferMultisampled) {
413  // BEWARE: these functions are not at all equivalent! the extensions
414  // are from EXT_multisampled_render_to_texture and cannot be used
415  // with regular GLES 3.0 multisampled renderbuffers/textures.
416  if (gl.GetCapabilities()->SupportsImplicitResolvingMSAA()) {
417  gl.RenderbufferStorageMultisampleEXT(
418  /*target=*/GL_RENDERBUFFER, //
419  /*samples=*/4, //
420  /*internal_format=*/render_buffer_format.value(), //
421  /*width=*/size.width, //
422  /*height=*/size.height //
423  );
424  } else {
425  gl.RenderbufferStorageMultisample(
426  /*target=*/GL_RENDERBUFFER, //
427  /*samples=*/4, //
428  /*internal_format=*/render_buffer_format.value(), //
429  /*width=*/size.width, //
430  /*height=*/size.height //
431  );
432  }
433  } else {
434  gl.RenderbufferStorage(
435  /*target=*/GL_RENDERBUFFER, //
436  /*internal_format=*/render_buffer_format.value(), //
437  /*width=*/size.width, //
438  /*height=*/size.height //
439  );
440  }
441  }
442  } break;
443  }
444 }
445 
446 std::optional<GLuint> TextureGLES::GetGLHandle() const {
447  if (!IsValid()) {
448  return std::nullopt;
449  }
450  return reactor_->GetGLHandle(handle_);
451 }
452 
453 bool TextureGLES::Bind() const {
454  auto handle = GetGLHandle();
455  if (!handle.has_value()) {
456  return false;
457  }
458  const auto& gl = reactor_->GetProcTable();
459 
460  if (fence_.has_value()) {
461  std::optional<GLsync> fence = reactor_->GetGLFence(fence_.value());
462  if (fence.has_value()) {
463  gl.WaitSync(fence.value(), 0, GL_TIMEOUT_IGNORED);
464  }
465  reactor_->CollectHandle(fence_.value());
466  fence_ = std::nullopt;
467  }
468 
469  switch (type_) {
470  case Type::kTexture:
472  const auto target = ToTextureTarget(GetTextureDescriptor().type);
473  if (!target.has_value()) {
474  VALIDATION_LOG << "Could not bind texture of this type.";
475  return false;
476  }
477  gl.BindTexture(target.value(), handle.value());
478  } break;
479  case Type::kRenderBuffer:
481  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
482  break;
483  }
484  InitializeContentsIfNecessary();
485  return true;
486 }
487 
489  for (size_t i = 0; i < slices_initialized_.size(); i++) {
490  slices_initialized_[i] = true;
491  }
492 }
493 
494 void TextureGLES::MarkSliceInitialized(size_t slice) const {
495  slices_initialized_[slice] = true;
496 }
497 
498 bool TextureGLES::IsSliceInitialized(size_t slice) const {
499  return slices_initialized_[slice];
500 }
501 
503  if (!IsValid()) {
504  return false;
505  }
506 
507  auto type = GetTextureDescriptor().type;
508  switch (type) {
510  break;
512  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
513  "supported in the GLES backend.";
514  return false;
516  break;
518  break;
519  }
520 
521  if (!Bind()) {
522  return false;
523  }
524 
525  auto handle = GetGLHandle();
526  if (!handle.has_value()) {
527  return false;
528  }
529 
530  const auto& gl = reactor_->GetProcTable();
531  gl.GenerateMipmap(ToTextureType(type));
532  mipmap_generated_ = true;
533  return true;
534 }
535 
537  return type_;
538 }
539 
541  switch (point) {
543  return GL_COLOR_ATTACHMENT0;
545  return GL_DEPTH_ATTACHMENT;
547  return GL_STENCIL_ATTACHMENT;
548  }
549 }
550 
552  GLenum target,
553  AttachmentType attachment_type) const {
554  if (!IsValid()) {
555  return false;
556  }
557  InitializeContentsIfNecessary();
558  auto handle = GetGLHandle();
559  if (!handle.has_value()) {
560  return false;
561  }
562  const auto& gl = reactor_->GetProcTable();
563 
564  switch (ComputeTypeForBinding(target)) {
565  case Type::kTexture:
566  gl.FramebufferTexture2D(target, // target
567  ToAttachmentType(attachment_type), // attachment
568  GL_TEXTURE_2D, // textarget
569  handle.value(), // texture
570  0 // level
571  );
572  break;
574  gl.FramebufferTexture2DMultisampleEXT(
575  target, // target
576  ToAttachmentType(attachment_type), // attachment
577  GL_TEXTURE_2D, // textarget
578  handle.value(), // texture
579  0, // level
580  4 // samples
581  );
582  break;
583  case Type::kRenderBuffer:
585  gl.FramebufferRenderbuffer(
586  target, // target
587  ToAttachmentType(attachment_type), // attachment
588  GL_RENDERBUFFER, // render-buffer target
589  handle.value() // render-buffer
590  );
591  break;
592  }
593 
594  return true;
595 }
596 
597 // |Texture|
598 Scalar TextureGLES::GetYCoordScale() const {
599  switch (GetCoordinateSystem()) {
601  return 1.0;
603  return -1.0;
604  }
605  FML_UNREACHABLE();
606 }
607 
609  return is_wrapped_;
610 }
611 
612 std::optional<GLuint> TextureGLES::GetFBO() const {
613  return wrapped_fbo_;
614 }
615 
617  FML_DCHECK(!fence_.has_value());
618  fence_ = fence;
619 }
620 
621 // Visible for testing.
622 std::optional<HandleGLES> TextureGLES::GetSyncFence() const {
623  return fence_;
624 }
625 
627  cached_fbo_ = fbo;
628 }
629 
631  return cached_fbo_;
632 }
633 
634 } // namespace impeller
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition: handle_gles.h:42
constexpr bool IsDead() const
Determines if the handle is dead.
Definition: handle_gles.h:58
HandleType GetType() const
Definition: handle_gles.h:79
static HandleGLES DeadHandle()
Creates a dead handle.
Definition: handle_gles.h:49
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.
Definition: texture_gles.cc:78
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...
Definition: texture_gles.cc:90
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
std::optional< PixelFormatGLES > ToPixelFormatGLES(PixelFormat pixel_format, bool supports_bgra)
Definition: formats_gles.cc:26
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:311
HandleType ToHandleType(TextureGLES::Type type)
Definition: texture_gles.cc:66
Definition: comparable.h:93
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define VALIDATION_LOG
Definition: validation.h:91