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) {}
144 
146  TextureDescriptor desc,
147  enum IsWrapped wrapped)
148  : TextureGLES(std::move(reactor), desc, true, std::nullopt) {}
149 
150 std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(ReactorGLES::Ref reactor,
151  TextureDescriptor desc,
152  GLuint fbo) {
153  return std::shared_ptr<TextureGLES>(
154  new TextureGLES(std::move(reactor), desc, true, fbo));
155 }
156 
157 TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
158  TextureDescriptor desc,
159  bool is_wrapped,
160  std::optional<GLuint> fbo)
161  : Texture(desc),
162  reactor_(std::move(reactor)),
163  type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
164  handle_(reactor_->CreateHandle(ToHandleType(type_))),
165  is_wrapped_(is_wrapped),
166  wrapped_fbo_(fbo) {
167  // Ensure the texture descriptor itself is valid.
168  if (!GetTextureDescriptor().IsValid()) {
169  VALIDATION_LOG << "Invalid texture descriptor.";
170  return;
171  }
172  // Ensure the texture doesn't exceed device capabilities.
173  const auto tex_size = GetTextureDescriptor().size;
174  const auto max_size =
175  reactor_->GetProcTable().GetCapabilities()->max_texture_size;
176  if (tex_size.Max(max_size) != max_size) {
177  VALIDATION_LOG << "Texture of size " << tex_size
178  << " would exceed max supported size of " << max_size << ".";
179  return;
180  }
181 
182  is_valid_ = true;
183 }
184 
185 // |Texture|
187  reactor_->CollectHandle(handle_);
188 }
189 
190 // |Texture|
191 bool TextureGLES::IsValid() const {
192  return is_valid_;
193 }
194 
195 // |Texture|
196 void TextureGLES::SetLabel(std::string_view label) {
197  reactor_->SetDebugLabel(handle_, std::string{label.data(), label.size()});
198 }
199 
200 // |Texture|
201 bool TextureGLES::OnSetContents(const uint8_t* contents,
202  size_t length,
203  size_t slice) {
204  return OnSetContents(CreateMappingWithCopy(contents, length), slice);
205 }
206 
207 // |Texture|
208 bool TextureGLES::OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
209  size_t slice) {
210  if (!mapping) {
211  return false;
212  }
213 
214  if (mapping->GetSize() == 0u) {
215  return true;
216  }
217 
218  if (mapping->GetMapping() == nullptr) {
219  return false;
220  }
221 
222  if (GetType() != Type::kTexture) {
223  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
224  "this texture object.";
225  return false;
226  }
227 
228  if (is_wrapped_) {
229  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
230  return false;
231  }
232 
233  const auto& tex_descriptor = GetTextureDescriptor();
234 
235  if (tex_descriptor.size.IsEmpty()) {
236  return true;
237  }
238 
239  if (!tex_descriptor.IsValid() ||
240  mapping->GetSize() < tex_descriptor.GetByteSizeOfBaseMipLevel()) {
241  return false;
242  }
243 
244  GLenum texture_type;
245  GLenum texture_target;
246  switch (tex_descriptor.type) {
248  texture_type = GL_TEXTURE_2D;
249  texture_target = GL_TEXTURE_2D;
250  break;
252  VALIDATION_LOG << "Multisample texture uploading is not supported for "
253  "the OpenGLES backend.";
254  return false;
256  texture_type = GL_TEXTURE_CUBE_MAP;
257  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
258  break;
260  texture_type = GL_TEXTURE_EXTERNAL_OES;
261  texture_target = GL_TEXTURE_EXTERNAL_OES;
262  break;
263  }
264 
265  auto data = std::make_shared<TexImage2DData>(tex_descriptor.format,
266  std::move(mapping));
267  if (!data || !data->IsValid()) {
268  VALIDATION_LOG << "Invalid texture format.";
269  return false;
270  }
271 
272  ReactorGLES::Operation texture_upload = [handle = handle_, //
273  data, //
274  size = tex_descriptor.size, //
275  texture_type, //
276  texture_target //
277  ](const auto& reactor) {
278  auto gl_handle = reactor.GetGLHandle(handle);
279  if (!gl_handle.has_value()) {
281  << "Texture was collected before it could be uploaded to the GPU.";
282  return;
283  }
284  const auto& gl = reactor.GetProcTable();
285  gl.BindTexture(texture_type, gl_handle.value());
286  const GLvoid* tex_data = nullptr;
287  if (data->data) {
288  tex_data = data->data->GetMapping();
289  }
290 
291  {
292  TRACE_EVENT1("impeller", "TexImage2DUpload", "Bytes",
293  std::to_string(data->data->GetSize()).c_str());
294  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
295  gl.TexImage2D(texture_target, // target
296  0u, // LOD level
297  data->internal_format, // internal format
298  size.width, // width
299  size.height, // height
300  0u, // border
301  data->external_format, // external format
302  data->type, // type
303  tex_data // data
304  );
305  }
306  };
307 
308  slices_initialized_ = reactor_->AddOperation(texture_upload);
309  return slices_initialized_[0];
310 }
311 
312 // |Texture|
313 ISize TextureGLES::GetSize() const {
314  return GetTextureDescriptor().size;
315 }
316 
317 static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
318  switch (format) {
321  return GL_RGBA4;
323  return GL_RGBA32F;
325  return GL_RGBA16F;
327  return GL_STENCIL_INDEX8;
329  return GL_DEPTH24_STENCIL8;
331  return GL_DEPTH32F_STENCIL8;
341  return std::nullopt;
342  }
343  FML_UNREACHABLE();
344 }
345 
346 void TextureGLES::InitializeContentsIfNecessary() const {
347  if (!IsValid() || slices_initialized_[0]) {
348  return;
349  }
350  slices_initialized_[0] = true;
351 
352  if (is_wrapped_) {
353  return;
354  }
355 
356  auto size = GetSize();
357 
358  if (size.IsEmpty()) {
359  return;
360  }
361 
362  const auto& gl = reactor_->GetProcTable();
363  auto handle = reactor_->GetGLHandle(handle_);
364  if (!handle.has_value()) {
365  VALIDATION_LOG << "Could not initialize the contents of texture.";
366  return;
367  }
368 
369  switch (type_) {
370  case Type::kTexture:
372  TexImage2DData tex_data(GetTextureDescriptor().format);
373  if (!tex_data.IsValid()) {
374  VALIDATION_LOG << "Invalid format for texture image.";
375  return;
376  }
377  gl.BindTexture(GL_TEXTURE_2D, handle.value());
378  {
379  TRACE_EVENT0("impeller", "TexImage2DInitialization");
380  gl.TexImage2D(GL_TEXTURE_2D, // target
381  0u, // LOD level (base mip level size checked)
382  tex_data.internal_format, // internal format
383  size.width, // width
384  size.height, // height
385  0u, // border
386  tex_data.external_format, // format
387  tex_data.type, // type
388  nullptr // data
389  );
390  }
391  } break;
392  case Type::kRenderBuffer:
394  auto render_buffer_format =
396  if (!render_buffer_format.has_value()) {
397  VALIDATION_LOG << "Invalid format for render-buffer image.";
398  return;
399  }
400  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
401  {
402  TRACE_EVENT0("impeller", "RenderBufferStorageInitialization");
403  if (type_ == Type::kRenderBufferMultisampled) {
404  gl.RenderbufferStorageMultisampleEXT(
405  GL_RENDERBUFFER, // target
406  4, // samples
407  render_buffer_format.value(), // internal format
408  size.width, // width
409  size.height // height
410  );
411  } else {
412  gl.RenderbufferStorage(
413  GL_RENDERBUFFER, // target
414  render_buffer_format.value(), // internal format
415  size.width, // width
416  size.height // height
417  );
418  }
419  }
420  } break;
421  }
422 }
423 
424 std::optional<GLuint> TextureGLES::GetGLHandle() const {
425  if (!IsValid()) {
426  return std::nullopt;
427  }
428  return reactor_->GetGLHandle(handle_);
429 }
430 
431 bool TextureGLES::Bind() const {
432  auto handle = GetGLHandle();
433  if (!handle.has_value()) {
434  return false;
435  }
436  const auto& gl = reactor_->GetProcTable();
437  switch (type_) {
438  case Type::kTexture:
440  const auto target = ToTextureTarget(GetTextureDescriptor().type);
441  if (!target.has_value()) {
442  VALIDATION_LOG << "Could not bind texture of this type.";
443  return false;
444  }
445  gl.BindTexture(target.value(), handle.value());
446  } break;
447  case Type::kRenderBuffer:
449  gl.BindRenderbuffer(GL_RENDERBUFFER, handle.value());
450  break;
451  }
452  InitializeContentsIfNecessary();
453  return true;
454 }
455 
456 void TextureGLES::MarkSliceInitialized(size_t slice) const {
457  slices_initialized_[slice] = true;
458 }
459 
460 bool TextureGLES::IsSliceInitialized(size_t slice) const {
461  return slices_initialized_[slice];
462 }
463 
465  if (!IsValid()) {
466  return false;
467  }
468 
469  auto type = GetTextureDescriptor().type;
470  switch (type) {
472  break;
474  VALIDATION_LOG << "Generating mipmaps for multisample textures is not "
475  "supported in the GLES backend.";
476  return false;
478  break;
480  break;
481  }
482 
483  if (!Bind()) {
484  return false;
485  }
486 
487  auto handle = GetGLHandle();
488  if (!handle.has_value()) {
489  return false;
490  }
491 
492  const auto& gl = reactor_->GetProcTable();
493  gl.GenerateMipmap(ToTextureType(type));
494  mipmap_generated_ = true;
495  return true;
496 }
497 
499  return type_;
500 }
501 
503  switch (point) {
505  return GL_COLOR_ATTACHMENT0;
507  return GL_DEPTH_ATTACHMENT;
509  return GL_STENCIL_ATTACHMENT;
510  }
511 }
512 
514  GLenum target,
515  AttachmentType attachment_type) const {
516  if (!IsValid()) {
517  return false;
518  }
519  InitializeContentsIfNecessary();
520  auto handle = GetGLHandle();
521  if (!handle.has_value()) {
522  return false;
523  }
524  const auto& gl = reactor_->GetProcTable();
525 
526  switch (type_) {
527  case Type::kTexture:
528  gl.FramebufferTexture2D(target, // target
529  ToAttachmentType(attachment_type), // attachment
530  GL_TEXTURE_2D, // textarget
531  handle.value(), // texture
532  0 // level
533  );
534  break;
536  gl.FramebufferTexture2DMultisampleEXT(
537  target, // target
538  ToAttachmentType(attachment_type), // attachment
539  GL_TEXTURE_2D, // textarget
540  handle.value(), // texture
541  0, // level
542  4 // samples
543  );
544  break;
545  case Type::kRenderBuffer:
547  gl.FramebufferRenderbuffer(
548  target, // target
549  ToAttachmentType(attachment_type), // attachment
550  GL_RENDERBUFFER, // render-buffer target
551  handle.value() // render-buffer
552  );
553  break;
554  }
555 
556  return true;
557 }
558 
559 // |Texture|
560 Scalar TextureGLES::GetYCoordScale() const {
561  switch (GetCoordinateSystem()) {
563  return 1.0;
565  return -1.0;
566  }
567  FML_UNREACHABLE();
568 }
569 
570 } // namespace impeller
impeller::ReactorGLES::Operation
std::function< void(const ReactorGLES &reactor)> Operation
Definition: reactor_gles.h:194
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:464
impeller::PixelFormat::kB10G10R10A10XR
@ kB10G10R10A10XR
impeller::PixelFormat::kB8G8R8A8UNormIntSRGB
@ kB8G8R8A8UNormIntSRGB
impeller::TextureGLES::AttachmentType::kDepth
@ kDepth
impeller::PixelFormat::kA8UNormInt
@ kA8UNormInt
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:456
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:424
texture_descriptor.h
impeller::HandleType::kTexture
@ kTexture
formats.h
texture_gles.h
impeller::TextureGLES::Type::kRenderBufferMultisampled
@ kRenderBufferMultisampled
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::TextureGLES::AttachmentType::kColor0
@ kColor0
validation.h
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:460
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:150
impeller::PixelFormat::kR8G8UNormInt
@ kR8G8UNormInt
external_format
GLenum external_format
Definition: texture_gles.cc:61
impeller::PixelFormat::kB10G10R10XR
@ kB10G10R10XR
impeller::PixelFormat::kD24UnormS8Uint
@ kD24UnormS8Uint
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:169
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
formats_gles.h
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::TextureGLES::GetType
Type GetType() const
Definition: texture_gles.cc:498
impeller::TextureGLES::~TextureGLES
~TextureGLES() override
Definition: texture_gles.cc:186
impeller::PixelFormat::kR8G8B8A8UNormIntSRGB
@ kR8G8B8A8UNormIntSRGB
std
Definition: comparable.h:95
impeller::TextureGLES::AttachmentType
AttachmentType
Definition: texture_gles.h:50
impeller::CreateMappingWithCopy
std::shared_ptr< fml::Mapping > CreateMappingWithCopy(const uint8_t *contents, size_t length)
Definition: allocation.cc:83
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:183
impeller::TextureGLES::IsWrapped
IsWrapped
Definition: texture_gles.h:27
impeller::PixelFormat::kB10G10R10XRSRGB
@ kB10G10R10XRSRGB
impeller::TextureGLES
Definition: texture_gles.h:17
impeller::TextureGLES::Bind
bool Bind() const
Definition: texture_gles.cc:431
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller::TextureGLES::Type::kTexture
@ kTexture
impeller::Texture::GetCoordinateSystem
TextureCoordinateSystem GetCoordinateSystem() const
Definition: texture.cc:77
impeller
Definition: aiks_blend_unittests.cc:18
impeller::ToRenderBufferFormat
static std::optional< GLenum > ToRenderBufferFormat(PixelFormat format)
Definition: texture_gles.cc:317
impeller::ToAttachmentType
static GLenum ToAttachmentType(TextureGLES::AttachmentType point)
Definition: texture_gles.cc:502
impeller::TextureGLES::SetAsFramebufferAttachment
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type) const
Definition: texture_gles.cc:513