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"
15 #include "impeller/core/formats.h"
18 
19 namespace impeller {
20 
21 namespace {
22 static bool IsDepthStencilFormat(PixelFormat format) {
23  switch (format) {
27  return true;
41  return false;
42  }
43  FML_UNREACHABLE();
44 }
45 
46 static TextureGLES::Type GetTextureTypeFromDescriptor(
47  const TextureDescriptor& desc) {
48  const auto usage = static_cast<TextureUsageMask>(desc.usage);
49  const auto render_target = TextureUsage::kRenderTarget;
50  const auto is_msaa = desc.sample_count == SampleCount::kCount4;
51  if (usage == render_target && IsDepthStencilFormat(desc.format)) {
54  }
57 }
58 
59 struct TexImage2DData {
60  GLint internal_format = 0;
61  GLenum external_format = GL_NONE;
62  GLenum type = GL_NONE;
63  std::shared_ptr<const fml::Mapping> data;
64 
65  explicit TexImage2DData(PixelFormat pixel_format) {
66  switch (pixel_format) {
68  internal_format = GL_ALPHA;
69  external_format = GL_ALPHA;
70  type = GL_UNSIGNED_BYTE;
71  break;
73  internal_format = GL_RED;
74  external_format = GL_RED;
75  type = GL_UNSIGNED_BYTE;
76  break;
81  internal_format = GL_RGBA;
82  external_format = GL_RGBA;
83  type = GL_UNSIGNED_BYTE;
84  break;
86  internal_format = GL_RGBA;
87  external_format = GL_RGBA;
88  type = GL_FLOAT;
89  break;
91  internal_format = GL_RGBA;
92  external_format = GL_RGBA;
93  type = GL_HALF_FLOAT;
94  break;
96  // Pure stencil textures are only available in OpenGL 4.4+, which is
97  // ~0% of mobile devices. Instead, we use a depth-stencil texture and
98  // only use the stencil component.
99  //
100  // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
102  internal_format = GL_DEPTH_STENCIL;
103  external_format = GL_DEPTH_STENCIL;
104  type = GL_UNSIGNED_INT_24_8;
105  break;
112  return;
113  }
114  is_valid_ = true;
115  }
116 
117  TexImage2DData(PixelFormat pixel_format,
118  std::shared_ptr<const fml::Mapping> mapping)
119  : TexImage2DData(pixel_format) {
120  data = std::move(mapping);
121  }
122 
123  bool IsValid() const { return is_valid_; }
124 
125  private:
126  bool is_valid_ = false;
127 };
128 } // namespace
129 
131  switch (type) {
134  return HandleType::kTexture;
138  }
139  FML_UNREACHABLE();
140 }
141 
143  : TextureGLES(std::move(reactor), desc, false, std::nullopt, std::nullopt) {
144 }
145 
147  TextureDescriptor desc,
148  enum IsWrapped wrapped)
149  : TextureGLES(std::move(reactor), desc, true, std::nullopt, std::nullopt) {}
150 
152  TextureDescriptor desc,
153  HandleGLES external_handle)
154  : TextureGLES(std::move(reactor),
155  desc,
156  true,
157  std::nullopt,
158  external_handle) {}
159 
160 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(ReactorGLES::Ref reactor,
161  TextureDescriptor desc,
162  GLuint fbo) {
163  return std::shared_ptr<TextureGLES>(
164  new TextureGLES(std::move(reactor), desc, true, fbo, std::nullopt));
165 }
166 
167 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
168  TextureDescriptor desc,
169  bool is_wrapped,
170  std::optional<GLuint> fbo,
171  std::optional<HandleGLES> external_handle)
172  : Texture(desc),
173  reactor_(std::move(reactor)),
174  type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
175  handle_(external_handle.has_value()
176  ? external_handle.value()
177  : reactor_->CreateHandle(ToHandleType(type_))),
178  is_wrapped_(is_wrapped),
179  wrapped_fbo_(fbo) {
180  // Ensure the texture descriptor itself is valid.
181  if (!GetTextureDescriptor().IsValid()) {
182  VALIDATION_LOG << "Invalid texture descriptor.";
183  return;
184  }
185  // Ensure the texture doesn't exceed device capabilities.
186  const auto tex_size = GetTextureDescriptor().size;
187  const auto max_size =
188  reactor_->GetProcTable().GetCapabilities()->max_texture_size;
189  if (tex_size.Max(max_size) != max_size) {
190  VALIDATION_LOG << "Texture of size " << tex_size
191  << " would exceed max supported size of " << max_size << ".";
192  return;
193  }
194 
195  is_valid_ = true;
196 }
197 
198 // |Texture|
200  reactor_->CollectHandle(handle_);
201 }
202 
203 // |Texture|
204 bool TextureGLES::IsValid() const {
205  return is_valid_;
206 }
207 
208 // |Texture|
209 void TextureGLES::SetLabel(std::string_view label) {
210  reactor_->SetDebugLabel(handle_, std::string{label.data(), label.size()});
211 }
212 
213 // |Texture|
214 bool TextureGLES::OnSetContents(const uint8_t* contents,
215  size_t length,
216  size_t slice) {
217  return OnSetContents(CreateMappingWithCopy(contents, Bytes{length}), slice);
218 }
219 
220 // |Texture|
221 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
222  size_t slice) {
223  if (!mapping) {
224  return false;
225  }
226 
227  if (mapping->GetSize() == 0u) {
228  return true;
229  }
230 
231  if (mapping->GetMapping() == nullptr) {
232  return false;
233  }
234 
235  if (GetType() != Type::kTexture) {
236  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
237  "this texture object.";
238  return false;
239  }
240 
241  if (is_wrapped_) {
242  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
243  return false;
244  }
245 
246  const auto& tex_descriptor = GetTextureDescriptor();
247 
248  if (tex_descriptor.size.IsEmpty()) {
249  return true;
250  }
251 
252  if (!tex_descriptor.IsValid() ||
253  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
254  return false;
255  }
256 
257  GLenum texture_type;
258  GLenum texture_target;
259  switch (tex_descriptor.type) {
261  texture_type = GL_TEXTURE_2D;
262  texture_target = GL_TEXTURE_2D;
263  break;
265  VALIDATION_LOG << "Multisample texture uploading is not supported for "
266  "the OpenGLES backend.";
267  return false;
269  texture_type = GL_TEXTURE_CUBE_MAP;
270  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
271  break;
273  texture_type = GL_TEXTURE_EXTERNAL_OES;
274  texture_target = GL_TEXTURE_EXTERNAL_OES;
275  break;
276  }
277 
278  auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
279  std::move(mapping));
280  if (!data || !data->IsValid()) {
281  VALIDATION_LOG << "Invalid texture format.";
282  return false;
283  }
284 
285  ReactorGLES::Operation texture_upload = [handle = handle_, //
286  data, //
287  size = tex_descriptor.size, //
288  texture_type, //
289  texture_target //
290  ](const auto& reactor) {
291  auto gl_handle = reactor.GetGLHandle(handle);
292  if (!gl_handle.has_value()) {
294  << "Texture was collected before it could be uploaded to the GPU.";
295  return;
296  }
297  const auto& gl = reactor.GetProcTable();
298  gl.BindTexture(texture_type, gl_handle.value());
299  const GLvoid* tex_data = nullptr;
300  if (data->data) {
301  tex_data = data->data->GetMapping();
302  }
303 
304  {
305  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
306  std::to_string(data->data->GetSize()).c_str());
307  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
308  gl.TexImage2D(texture_target, // target
309  0u, // LOD level
310  data->internal_format, // internal format
311  size.width, // width
312  size.height, // height
313  0u, // border
314  data->external_format, // external format
315  data->type, // type
316  tex_data // data
317  );
318  }
319  };
320 
321  slices_initialized_ = reactor_->AddOperation(texture_upload);
322  return slices_initialized_[0];
323 }
324 
325 // |Texture|
326 ISize TextureGLES::GetSize() const {
327  return GetTextureDescriptor().size;
328 }
329 
330 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
331  switch (format) {
334  return GL_RGBA4;
336  return GL_RGBA32F;
338  return GL_RGBA16F;
340  return GL_STENCIL_INDEX8;
342  return GL_DEPTH24_STENCIL8;
344  return GL_DEPTH32F_STENCIL8;
354  return std::nullopt;
355  }
356  FML_UNREACHABLE();
357 }
358 
359 void TextureGLES::InitializeContentsIfNecessary() const {
360  if (!IsValid() || slices_initialized_[0]) {
361  return;
362  }
363  slices_initialized_[0] = true;
364 
365  if (is_wrapped_) {
366  return;
367  }
368 
369  auto size = GetSize();
370 
371  if (size.IsEmpty()) {
372  return;
373  }
374 
375  const auto& gl = reactor_->GetProcTable();
376  auto handle = reactor_->GetGLHandle(handle_);
377  if (!handle.has_value()) {
378  VALIDATION_LOG << "Could not initialize the contents of texture.";
379  return;
380  }
381 
382  switch (type_) {
383  case Type::kTexture:
385  TexImage2DData tex_data(GetTextureDescriptor().format);
386  if (!tex_data.IsValid()) {
387  VALIDATION_LOG << "Invalid format for texture image.";
388  return;
389  }
390  gl.BindTexture(GL_TEXTURE_2D, handle.value());
391  {
392  TRACE_EVENT0("impeller", "TexImage2DInitialization");
393  gl.TexImage2D(GL_TEXTURE_2D, // target
394  0u, // LOD level (base mip level size checked)
395  tex_data.internal_format, // internal format
396  size.width, // width
397  size.height, // height
398  0u, // border
399  tex_data.external_format, // format
400  tex_data.type, // type
401  nullptr // data
402  );
403  }
404  } break;
405  case Type::kRenderBuffer:
407  auto render_buffer_format =
409  if (!render_buffer_format.has_value()) {
410  VALIDATION_LOG << "Invalid format for render-buffer image.";
411  return;
412  }
413  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
414  {
415  TRACE_EVENT0("impeller", "RenderBufferStorageInitialization");
416  if (type_ == Type::kRenderBufferMultisampled) {
417  gl.RenderbufferStorageMultisampleEXT(
418  GL_RENDERBUFFER, // target
419  4, // samples
420  render_buffer_format.value(), // internal format
421  size.width, // width
422  size.height // height
423  );
424  } else {
425  gl.RenderbufferStorage(
426  GL_RENDERBUFFER, // target
427  render_buffer_format.value(), // internal format
428  size.width, // width
429  size.height // height
430  );
431  }
432  }
433  } break;
434  }
435 }
436 
437 std::optional<GLuint> TextureGLES::GetGLHandle() const {
438  if (!IsValid()) {
439  return std::nullopt;
440  }
441  return reactor_->GetGLHandle(handle_);
442 }
443 
444 bool TextureGLES::Bind() const {
445  auto handle = GetGLHandle();
446  if (!handle.has_value()) {
447  return false;
448  }
449  const auto& gl = reactor_->GetProcTable();
450  switch (type_) {
451  case Type::kTexture:
453  const auto target = ToTextureTarget(GetTextureDescriptor().type);
454  if (!target.has_value()) {
455  VALIDATION_LOG << "Could not bind texture of this type.";
456  return false;
457  }
458  gl.BindTexture(target.value(), handle.value());
459  } break;
460  case Type::kRenderBuffer:
462  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
463  break;
464  }
465  InitializeContentsIfNecessary();
466  return true;
467 }
468 
469 void TextureGLES::MarkSliceInitialized(size_t slice) const {
470  slices_initialized_[slice] = true;
471 }
472 
473 bool TextureGLES::IsSliceInitialized(size_t slice) const {
474  return slices_initialized_[slice];
475 }
476 
478  if (!IsValid()) {
479  return false;
480  }
481 
482  auto type = GetTextureDescriptor().type;
483  switch (type) {
485  break;
487  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
488  "supported in the GLES backend.";
489  return false;
491  break;
493  break;
494  }
495 
496  if (!Bind()) {
497  return false;
498  }
499 
500  auto handle = GetGLHandle();
501  if (!handle.has_value()) {
502  return false;
503  }
504 
505  const auto& gl = reactor_->GetProcTable();
506  gl.GenerateMipmap(ToTextureType(type));
507  mipmap_generated_ = true;
508  return true;
509 }
510 
512  return type_;
513 }
514 
516  switch (point) {
518  return GL_COLOR_ATTACHMENT0;
520  return GL_DEPTH_ATTACHMENT;
522  return GL_STENCIL_ATTACHMENT;
523  }
524 }
525 
527  GLenum target,
528  AttachmentType attachment_type) const {
529  if (!IsValid()) {
530  return false;
531  }
532  InitializeContentsIfNecessary();
533  auto handle = GetGLHandle();
534  if (!handle.has_value()) {
535  return false;
536  }
537  const auto& gl = reactor_->GetProcTable();
538 
539  switch (type_) {
540  case Type::kTexture:
541  gl.FramebufferTexture2D(target, // target
542  ToAttachmentType(attachment_type), // attachment
543  GL_TEXTURE_2D, // textarget
544  handle.value(), // texture
545  0 // level
546  );
547  break;
549  gl.FramebufferTexture2DMultisampleEXT(
550  target, // target
551  ToAttachmentType(attachment_type), // attachment
552  GL_TEXTURE_2D, // textarget
553  handle.value(), // texture
554  0, // level
555  4 // samples
556  );
557  break;
558  case Type::kRenderBuffer:
560  gl.FramebufferRenderbuffer(
561  target, // target
562  ToAttachmentType(attachment_type), // attachment
563  GL_RENDERBUFFER, // render-buffer target
564  handle.value() // render-buffer
565  );
566  break;
567  }
568 
569  return true;
570 }
571 
572 // |Texture|
573 Scalar TextureGLES::GetYCoordScale() const {
574  switch (GetCoordinateSystem()) {
576  return 1.0;
578  return -1.0;
579  }
580  FML_UNREACHABLE();
581 }
582 
583 bool TextureGLES::IsWrapped() const {
584  return is_wrapped_;
585 }
586 
587 std::optional<GLuint> TextureGLES::GetFBO() const {
588  return wrapped_fbo_;
589 }
590 
591 } // namespace impeller
impeller::ReactorGLES::Operation
std::function< void(const ReactorGLES &reactor)> Operation
Definition: reactor_gles.h:195
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::TextureType::kTextureExternalOES
@ kTextureExternalOES
impeller::HandleType::kRenderBuffer
@ kRenderBuffer
impeller::TextureGLES::Type
Type
Definition: texture_gles.h:20
impeller::TextureGLES::Type::kTextureMultisampled
@ kTextureMultisampled
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::TextureGLES::GenerateMipmap
bool GenerateMipmap()
Definition: texture_gles.cc:477
impeller::PixelFormat::kB10G10R10A10XR
@ kB10G10R10A10XR
impeller::PixelFormat::kB8G8R8A8UNormIntSRGB
@ kB8G8R8A8UNormIntSRGB
impeller::TextureGLES::AttachmentType::kDepth
@ kDepth
impeller::PixelFormat::kA8UNormInt
@ kA8UNormInt
impeller::TextureGLES::GetFBO
std::optional< GLuint > GetFBO() const
Definition: texture_gles.cc:587
allocation.h
impeller::PixelFormat::kR8UNormInt
@ kR8UNormInt
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::TextureGLES::MarkSliceInitialized
void MarkSliceInitialized(size_t slice) const
Definition: texture_gles.cc:469
impeller::ReactorGLES::Ref
std::shared_ptr< ReactorGLES > Ref
Definition: reactor_gles.h:86
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::Texture::GetTextureDescriptor
const TextureDescriptor & GetTextureDescriptor() const
Definition: texture.cc:57
impeller::HandleType
HandleType
Definition: handle_gles.h:18
impeller::TextureGLES::GetGLHandle
std::optional< GLuint > GetGLHandle() const
Definition: texture_gles.cc:437
texture_descriptor.h
impeller::HandleType::kTexture
@ kTexture
texture_gles.h
formats.h
impeller::TextureGLES::Type::kRenderBufferMultisampled
@ kRenderBufferMultisampled
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::TextureGLES::IsWrapped
IsWrapped
Definition: texture_gles.h:27
impeller::TextureGLES::AttachmentType::kColor0
@ kColor0
validation.h
impeller::TextureGLES::IsValid
bool IsValid() const override
Definition: texture_gles.cc:204
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::TextureCoordinateSystem::kUploadFromHost
@ kUploadFromHost
impeller::TextureDescriptor::type
TextureType type
Definition: texture_descriptor.h:40
impeller::TextureGLES::IsSliceInitialized
bool IsSliceInitialized(size_t slice) const
Definition: texture_gles.cc:473
impeller::TextureType::kTexture2DMultisample
@ kTexture2DMultisample
impeller::TextureCoordinateSystem::kRenderToTexture
@ kRenderToTexture
impeller::Texture
Definition: texture.h:17
impeller::TextureGLES::Type::kRenderBuffer
@ kRenderBuffer
impeller::TextureGLES::TextureGLES
TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc)
Definition: texture_gles.cc:142
type
GLenum type
Definition: texture_gles.cc:62
impeller::TextureGLES::WrapFBO
static std::shared_ptr< TextureGLES > WrapFBO(ReactorGLES::Ref reactor, TextureDescriptor desc, GLuint fbo)
Definition: texture_gles.cc:160
impeller::PixelFormat::kR8G8UNormInt
@ kR8G8UNormInt
external_format
GLenum external_format
Definition: texture_gles.cc:61
impeller::PixelFormat::kB10G10R10XR
@ kB10G10R10XR
impeller::PixelFormat::kD24UnormS8Uint
@ kD24UnormS8Uint
impeller::HandleGLES
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition: handle_gles.h:36
impeller::TextureType::kTextureCube
@ kTextureCube
impeller::PixelFormat::kR16G16B16A16Float
@ kR16G16B16A16Float
impeller::ToHandleType
HandleType ToHandleType(TextureGLES::Type type)
Definition: texture_gles.cc:130
internal_format
GLint internal_format
Definition: texture_gles.cc:60
impeller::ToTextureType
constexpr GLenum ToTextureType(TextureType type)
Definition: formats_gles.h:171
impeller::Texture::mipmap_generated_
bool mipmap_generated_
Definition: texture.h:70
impeller::TextureUsageMask
Mask< TextureUsage > TextureUsageMask
Definition: formats.h:308
impeller::TextureGLES::AttachmentType::kStencil
@ kStencil
impeller::TextureType::kTexture2D
@ kTexture2D
impeller::PixelFormat::kR32G32B32A32Float
@ kR32G32B32A32Float
impeller::PixelFormat::kUnknown
@ kUnknown
impeller::CreateMappingWithCopy
std::shared_ptr< fml::Mapping > CreateMappingWithCopy(const uint8_t *contents, Bytes length)
Creates a mapping with copy of the bytes.
Definition: allocation.cc:83
formats_gles.h
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
impeller::TextureGLES::GetType
Type GetType() const
Definition: texture_gles.cc:511
impeller::TextureGLES::~TextureGLES
~TextureGLES() override
Definition: texture_gles.cc:199
impeller::PixelFormat::kR8G8B8A8UNormIntSRGB
@ kR8G8B8A8UNormIntSRGB
std
Definition: comparable.h:95
impeller::TextureGLES::AttachmentType
AttachmentType
Definition: texture_gles.h:57
impeller::SampleCount::kCount4
@ kCount4
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::ToTextureTarget
constexpr std::optional< GLenum > ToTextureTarget(TextureType type)
Definition: formats_gles.h:185
impeller::PixelFormat::kB10G10R10XRSRGB
@ kB10G10R10XRSRGB
impeller::TextureGLES
Definition: texture_gles.h:17
impeller::TextureGLES::Bind
bool Bind() const
Definition: texture_gles.cc:444
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller::TextureGLES::Type::kTexture
@ kTexture
impeller::Texture::GetCoordinateSystem
TextureCoordinateSystem GetCoordinateSystem() const
Definition: texture.cc:77
impeller
Definition: allocation.cc:12
impeller::ToRenderBufferFormat
static std::optional< GLenum > ToRenderBufferFormat(PixelFormat format)
Definition: texture_gles.cc:330
impeller::ToAttachmentType
static GLenum ToAttachmentType(TextureGLES::AttachmentType point)
Definition: texture_gles.cc:515
impeller::Bytes
AllocationSize< 1u > Bytes
Definition: allocation_size.h:151
impeller::TextureGLES::SetAsFramebufferAttachment
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type) const
Definition: texture_gles.cc:526