Flutter Impeller
blit_command_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 "flutter/fml/closure.h"
15 
16 namespace impeller {
17 
18 namespace {
19 static void FlipImage(uint8_t* buffer,
20  size_t width,
21  size_t height,
22  size_t stride) {
23  if (buffer == nullptr || stride == 0) {
24  return;
25  }
26 
27  const auto byte_width = width * stride;
28 
29  for (size_t top = 0; top < height; top++) {
30  size_t bottom = height - top - 1;
31  if (top >= bottom) {
32  break;
33  }
34  auto* top_row = buffer + byte_width * top;
35  auto* bottom_row = buffer + byte_width * bottom;
36  std::swap_ranges(top_row, top_row + byte_width, bottom_row);
37  }
38 }
39 } // namespace
40 
42 
43 static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) {
44  if (fbo != GL_NONE) {
45  gl.BindFramebuffer(type, GL_NONE);
46  gl.DeleteFramebuffers(1u, &fbo);
47  }
48 };
49 
50 static std::optional<GLuint> ConfigureFBO(
51  const ProcTableGLES& gl,
52  const std::shared_ptr<Texture>& texture,
53  GLenum fbo_type) {
54  auto handle = TextureGLES::Cast(texture.get())->GetGLHandle();
55  if (!handle.has_value()) {
56  return std::nullopt;
57  }
58 
59  if (TextureGLES::Cast(*texture).IsWrapped()) {
60  // The texture is attached to the default FBO, so there's no need to
61  // create/configure one.
62  gl.BindFramebuffer(fbo_type, 0);
63  return 0;
64  }
65 
66  GLuint fbo;
67  gl.GenFramebuffers(1u, &fbo);
68  gl.BindFramebuffer(fbo_type, fbo);
69 
72  VALIDATION_LOG << "Could not attach texture to framebuffer.";
73  DeleteFBO(gl, fbo, fbo_type);
74  return std::nullopt;
75  }
76 
77  GLenum status = gl.CheckFramebufferStatus(fbo_type);
78  if (status != GL_FRAMEBUFFER_COMPLETE) {
79  VALIDATION_LOG << "Could not create a complete framebuffer: "
80  << DebugToFramebufferError(status);
81  DeleteFBO(gl, fbo, fbo_type);
82  return std::nullopt;
83  }
84 
85  return fbo;
86 };
87 
89  default;
90 
92  return label;
93 }
94 
96  const ReactorGLES& reactor) const {
97  const auto& gl = reactor.GetProcTable();
98 
99  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
100  // emulate the blit when it's not available in the driver.
101  if (!gl.BlitFramebuffer.IsAvailable()) {
102  // TODO(157064): Emulate the blit using a raster draw call here.
103  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
104  return false;
105  }
106 
107  GLuint read_fbo = GL_NONE;
108  GLuint draw_fbo = GL_NONE;
109  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
110  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
111  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
112  });
113 
114  {
115  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
116  if (!read.has_value()) {
117  return false;
118  }
119  read_fbo = read.value();
120  }
121 
122  {
123  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
124  if (!draw.has_value()) {
125  return false;
126  }
127  draw_fbo = draw.value();
128  }
129 
130  gl.Disable(GL_SCISSOR_TEST);
131  gl.Disable(GL_DEPTH_TEST);
132  gl.Disable(GL_STENCIL_TEST);
133 
134  gl.BlitFramebuffer(source_region.GetX(), // srcX0
135  source_region.GetY(), // srcY0
136  source_region.GetWidth(), // srcX1
137  source_region.GetHeight(), // srcY1
138  destination_origin.x, // dstX0
139  destination_origin.y, // dstY0
140  source_region.GetWidth(), // dstX1
141  source_region.GetHeight(), // dstY1
142  GL_COLOR_BUFFER_BIT, // mask
143  GL_NEAREST // filter
144  );
145 
146  return true;
147 };
148 
149 namespace {
150 struct TexImage2DData {
151  GLint internal_format = 0;
152  GLenum external_format = GL_NONE;
153  GLenum type = GL_NONE;
154  BufferView buffer_view;
155 
156  explicit TexImage2DData(PixelFormat pixel_format) {
157  switch (pixel_format) {
159  internal_format = GL_ALPHA;
160  external_format = GL_ALPHA;
161  type = GL_UNSIGNED_BYTE;
162  break;
164  internal_format = GL_RED;
165  external_format = GL_RED;
166  type = GL_UNSIGNED_BYTE;
167  break;
172  internal_format = GL_RGBA;
173  external_format = GL_RGBA;
174  type = GL_UNSIGNED_BYTE;
175  break;
177  internal_format = GL_RGBA;
178  external_format = GL_RGBA;
179  type = GL_FLOAT;
180  break;
182  internal_format = GL_RED;
183  external_format = GL_RED;
184  type = GL_FLOAT;
185  break;
187  internal_format = GL_RGBA;
188  external_format = GL_RGBA;
189  type = GL_HALF_FLOAT;
190  break;
192  // Pure stencil textures are only available in OpenGL 4.4+, which is
193  // ~0% of mobile devices. Instead, we use a depth-stencil texture and
194  // only use the stencil component.
195  //
196  // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
198  internal_format = GL_DEPTH_STENCIL;
199  external_format = GL_DEPTH_STENCIL;
200  type = GL_UNSIGNED_INT_24_8;
201  break;
208  return;
209  }
210  is_valid_ = true;
211  }
212 
213  TexImage2DData(PixelFormat pixel_format, BufferView p_buffer_view)
214  : TexImage2DData(pixel_format) {
215  buffer_view = std::move(p_buffer_view);
216  }
217 
218  bool IsValid() const { return is_valid_; }
219 
220  private:
221  bool is_valid_ = false;
222 };
223 } // namespace
224 
226  default;
227 
229  return label;
230 }
231 
233  const ReactorGLES& reactor) const {
234  TextureGLES& texture_gles = TextureGLES::Cast(*destination);
235 
236  if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
237  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
238  "this texture object.";
239  return false;
240  }
241 
242  if (texture_gles.IsWrapped()) {
243  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
244  return false;
245  }
246 
247  const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
248 
249  if (tex_descriptor.size.IsEmpty()) {
250  return true;
251  }
252 
253  if (!tex_descriptor.IsValid() ||
254  source.GetRange().length !=
255  BytesPerPixelForPixelFormat(tex_descriptor.format) *
257  return false;
258  }
259 
261 
262  GLenum texture_type;
263  GLenum texture_target;
264  switch (tex_descriptor.type) {
266  texture_type = GL_TEXTURE_2D;
267  texture_target = GL_TEXTURE_2D;
268  break;
270  VALIDATION_LOG << "Multisample texture uploading is not supported for "
271  "the OpenGLES backend.";
272  return false;
274  texture_type = GL_TEXTURE_CUBE_MAP;
275  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
276  break;
278  texture_type = GL_TEXTURE_EXTERNAL_OES;
279  texture_target = GL_TEXTURE_EXTERNAL_OES;
280  break;
281  }
282 
283  TexImage2DData data = TexImage2DData(tex_descriptor.format, source);
284  if (!data.IsValid()) {
285  VALIDATION_LOG << "Invalid texture format.";
286  return false;
287  }
288 
289  auto gl_handle = texture_gles.GetGLHandle();
290  if (!gl_handle.has_value()) {
292  << "Texture was collected before it could be uploaded to the GPU.";
293  return false;
294  }
295  const auto& gl = reactor.GetProcTable();
296  gl.BindTexture(texture_type, gl_handle.value());
297  const GLvoid* tex_data = data.buffer_view.GetBuffer()->OnGetContents() +
298  data.buffer_view.GetRange().offset;
299 
300  // GL_INVALID_OPERATION if the texture array has not been
301  // defined by a previous glTexImage2D operation.
302  if (!texture_gles.IsSliceInitialized(slice)) {
303  gl.TexImage2D(texture_target, // target
304  mip_level, // LOD level
305  data.internal_format, // internal format
306  tex_descriptor.size.width, // width
307  tex_descriptor.size.height, // height
308  0u, // border
309  data.external_format, // external format
310  data.type, // type
311  nullptr // data
312  );
313  texture_gles.MarkSliceInitialized(slice);
314  }
315 
316  {
317  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
318  gl.TexSubImage2D(texture_target, // target
319  mip_level, // LOD level
320  destination_region.GetX(), // xoffset
321  destination_region.GetY(), // yoffset
322  destination_region.GetWidth(), // width
323  destination_region.GetHeight(), // height
324  data.external_format, // external format
325  data.type, // type
326  tex_data // data
327 
328  );
329  }
330  return true;
331 }
332 
334  default;
335 
337  return label;
338 }
339 
341  const ReactorGLES& reactor) const {
342  if (source->GetTextureDescriptor().format != PixelFormat::kR8G8B8A8UNormInt) {
343  VALIDATION_LOG << "Only textures with pixel format RGBA are supported yet.";
344  return false;
345  }
346 
347  const auto& gl = reactor.GetProcTable();
348  TextureCoordinateSystem coord_system = source->GetCoordinateSystem();
349 
350  GLuint read_fbo = GL_NONE;
351  fml::ScopedCleanupClosure delete_fbos(
352  [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_FRAMEBUFFER); });
353 
354  {
355  auto read = ConfigureFBO(gl, source, GL_FRAMEBUFFER);
356  if (!read.has_value()) {
357  return false;
358  }
359  read_fbo = read.value();
360  }
361 
363  .UpdateBufferData([&gl, this, coord_system,
364  rows = source->GetSize().height](uint8_t* data,
365 
366  size_t length) {
367  gl.ReadPixels(source_region.GetX(), source_region.GetY(),
368  source_region.GetWidth(), source_region.GetHeight(),
369  GL_RGBA, GL_UNSIGNED_BYTE, data + destination_offset);
370  switch (coord_system) {
371  case TextureCoordinateSystem::kUploadFromHost:
372  break;
373  case TextureCoordinateSystem::kRenderToTexture:
374  // The texture is upside down, and must be inverted when copying
375  // byte data out.
376  FlipImage(data + destination_offset, source_region.GetWidth(),
377  source_region.GetHeight(), 4);
378  }
379  });
380 
381  return true;
382 };
383 
384 BlitGenerateMipmapCommandGLES::~BlitGenerateMipmapCommandGLES() = default;
385 
386 std::string BlitGenerateMipmapCommandGLES::GetLabel() const {
387  return label;
388 }
389 
390 bool BlitGenerateMipmapCommandGLES::Encode(const ReactorGLES& reactor) const {
391  auto texture_gles = TextureGLES::Cast(texture.get());
392  if (!texture_gles->GenerateMipmap()) {
393  return false;
394  }
395 
396  return true;
397 };
398 
399 ////// BlitResizeTextureCommandGLES
400 //////////////////////////////////////////////////////
401 
402 BlitResizeTextureCommandGLES::~BlitResizeTextureCommandGLES() = default;
403 
404 std::string BlitResizeTextureCommandGLES::GetLabel() const {
405  return "Resize Texture";
406 }
407 
408 bool BlitResizeTextureCommandGLES::Encode(const ReactorGLES& reactor) const {
409  const auto& gl = reactor.GetProcTable();
410 
411  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
412  // emulate the blit when it's not available in the driver.
413  if (!gl.BlitFramebuffer.IsAvailable()) {
414  // TODO(157064): Emulate the blit using a raster draw call here.
415  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
416  return false;
417  }
418 
419  destination->SetCoordinateSystem(source->GetCoordinateSystem());
420 
421  GLuint read_fbo = GL_NONE;
422  GLuint draw_fbo = GL_NONE;
423  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
424  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
425  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
426  });
427 
428  {
429  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
430  if (!read.has_value()) {
431  return false;
432  }
433  read_fbo = read.value();
434  }
435 
436  {
437  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
438  if (!draw.has_value()) {
439  return false;
440  }
441  draw_fbo = draw.value();
442  }
443 
444  gl.Disable(GL_SCISSOR_TEST);
445  gl.Disable(GL_DEPTH_TEST);
446  gl.Disable(GL_STENCIL_TEST);
447 
448  const IRect source_region = IRect::MakeSize(source->GetSize());
449  const IRect destination_region = IRect::MakeSize(destination->GetSize());
450 
451  gl.BlitFramebuffer(source_region.GetX(), // srcX0
452  source_region.GetY(), // srcY0
453  source_region.GetWidth(), // srcX1
454  source_region.GetHeight(), // srcY1
455  destination_region.GetX(), // dstX0
456  destination_region.GetY(), // dstY0
457  destination_region.GetWidth(), // dstX1
458  destination_region.GetHeight(), // dstY1
459  GL_COLOR_BUFFER_BIT, // mask
460  GL_LINEAR // filter
461  );
462 
463  return true;
464 }
465 
466 } // namespace impeller
GLenum type
GLenum external_format
GLint internal_format
BufferView buffer_view
static TextureGLES & Cast(Texture &base)
Definition: backend_cast.h:13
void UpdateBufferData(const std::function< void(uint8_t *, size_t length)> &update_buffer_data)
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
const ProcTableGLES & GetProcTable() const
Get the OpenGL proc. table the reactor uses to manage handles.
bool IsSliceInitialized(size_t slice) const
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type) const
void MarkSliceInitialized(size_t slice) const
Indicates that a specific texture slice has been initialized.
std::optional< GLuint > GetGLHandle() const
bool IsWrapped() const
const TextureDescriptor & GetTextureDescriptor() const
Definition: texture.cc:57
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Definition: formats.h:469
static std::optional< GLuint > ConfigureFBO(const ProcTableGLES &gl, const std::shared_ptr< Texture > &texture, GLenum fbo_type)
std::string DebugToFramebufferError(int status)
Definition: formats_gles.cc:9
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
static void DeleteFBO(const ProcTableGLES &gl, GLuint fbo, GLenum type)
TextureCoordinateSystem
Definition: formats.h:330
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< Texture > destination
Definition: blit_command.h:40
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< DeviceBuffer > destination
Definition: blit_command.h:33
std::shared_ptr< Texture > source
Definition: blit_command.h:32
bool Encode(const ReactorGLES &reactor) const override
std::shared_ptr< Texture > destination
Definition: blit_command.h:21
std::shared_ptr< Texture > source
Definition: blit_command.h:20
Range GetRange() const
Definition: buffer_view.h:27
size_t length
Definition: range.h:15
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition: rect.h:337
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:347
constexpr T Area() const
Get the area of the rectangle, equivalent to |GetSize().Area()|.
Definition: rect.h:376
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition: rect.h:333
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:341
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:69
#define VALIDATION_LOG
Definition: validation.h:91