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 
150  default;
151 
153  return label;
154 }
155 
157  const ReactorGLES& reactor) const {
158  TextureGLES& texture_gles = TextureGLES::Cast(*destination);
159 
160  if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
161  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
162  "this texture object.";
163  return false;
164  }
165 
166  if (texture_gles.IsWrapped()) {
167  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
168  return false;
169  }
170 
171  const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
172 
173  if (tex_descriptor.size.IsEmpty()) {
174  return true;
175  }
176 
177  if (!tex_descriptor.IsValid() ||
178  source.GetRange().length !=
179  BytesPerPixelForPixelFormat(tex_descriptor.format) *
181  return false;
182  }
183 
185 
186  GLenum texture_type;
187  GLenum texture_target;
188  switch (tex_descriptor.type) {
190  texture_type = GL_TEXTURE_2D;
191  texture_target = GL_TEXTURE_2D;
192  break;
194  VALIDATION_LOG << "Multisample texture uploading is not supported for "
195  "the OpenGLES backend.";
196  return false;
198  texture_type = GL_TEXTURE_CUBE_MAP;
199  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
200  break;
202  texture_type = GL_TEXTURE_EXTERNAL_OES;
203  texture_target = GL_TEXTURE_EXTERNAL_OES;
204  break;
205  }
206 
207  std::optional<PixelFormatGLES> gles_format =
208  ToPixelFormatGLES(tex_descriptor.format,
209  /*supports_bgra=*/
211  "GL_EXT_texture_format_BGRA8888"));
212  if (!gles_format.has_value()) {
213  VALIDATION_LOG << "Invalid texture format.";
214  return false;
215  }
216 
217  auto gl_handle = texture_gles.GetGLHandle();
218  if (!gl_handle.has_value()) {
220  << "Texture was collected before it could be uploaded to the GPU.";
221  return false;
222  }
223  const auto& gl = reactor.GetProcTable();
224  gl.BindTexture(texture_type, gl_handle.value());
225  const GLvoid* tex_data =
227 
228  // GL_INVALID_OPERATION if the texture array has not been
229  // defined by a previous glTexImage2D operation.
230  if (!texture_gles.IsSliceInitialized(slice)) {
231  gl.TexImage2D(texture_target, // target
232  mip_level, // LOD level
233  gles_format->internal_format, // internal format
234  tex_descriptor.size.width, // width
235  tex_descriptor.size.height, // height
236  0u, // border
237  gles_format->external_format, // format
238  gles_format->type, // type
239  nullptr); // data
240  texture_gles.MarkSliceInitialized(slice);
241  }
242 
243  {
244  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
245  gl.TexSubImage2D(texture_target, // target
246  mip_level, // LOD level
247  destination_region.GetX(), // xoffset
248  destination_region.GetY(), // yoffset
249  destination_region.GetWidth(), // width
250  destination_region.GetHeight(), // height
251  gles_format->external_format, // format
252  gles_format->type, // type
253  tex_data); // data
254  }
255  return true;
256 }
257 
259  default;
260 
262  return label;
263 }
264 
266  const ReactorGLES& reactor) const {
267  const auto& gl = reactor.GetProcTable();
268 
269  PixelFormat source_format = source->GetTextureDescriptor().format;
270  std::optional<PixelFormatGLES> gles_format =
271  ToPixelFormatGLES(source_format,
272  /*supports_bgra=*/
274  "GL_EXT_texture_format_BGRA8888"));
275 
276  if (!gles_format.has_value()) {
277  VALIDATION_LOG << "Texture has unsupported pixel format.";
278  return false;
279  }
280 
281  TextureCoordinateSystem coord_system = source->GetCoordinateSystem();
282 
283  GLuint read_fbo = GL_NONE;
284  fml::ScopedCleanupClosure delete_fbos(
285  [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_FRAMEBUFFER); });
286 
287  {
288  auto read = ConfigureFBO(gl, source, GL_FRAMEBUFFER);
289  if (!read.has_value()) {
290  return false;
291  }
292  read_fbo = read.value();
293  }
294 
297  [&gl, //
298  this, //
299  format = gles_format->external_format, //
300  type = gles_format->type, //
301  coord_system, //
302  bytes_per_pixel = BytesPerPixelForPixelFormat(source_format) //
303  ](uint8_t* data, size_t length) {
304  gl.ReadPixels(source_region.GetX(), source_region.GetY(),
306  format, type, data + destination_offset);
307  switch (coord_system) {
309  break;
311  // The texture is upside down, and must be inverted when copying
312  // byte data out.
313  FlipImage(data + destination_offset, source_region.GetWidth(),
314  source_region.GetHeight(), bytes_per_pixel);
315  }
316  });
317 
318  return true;
319 };
320 
322 
324  return label;
325 }
326 
328  auto texture_gles = TextureGLES::Cast(texture.get());
329  if (!texture_gles->GenerateMipmap()) {
330  return false;
331  }
332 
333  return true;
334 };
335 
336 ////// BlitResizeTextureCommandGLES
337 //////////////////////////////////////////////////////
338 
340 
342  return "Resize Texture";
343 }
344 
346  const auto& gl = reactor.GetProcTable();
347 
348  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
349  // emulate the blit when it's not available in the driver.
350  if (!gl.BlitFramebuffer.IsAvailable()) {
351  // TODO(157064): Emulate the blit using a raster draw call here.
352  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
353  return false;
354  }
355 
356  destination->SetCoordinateSystem(source->GetCoordinateSystem());
357 
358  GLuint read_fbo = GL_NONE;
359  GLuint draw_fbo = GL_NONE;
360  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
361  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
362  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
363  });
364 
365  {
366  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
367  if (!read.has_value()) {
368  return false;
369  }
370  read_fbo = read.value();
371  }
372 
373  {
374  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
375  if (!draw.has_value()) {
376  return false;
377  }
378  draw_fbo = draw.value();
379  }
380 
381  gl.Disable(GL_SCISSOR_TEST);
382  gl.Disable(GL_DEPTH_TEST);
383  gl.Disable(GL_STENCIL_TEST);
384 
385  const IRect source_region = IRect::MakeSize(source->GetSize());
386  const IRect destination_region = IRect::MakeSize(destination->GetSize());
387 
388  gl.BlitFramebuffer(source_region.GetX(), // srcX0
389  source_region.GetY(), // srcY0
390  source_region.GetWidth(), // srcX1
391  source_region.GetHeight(), // srcY1
392  destination_region.GetX(), // dstX0
393  destination_region.GetY(), // dstY0
394  destination_region.GetWidth(), // dstX1
395  destination_region.GetHeight(), // dstY1
396  GL_COLOR_BUFFER_BIT, // mask
397  GL_LINEAR // filter
398  );
399 
400  return true;
401 }
402 
403 } // namespace impeller
static TextureGLES & Cast(Texture &base)
Definition: backend_cast.h:13
bool HasExtension(const std::string &ext) const
void UpdateBufferData(const std::function< void(uint8_t *, size_t length)> &update_buffer_data)
virtual uint8_t * OnGetContents() const =0
const DescriptionGLES * GetDescription() const
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
std::optional< PixelFormatGLES > ToPixelFormatGLES(PixelFormat pixel_format, bool supports_bgra)
Definition: formats_gles.cc:26
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
bool Encode(const ReactorGLES &reactor) const override
std::string GetLabel() const override
std::shared_ptr< Texture > texture
Definition: blit_command.h:47
bool Encode(const ReactorGLES &reactor) const override
std::string GetLabel() const override
std::shared_ptr< Texture > destination
Definition: blit_command.h:28
std::shared_ptr< Texture > source
Definition: blit_command.h:27
Range GetRange() const
Definition: buffer_view.h:27
const DeviceBuffer * GetBuffer() const
Definition: buffer_view.cc:17
size_t length
Definition: range.h:15
size_t offset
Definition: range.h:14
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 static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:341
#define VALIDATION_LOG
Definition: validation.h:91