| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "framebuffer_blend_contents.h" |
| |
| #include "impeller/entity/contents/content_context.h" |
| #include "impeller/renderer/render_pass.h" |
| #include "impeller/renderer/sampler_library.h" |
| |
| namespace impeller { |
| |
| FramebufferBlendContents::FramebufferBlendContents() = default; |
| |
| FramebufferBlendContents::~FramebufferBlendContents() = default; |
| |
| void FramebufferBlendContents::SetBlendMode(BlendMode blend_mode) { |
| blend_mode_ = blend_mode; |
| } |
| |
| void FramebufferBlendContents::SetChildContents( |
| std::shared_ptr<Contents> child_contents) { |
| child_contents_ = std::move(child_contents); |
| } |
| |
| // |Contents| |
| std::optional<Rect> FramebufferBlendContents::GetCoverage( |
| const Entity& entity) const { |
| return child_contents_->GetCoverage(entity); |
| } |
| |
| bool FramebufferBlendContents::Render(const ContentContext& renderer, |
| const Entity& entity, |
| RenderPass& pass) const { |
| if (!renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) { |
| return false; |
| } |
| |
| using VS = FramebufferBlendScreenPipeline::VertexShader; |
| using FS = FramebufferBlendScreenPipeline::FragmentShader; |
| |
| auto& host_buffer = pass.GetTransientsBuffer(); |
| |
| auto src_snapshot = child_contents_->RenderToSnapshot( |
| renderer, // renderer |
| entity, // entity |
| Rect::MakeSize(pass.GetRenderTargetSize()), // coverage_limit |
| std::nullopt, // sampler_descriptor |
| true, // msaa_enabled |
| "FramebufferBlendContents Snapshot"); // label |
| if (!src_snapshot.has_value()) { |
| return true; |
| } |
| auto coverage = src_snapshot->GetCoverage(); |
| if (!coverage.has_value()) { |
| return true; |
| } |
| Rect src_coverage = coverage.value(); |
| auto maybe_src_uvs = src_snapshot->GetCoverageUVs(src_coverage); |
| if (!maybe_src_uvs.has_value()) { |
| return true; |
| } |
| std::array<Point, 4> src_uvs = maybe_src_uvs.value(); |
| |
| auto size = src_coverage.size; |
| VertexBufferBuilder<VS::PerVertexData> vtx_builder; |
| vtx_builder.AddVertices({ |
| {Point(0, 0), src_uvs[0]}, |
| {Point(size.width, 0), src_uvs[1]}, |
| {Point(size.width, size.height), src_uvs[3]}, |
| {Point(0, 0), src_uvs[0]}, |
| {Point(size.width, size.height), src_uvs[3]}, |
| {Point(0, size.height), src_uvs[2]}, |
| }); |
| auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); |
| |
| auto options = OptionsFromPass(pass); |
| options.blend_mode = BlendMode::kSource; |
| |
| Command cmd; |
| cmd.label = "Framebuffer Advanced Blend Filter"; |
| cmd.BindVertices(vtx_buffer); |
| cmd.stencil_reference = entity.GetStencilDepth(); |
| |
| switch (blend_mode_) { |
| case BlendMode::kScreen: |
| cmd.pipeline = renderer.GetFramebufferBlendScreenPipeline(options); |
| break; |
| case BlendMode::kOverlay: |
| cmd.pipeline = renderer.GetFramebufferBlendOverlayPipeline(options); |
| break; |
| case BlendMode::kDarken: |
| cmd.pipeline = renderer.GetFramebufferBlendDarkenPipeline(options); |
| break; |
| case BlendMode::kLighten: |
| cmd.pipeline = renderer.GetFramebufferBlendLightenPipeline(options); |
| break; |
| case BlendMode::kColorDodge: |
| cmd.pipeline = renderer.GetFramebufferBlendColorDodgePipeline(options); |
| break; |
| case BlendMode::kColorBurn: |
| cmd.pipeline = renderer.GetFramebufferBlendColorBurnPipeline(options); |
| break; |
| case BlendMode::kHardLight: |
| cmd.pipeline = renderer.GetFramebufferBlendHardLightPipeline(options); |
| break; |
| case BlendMode::kSoftLight: |
| cmd.pipeline = renderer.GetFramebufferBlendSoftLightPipeline(options); |
| break; |
| case BlendMode::kDifference: |
| cmd.pipeline = renderer.GetFramebufferBlendDifferencePipeline(options); |
| break; |
| case BlendMode::kExclusion: |
| cmd.pipeline = renderer.GetFramebufferBlendExclusionPipeline(options); |
| break; |
| case BlendMode::kMultiply: |
| cmd.pipeline = renderer.GetFramebufferBlendMultiplyPipeline(options); |
| break; |
| case BlendMode::kHue: |
| cmd.pipeline = renderer.GetFramebufferBlendHuePipeline(options); |
| break; |
| case BlendMode::kSaturation: |
| cmd.pipeline = renderer.GetFramebufferBlendSaturationPipeline(options); |
| break; |
| case BlendMode::kColor: |
| cmd.pipeline = renderer.GetFramebufferBlendColorPipeline(options); |
| break; |
| case BlendMode::kLuminosity: |
| cmd.pipeline = renderer.GetFramebufferBlendLuminosityPipeline(options); |
| break; |
| default: |
| return false; |
| } |
| |
| VS::FrameInfo frame_info; |
| FS::FragInfo frag_info; |
| |
| auto src_sampler_descriptor = src_snapshot->sampler_descriptor; |
| if (!renderer.GetDeviceCapabilities().SupportsDecalTileMode()) { |
| // No known devices that support framebuffer fetch but not decal tile mode. |
| return false; |
| } |
| src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; |
| src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; |
| auto src_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( |
| src_sampler_descriptor); |
| FS::BindTextureSamplerSrc(cmd, src_snapshot->texture, src_sampler); |
| |
| frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * |
| src_snapshot->transform; |
| frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale(); |
| VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); |
| |
| frag_info.src_input_alpha = src_snapshot->opacity; |
| FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); |
| |
| return pass.AddCommand(cmd); |
| } |
| |
| } // namespace impeller |