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 <algorithm>
8 
9 #include "flutter/fml/closure.h"
11 #include "impeller/core/formats.h"
17 
18 namespace impeller {
19 
21 
22 static void DeleteFBO(const ProcTableGLES& gl, GLuint fbo, GLenum type) {
23  if (fbo != GL_NONE) {
24  gl.BindFramebuffer(type, GL_NONE);
25  gl.DeleteFramebuffers(1u, &fbo);
26  }
27 };
28 
29 static std::optional<GLuint> ConfigureFBO(
30  const ProcTableGLES& gl,
31  const std::shared_ptr<Texture>& texture,
32  GLenum fbo_type) {
33  auto handle = TextureGLES::Cast(texture.get())->GetGLHandle();
34  if (!handle.has_value()) {
35  return std::nullopt;
36  }
37 
38  if (TextureGLES::Cast(*texture).IsWrapped()) {
39  // The texture is attached to the default FBO, so there's no need to
40  // create/configure one.
41  gl.BindFramebuffer(fbo_type, 0);
42  return 0;
43  }
44 
45  GLuint fbo;
46  gl.GenFramebuffers(1u, &fbo);
47  gl.BindFramebuffer(fbo_type, fbo);
48 
51  VALIDATION_LOG << "Could not attach texture to framebuffer.";
52  DeleteFBO(gl, fbo, fbo_type);
53  return std::nullopt;
54  }
55 
56  GLenum status = gl.CheckFramebufferStatus(fbo_type);
57  if (status != GL_FRAMEBUFFER_COMPLETE) {
58  VALIDATION_LOG << "Could not create a complete framebuffer: "
59  << DebugToFramebufferError(status);
60  DeleteFBO(gl, fbo, fbo_type);
61  return std::nullopt;
62  }
63 
64  return fbo;
65 };
66 
68  default;
69 
71  return label;
72 }
73 
75  const ReactorGLES& reactor) const {
76  const auto& gl = reactor.GetProcTable();
77 
78  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
79  // emulate the blit when it's not available in the driver.
80  if (!gl.BlitFramebuffer.IsAvailable()) {
81  // TODO(157064): Emulate the blit using a raster draw call here.
82  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
83  return false;
84  }
85 
86  GLuint read_fbo = GL_NONE;
87  GLuint draw_fbo = GL_NONE;
88  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
89  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
90  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
91  });
92 
93  {
94  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
95  if (!read.has_value()) {
96  return false;
97  }
98  read_fbo = read.value();
99  }
100 
101  {
102  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
103  if (!draw.has_value()) {
104  return false;
105  }
106  draw_fbo = draw.value();
107  }
108 
109  gl.Disable(GL_SCISSOR_TEST);
110  gl.Disable(GL_DEPTH_TEST);
111  gl.Disable(GL_STENCIL_TEST);
112 
113  const auto destination_right =
115  const auto destination_bottom =
117 
118  gl.BlitFramebuffer(source_region.GetX(), // srcX0
119  source_region.GetY(), // srcY0
120  source_region.GetRight(), // srcX1
121  source_region.GetBottom(), // srcY1
122  destination_origin.x, // dstX0
123  destination_origin.y, // dstY0
124  destination_right, // dstX1
125  destination_bottom, // dstY1
126  GL_COLOR_BUFFER_BIT, // mask
127  GL_NEAREST // filter
128  );
129 
130  return true;
131 };
132 
134  default;
135 
137  return label;
138 }
139 
141  const ReactorGLES& reactor) const {
142  TextureGLES& texture_gles = TextureGLES::Cast(*destination);
143 
144  if (texture_gles.GetType() != TextureGLES::Type::kTexture) {
145  VALIDATION_LOG << "Incorrect texture usage flags for setting contents on "
146  "this texture object.";
147  return false;
148  }
149 
150  if (texture_gles.IsWrapped()) {
151  VALIDATION_LOG << "Cannot set the contents of a wrapped texture.";
152  return false;
153  }
154 
155  const auto& tex_descriptor = texture_gles.GetTextureDescriptor();
156 
157  if (tex_descriptor.size.IsEmpty()) {
158  return true;
159  }
160 
161  if (!tex_descriptor.IsValid() ||
162  source.GetRange().length !=
163  BytesForTextureRegion(tex_descriptor.format,
166  return false;
167  }
168 
169  GLenum texture_type;
170  GLenum texture_target;
171  switch (tex_descriptor.type) {
173  texture_type = GL_TEXTURE_2D;
174  texture_target = GL_TEXTURE_2D;
175  break;
177  VALIDATION_LOG << "Multisample texture uploading is not supported for "
178  "the OpenGLES backend.";
179  return false;
181  texture_type = GL_TEXTURE_CUBE_MAP;
182  texture_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
183  break;
185  texture_type = GL_TEXTURE_EXTERNAL_OES;
186  texture_target = GL_TEXTURE_EXTERNAL_OES;
187  break;
188  }
189 
190  std::optional<PixelFormatGLES> gles_format =
191  ToPixelFormatGLES(tex_descriptor.format,
192  /*supports_bgra=*/
193  reactor.GetProcTable().GetDescription()->HasExtension(
194  "GL_EXT_texture_format_BGRA8888"));
195  if (!gles_format.has_value()) {
196  VALIDATION_LOG << "Invalid texture format.";
197  return false;
198  }
199 
200  auto gl_handle = texture_gles.GetGLHandle();
201  if (!gl_handle.has_value()) {
203  << "Texture was collected before it could be uploaded to the GPU.";
204  return false;
205  }
206  const auto& gl = reactor.GetProcTable();
207  gl.BindTexture(texture_type, gl_handle.value());
208  const GLvoid* tex_data =
210 
211  // Block-compressed textures cannot be allocated empty and then filled with a
212  // sub-image; glCompressedTexImage2D redefines the entire mip level. Require
213  // the upload to cover the full mip level starting at the origin.
214  if (gles_format->is_compressed) {
215  const auto mip_width =
216  std::max<int32_t>(1, tex_descriptor.size.width >> mip_level);
217  const auto mip_height =
218  std::max<int32_t>(1, tex_descriptor.size.height >> mip_level);
219  if (destination_region.GetX() != 0 || destination_region.GetY() != 0 ||
220  destination_region.GetWidth() != mip_width ||
221  destination_region.GetHeight() != mip_height) {
222  VALIDATION_LOG << "Compressed textures must be uploaded as a full mip "
223  "level starting at the origin.";
224  return false;
225  }
226  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
227  gl.CompressedTexImage2D(texture_target, // target
228  mip_level, // LOD level
229  gles_format->internal_format, // internal format
230  mip_width, // width
231  mip_height, // height
232  0u, // border
233  source.GetRange().length, // image size
234  tex_data); // data
236  return true;
237  }
238 
239  // GL_INVALID_OPERATION if the requested mip level has not been defined by
240  // a previous glTexImage2D operation. Allocate the requested mip lazily on
241  // first write, only for the level the upload is actually targeting. The
242  // snapshot pipeline (single base-level allocation followed by
243  // glGenerateMipmap) keeps its existing GL footprint, and per-level uploads
244  // pay only for the levels they touch.
245  if (!texture_gles.IsSliceMipLevelInitialized(slice, mip_level)) {
246  const auto level_width =
247  std::max<int32_t>(1, tex_descriptor.size.width >> mip_level);
248  const auto level_height =
249  std::max<int32_t>(1, tex_descriptor.size.height >> mip_level);
250  gl.TexImage2D(texture_target, // target
251  mip_level, // LOD level
252  gles_format->internal_format, // internal format
253  level_width, // width
254  level_height, // height
255  0u, // border
256  gles_format->external_format, // format
257  gles_format->type, // type
258  nullptr); // data
260  }
261 
262  {
263  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
264  gl.TexSubImage2D(texture_target, // target
265  mip_level, // LOD level
266  destination_region.GetX(), // xoffset
267  destination_region.GetY(), // yoffset
268  destination_region.GetWidth(), // width
269  destination_region.GetHeight(), // height
270  gles_format->external_format, // format
271  gles_format->type, // type
272  tex_data); // data
273  }
274  return true;
275 }
276 
278  default;
279 
281  return label;
282 }
283 
285  const ReactorGLES& reactor) const {
286  const auto& gl = reactor.GetProcTable();
287 
288  PixelFormat source_format = source->GetTextureDescriptor().format;
289  std::optional<PixelFormatGLES> gles_format =
290  ToPixelFormatGLES(source_format,
291  /*supports_bgra=*/
292  reactor.GetProcTable().GetDescription()->HasExtension(
293  "GL_EXT_texture_format_BGRA8888"));
294 
295  if (!gles_format.has_value()) {
296  VALIDATION_LOG << "Texture has unsupported pixel format.";
297  return false;
298  }
299 
300  GLuint read_fbo = GL_NONE;
301  fml::ScopedCleanupClosure delete_fbos(
302  [&gl, &read_fbo]() { DeleteFBO(gl, read_fbo, GL_FRAMEBUFFER); });
303 
304  {
305  auto read = ConfigureFBO(gl, source, GL_FRAMEBUFFER);
306  if (!read.has_value()) {
307  return false;
308  }
309  read_fbo = read.value();
310  }
311 
313  .UpdateBufferData([&gl, //
314  this, //
315  format = gles_format->external_format, //
316  type = gles_format->type //
317  ](uint8_t* data, size_t length) {
318  gl.ReadPixels(source_region.GetX(), source_region.GetY(),
319  source_region.GetWidth(), source_region.GetHeight(),
320  format, type, data + destination_offset);
321  });
322 
323  return true;
324 };
325 
327 
329  return label;
330 }
331 
333  auto texture_gles = TextureGLES::Cast(texture.get());
334  if (!texture_gles->GenerateMipmap()) {
335  return false;
336  }
337 
338  return true;
339 };
340 
341 ////// BlitResizeTextureCommandGLES
342 //////////////////////////////////////////////////////
343 
345 
347  return "Resize Texture";
348 }
349 
351  const auto& gl = reactor.GetProcTable();
352 
353  // glBlitFramebuffer is a GLES3 proc. Since we target GLES2, we need to
354  // emulate the blit when it's not available in the driver.
355  if (!gl.BlitFramebuffer.IsAvailable()) {
356  // TODO(157064): Emulate the blit using a raster draw call here.
357  VALIDATION_LOG << "Texture blit fallback not implemented yet for GLES2.";
358  return false;
359  }
360 
361  GLuint read_fbo = GL_NONE;
362  GLuint draw_fbo = GL_NONE;
363  fml::ScopedCleanupClosure delete_fbos([&gl, &read_fbo, &draw_fbo]() {
364  DeleteFBO(gl, read_fbo, GL_READ_FRAMEBUFFER);
365  DeleteFBO(gl, draw_fbo, GL_DRAW_FRAMEBUFFER);
366  });
367 
368  {
369  auto read = ConfigureFBO(gl, source, GL_READ_FRAMEBUFFER);
370  if (!read.has_value()) {
371  return false;
372  }
373  read_fbo = read.value();
374  }
375 
376  {
377  auto draw = ConfigureFBO(gl, destination, GL_DRAW_FRAMEBUFFER);
378  if (!draw.has_value()) {
379  return false;
380  }
381  draw_fbo = draw.value();
382  }
383 
384  gl.Disable(GL_SCISSOR_TEST);
385  gl.Disable(GL_DEPTH_TEST);
386  gl.Disable(GL_STENCIL_TEST);
387 
388  const IRect source_region = IRect::MakeSize(source->GetSize());
389  const IRect destination_region = IRect::MakeSize(destination->GetSize());
390 
391  gl.BlitFramebuffer(source_region.GetX(), // srcX0
392  source_region.GetY(), // srcY0
393  source_region.GetWidth(), // srcX1
394  source_region.GetHeight(), // srcY1
395  destination_region.GetX(), // dstX0
396  destination_region.GetY(), // dstY0
397  destination_region.GetWidth(), // dstX1
398  destination_region.GetHeight(), // dstY1
399  GL_COLOR_BUFFER_BIT, // mask
400  GL_LINEAR // filter
401  );
402 
403  return true;
404 }
405 
406 } // namespace impeller
static TextureGLES & Cast(Texture &base)
Definition: backend_cast.h:13
void UpdateBufferData(const std::function< void(uint8_t *, size_t length)> &update_buffer_data)
virtual uint8_t * OnGetContents() const =0
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
bool IsSliceMipLevelInitialized(size_t slice, size_t mip_level) const
void MarkSliceMipLevelInitialized(size_t slice, size_t mip_level)
Indicates that storage for mip_level of slice has been allocated by a glTexImage2D call (or equivalen...
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type, uint32_t mip_level=0, uint32_t slice=0)
std::optional< GLuint > GetGLHandle() const
bool IsWrapped() const
const TextureDescriptor & GetTextureDescriptor() const
Definition: texture.cc:55
std::optional< PixelFormatGLES > ToPixelFormatGLES(PixelFormat pixel_format, bool supports_bgra)
Definition: formats_gles.cc:76
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:59
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)
constexpr size_t BytesForTextureRegion(PixelFormat format, int64_t width, int64_t height)
The number of bytes required to store a width x height texel region in format. Block-compressed forma...
Definition: formats.h:727
std::shared_ptr< ReactorGLES > reactor
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 auto GetBottom() const
Definition: rect.h:391
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition: rect.h:371
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:381
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition: rect.h:367
constexpr auto GetRight() const
Definition: rect.h:389
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:375
#define VALIDATION_LOG
Definition: validation.h:91