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_RGBA;
183  external_format = GL_RGBA;
184  type = GL_HALF_FLOAT;
185  break;
187  // Pure stencil textures are only available in OpenGL 4.4+, which is
188  // ~0% of mobile devices. Instead, we use a depth-stencil texture and
189  // only use the stencil component.
190  //
191  // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
193  internal_format = GL_DEPTH_STENCIL;
194  external_format = GL_DEPTH_STENCIL;
195  type = GL_UNSIGNED_INT_24_8;
196  break;
203  return;
204  }
205  is_valid_ = true;
206  }
207 
208  TexImage2DData(PixelFormat pixel_format, BufferView p_buffer_view)
209  : TexImage2DData(pixel_format) {
210  buffer_view = std::move(p_buffer_view);
211  }
212 
213  bool IsValid() const { return is_valid_; }
214 
215  private:
216  bool is_valid_ = false;
217 };
218 } // namespace
219 
221  default;
222 
224  return label;
225 }
226 
228  const ReactorGLES& reactor) const {
229  TextureGLES& texture_gles = TextureGLES::Cast(*destination);
230 
231  if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
232  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
233  "this texture object.";
234  return false;
235  }
236 
237  if (texture_gles.IsWrapped()) {
238  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
239  return false;
240  }
241 
242  const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
243 
244  if (tex_descriptor.size.IsEmpty()) {
245  return true;
246  }
247 
248  if (!tex_descriptor.IsValid() ||
249  source.GetRange().length !=
250  BytesPerPixelForPixelFormat(tex_descriptor.format) *
252  return false;
253  }
254 
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  TexImage2DData data = TexImage2DData(tex_descriptor.format, source);
279  if (!data.IsValid()) {
280  VALIDATION_LOG << "Invalid texture format.";
281  return false;
282  }
283 
284  auto gl_handle = texture_gles.GetGLHandle();
285  if (!gl_handle.has_value()) {
287  << "Texture was collected before it could be uploaded to the GPU.";
288  return false;
289  }
290  const auto& gl = reactor.GetProcTable();
291  gl.BindTexture(texture_type, gl_handle.value());
292  const GLvoid* tex_data = data.buffer_view.GetBuffer()->OnGetContents() +
293  data.buffer_view.GetRange().offset;
294 
295  // GL_INVALID_OPERATION if the texture array has not been
296  // defined by a previous glTexImage2D operation.
297  if (!texture_gles.IsSliceInitialized(slice)) {
298  gl.TexImage2D(texture_target, // target
299  mip_level, // LOD level
300  data.internal_format, // internal format
301  tex_descriptor.size.width, // width
302  tex_descriptor.size.height, // height
303  0u, // border
304  data.external_format, // external format
305  data.type, // type
306  nullptr // data
307  );
308  texture_gles.MarkSliceInitialized(slice);
309  }
310 
311  {
312  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
313  gl.TexSubImage2D(texture_target, // target
314  mip_level, // LOD level
315  destination_region.GetX(), // xoffset
316  destination_region.GetY(), // yoffset
317  destination_region.GetWidth(), // width
318  destination_region.GetHeight(), // height
319  data.external_format, // external format
320  data.type, // type
321  tex_data // data
322 
323  );
324  }
325  return true;
326 }
327 
329  default;
330 
332  return label;
333 }
334 
336  const ReactorGLES& reactor) const {
337  if (source->GetTextureDescriptor().format != PixelFormat::kR8G8B8A8UNormInt) {
338  VALIDATION_LOG << "Only textures with pixel format RGBA are supported yet.";
339  return false;
340  }
341 
342  const auto& gl = reactor.GetProcTable();
343  TextureCoordinateSystem coord_system = source->GetCoordinateSystem();
344 
345  GLuint read_fbo = GL_NONE;
346  fml::ScopedCleanupClosure delete_fbos(
347  [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_FRAMEBUFFER); });
348 
349  {
350  auto read = ConfigureFBO(gl, source, GL_FRAMEBUFFER);
351  if (!read.has_value()) {
352  return false;
353  }
354  read_fbo = read.value();
355  }
356 
358  .UpdateBufferData([&gl, this, coord_system,
359  rows = source->GetSize().height](uint8_t* data,
360 
361  size_t length) {
362  gl.ReadPixels(source_region.GetX(), source_region.GetY(),
363  source_region.GetWidth(), source_region.GetHeight(),
364  GL_RGBA, GL_UNSIGNED_BYTE, data + destination_offset);
365  switch (coord_system) {
366  case TextureCoordinateSystem::kUploadFromHost:
367  break;
368  case TextureCoordinateSystem::kRenderToTexture:
369  // The texture is upside down, and must be inverted when copying
370  // byte data out.
371  FlipImage(data + destination_offset, source_region.GetWidth(),
372  source_region.GetHeight(), 4);
373  }
374  });
375 
376  return true;
377 };
378 
379 BlitGenerateMipmapCommandGLES::~BlitGenerateMipmapCommandGLES() = default;
380 
381 std::string BlitGenerateMipmapCommandGLES::GetLabel() const {
382  return label;
383 }
384 
385 bool BlitGenerateMipmapCommandGLES::Encode(const ReactorGLES& reactor) const {
386  auto texture_gles = TextureGLES::Cast(texture.get());
387  if (!texture_gles->GenerateMipmap()) {
388  return false;
389  }
390 
391  return true;
392 };
393 
394 ////// BlitResizeTextureCommandGLES
395 //////////////////////////////////////////////////////
396 
397 BlitResizeTextureCommandGLES::~BlitResizeTextureCommandGLES() = default;
398 
399 std::string BlitResizeTextureCommandGLES::GetLabel() const {
400  return "Resize Texture";
401 }
402 
403 bool BlitResizeTextureCommandGLES::Encode(const ReactorGLES& reactor) const {
404  const auto& gl = reactor.GetProcTable();
405 
406  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
407  // emulate the blit when it's not available in the driver.
408  if (!gl.BlitFramebuffer.IsAvailable()) {
409  // TODO(157064): Emulate the blit using a raster draw call here.
410  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
411  return false;
412  }
413 
414  destination->SetCoordinateSystem(source->GetCoordinateSystem());
415 
416  GLuint read_fbo = GL_NONE;
417  GLuint draw_fbo = GL_NONE;
418  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
419  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
420  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
421  });
422 
423  {
424  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
425  if (!read.has_value()) {
426  return false;
427  }
428  read_fbo = read.value();
429  }
430 
431  {
432  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
433  if (!draw.has_value()) {
434  return false;
435  }
436  draw_fbo = draw.value();
437  }
438 
439  gl.Disable(GL_SCISSOR_TEST);
440  gl.Disable(GL_DEPTH_TEST);
441  gl.Disable(GL_STENCIL_TEST);
442 
443  const IRect source_region = IRect::MakeSize(source->GetSize());
444  const IRect destination_region = IRect::MakeSize(destination->GetSize());
445 
446  gl.BlitFramebuffer(source_region.GetX(), // srcX0
447  source_region.GetY(), // srcY0
448  source_region.GetWidth(), // srcX1
449  source_region.GetHeight(), // srcY1
450  destination_region.GetX(), // dstX0
451  destination_region.GetY(), // dstY0
452  destination_region.GetWidth(), // dstX1
453  destination_region.GetHeight(), // dstY1
454  GL_COLOR_BUFFER_BIT, // mask
455  GL_LINEAR // filter
456  );
457 
458  return true;
459 }
460 
461 } // 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:466
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:327
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:68
#define VALIDATION_LOG
Definition: validation.h:91