| // 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 "impeller/entity/contents/content_context.h" |
| |
| #include <memory> |
| |
| #include "impeller/base/strings.h" |
| #include "impeller/core/formats.h" |
| #include "impeller/entity/contents/framebuffer_blend_contents.h" |
| #include "impeller/entity/entity.h" |
| #include "impeller/entity/render_target_cache.h" |
| #include "impeller/renderer/command_buffer.h" |
| #include "impeller/renderer/pipeline_descriptor.h" |
| #include "impeller/renderer/pipeline_library.h" |
| #include "impeller/renderer/render_target.h" |
| #include "impeller/tessellator/tessellator.h" |
| #include "impeller/typographer/typographer_context.h" |
| |
| namespace impeller { |
| |
| void ContentContextOptions::ApplyToPipelineDescriptor( |
| PipelineDescriptor& desc) const { |
| auto pipeline_blend = blend_mode; |
| if (blend_mode > Entity::kLastPipelineBlendMode) { |
| VALIDATION_LOG << "Cannot use blend mode " << static_cast<int>(blend_mode) |
| << " as a pipeline blend."; |
| pipeline_blend = BlendMode::kSourceOver; |
| } |
| |
| desc.SetSampleCount(sample_count); |
| |
| ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u); |
| color0.format = color_attachment_pixel_format; |
| color0.alpha_blend_op = BlendOperation::kAdd; |
| color0.color_blend_op = BlendOperation::kAdd; |
| |
| switch (pipeline_blend) { |
| case BlendMode::kClear: |
| if (is_for_rrect_blur_clear) { |
| color0.alpha_blend_op = BlendOperation::kReverseSubtract; |
| color0.color_blend_op = BlendOperation::kReverseSubtract; |
| color0.dst_alpha_blend_factor = BlendFactor::kOne; |
| color0.dst_color_blend_factor = BlendFactor::kOne; |
| color0.src_alpha_blend_factor = BlendFactor::kDestinationColor; |
| color0.src_color_blend_factor = BlendFactor::kDestinationColor; |
| } else { |
| color0.dst_alpha_blend_factor = BlendFactor::kZero; |
| color0.dst_color_blend_factor = BlendFactor::kZero; |
| color0.src_alpha_blend_factor = BlendFactor::kZero; |
| color0.src_color_blend_factor = BlendFactor::kZero; |
| } |
| break; |
| case BlendMode::kSource: |
| color0.blending_enabled = false; |
| color0.dst_alpha_blend_factor = BlendFactor::kZero; |
| color0.dst_color_blend_factor = BlendFactor::kZero; |
| color0.src_alpha_blend_factor = BlendFactor::kOne; |
| color0.src_color_blend_factor = BlendFactor::kOne; |
| break; |
| case BlendMode::kDestination: |
| color0.dst_alpha_blend_factor = BlendFactor::kOne; |
| color0.dst_color_blend_factor = BlendFactor::kOne; |
| color0.src_alpha_blend_factor = BlendFactor::kZero; |
| color0.src_color_blend_factor = BlendFactor::kZero; |
| break; |
| case BlendMode::kSourceOver: |
| color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.src_alpha_blend_factor = BlendFactor::kOne; |
| color0.src_color_blend_factor = BlendFactor::kOne; |
| break; |
| case BlendMode::kDestinationOver: |
| color0.dst_alpha_blend_factor = BlendFactor::kOne; |
| color0.dst_color_blend_factor = BlendFactor::kOne; |
| color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| break; |
| case BlendMode::kSourceIn: |
| color0.dst_alpha_blend_factor = BlendFactor::kZero; |
| color0.dst_color_blend_factor = BlendFactor::kZero; |
| color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; |
| color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; |
| break; |
| case BlendMode::kDestinationIn: |
| color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; |
| color0.src_alpha_blend_factor = BlendFactor::kZero; |
| color0.src_color_blend_factor = BlendFactor::kZero; |
| break; |
| case BlendMode::kSourceOut: |
| color0.dst_alpha_blend_factor = BlendFactor::kZero; |
| color0.dst_color_blend_factor = BlendFactor::kZero; |
| color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| break; |
| case BlendMode::kDestinationOut: |
| color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.src_alpha_blend_factor = BlendFactor::kZero; |
| color0.src_color_blend_factor = BlendFactor::kZero; |
| break; |
| case BlendMode::kSourceATop: |
| color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; |
| color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; |
| break; |
| case BlendMode::kDestinationATop: |
| color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; |
| color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| break; |
| case BlendMode::kXor: |
| color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; |
| color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; |
| break; |
| case BlendMode::kPlus: |
| color0.dst_alpha_blend_factor = BlendFactor::kOne; |
| color0.dst_color_blend_factor = BlendFactor::kOne; |
| color0.src_alpha_blend_factor = BlendFactor::kOne; |
| color0.src_color_blend_factor = BlendFactor::kOne; |
| break; |
| case BlendMode::kModulate: |
| color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; |
| color0.dst_color_blend_factor = BlendFactor::kSourceColor; |
| color0.src_alpha_blend_factor = BlendFactor::kZero; |
| color0.src_color_blend_factor = BlendFactor::kZero; |
| break; |
| default: |
| FML_UNREACHABLE(); |
| } |
| desc.SetColorAttachmentDescriptor(0u, color0); |
| |
| if (!has_stencil_attachment) { |
| desc.ClearStencilAttachments(); |
| } |
| |
| auto maybe_stencil = desc.GetFrontStencilAttachmentDescriptor(); |
| if (maybe_stencil.has_value()) { |
| StencilAttachmentDescriptor stencil = maybe_stencil.value(); |
| stencil.stencil_compare = stencil_compare; |
| stencil.depth_stencil_pass = stencil_operation; |
| desc.SetStencilAttachmentDescriptors(stencil); |
| } |
| |
| desc.SetPrimitiveType(primitive_type); |
| |
| desc.SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill); |
| } |
| |
| template <typename PipelineT> |
| static std::unique_ptr<PipelineT> CreateDefaultPipeline( |
| const Context& context) { |
| auto desc = PipelineT::Builder::MakeDefaultPipelineDescriptor(context); |
| if (!desc.has_value()) { |
| return nullptr; |
| } |
| // Apply default ContentContextOptions to the descriptor. |
| const auto default_color_format = |
| context.GetCapabilities()->GetDefaultColorFormat(); |
| ContentContextOptions{.sample_count = SampleCount::kCount4, |
| .primitive_type = PrimitiveType::kTriangleStrip, |
| .color_attachment_pixel_format = default_color_format} |
| .ApplyToPipelineDescriptor(*desc); |
| return std::make_unique<PipelineT>(context, desc); |
| } |
| |
| ContentContext::ContentContext( |
| std::shared_ptr<Context> context, |
| std::shared_ptr<TypographerContext> typographer_context, |
| std::shared_ptr<RenderTargetAllocator> render_target_allocator) |
| : context_(std::move(context)), |
| lazy_glyph_atlas_( |
| std::make_shared<LazyGlyphAtlas>(std::move(typographer_context))), |
| tessellator_(std::make_shared<Tessellator>()), |
| #if IMPELLER_ENABLE_3D |
| scene_context_(std::make_shared<scene::SceneContext>(context_)), |
| #endif // IMPELLER_ENABLE_3D |
| render_target_cache_(render_target_allocator == nullptr |
| ? std::make_shared<RenderTargetCache>( |
| context_->GetResourceAllocator()) |
| : std::move(render_target_allocator)) { |
| if (!context_ || !context_->IsValid()) { |
| return; |
| } |
| auto options = ContentContextOptions{ |
| .sample_count = SampleCount::kCount4, |
| .color_attachment_pixel_format = |
| context_->GetCapabilities()->GetDefaultColorFormat()}; |
| auto options_trianglestrip = ContentContextOptions{ |
| .sample_count = SampleCount::kCount4, |
| .primitive_type = PrimitiveType::kTriangleStrip, |
| .color_attachment_pixel_format = |
| context_->GetCapabilities()->GetDefaultColorFormat()}; |
| const auto supports_decal = |
| context_->GetCapabilities()->SupportsDecalSamplerAddressMode(); |
| |
| #ifdef IMPELLER_DEBUG |
| checkerboard_pipelines_.CreateDefault(*context_, options); |
| #endif // IMPELLER_DEBUG |
| |
| solid_fill_pipelines_.CreateDefault(*context_, options); |
| |
| if (context_->GetCapabilities()->SupportsSSBO()) { |
| linear_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); |
| radial_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); |
| conical_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); |
| sweep_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); |
| } else { |
| linear_gradient_fill_pipelines_.CreateDefault(*context_, options); |
| radial_gradient_fill_pipelines_.CreateDefault(*context_, options); |
| conical_gradient_fill_pipelines_.CreateDefault(*context_, options); |
| sweep_gradient_fill_pipelines_.CreateDefault(*context_, options); |
| } |
| |
| if (context_->GetCapabilities()->SupportsFramebufferFetch()) { |
| framebuffer_blend_color_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kColor), supports_decal}); |
| framebuffer_blend_colorburn_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kColorBurn), supports_decal}); |
| framebuffer_blend_colordodge_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kColorDodge), supports_decal}); |
| framebuffer_blend_darken_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kDarken), supports_decal}); |
| framebuffer_blend_difference_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kDifference), supports_decal}); |
| framebuffer_blend_exclusion_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kExclusion), supports_decal}); |
| framebuffer_blend_hardlight_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kHardLight), supports_decal}); |
| framebuffer_blend_hue_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kHue), supports_decal}); |
| framebuffer_blend_lighten_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kLighten), supports_decal}); |
| framebuffer_blend_luminosity_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kLuminosity), supports_decal}); |
| framebuffer_blend_multiply_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kMultiply), supports_decal}); |
| framebuffer_blend_overlay_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kOverlay), supports_decal}); |
| framebuffer_blend_saturation_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kSaturation), supports_decal}); |
| framebuffer_blend_screen_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kScreen), supports_decal}); |
| framebuffer_blend_softlight_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kSoftLight), supports_decal}); |
| } |
| |
| blend_color_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kColor), supports_decal}); |
| blend_colorburn_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kColorBurn), supports_decal}); |
| blend_colordodge_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kColorDodge), supports_decal}); |
| blend_darken_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kDarken), supports_decal}); |
| blend_difference_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kDifference), supports_decal}); |
| blend_exclusion_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kExclusion), supports_decal}); |
| blend_hardlight_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kHardLight), supports_decal}); |
| blend_hue_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kHue), supports_decal}); |
| blend_lighten_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kLighten), supports_decal}); |
| blend_luminosity_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kLuminosity), supports_decal}); |
| blend_multiply_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kMultiply), supports_decal}); |
| blend_overlay_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kOverlay), supports_decal}); |
| blend_saturation_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kSaturation), supports_decal}); |
| blend_screen_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kScreen), supports_decal}); |
| blend_softlight_pipelines_.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<int32_t>(BlendSelectValues::kSoftLight), supports_decal}); |
| |
| rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); |
| texture_blend_pipelines_.CreateDefault(*context_, options); |
| texture_pipelines_.CreateDefault(*context_, options); |
| position_uv_pipelines_.CreateDefault(*context_, options); |
| tiled_texture_pipelines_.CreateDefault(*context_, options); |
| gaussian_blur_noalpha_decal_pipelines_.CreateDefault(*context_, |
| options_trianglestrip); |
| gaussian_blur_noalpha_nodecal_pipelines_.CreateDefault(*context_, |
| options_trianglestrip); |
| border_mask_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); |
| morphology_filter_pipelines_.CreateDefault(*context_, options_trianglestrip, |
| {supports_decal}); |
| color_matrix_color_filter_pipelines_.CreateDefault(*context_, |
| options_trianglestrip); |
| linear_to_srgb_filter_pipelines_.CreateDefault(*context_, |
| options_trianglestrip); |
| srgb_to_linear_filter_pipelines_.CreateDefault(*context_, |
| options_trianglestrip); |
| glyph_atlas_pipelines_.CreateDefault(*context_, options); |
| glyph_atlas_color_pipelines_.CreateDefault(*context_, options); |
| geometry_color_pipelines_.CreateDefault(*context_, options); |
| yuv_to_rgb_filter_pipelines_.CreateDefault(*context_, options_trianglestrip); |
| porter_duff_blend_pipelines_.CreateDefault(*context_, options_trianglestrip, |
| {supports_decal}); |
| // GLES only shader that is unsupported on macOS. |
| #if defined(IMPELLER_ENABLE_OPENGLES) && !defined(FML_OS_MACOSX) |
| if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) { |
| texture_external_pipelines_.CreateDefault(*context_, options); |
| } |
| if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) { |
| tiled_texture_external_pipelines_.CreateDefault(*context_, options); |
| } |
| #endif // IMPELLER_ENABLE_OPENGLES |
| if (context_->GetCapabilities()->SupportsCompute()) { |
| auto pipeline_desc = |
| PointsComputeShaderPipeline::MakeDefaultPipelineDescriptor(*context_); |
| point_field_compute_pipelines_ = |
| context_->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); |
| |
| auto uv_pipeline_desc = |
| UvComputeShaderPipeline::MakeDefaultPipelineDescriptor(*context_); |
| uv_compute_pipelines_ = |
| context_->GetPipelineLibrary()->GetPipeline(uv_pipeline_desc).Get(); |
| } |
| |
| /// Setup default clip pipeline. |
| |
| auto clip_pipeline_descriptor = |
| ClipPipeline::Builder::MakeDefaultPipelineDescriptor(*context_); |
| if (!clip_pipeline_descriptor.has_value()) { |
| return; |
| } |
| ContentContextOptions{ |
| .sample_count = SampleCount::kCount4, |
| .color_attachment_pixel_format = |
| context_->GetCapabilities()->GetDefaultColorFormat()} |
| .ApplyToPipelineDescriptor(*clip_pipeline_descriptor); |
| // Disable write to all color attachments. |
| auto clip_color_attachments = |
| clip_pipeline_descriptor->GetColorAttachmentDescriptors(); |
| for (auto& color_attachment : clip_color_attachments) { |
| color_attachment.second.write_mask = |
| static_cast<uint64_t>(ColorWriteMask::kNone); |
| } |
| clip_pipeline_descriptor->SetColorAttachmentDescriptors( |
| std::move(clip_color_attachments)); |
| clip_pipelines_.SetDefault(options, std::make_unique<ClipPipeline>( |
| *context_, clip_pipeline_descriptor)); |
| |
| is_valid_ = true; |
| } |
| |
| ContentContext::~ContentContext() = default; |
| |
| bool ContentContext::IsValid() const { |
| return is_valid_; |
| } |
| |
| std::shared_ptr<Texture> ContentContext::MakeSubpass( |
| const std::string& label, |
| ISize texture_size, |
| const SubpassCallback& subpass_callback, |
| bool msaa_enabled) const { |
| auto context = GetContext(); |
| |
| RenderTarget subpass_target; |
| if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) { |
| subpass_target = RenderTarget::CreateOffscreenMSAA( |
| *context, *GetRenderTargetCache(), texture_size, |
| SPrintF("%s Offscreen", label.c_str()), |
| RenderTarget::kDefaultColorAttachmentConfigMSAA, |
| std::nullopt // stencil_attachment_config |
| ); |
| } else { |
| subpass_target = RenderTarget::CreateOffscreen( |
| *context, *GetRenderTargetCache(), texture_size, |
| SPrintF("%s Offscreen", label.c_str()), |
| RenderTarget::kDefaultColorAttachmentConfig, // |
| std::nullopt // stencil_attachment_config |
| ); |
| } |
| auto subpass_texture = subpass_target.GetRenderTargetTexture(); |
| if (!subpass_texture) { |
| return nullptr; |
| } |
| |
| auto sub_command_buffer = context->CreateCommandBuffer(); |
| sub_command_buffer->SetLabel(SPrintF("%s CommandBuffer", label.c_str())); |
| if (!sub_command_buffer) { |
| return nullptr; |
| } |
| |
| auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); |
| if (!sub_renderpass) { |
| return nullptr; |
| } |
| sub_renderpass->SetLabel(SPrintF("%s RenderPass", label.c_str())); |
| |
| if (!subpass_callback(*this, *sub_renderpass)) { |
| return nullptr; |
| } |
| |
| if (!sub_command_buffer->EncodeAndSubmit(sub_renderpass)) { |
| return nullptr; |
| } |
| |
| return subpass_texture; |
| } |
| |
| #if IMPELLER_ENABLE_3D |
| std::shared_ptr<scene::SceneContext> ContentContext::GetSceneContext() const { |
| return scene_context_; |
| } |
| #endif // IMPELLER_ENABLE_3D |
| |
| std::shared_ptr<Tessellator> ContentContext::GetTessellator() const { |
| return tessellator_; |
| } |
| |
| std::shared_ptr<Context> ContentContext::GetContext() const { |
| return context_; |
| } |
| |
| const Capabilities& ContentContext::GetDeviceCapabilities() const { |
| return *context_->GetCapabilities(); |
| } |
| |
| void ContentContext::SetWireframe(bool wireframe) { |
| wireframe_ = wireframe; |
| } |
| |
| } // namespace impeller |