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"
8 #include "fml/trace_event.h"
10 #include "impeller/core/formats.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  if (gl.CheckFramebufferStatus(fbo_type) != GL_FRAMEBUFFER_COMPLETE) {
78  VALIDATION_LOG << "Could not create a complete framebuffer.";
79  DeleteFBO(gl, fbo, fbo_type);
80  return std::nullopt;
81  }
82 
83  return fbo;
84 };
85 
87  default;
88 
90  return label;
91 }
92 
94  const ReactorGLES& reactor) const {
95  const auto& gl = reactor.GetProcTable();
96 
97  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
98  // emulate the blit when it's not available in the driver.
99  if (!gl.BlitFramebuffer.IsAvailable()) {
100  // TODO(157064): Emulate the blit using a raster draw call here.
101  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
102  return false;
103  }
104 
105  GLuint read_fbo = GL_NONE;
106  GLuint draw_fbo = GL_NONE;
107  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
108  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
109  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
110  });
111 
112  {
113  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
114  if (!read.has_value()) {
115  return false;
116  }
117  read_fbo = read.value();
118  }
119 
120  {
121  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
122  if (!draw.has_value()) {
123  return false;
124  }
125  draw_fbo = draw.value();
126  }
127 
128  gl.Disable(GL_SCISSOR_TEST);
129  gl.Disable(GL_DEPTH_TEST);
130  gl.Disable(GL_STENCIL_TEST);
131 
132  gl.BlitFramebuffer(source_region.GetX(), // srcX0
133  source_region.GetY(), // srcY0
134  source_region.GetWidth(), // srcX1
135  source_region.GetHeight(), // srcY1
136  destination_origin.x, // dstX0
137  destination_origin.y, // dstY0
138  source_region.GetWidth(), // dstX1
139  source_region.GetHeight(), // dstY1
140  GL_COLOR_BUFFER_BIT, // mask
141  GL_NEAREST // filter
142  );
143 
144  return true;
145 };
146 
147 namespace {
148 struct TexImage2DData {
149  GLint internal_format = 0;
150  GLenum external_format = GL_NONE;
151  GLenum type = GL_NONE;
152  BufferView buffer_view;
153 
154  explicit TexImage2DData(PixelFormat pixel_format) {
155  switch (pixel_format) {
157  internal_format = GL_ALPHA;
158  external_format = GL_ALPHA;
159  type = GL_UNSIGNED_BYTE;
160  break;
162  internal_format = GL_RED;
163  external_format = GL_RED;
164  type = GL_UNSIGNED_BYTE;
165  break;
170  internal_format = GL_RGBA;
171  external_format = GL_RGBA;
172  type = GL_UNSIGNED_BYTE;
173  break;
175  internal_format = GL_RGBA;
176  external_format = GL_RGBA;
177  type = GL_FLOAT;
178  break;
180  internal_format = GL_RGBA;
181  external_format = GL_RGBA;
182  type = GL_HALF_FLOAT;
183  break;
185  // Pure stencil textures are only available in OpenGL 4.4+, which is
186  // ~0% of mobile devices. Instead, we use a depth-stencil texture and
187  // only use the stencil component.
188  //
189  // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml
191  internal_format = GL_DEPTH_STENCIL;
192  external_format = GL_DEPTH_STENCIL;
193  type = GL_UNSIGNED_INT_24_8;
194  break;
201  return;
202  }
203  is_valid_ = true;
204  }
205 
206  TexImage2DData(PixelFormat pixel_format, BufferView p_buffer_view)
207  : TexImage2DData(pixel_format) {
208  buffer_view = std::move(p_buffer_view);
209  }
210 
211  bool IsValid() const { return is_valid_; }
212 
213  private:
214  bool is_valid_ = false;
215 };
216 } // namespace
217 
219  default;
220 
222  return label;
223 }
224 
226  const ReactorGLES& reactor) const {
227  TextureGLES& texture_gles = TextureGLES::Cast(*destination);
228 
229  if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
230  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
231  "this texture object.";
232  return false;
233  }
234 
235  if (texture_gles.IsWrapped()) {
236  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
237  return false;
238  }
239 
240  const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
241 
242  if (tex_descriptor.size.IsEmpty()) {
243  return true;
244  }
245 
246  if (!tex_descriptor.IsValid() ||
247  source.GetRange().length !=
248  BytesPerPixelForPixelFormat(tex_descriptor.format) *
250  return false;
251  }
252 
254 
255  GLenum texture_type;
256  GLenum texture_target;
257  switch (tex_descriptor.type) {
259  texture_type = GL_TEXTURE_2D;
260  texture_target = GL_TEXTURE_2D;
261  break;
263  VALIDATION_LOG << "Multisample texture uploading is not supported for "
264  "the OpenGLES backend.";
265  return false;
267  texture_type = GL_TEXTURE_CUBE_MAP;
268  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
269  break;
271  texture_type = GL_TEXTURE_EXTERNAL_OES;
272  texture_target = GL_TEXTURE_EXTERNAL_OES;
273  break;
274  }
275 
276  TexImage2DData data = TexImage2DData(tex_descriptor.format, source);
277  if (!data.IsValid()) {
278  VALIDATION_LOG << "Invalid texture format.";
279  return false;
280  }
281 
282  auto gl_handle = texture_gles.GetGLHandle();
283  if (!gl_handle.has_value()) {
285  << "Texture was collected before it could be uploaded to the GPU.";
286  return false;
287  }
288  const auto& gl = reactor.GetProcTable();
289  gl.BindTexture(texture_type, gl_handle.value());
290  const GLvoid* tex_data = data.buffer_view.GetBuffer()->OnGetContents() +
291  data.buffer_view.GetRange().offset;
292 
293  // GL_INVALID_OPERATION if the texture array has not been
294  // defined by a previous glTexImage2D operation.
295  if (!texture_gles.IsSliceInitialized(slice)) {
296  gl.TexImage2D(texture_target, // target
297  mip_level, // LOD level
298  data.internal_format, // internal format
299  tex_descriptor.size.width, // width
300  tex_descriptor.size.height, // height
301  0u, // border
302  data.external_format, // external format
303  data.type, // type
304  nullptr // data
305  );
306  texture_gles.MarkSliceInitialized(slice);
307  }
308 
309  {
310  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
311  gl.TexSubImage2D(texture_target, // target
312  mip_level, // LOD level
313  destination_region.GetX(), // xoffset
314  destination_region.GetY(), // yoffset
315  destination_region.GetWidth(), // width
316  destination_region.GetHeight(), // height
317  data.external_format, // external format
318  data.type, // type
319  tex_data // data
320 
321  );
322  }
323  return true;
324 }
325 
327  default;
328 
330  return label;
331 }
332 
334  const ReactorGLES& reactor) const {
335  if (source->GetTextureDescriptor().format != PixelFormat::kR8G8B8A8UNormInt) {
336  VALIDATION_LOG << "Only textures with pixel format RGBA are supported yet.";
337  return false;
338  }
339 
340  const auto& gl = reactor.GetProcTable();
341  TextureCoordinateSystem coord_system = source->GetCoordinateSystem();
342 
343  GLuint read_fbo = GL_NONE;
344  fml::ScopedCleanupClosure delete_fbos(
345  [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER); });
346 
347  {
348  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
349  if (!read.has_value()) {
350  return false;
351  }
352  read_fbo = read.value();
353  }
354 
356  .UpdateBufferData([&gl, this, coord_system,
357  rows = source->GetSize().height](uint8_t* data,
358 
359  size_t length) {
360  gl.ReadPixels(source_region.GetX(), source_region.GetY(),
361  source_region.GetWidth(), source_region.GetHeight(),
362  GL_RGBA, GL_UNSIGNED_BYTE, data + destination_offset);
363  switch (coord_system) {
364  case TextureCoordinateSystem::kUploadFromHost:
365  break;
366  case TextureCoordinateSystem::kRenderToTexture:
367  // The texture is upside down, and must be inverted when copying
368  // byte data out.
369  FlipImage(data + destination_offset, source_region.GetWidth(),
370  source_region.GetHeight(), 4);
371  }
372  });
373 
374  return true;
375 };
376 
377 BlitGenerateMipmapCommandGLES::~BlitGenerateMipmapCommandGLES() = default;
378 
379 std::string BlitGenerateMipmapCommandGLES::GetLabel() const {
380  return label;
381 }
382 
383 bool BlitGenerateMipmapCommandGLES::Encode(const ReactorGLES& reactor) const {
384  auto texture_gles = TextureGLES::Cast(texture.get());
385  if (!texture_gles->GenerateMipmap()) {
386  return false;
387  }
388 
389  return true;
390 };
391 
392 ////// BlitResizeTextureCommandGLES
393 //////////////////////////////////////////////////////
394 
395 BlitResizeTextureCommandGLES::~BlitResizeTextureCommandGLES() = default;
396 
397 std::string BlitResizeTextureCommandGLES::GetLabel() const {
398  return "Resize Texture";
399 }
400 
401 bool BlitResizeTextureCommandGLES::Encode(const ReactorGLES& reactor) const {
402  const auto& gl = reactor.GetProcTable();
403 
404  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
405  // emulate the blit when it's not available in the driver.
406  if (!gl.BlitFramebuffer.IsAvailable()) {
407  // TODO(157064): Emulate the blit using a raster draw call here.
408  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
409  return false;
410  }
411 
412  destination->SetCoordinateSystem(source->GetCoordinateSystem());
413 
414  GLuint read_fbo = GL_NONE;
415  GLuint draw_fbo = GL_NONE;
416  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
417  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
418  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
419  });
420 
421  {
422  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
423  if (!read.has_value()) {
424  return false;
425  }
426  read_fbo = read.value();
427  }
428 
429  {
430  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
431  if (!draw.has_value()) {
432  return false;
433  }
434  draw_fbo = draw.value();
435  }
436 
437  gl.Disable(GL_SCISSOR_TEST);
438  gl.Disable(GL_DEPTH_TEST);
439  gl.Disable(GL_STENCIL_TEST);
440 
441  const IRect source_region = IRect::MakeSize(source->GetSize());
442  const IRect destination_region = IRect::MakeSize(destination->GetSize());
443 
444  gl.BlitFramebuffer(source_region.GetX(), // srcX0
445  source_region.GetY(), // srcY0
446  source_region.GetWidth(), // srcX1
447  source_region.GetHeight(), // srcY1
448  destination_region.GetX(), // dstX0
449  destination_region.GetY(), // dstY0
450  destination_region.GetWidth(), // dstX1
451  destination_region.GetHeight(), // dstY1
452  GL_COLOR_BUFFER_BIT, // mask
453  GL_LINEAR // filter
454  );
455 
456  return true;
457 }
458 
459 } // 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)
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:341
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:351
constexpr T Area() const
Get the area of the rectangle, equivalent to |GetSize().Area()|.
Definition: rect.h:380
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition: rect.h:337
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:345
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:68
#define VALIDATION_LOG
Definition: validation.h:91