Flutter Impeller
render_pass_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 <cstdint>
8 
9 #include "flutter/fml/trace_event.h"
10 #include "fml/closure.h"
11 #include "fml/logging.h"
14 #include "impeller/core/formats.h"
23 
24 namespace impeller {
25 
26 RenderPassGLES::RenderPassGLES(std::shared_ptr<const Context> context,
27  const RenderTarget& target,
28  std::shared_ptr<ReactorGLES> reactor)
29  : RenderPass(std::move(context), target),
30  reactor_(std::move(reactor)),
31  is_valid_(reactor_ && reactor_->IsValid()) {}
32 
33 // |RenderPass|
34 RenderPassGLES::~RenderPassGLES() = default;
35 
36 // |RenderPass|
37 bool RenderPassGLES::IsValid() const {
38  return is_valid_;
39 }
40 
41 // |RenderPass|
42 void RenderPassGLES::OnSetLabel(std::string_view label) {
43  label_ = label;
44 }
45 
47  const ColorAttachmentDescriptor* color) {
48  if (color->blending_enabled) {
49  gl.Enable(GL_BLEND);
50  gl.BlendFuncSeparate(
51  ToBlendFactor(color->src_color_blend_factor), // src color
52  ToBlendFactor(color->dst_color_blend_factor), // dst color
53  ToBlendFactor(color->src_alpha_blend_factor), // src alpha
54  ToBlendFactor(color->dst_alpha_blend_factor) // dst alpha
55  );
56  gl.BlendEquationSeparate(
57  ToBlendOperation(color->color_blend_op), // mode color
58  ToBlendOperation(color->alpha_blend_op) // mode alpha
59  );
60  } else {
61  gl.Disable(GL_BLEND);
62  }
63 
64  {
65  const auto is_set = [](ColorWriteMask mask,
66  ColorWriteMask check) -> GLboolean {
67  return (mask & check) ? GL_TRUE : GL_FALSE;
68  };
69 
70  gl.ColorMask(
71  is_set(color->write_mask, ColorWriteMaskBits::kRed), // red
72  is_set(color->write_mask, ColorWriteMaskBits::kGreen), // green
73  is_set(color->write_mask, ColorWriteMaskBits::kBlue), // blue
74  is_set(color->write_mask, ColorWriteMaskBits::kAlpha) // alpha
75  );
76  }
77 }
78 
79 void ConfigureStencil(GLenum face,
80  const ProcTableGLES& gl,
81  const StencilAttachmentDescriptor& stencil,
82  uint32_t stencil_reference) {
83  gl.StencilOpSeparate(
84  face, // face
85  ToStencilOp(stencil.stencil_failure), // stencil fail
86  ToStencilOp(stencil.depth_failure), // depth fail
87  ToStencilOp(stencil.depth_stencil_pass) // depth stencil pass
88  );
89  gl.StencilFuncSeparate(face, // face
90  ToCompareFunction(stencil.stencil_compare), // func
91  stencil_reference, // ref
92  stencil.read_mask // mask
93  );
94  gl.StencilMaskSeparate(face, stencil.write_mask);
95 }
96 
99  uint32_t stencil_reference) {
100  if (!pipeline.HasStencilAttachmentDescriptors()) {
101  gl.Disable(GL_STENCIL_TEST);
102  return;
103  }
104 
105  gl.Enable(GL_STENCIL_TEST);
106  const auto& front = pipeline.GetFrontStencilAttachmentDescriptor();
107  const auto& back = pipeline.GetBackStencilAttachmentDescriptor();
108 
109  if (front.has_value() && back.has_value() && front == back) {
110  ConfigureStencil(GL_FRONT_AND_BACK, gl, *front, stencil_reference);
111  return;
112  }
113  if (front.has_value()) {
114  ConfigureStencil(GL_FRONT, gl, *front, stencil_reference);
115  }
116  if (back.has_value()) {
117  ConfigureStencil(GL_BACK, gl, *back, stencil_reference);
118  }
119 }
120 
121 //------------------------------------------------------------------------------
122 /// @brief Encapsulates data that will be needed in the reactor for the
123 /// encoding of commands for this render pass.
124 ///
127 
129  uint32_t clear_stencil = 0u;
130  Scalar clear_depth = 1.0;
131 
132  std::shared_ptr<Texture> color_attachment;
133  std::shared_ptr<Texture> resolve_attachment;
134  std::shared_ptr<Texture> depth_attachment;
135  std::shared_ptr<Texture> stencil_attachment;
136 
137  // The subresource of each attachment to render into.
138  uint32_t color_mip_level = 0u;
139  uint32_t color_slice = 0u;
140  uint32_t depth_mip_level = 0u;
141  uint32_t depth_slice = 0u;
142  uint32_t stencil_mip_level = 0u;
143  uint32_t stencil_slice = 0u;
144 
145  bool clear_color_attachment = true;
146  bool clear_depth_attachment = true;
147  bool clear_stencil_attachment = true;
148 
149  bool discard_color_attachment = true;
150  bool discard_depth_attachment = true;
151  bool discard_stencil_attachment = true;
152 
153  std::string label;
154 };
155 
156 static bool BindVertexBuffer(const ProcTableGLES& gl,
157  BufferBindingsGLES* vertex_desc_gles,
158  const BufferView& vertex_buffer_view,
159  size_t buffer_index,
160  size_t instance = 0) {
161  if (!vertex_buffer_view) {
162  return false;
163  }
164 
165  const DeviceBuffer* vertex_buffer = vertex_buffer_view.GetBuffer();
166 
167  if (!vertex_buffer) {
168  return false;
169  }
170 
171  const auto& vertex_buffer_gles = DeviceBufferGLES::Cast(*vertex_buffer);
172  if (!vertex_buffer_gles.BindAndUploadDataIfNecessary(
173  DeviceBufferGLES::BindingType::kArrayBuffer)) {
174  return false;
175  }
176 
177  //--------------------------------------------------------------------------
178  /// Bind the vertex attributes associated with vertex buffer.
179  ///
180  if (!vertex_desc_gles->BindVertexAttributes(
181  gl, buffer_index, vertex_buffer_view.GetRange().offset, instance)) {
182  return false;
183  }
184 
185  return true;
186 }
187 
188 void RenderPassGLES::ResetGLState(const ProcTableGLES& gl) {
189  gl.Disable(GL_SCISSOR_TEST);
190  gl.Disable(GL_DEPTH_TEST);
191  gl.Disable(GL_STENCIL_TEST);
192  gl.Disable(GL_CULL_FACE);
193  gl.Disable(GL_BLEND);
194  gl.Disable(GL_DITHER);
195  gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
196  gl.DepthMask(GL_TRUE);
197  gl.StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
198  gl.StencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
199 }
200 
201 static void EncodeViewport(const ProcTableGLES& gl,
202  const RenderPassData& pass_data,
203  const std::optional<Viewport>& command_viewport,
204  const ISize& target_size,
205  bool flip_y,
206  std::optional<Viewport>& current_viewport) {
207  auto new_viewport = command_viewport.value_or(pass_data.viewport);
208 
209  if (current_viewport.has_value() &&
210  current_viewport.value() == new_viewport) {
211  // The viewport is the same as the last command. Skip an unnecessary call.
212  return;
213  }
214 
215  current_viewport = new_viewport;
216 
217  // FBO passes flip in the vertex shader; swapchain keeps the old
218  // top-down -> bottom-up viewport conversion.
219  const auto viewport_y_gl = flip_y ? new_viewport.rect.GetY()
220  : target_size.height -
221  new_viewport.rect.GetY() -
222  new_viewport.rect.GetHeight();
223  gl.Viewport(new_viewport.rect.GetX(), // x
224  viewport_y_gl, // y
225  new_viewport.rect.GetWidth(), new_viewport.rect.GetHeight());
226  if (pass_data.depth_attachment) {
227  if (gl.DepthRangef.IsAvailable()) {
228  gl.DepthRangef(new_viewport.depth_range.z_near,
229  new_viewport.depth_range.z_far);
230  } else {
231  gl.DepthRange(new_viewport.depth_range.z_near,
232  new_viewport.depth_range.z_far);
233  }
234  }
235 }
236 
237 [[nodiscard]] bool EncodeCommandsInReactor(
238  const RenderPassData& pass_data,
239  const ReactorGLES& reactor,
240  const std::vector<Command>& commands,
241  const std::vector<BufferView>& vertex_buffers,
242  const std::vector<TextureAndSampler>& bound_textures,
243  const std::vector<BufferResource>& bound_buffers,
244  const std::shared_ptr<GPUTracerGLES>& tracer,
245  const std::shared_ptr<const Context>& impeller_context) {
246  TRACE_EVENT0("impeller", "RenderPassGLES::EncodeCommandsInReactor");
247 
248  const auto& gl = reactor.GetProcTable();
249 #ifdef IMPELLER_DEBUG
250  tracer->MarkFrameStart(gl);
251 
252  fml::ScopedCleanupClosure pop_pass_debug_marker(
253  [&gl]() { gl.PopDebugGroup(); });
254  if (!pass_data.label.empty()) {
255  gl.PushDebugGroup(pass_data.label);
256  } else {
257  pop_pass_debug_marker.Release();
258  }
259 #endif // IMPELLER_DEBUG
260 
261  TextureGLES& color_gles = TextureGLES::Cast(*pass_data.color_attachment);
262  const bool is_wrapped_fbo = color_gles.IsWrapped();
263 
264  std::optional<GLuint> fbo = 0;
265  if (is_wrapped_fbo) {
266  if (color_gles.GetFBO().has_value()) {
267  // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
268  gl.BindFramebuffer(GL_FRAMEBUFFER, *color_gles.GetFBO());
269  }
270  } else {
271  // Create (once) and bind an offscreen FBO. The cached FBO remembers which
272  // subresource it is bound to, so it is re-attached only when freshly
273  // created or when rendering into a different mip level or slice of the
274  // same texture.
275  bool needs_attachment = false;
276  if (color_gles.GetCachedFBO().IsDead()) {
277  color_gles.SetCachedFBO(
278  reactor.CreateUntrackedHandle(HandleType::kFrameBuffer));
279  needs_attachment = true;
280  }
281  fbo = reactor.GetGLHandle(color_gles.GetCachedFBO());
282  if (!fbo.has_value()) {
283  return false;
284  }
285  gl.BindFramebuffer(GL_FRAMEBUFFER, fbo.value());
286 
287  if (needs_attachment ||
288  !color_gles.CachedFBOMatchesSubresource(pass_data.color_mip_level,
289  pass_data.color_slice)) {
290  if (!color_gles.SetAsFramebufferAttachment(
291  GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0,
292  pass_data.color_mip_level, pass_data.color_slice)) {
293  return false;
294  }
295 
296  if (auto depth = TextureGLES::Cast(pass_data.depth_attachment.get())) {
297  if (!depth->SetAsFramebufferAttachment(
298  GL_FRAMEBUFFER, TextureGLES::AttachmentType::kDepth,
299  pass_data.depth_mip_level, pass_data.depth_slice)) {
300  return false;
301  }
302  }
303  if (auto stencil =
304  TextureGLES::Cast(pass_data.stencil_attachment.get())) {
305  if (!stencil->SetAsFramebufferAttachment(
306  GL_FRAMEBUFFER, TextureGLES::AttachmentType::kStencil,
307  pass_data.stencil_mip_level, pass_data.stencil_slice)) {
308  return false;
309  }
310  }
311 
312  auto status = gl.CheckFramebufferStatusDebug(GL_FRAMEBUFFER);
313  if (status != GL_FRAMEBUFFER_COMPLETE) {
314  VALIDATION_LOG << "Could not create a complete framebuffer: "
315  << DebugToFramebufferError(status);
316  return false;
317  }
318  color_gles.SetCachedFBOSubresource(pass_data.color_mip_level,
319  pass_data.color_slice);
320  }
321  }
322 
323  gl.ClearColor(pass_data.clear_color.red, // red
324  pass_data.clear_color.green, // green
325  pass_data.clear_color.blue, // blue
326  pass_data.clear_color.alpha // alpha
327  );
328  if (pass_data.depth_attachment) {
329  if (gl.DepthRangef.IsAvailable()) {
330  gl.ClearDepthf(pass_data.clear_depth);
331  } else {
332  gl.ClearDepth(pass_data.clear_depth);
333  }
334  }
335  if (pass_data.stencil_attachment) {
336  gl.ClearStencil(pass_data.clear_stencil);
337  }
338 
339  GLenum clear_bits = 0u;
340  if (pass_data.clear_color_attachment) {
341  clear_bits |= GL_COLOR_BUFFER_BIT;
342  }
343  if (pass_data.clear_depth_attachment) {
344  clear_bits |= GL_DEPTH_BUFFER_BIT;
345  }
346  if (pass_data.clear_stencil_attachment) {
347  clear_bits |= GL_STENCIL_BUFFER_BIT;
348  }
349 
350  RenderPassGLES::ResetGLState(gl);
351 
352  gl.Clear(clear_bits);
353 
354  // Both the viewport and scissor are specified in framebuffer coordinates.
355  // Impeller's framebuffer coordinate system is top left origin, but OpenGL's
356  // is bottom left origin, so we convert the coordinates here.
357  ISize target_size = pass_data.color_attachment->GetSize();
358 
359  // Offscreen FBO passes flip in the vertex shader (the swapchain is
360  // left alone); see https://github.com/flutter/flutter/issues/186554.
361  const bool flip_y = !is_wrapped_fbo;
362  const float y_flip_value = flip_y ? -1.0f : 1.0f;
363 
364  std::optional<Viewport> current_viewport;
365  CullMode current_cull_mode = CullMode::kNone;
366  WindingOrder current_winding_order = WindingOrder::kClockwise;
367  // Inverted to keep front-facing consistent under the vertex y-flip.
368  gl.FrontFace(flip_y ? GL_CCW : GL_CW);
369 
370  for (const auto& command : commands) {
371 #ifdef IMPELLER_DEBUG
372  fml::ScopedCleanupClosure pop_cmd_debug_marker(
373  [&gl]() { gl.PopDebugGroup(); });
374  if (!command.label.empty()) {
375  gl.PushDebugGroup(command.label);
376  } else {
377  pop_cmd_debug_marker.Release();
378  }
379 #endif // IMPELLER_DEBUG
380  const auto& pipeline = PipelineGLES::Cast(*command.pipeline);
381  impeller_context->GetPipelineLibrary()->LogPipelineUsage(
382  pipeline.GetDescriptor());
383  const auto* color_attachment =
384  pipeline.GetDescriptor().GetLegacyCompatibleColorAttachment();
385  if (!color_attachment) {
387  << "Color attachment is too complicated for a legacy renderer.";
388  return false;
389  }
390 
391  //--------------------------------------------------------------------------
392  /// Configure blending.
393  ///
394  ConfigureBlending(gl, color_attachment);
395 
396  //--------------------------------------------------------------------------
397  /// Setup stencil.
398  ///
399  ConfigureStencil(gl, pipeline.GetDescriptor(), command.stencil_reference);
400 
401  //--------------------------------------------------------------------------
402  /// Configure depth.
403  ///
404  if (auto depth =
405  pipeline.GetDescriptor().GetDepthStencilAttachmentDescriptor();
406  depth.has_value()) {
407  gl.Enable(GL_DEPTH_TEST);
408  gl.DepthFunc(ToCompareFunction(depth->depth_compare));
409  gl.DepthMask(depth->depth_write_enabled ? GL_TRUE : GL_FALSE);
410  } else {
411  gl.Disable(GL_DEPTH_TEST);
412  }
413 
414  //--------------------------------------------------------------------------
415  /// Setup the viewport.
416  ///
417  EncodeViewport(gl, //
418  pass_data, //
419  command.viewport, //
420  target_size, //
421  flip_y, //
422  current_viewport //
423  );
424 
425  //--------------------------------------------------------------------------
426  /// Setup the scissor rect.
427  ///
428  if (command.scissor.has_value()) {
429  const auto& scissor = command.scissor.value();
430  gl.Enable(GL_SCISSOR_TEST);
431  // Same flip handling as the viewport above.
432  const auto scissor_y_gl =
433  flip_y ? scissor.GetY()
434  : target_size.height - scissor.GetY() - scissor.GetHeight();
435  gl.Scissor(scissor.GetX(), // x
436  scissor_y_gl, // y
437  scissor.GetWidth(), scissor.GetHeight());
438  }
439 
440  //--------------------------------------------------------------------------
441  /// Setup culling.
442  ///
443  CullMode pipeline_cull_mode = pipeline.GetDescriptor().GetCullMode();
444  if (current_cull_mode != pipeline_cull_mode) {
445  switch (pipeline_cull_mode) {
446  case CullMode::kNone:
447  gl.Disable(GL_CULL_FACE);
448  break;
449  case CullMode::kFrontFace:
450  gl.Enable(GL_CULL_FACE);
451  gl.CullFace(GL_FRONT);
452  break;
453  case CullMode::kBackFace:
454  gl.Enable(GL_CULL_FACE);
455  gl.CullFace(GL_BACK);
456  break;
457  }
458  current_cull_mode = pipeline_cull_mode;
459  }
460 
461  //--------------------------------------------------------------------------
462  /// Setup winding order. The pipeline's winding is inverted when
463  /// `flip_y` is in effect (the vertex flip reverses the rasterizer's
464  /// view of winding).
465  WindingOrder pipeline_winding_order =
466  pipeline.GetDescriptor().GetWindingOrder();
467  if (current_winding_order != pipeline_winding_order) {
468  switch (pipeline.GetDescriptor().GetWindingOrder()) {
469  case WindingOrder::kClockwise:
470  gl.FrontFace(flip_y ? GL_CCW : GL_CW);
471  break;
472  case WindingOrder::kCounterClockwise:
473  gl.FrontFace(flip_y ? GL_CW : GL_CCW);
474  break;
475  }
476  current_winding_order = pipeline_winding_order;
477  }
478 
479  BufferBindingsGLES* vertex_desc_gles = pipeline.GetBufferBindings();
480 
481  //--------------------------------------------------------------------------
482  /// Bind vertex buffers.
483  ///
484  /// Note: There is no need to run `RenderPass::ValidateVertexBuffers` or
485  /// `RenderPass::ValidateIndexBuffer` here, as validation already runs
486  /// when the vertex/index buffers are set on the command.
487  ///
488  for (size_t i = 0; i < command.vertex_buffers.length; i++) {
489  if (!BindVertexBuffer(gl, vertex_desc_gles,
490  vertex_buffers[i + command.vertex_buffers.offset],
491  i)) {
492  return false;
493  }
494  }
495 
496  //--------------------------------------------------------------------------
497  /// Bind the pipeline program.
498  ///
499  if (!pipeline.BindProgram()) {
500  return false;
501  }
502 
503  //--------------------------------------------------------------------------
504  /// Bind the y-flip uniform if the vertex shader declares it.
505  const GLint y_flip_loc = pipeline.GetYFlipUniformLocation();
506  if (y_flip_loc >= 0) {
507  gl.Uniform1fv(y_flip_loc, 1, &y_flip_value);
508  }
509 
510  //--------------------------------------------------------------------------
511  /// Bind uniform data.
512  ///
513  if (!vertex_desc_gles->BindUniformData(
514  gl, //
515  bound_textures, //
516  bound_buffers, //
517  /*texture_range=*/command.bound_textures, //
518  /*buffer_range=*/command.bound_buffers //
519  )) {
520  return false;
521  }
522 
523  //--------------------------------------------------------------------------
524  /// Determine the primitive type.
525  ///
526  // GLES doesn't support setting the fill mode, so override the primitive
527  // with GL_LINE_STRIP to somewhat emulate PolygonMode::kLine. This isn't
528  // correct; full triangle outlines won't be drawn and disconnected
529  // geometry may appear connected. However this can still be useful for
530  // wireframe debug views.
531  GLenum mode =
532  pipeline.GetDescriptor().GetPolygonMode() == PolygonMode::kLine
533  ? GL_LINE_STRIP
534  : ToMode(pipeline.GetDescriptor().GetPrimitiveType());
535 
536  //--------------------------------------------------------------------------
537  /// Finally! Invoke the draw call.
538  ///
539  /// An instanced draw uses the hardware instanced entry points when the
540  /// driver exposes them (ES 3.0 core, or GL_EXT_instanced_arrays on ES
541  /// 2.0). When it does not, the draw is emulated by repeating it once per
542  /// instance, re-pointing the instance-rate vertex attributes at each
543  /// instance in between.
544  ///
545  const bool instanced = command.instance_count > 1u;
546  const bool hardware_instanced =
547  instanced &&
548  (gl.DrawArraysInstanced.IsAvailable() ||
549  gl.DrawArraysInstancedEXT.IsAvailable()) &&
550  (gl.DrawElementsInstanced.IsAvailable() ||
551  gl.DrawElementsInstancedEXT.IsAvailable()) &&
552  (gl.VertexAttribDivisor.IsAvailable() ||
553  gl.VertexAttribDivisorEXT.IsAvailable());
554  const bool emulate_instanced = instanced && !hardware_instanced;
555  const GLsizei instance_count = static_cast<GLsizei>(command.instance_count);
556 
557  // A draw is indexed only when a real index type was set. A command that
558  // never bound an index buffer leaves `index_type` at its `kUnknown`
559  // default, which must be treated as non-indexed (matching the Metal and
560  // Vulkan backends, which key off the presence of the index buffer).
561  const bool is_indexed = command.index_type != IndexType::kNone &&
562  command.index_type != IndexType::kUnknown;
563 
564  // Bind the index buffer once, before any (possibly repeated) draw.
565  const GLvoid* index_offset = nullptr;
566  if (is_indexed) {
567  auto index_buffer_view = command.index_buffer;
568  const DeviceBuffer* index_buffer = index_buffer_view.GetBuffer();
569  const auto& index_buffer_gles = DeviceBufferGLES::Cast(*index_buffer);
570  if (!index_buffer_gles.BindAndUploadDataIfNecessary(
571  DeviceBufferGLES::BindingType::kElementArrayBuffer)) {
572  return false;
573  }
574  index_offset = reinterpret_cast<const GLvoid*>(
575  static_cast<uintptr_t>(index_buffer_view.GetRange().offset));
576  }
577 
578  // A non-instanced draw of the bound geometry. Used directly for ordinary
579  // draws and once per instance when emulating instancing.
580  const auto draw_geometry = [&]() {
581  if (!is_indexed) {
582  gl.DrawArrays(mode, command.base_vertex, command.element_count);
583  } else {
584  gl.DrawElements(mode, command.element_count,
585  ToIndexType(command.index_type), index_offset);
586  }
587  };
588 
589  if (command.instance_count == 0u) {
590  // A zero instance count draws nothing, matching the Metal and Vulkan
591  // backends.
592  } else if (emulate_instanced) {
593  // Repeat the draw once per instance, re-pointing the instance-rate
594  // vertex attributes at each instance in between. The vertex buffers
595  // were already bound above for instance 0.
596  for (size_t instance = 0; instance < command.instance_count; instance++) {
597  if (instance > 0u) {
598  for (size_t i = 0; i < command.vertex_buffers.length; i++) {
599  if (!BindVertexBuffer(
600  gl, vertex_desc_gles,
601  vertex_buffers[i + command.vertex_buffers.offset], i,
602  instance)) {
603  return false;
604  }
605  }
606  }
607  draw_geometry();
608  }
609  } else if (hardware_instanced) {
610  // A single instanced call covers every instance.
611  if (!is_indexed) {
612  const auto& gl_draw_arrays_instanced =
613  gl.DrawArraysInstanced.IsAvailable() ? gl.DrawArraysInstanced
614  : gl.DrawArraysInstancedEXT;
615  gl_draw_arrays_instanced(mode, command.base_vertex,
616  command.element_count, instance_count);
617  } else {
618  const auto& gl_draw_elements_instanced =
619  gl.DrawElementsInstanced.IsAvailable()
620  ? gl.DrawElementsInstanced
621  : gl.DrawElementsInstancedEXT;
622  gl_draw_elements_instanced(mode, command.element_count,
623  ToIndexType(command.index_type),
624  index_offset, instance_count);
625  }
626  } else {
627  draw_geometry();
628  }
629 
630  //--------------------------------------------------------------------------
631  /// Unbind vertex attribs.
632  ///
633  if (!vertex_desc_gles->UnbindVertexAttributes(gl)) {
634  return false;
635  }
636  }
637 
638  if (pass_data.resolve_attachment &&
639  !gl.GetCapabilities()->SupportsImplicitResolvingMSAA() &&
640  !is_wrapped_fbo) {
641  FML_DCHECK(pass_data.resolve_attachment != pass_data.color_attachment);
642  TextureGLES& resolve_gles =
643  TextureGLES::Cast(*pass_data.resolve_attachment);
644  std::optional<GLuint> resolve_fbo_opt;
645 
646  if (!resolve_gles.GetCachedFBO().IsDead()) {
647  resolve_fbo_opt = reactor.GetGLHandle(resolve_gles.GetCachedFBO());
648  }
649 
650  if (!resolve_fbo_opt.has_value()) {
651  HandleGLES cached_fbo =
652  reactor.CreateUntrackedHandle(HandleType::kFrameBuffer);
653  resolve_gles.SetCachedFBO(cached_fbo);
654  resolve_fbo_opt = reactor.GetGLHandle(cached_fbo);
655  gl.BindFramebuffer(GL_FRAMEBUFFER, resolve_fbo_opt.value());
656 
657  if (!resolve_gles.SetAsFramebufferAttachment(
658  GL_FRAMEBUFFER, TextureGLES::AttachmentType::kColor0)) {
659  return false;
660  }
661 
662  auto status = gl.CheckFramebufferStatusDebug(GL_FRAMEBUFFER);
663  if (status != GL_FRAMEBUFFER_COMPLETE) {
664  VALIDATION_LOG << "Could not create a complete frambuffer: "
665  << DebugToFramebufferError(status);
666  return false;
667  }
668  } else {
669  gl.BindFramebuffer(GL_FRAMEBUFFER, resolve_fbo_opt.value());
670  }
671 
672  // Bind MSAA renderbuffer to read framebuffer.
673  gl.BindFramebuffer(GL_READ_FRAMEBUFFER, fbo.value());
674  gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo_opt.value());
675 
676  RenderPassGLES::ResetGLState(gl);
677  auto size = pass_data.color_attachment->GetSize();
678 
679  gl.BlitFramebuffer(/*srcX0=*/0,
680  /*srcY0=*/0,
681  /*srcX1=*/size.width,
682  /*srcY1=*/size.height,
683  /*dstX0=*/0,
684  /*dstY0=*/0,
685  /*dstX1=*/size.width,
686  /*dstY1=*/size.height,
687  /*mask=*/GL_COLOR_BUFFER_BIT,
688  /*filter=*/GL_NEAREST);
689 
690  gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, GL_NONE);
691  gl.BindFramebuffer(GL_READ_FRAMEBUFFER, GL_NONE);
692  // Rebind the original FBO so that we can discard it below.
693  gl.BindFramebuffer(GL_FRAMEBUFFER, fbo.value());
694  }
695 
696  GLint framebuffer_id = 0;
697  gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer_id);
698  const bool is_default_fbo = framebuffer_id == 0;
699 
700  if (gl.InvalidateFramebuffer.IsAvailable()) {
701  std::array<GLenum, 3> attachments;
702  size_t attachment_count = 0;
703 
704  bool angle_safe = gl.GetCapabilities()->IsANGLE() ? !is_default_fbo : true;
705 
706  if (pass_data.discard_color_attachment) {
707  attachments[attachment_count++] =
708  (is_default_fbo ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0);
709  }
710 
711  if (pass_data.discard_depth_attachment && angle_safe) {
712  attachments[attachment_count++] =
713  (is_default_fbo ? GL_DEPTH_EXT : GL_DEPTH_ATTACHMENT);
714  }
715 
716  if (pass_data.discard_stencil_attachment && angle_safe) {
717  attachments[attachment_count++] =
718  (is_default_fbo ? GL_STENCIL_EXT : GL_STENCIL_ATTACHMENT);
719  }
720  gl.InvalidateFramebuffer(GL_FRAMEBUFFER, // target
721  attachment_count, // attachments to discard
722  attachments.data() // size
723  );
724  } else if (gl.DiscardFramebufferEXT.IsAvailable()) {
725  std::array<GLenum, 3> attachments;
726  size_t attachment_count = 0;
727 
728  // TODO(130048): discarding stencil or depth on the default fbo causes Angle
729  // to discard the entire render target. Until we know the reason, default to
730  // storing.
731  bool angle_safe = gl.GetCapabilities()->IsANGLE() ? !is_default_fbo : true;
732 
733  if (pass_data.discard_color_attachment) {
734  attachments[attachment_count++] =
735  (is_default_fbo ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0);
736  }
737 
738  if (pass_data.discard_depth_attachment && angle_safe) {
739  attachments[attachment_count++] =
740  (is_default_fbo ? GL_DEPTH_EXT : GL_DEPTH_ATTACHMENT);
741  }
742 
743  if (pass_data.discard_stencil_attachment && angle_safe) {
744  attachments[attachment_count++] =
745  (is_default_fbo ? GL_STENCIL_EXT : GL_STENCIL_ATTACHMENT);
746  }
747  gl.DiscardFramebufferEXT(GL_FRAMEBUFFER, // target
748  attachment_count, // attachments to discard
749  attachments.data() // size
750  );
751  }
752 
753 #ifdef IMPELLER_DEBUG
754  if (is_default_fbo) {
755  tracer->MarkFrameEnd(gl);
756  }
757 #endif // IMPELLER_DEBUG
758 
759  return true;
760 }
761 
762 // |RenderPass|
763 bool RenderPassGLES::OnEncodeCommands(const Context& context) const {
764  if (!IsValid()) {
765  return false;
766  }
767  const auto& render_target = GetRenderTarget();
768  if (!render_target.HasColorAttachment(0u)) {
769  return false;
770  }
771  const ColorAttachment& color0 = render_target.GetColorAttachment(0);
772  const std::optional<DepthAttachment>& depth0 =
773  render_target.GetDepthAttachment();
774  const std::optional<StencilAttachment>& stencil0 =
775  render_target.GetStencilAttachment();
776 
777  auto pass_data = std::make_shared<RenderPassData>();
778  pass_data->label = label_;
779  pass_data->viewport.rect = Rect::MakeSize(GetRenderTargetSize());
780 
781  //----------------------------------------------------------------------------
782  /// Setup color data.
783  ///
784  pass_data->color_attachment = color0.texture;
785  pass_data->resolve_attachment = color0.resolve_texture;
786  pass_data->color_mip_level = color0.mip_level;
787  pass_data->color_slice = color0.slice;
788  pass_data->clear_color = color0.clear_color;
789  pass_data->clear_color_attachment = CanClearAttachment(color0.load_action);
790  pass_data->discard_color_attachment =
791  CanDiscardAttachmentWhenDone(color0.store_action);
792 
793  // When we are using EXT_multisampled_render_to_texture, it is implicitly
794  // resolved when we bind the texture to the framebuffer. We don't need to
795  // discard the attachment when we are done. If not using
796  // EXT_multisampled_render_to_texture but still using MSAA we discard the
797  // attachment as normal.
798  if (color0.resolve_texture) {
799  pass_data->discard_color_attachment =
800  pass_data->discard_color_attachment &&
801  !context.GetCapabilities()->SupportsImplicitResolvingMSAA();
802  }
803 
804  //----------------------------------------------------------------------------
805  /// Setup depth data.
806  ///
807  if (depth0.has_value()) {
808  pass_data->depth_attachment = depth0->texture;
809  pass_data->depth_mip_level = depth0->mip_level;
810  pass_data->depth_slice = depth0->slice;
811  pass_data->clear_depth = depth0->clear_depth;
812  pass_data->clear_depth_attachment = CanClearAttachment(depth0->load_action);
813  pass_data->discard_depth_attachment =
814  CanDiscardAttachmentWhenDone(depth0->store_action);
815  }
816 
817  //----------------------------------------------------------------------------
818  /// Setup stencil data.
819  ///
820  if (stencil0.has_value()) {
821  pass_data->stencil_attachment = stencil0->texture;
822  pass_data->stencil_mip_level = stencil0->mip_level;
823  pass_data->stencil_slice = stencil0->slice;
824  pass_data->clear_stencil = stencil0->clear_stencil;
825  pass_data->clear_stencil_attachment =
826  CanClearAttachment(stencil0->load_action);
827  pass_data->discard_stencil_attachment =
828  CanDiscardAttachmentWhenDone(stencil0->store_action);
829  }
830 
831  return reactor_->AddOperation(
832  [pass_data = std::move(pass_data), render_pass = shared_from_this(),
833  tracer =
834  ContextGLES::Cast(context).GetGPUTracer()](const auto& reactor) {
835  auto result = EncodeCommandsInReactor(
836  /*pass_data=*/*pass_data, //
837  /*reactor=*/reactor, //
838  /*commands=*/render_pass->commands_, //
839  /*vertex_buffers=*/render_pass->vertex_buffers_, //
840  /*bound_textures=*/render_pass->bound_textures_, //
841  /*bound_buffers=*/render_pass->bound_buffers_, //
842  /*tracer=*/tracer, //
843  /*impeller_context=*/render_pass->context_);
844  FML_CHECK(result)
845  << "Must be able to encode GL commands without error.";
846  },
847  /*defer=*/true);
848 }
849 
850 } // namespace impeller
Sets up stage bindings for single draw call in the OpenGLES backend.
bool BindVertexAttributes(const ProcTableGLES &gl, size_t binding, size_t vertex_offset, size_t instance=0)
bool UnbindVertexAttributes(const ProcTableGLES &gl)
bool BindUniformData(const ProcTableGLES &gl, const std::vector< TextureAndSampler > &bound_textures, const std::vector< BufferResource > &bound_buffers, Range texture_range, Range buffer_range)
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition: handle_gles.h:42
constexpr bool IsDead() const
Determines if the handle is dead.
Definition: handle_gles.h:58
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
const HandleGLES & GetCachedFBO() const
Retrieve the cached FBO object, or a dead handle if there is no object.
bool CachedFBOMatchesSubresource(uint32_t mip_level, uint32_t slice) const
void SetCachedFBOSubresource(uint32_t mip_level, uint32_t slice)
Records the subresource the cached FBO is currently bound to.
void SetCachedFBO(HandleGLES fbo)
bool SetAsFramebufferAttachment(GLenum target, AttachmentType attachment_type, uint32_t mip_level=0, uint32_t slice=0)
std::optional< GLuint > GetFBO() const
bool IsWrapped() const
bool EncodeCommandsInReactor(const RenderPassData &pass_data, const ReactorGLES &reactor, const std::vector< Command > &commands, const std::vector< BufferView > &vertex_buffers, const std::vector< TextureAndSampler > &bound_textures, const std::vector< BufferResource > &bound_buffers, const std::shared_ptr< GPUTracerGLES > &tracer, const std::shared_ptr< const Context > &impeller_context)
constexpr bool CanClearAttachment(LoadAction action)
Definition: formats.h:427
float Scalar
Definition: scalar.h:19
constexpr GLenum ToIndexType(IndexType type)
Definition: formats_gles.h:35
static void EncodeViewport(const ProcTableGLES &gl, const RenderPassData &pass_data, const std::optional< Viewport > &command_viewport, const ISize &target_size, bool flip_y, std::optional< Viewport > &current_viewport)
constexpr GLenum ToCompareFunction(CompareFunction func)
Definition: formats_gles.h:70
std::string DebugToFramebufferError(int status)
Definition: formats_gles.cc:59
constexpr GLenum ToStencilOp(StencilOperation op)
Definition: formats_gles.h:48
constexpr GLenum ToMode(PrimitiveType primitive_type)
Definition: formats_gles.h:17
WindingOrder
Definition: formats.h:22
static bool BindVertexBuffer(const ProcTableGLES &gl, BufferBindingsGLES *vertex_desc_gles, const BufferView &vertex_buffer_view, size_t buffer_index, size_t instance=0)
void ConfigureBlending(const ProcTableGLES &gl, const ColorAttachmentDescriptor *color)
void ConfigureStencil(const ProcTableGLES &gl, const PipelineDescriptor &pipeline, uint32_t stencil_reference)
constexpr GLenum ToBlendFactor(BlendFactor factor)
Definition: formats_gles.h:92
constexpr GLenum ToBlendOperation(BlendOperation op)
Definition: formats_gles.h:128
constexpr bool CanDiscardAttachmentWhenDone(StoreAction action)
Definition: formats.h:438
Definition: comparable.h:93
std::shared_ptr< ReactorGLES > reactor
std::shared_ptr< ContextGLES > context
std::shared_ptr< RenderPass > render_pass
std::shared_ptr< PipelineGLES > pipeline
Range GetRange() const
Definition: buffer_view.h:27
const DeviceBuffer * GetBuffer() const
Definition: buffer_view.cc:17
Describe the color attachment that will be used with this pipeline.
Definition: formats.h:770
Scalar blue
Definition: color.h:138
Scalar alpha
Definition: color.h:143
Scalar red
Definition: color.h:128
Scalar green
Definition: color.h:133
size_t offset
Definition: range.h:14
Encapsulates data that will be needed in the reactor for the encoding of commands for this render pas...
std::shared_ptr< Texture > depth_attachment
std::shared_ptr< Texture > color_attachment
std::shared_ptr< Texture > stencil_attachment
std::shared_ptr< Texture > resolve_attachment
StencilOperation depth_stencil_pass
Definition: formats.h:881
Type height
Definition: size.h:29
#define VALIDATION_LOG
Definition: validation.h:91