Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
10#include "fml/closure.h"
11#include "fml/logging.h"
23
24namespace impeller {
25
26RenderPassGLES::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|
34RenderPassGLES::~RenderPassGLES() = default;
35
36// |RenderPass|
37bool RenderPassGLES::IsValid() const {
38 return is_valid_;
39}
40
41// |RenderPass|
42void 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
79void 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
156static 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
188void 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
201static 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|
763bool 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
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
fml::closure Release()
Definition closure.h:58
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.
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
uint32_t framebuffer_id
VkInstance instance
Definition main.cc:64
uint32_t * target
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
static void BindVertexBuffer(flutter::gpu::RenderPass *wrapper, const std::shared_ptr< const impeller::DeviceBuffer > &buffer, int offset_in_bytes, int length_in_bytes, int slot)
constexpr bool CanClearAttachment(LoadAction action)
Definition formats.h:427
float Scalar
Definition scalar.h:19
constexpr GLenum ToIndexType(IndexType type)
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)
std::string DebugToFramebufferError(int status)
constexpr GLenum ToStencilOp(StencilOperation op)
constexpr GLenum ToMode(PrimitiveType primitive_type)
WindingOrder
Definition formats.h:22
void ConfigureBlending(const ProcTableGLES &gl, const ColorAttachmentDescriptor *color)
bool EncodeCommandsInReactor(const ReactorGLES &reactor, const std::vector< std::unique_ptr< BlitEncodeGLES > > &commands, const std::string &label)
constexpr GLenum ToBlendFactor(BlendFactor factor)
void ConfigureStencil(GLenum face, const ProcTableGLES &gl, const StencilAttachmentDescriptor &stencil, uint32_t stencil_reference)
constexpr GLenum ToBlendOperation(BlendOperation op)
constexpr bool CanDiscardAttachmentWhenDone(StoreAction action)
Definition formats.h:438
Definition ref_ptr.h:261
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
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
Type height
Definition size.h:29
#define TRACE_EVENT0(category_group, name)
#define VALIDATION_LOG
Definition validation.h:91