| // 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 <format> |
| #include <memory> |
| #include <utility> |
| |
| #include "fml/trace_event.h" |
| #include "impeller/base/validation.h" |
| #include "impeller/core/formats.h" |
| #include "impeller/core/texture_descriptor.h" |
| #include "impeller/entity/contents/framebuffer_blend_contents.h" |
| #include "impeller/entity/contents/pipelines.h" |
| #include "impeller/entity/contents/text_shadow_cache.h" |
| #include "impeller/entity/entity.h" |
| #include "impeller/entity/render_target_cache.h" |
| #include "impeller/renderer/command_buffer.h" |
| #include "impeller/renderer/pipeline.h" |
| #include "impeller/renderer/pipeline_descriptor.h" |
| #include "impeller/renderer/pipeline_library.h" |
| #include "impeller/renderer/render_target.h" |
| #include "impeller/renderer/texture_util.h" |
| #include "impeller/tessellator/tessellator.h" |
| #include "impeller/typographer/typographer_context.h" |
| |
| namespace impeller { |
| |
| namespace { |
| |
| /// A generic version of `Variants` which mostly exists to reduce code size. |
| class GenericVariants { |
| public: |
| void Set(const ContentContextOptions& options, |
| std::unique_ptr<GenericRenderPipelineHandle> pipeline) { |
| uint64_t p_key = options.ToKey(); |
| for (const auto& [key, pipeline] : pipelines_) { |
| if (key == p_key) { |
| return; |
| } |
| } |
| pipelines_.push_back(std::make_pair(p_key, std::move(pipeline))); |
| } |
| |
| void SetDefault(const ContentContextOptions& options, |
| std::unique_ptr<GenericRenderPipelineHandle> pipeline) { |
| default_options_ = options; |
| if (pipeline) { |
| Set(options, std::move(pipeline)); |
| } |
| } |
| |
| GenericRenderPipelineHandle* Get(const ContentContextOptions& options) const { |
| uint64_t p_key = options.ToKey(); |
| for (const auto& [key, pipeline] : pipelines_) { |
| if (key == p_key) { |
| return pipeline.get(); |
| } |
| } |
| return nullptr; |
| } |
| |
| void SetDefaultDescriptor(std::optional<PipelineDescriptor> desc) { |
| desc_ = std::move(desc); |
| } |
| |
| size_t GetPipelineCount() const { return pipelines_.size(); } |
| |
| bool IsDefault(const ContentContextOptions& opts) { |
| return default_options_.has_value() && |
| opts.ToKey() == default_options_.value().ToKey(); |
| } |
| |
| protected: |
| std::optional<PipelineDescriptor> desc_; |
| std::optional<ContentContextOptions> default_options_; |
| std::vector<std::pair<uint64_t, std::unique_ptr<GenericRenderPipelineHandle>>> |
| pipelines_; |
| }; |
| |
| /// Holds multiple Pipelines associated with the same PipelineHandle types. |
| /// |
| /// For example, it may have multiple |
| /// RenderPipelineHandle<SolidFillVertexShader, SolidFillFragmentShader> |
| /// instances for different blend modes. From them you can access the |
| /// Pipeline. |
| /// |
| /// See also: |
| /// - impeller::ContentContextOptions - options from which variants are |
| /// created. |
| /// - impeller::Pipeline::CreateVariant |
| /// - impeller::RenderPipelineHandle<> - The type of objects this typically |
| /// contains. |
| template <class PipelineHandleT> |
| class Variants : public GenericVariants { |
| static_assert( |
| ShaderStageCompatibilityChecker< |
| typename PipelineHandleT::VertexShader, |
| typename PipelineHandleT::FragmentShader>::Check(), |
| "The output slots for the fragment shader don't have matches in the " |
| "vertex shader's output slots. This will result in a linker error."); |
| |
| public: |
| Variants() = default; |
| |
| void Set(const ContentContextOptions& options, |
| std::unique_ptr<PipelineHandleT> pipeline) { |
| GenericVariants::Set(options, std::move(pipeline)); |
| } |
| |
| void SetDefault(const ContentContextOptions& options, |
| std::unique_ptr<PipelineHandleT> pipeline) { |
| GenericVariants::SetDefault(options, std::move(pipeline)); |
| } |
| |
| void CreateDefault(const Context& context, |
| const ContentContextOptions& options, |
| const std::vector<Scalar>& constants = {}) { |
| std::optional<PipelineDescriptor> desc = |
| PipelineHandleT::Builder::MakeDefaultPipelineDescriptor(context, |
| constants); |
| if (!desc.has_value()) { |
| VALIDATION_LOG << "Failed to create default pipeline."; |
| return; |
| } |
| context.GetPipelineLibrary()->LogPipelineCreation(*desc); |
| options.ApplyToPipelineDescriptor(*desc); |
| desc_ = desc; |
| SetDefault(options, std::make_unique<PipelineHandleT>(context, desc_, |
| /*async=*/true)); |
| } |
| |
| PipelineHandleT* Get(const ContentContextOptions& options) const { |
| return static_cast<PipelineHandleT*>(GenericVariants::Get(options)); |
| } |
| |
| PipelineHandleT* GetDefault(const Context& context) { |
| if (!default_options_.has_value()) { |
| return nullptr; |
| } |
| PipelineHandleT* result = Get(default_options_.value()); |
| if (result != nullptr) { |
| return result; |
| } |
| SetDefault(default_options_.value(), std::make_unique<PipelineHandleT>( |
| context, desc_, /*async=*/false)); |
| // NOLINTNEXTLINE(bugprone-unchecked-optional-access) |
| return Get(default_options_.value()); |
| } |
| |
| private: |
| Variants(const Variants&) = delete; |
| |
| Variants& operator=(const Variants&) = delete; |
| }; |
| |
| template <class RenderPipelineHandleT> |
| RenderPipelineHandleT* CreateIfNeeded( |
| const ContentContext* context, |
| Variants<RenderPipelineHandleT>& container, |
| ContentContextOptions opts, |
| PipelineCompileQueue* compile_queue) { |
| if (!context->IsValid()) { |
| return nullptr; |
| } |
| |
| if (RenderPipelineHandleT* found = container.Get(opts)) { |
| return found; |
| } |
| |
| RenderPipelineHandleT* default_handle = |
| container.GetDefault(*context->GetContext()); |
| if (container.IsDefault(opts)) { |
| return default_handle; |
| } |
| |
| // The default must always be initialized in the constructor. |
| FML_CHECK(default_handle != nullptr); |
| |
| const std::shared_ptr<Pipeline<PipelineDescriptor>>& pipeline = |
| default_handle->WaitAndGet(compile_queue); |
| if (!pipeline) { |
| return nullptr; |
| } |
| |
| auto variant_future = pipeline->CreateVariant( |
| /*async=*/false, [&opts, variants_count = container.GetPipelineCount()]( |
| PipelineDescriptor& desc) { |
| opts.ApplyToPipelineDescriptor(desc); |
| desc.SetLabel(std::format("{} V#{}", desc.GetLabel(), variants_count)); |
| }); |
| std::unique_ptr<RenderPipelineHandleT> variant = |
| std::make_unique<RenderPipelineHandleT>(std::move(variant_future)); |
| container.Set(opts, std::move(variant)); |
| return container.Get(opts); |
| } |
| |
| template <class TypedPipeline> |
| PipelineRef GetPipeline(const ContentContext* context, |
| Variants<TypedPipeline>& container, |
| ContentContextOptions opts) { |
| auto compile_queue = |
| context->GetContext()->GetPipelineLibrary()->GetPipelineCompileQueue(); |
| TypedPipeline* pipeline = |
| CreateIfNeeded(context, container, opts, compile_queue); |
| if (!pipeline) { |
| return raw_ptr<Pipeline<PipelineDescriptor>>(); |
| } |
| return raw_ptr(pipeline->WaitAndGet(compile_queue)); |
| } |
| |
| } // namespace |
| |
| struct ContentContext::Pipelines { |
| // clang-format off |
| Variants<BlendColorBurnPipeline> blend_colorburn; |
| Variants<BlendColorDodgePipeline> blend_colordodge; |
| Variants<BlendColorPipeline> blend_color; |
| Variants<BlendDarkenPipeline> blend_darken; |
| Variants<BlendDifferencePipeline> blend_difference; |
| Variants<BlendExclusionPipeline> blend_exclusion; |
| Variants<BlendHardLightPipeline> blend_hardlight; |
| Variants<BlendHuePipeline> blend_hue; |
| Variants<BlendLightenPipeline> blend_lighten; |
| Variants<BlendLuminosityPipeline> blend_luminosity; |
| Variants<BlendMultiplyPipeline> blend_multiply; |
| Variants<BlendOverlayPipeline> blend_overlay; |
| Variants<BlendSaturationPipeline> blend_saturation; |
| Variants<BlendScreenPipeline> blend_screen; |
| Variants<BlendSoftLightPipeline> blend_softlight; |
| Variants<BorderMaskBlurPipeline> border_mask_blur; |
| Variants<CirclePipeline> circle; |
| Variants<ClipPipeline> clip; |
| Variants<ColorMatrixColorFilterPipeline> color_matrix_color_filter; |
| Variants<ConicalGradientFillConicalPipeline> conical_gradient_fill; |
| Variants<ConicalGradientFillRadialPipeline> conical_gradient_fill_radial; |
| Variants<ConicalGradientFillStripPipeline> conical_gradient_fill_strip; |
| Variants<ConicalGradientFillStripRadialPipeline> conical_gradient_fill_strip_and_radial; |
| Variants<ConicalGradientSSBOFillPipeline> conical_gradient_ssbo_fill; |
| Variants<ConicalGradientSSBOFillPipeline> conical_gradient_ssbo_fill_radial; |
| Variants<ConicalGradientSSBOFillPipeline> conical_gradient_ssbo_fill_strip_and_radial; |
| Variants<ConicalGradientSSBOFillPipeline> conical_gradient_ssbo_fill_strip; |
| Variants<ConicalGradientUniformFillConicalPipeline> conical_gradient_uniform_fill; |
| Variants<ConicalGradientUniformFillRadialPipeline> conical_gradient_uniform_fill_radial; |
| Variants<ConicalGradientUniformFillStripPipeline> conical_gradient_uniform_fill_strip; |
| Variants<ConicalGradientUniformFillStripRadialPipeline> conical_gradient_uniform_fill_strip_and_radial; |
| Variants<FastGradientPipeline> fast_gradient; |
| Variants<FramebufferBlendColorBurnPipeline> framebuffer_blend_colorburn; |
| Variants<FramebufferBlendColorDodgePipeline> framebuffer_blend_colordodge; |
| Variants<FramebufferBlendColorPipeline> framebuffer_blend_color; |
| Variants<FramebufferBlendDarkenPipeline> framebuffer_blend_darken; |
| Variants<FramebufferBlendDifferencePipeline> framebuffer_blend_difference; |
| Variants<FramebufferBlendExclusionPipeline> framebuffer_blend_exclusion; |
| Variants<FramebufferBlendHardLightPipeline> framebuffer_blend_hardlight; |
| Variants<FramebufferBlendHuePipeline> framebuffer_blend_hue; |
| Variants<FramebufferBlendLightenPipeline> framebuffer_blend_lighten; |
| Variants<FramebufferBlendLuminosityPipeline> framebuffer_blend_luminosity; |
| Variants<FramebufferBlendMultiplyPipeline> framebuffer_blend_multiply; |
| Variants<FramebufferBlendOverlayPipeline> framebuffer_blend_overlay; |
| Variants<FramebufferBlendSaturationPipeline> framebuffer_blend_saturation; |
| Variants<FramebufferBlendScreenPipeline> framebuffer_blend_screen; |
| Variants<FramebufferBlendSoftLightPipeline> framebuffer_blend_softlight; |
| Variants<GaussianBlurPipeline> gaussian_blur; |
| Variants<GlyphAtlasPipeline> glyph_atlas; |
| Variants<LinePipeline> line; |
| Variants<LinearGradientFillPipeline> linear_gradient_fill; |
| Variants<LinearGradientSSBOFillPipeline> linear_gradient_ssbo_fill; |
| Variants<LinearGradientUniformFillPipeline> linear_gradient_uniform_fill; |
| Variants<LinearToSrgbFilterPipeline> linear_to_srgb_filter; |
| Variants<MorphologyFilterPipeline> morphology_filter; |
| Variants<PorterDuffBlendPipeline> clear_blend; |
| Variants<PorterDuffBlendPipeline> destination_a_top_blend; |
| Variants<PorterDuffBlendPipeline> destination_blend; |
| Variants<PorterDuffBlendPipeline> destination_in_blend; |
| Variants<PorterDuffBlendPipeline> destination_out_blend; |
| Variants<PorterDuffBlendPipeline> destination_over_blend; |
| Variants<PorterDuffBlendPipeline> modulate_blend; |
| Variants<PorterDuffBlendPipeline> plus_blend; |
| Variants<PorterDuffBlendPipeline> screen_blend; |
| Variants<PorterDuffBlendPipeline> source_a_top_blend; |
| Variants<PorterDuffBlendPipeline> source_blend; |
| Variants<PorterDuffBlendPipeline> source_in_blend; |
| Variants<PorterDuffBlendPipeline> source_out_blend; |
| Variants<PorterDuffBlendPipeline> source_over_blend; |
| Variants<PorterDuffBlendPipeline> xor_blend; |
| Variants<RadialGradientFillPipeline> radial_gradient_fill; |
| Variants<RadialGradientSSBOFillPipeline> radial_gradient_ssbo_fill; |
| Variants<RadialGradientUniformFillPipeline> radial_gradient_uniform_fill; |
| Variants<RRectBlurPipeline> rrect_blur; |
| Variants<RSuperellipseBlurPipeline> rsuperellipse_blur; |
| Variants<ShadowVerticesShader> shadow_vertices_; |
| Variants<SolidFillPipeline> solid_fill; |
| Variants<SrgbToLinearFilterPipeline> srgb_to_linear_filter; |
| Variants<SweepGradientFillPipeline> sweep_gradient_fill; |
| Variants<SweepGradientSSBOFillPipeline> sweep_gradient_ssbo_fill; |
| Variants<SweepGradientUniformFillPipeline> sweep_gradient_uniform_fill; |
| Variants<TextureDownsamplePipeline> texture_downsample; |
| Variants<TextureDownsampleBoundedPipeline> texture_downsample_bounded; |
| Variants<TexturePipeline> texture; |
| Variants<TextureStrictSrcPipeline> texture_strict_src; |
| Variants<TiledTexturePipeline> tiled_texture; |
| Variants<VerticesUber1Shader> vertices_uber_1_; |
| Variants<VerticesUber2Shader> vertices_uber_2_; |
| Variants<YUVToRGBFilterPipeline> yuv_to_rgb_filter; |
| |
| // Web doesn't support external texture OpenGL extensions |
| #if defined(IMPELLER_ENABLE_OPENGLES) && !defined(FML_OS_EMSCRIPTEN) |
| Variants<TiledTextureExternalPipeline> tiled_texture_external; |
| Variants<TiledTextureUvExternalPipeline> tiled_texture_uv_external; |
| #endif |
| |
| #if defined(IMPELLER_ENABLE_OPENGLES) |
| Variants<TextureDownsampleGlesPipeline> texture_downsample_gles; |
| #endif // IMPELLER_ENABLE_OPENGLES |
| // clang-format on |
| }; |
| |
| 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::kSrcOver; |
| } |
| |
| 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; |
| color0.write_mask = ColorWriteMaskBits::kAll; |
| |
| 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::kSrc: |
| 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::kDst: |
| 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; |
| color0.write_mask = ColorWriteMaskBits::kNone; |
| break; |
| case BlendMode::kSrcOver: |
| 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::kDstOver: |
| 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::kSrcIn: |
| 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::kDstIn: |
| 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::kSrcOut: |
| 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::kDstOut: |
| 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::kSrcATop: |
| 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::kDstATop: |
| 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_depth_stencil_attachments) { |
| desc.ClearDepthAttachment(); |
| desc.ClearStencilAttachments(); |
| } |
| |
| auto maybe_stencil = desc.GetFrontStencilAttachmentDescriptor(); |
| auto maybe_depth = desc.GetDepthStencilAttachmentDescriptor(); |
| FML_DCHECK(has_depth_stencil_attachments == maybe_depth.has_value()) |
| << "Depth attachment doesn't match expected pipeline state. " |
| "has_depth_stencil_attachments=" |
| << has_depth_stencil_attachments; |
| FML_DCHECK(has_depth_stencil_attachments == maybe_stencil.has_value()) |
| << "Stencil attachment doesn't match expected pipeline state. " |
| "has_depth_stencil_attachments=" |
| << has_depth_stencil_attachments; |
| if (maybe_stencil.has_value()) { |
| StencilAttachmentDescriptor front_stencil = maybe_stencil.value(); |
| StencilAttachmentDescriptor back_stencil = front_stencil; |
| |
| switch (stencil_mode) { |
| case StencilMode::kIgnore: |
| front_stencil.stencil_compare = CompareFunction::kAlways; |
| front_stencil.depth_stencil_pass = StencilOperation::kKeep; |
| desc.SetStencilAttachmentDescriptors(front_stencil); |
| break; |
| case StencilMode::kStencilNonZeroFill: |
| // The stencil ref should be 0 on commands that use this mode. |
| front_stencil.stencil_compare = CompareFunction::kAlways; |
| front_stencil.depth_stencil_pass = StencilOperation::kIncrementWrap; |
| back_stencil.stencil_compare = CompareFunction::kAlways; |
| back_stencil.depth_stencil_pass = StencilOperation::kDecrementWrap; |
| desc.SetStencilAttachmentDescriptors(front_stencil, back_stencil); |
| break; |
| case StencilMode::kStencilEvenOddFill: |
| // The stencil ref should be 0 on commands that use this mode. |
| front_stencil.stencil_compare = CompareFunction::kEqual; |
| front_stencil.depth_stencil_pass = StencilOperation::kIncrementWrap; |
| front_stencil.stencil_failure = StencilOperation::kDecrementWrap; |
| desc.SetStencilAttachmentDescriptors(front_stencil); |
| break; |
| case StencilMode::kStencilIncrementAll: |
| // The stencil ref should be 0 on commands that use this mode. |
| front_stencil.stencil_compare = CompareFunction::kEqual; |
| front_stencil.depth_stencil_pass = StencilOperation::kIncrementWrap; |
| desc.SetStencilAttachmentDescriptors(front_stencil); |
| break; |
| case StencilMode::kCoverCompare: |
| // The stencil ref should be 0 on commands that use this mode. |
| front_stencil.stencil_compare = CompareFunction::kNotEqual; |
| front_stencil.depth_stencil_pass = |
| StencilOperation::kSetToReferenceValue; |
| desc.SetStencilAttachmentDescriptors(front_stencil); |
| break; |
| case StencilMode::kCoverCompareInverted: |
| // The stencil ref should be 0 on commands that use this mode. |
| front_stencil.stencil_compare = CompareFunction::kEqual; |
| front_stencil.stencil_failure = StencilOperation::kSetToReferenceValue; |
| desc.SetStencilAttachmentDescriptors(front_stencil); |
| break; |
| } |
| } |
| if (maybe_depth.has_value()) { |
| DepthAttachmentDescriptor depth = maybe_depth.value(); |
| depth.depth_write_enabled = depth_write_enabled; |
| depth.depth_compare = depth_compare; |
| desc.SetDepthStencilAttachmentDescriptor(depth); |
| } |
| |
| desc.SetPrimitiveType(primitive_type); |
| desc.SetPolygonMode(PolygonMode::kFill); |
| } |
| |
| std::array<std::vector<Scalar>, 15> GetPorterDuffSpecConstants( |
| bool supports_decal) { |
| Scalar x = supports_decal ? 1 : 0; |
| return {{ |
| {x, 0, 0, 0, 0, 0}, // Clear |
| {x, 1, 0, 0, 0, 0}, // Source |
| {x, 0, 0, 1, 0, 0}, // Destination |
| {x, 1, 0, 1, -1, 0}, // SourceOver |
| {x, 1, -1, 1, 0, 0}, // DestinationOver |
| {x, 0, 1, 0, 0, 0}, // SourceIn |
| {x, 0, 0, 0, 1, 0}, // DestinationIn |
| {x, 1, -1, 0, 0, 0}, // SourceOut |
| {x, 0, 0, 1, -1, 0}, // DestinationOut |
| {x, 0, 1, 1, -1, 0}, // SourceATop |
| {x, 1, -1, 0, 1, 0}, // DestinationATop |
| {x, 1, -1, 1, -1, 0}, // Xor |
| {x, 1, 0, 1, 0, 0}, // Plus |
| {x, 0, 0, 0, 0, 1}, // Modulate |
| {x, 0, 0, 1, 0, -1}, // Screen |
| }}; |
| } |
| |
| 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))), |
| pipelines_(new Pipelines()), |
| tessellator_(std::make_shared<Tessellator>( |
| context_->GetCapabilities()->Supports32BitPrimitiveIndices())), |
| render_target_cache_(render_target_allocator == nullptr |
| ? std::make_shared<RenderTargetCache>( |
| context_->GetResourceAllocator()) |
| : std::move(render_target_allocator)), |
| data_host_buffer_(HostBuffer::Create( |
| context_->GetResourceAllocator(), |
| context_->GetIdleWaiter(), |
| context_->GetCapabilities()->GetMinimumUniformAlignment())), |
| text_shadow_cache_(std::make_unique<TextShadowCache>()) { |
| if (!context_ || !context_->IsValid()) { |
| return; |
| } |
| |
| // On most backends, indexes and other data can be allocated into the same |
| // buffers. However, some backends (namely WebGL) require indexes used in |
| // indexed draws to be allocated separately from other data. For those |
| // backends, we allocate a separate host buffer just for indexes. |
| indexes_host_buffer_ = |
| context_->GetCapabilities()->NeedsPartitionedHostBuffer() |
| ? HostBuffer::Create( |
| context_->GetResourceAllocator(), context_->GetIdleWaiter(), |
| context_->GetCapabilities()->GetMinimumUniformAlignment()) |
| : data_host_buffer_; |
| { |
| TextureDescriptor desc; |
| desc.storage_mode = StorageMode::kDevicePrivate; |
| desc.format = PixelFormat::kR8G8B8A8UNormInt; |
| desc.size = ISize{1, 1}; |
| empty_texture_ = GetContext()->GetResourceAllocator()->CreateTexture(desc); |
| |
| std::array<uint8_t, 4> data = Color::BlackTransparent().ToR8G8B8A8(); |
| std::shared_ptr<CommandBuffer> cmd_buffer = |
| GetContext()->CreateCommandBuffer(); |
| std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass(); |
| HostBuffer& data_host_buffer = GetTransientsDataBuffer(); |
| BufferView buffer_view = data_host_buffer.Emplace(data); |
| blit_pass->AddCopy(buffer_view, empty_texture_); |
| |
| if (!blit_pass->EncodeCommands() || !GetContext() |
| ->GetCommandQueue() |
| ->Submit({std::move(cmd_buffer)}) |
| .ok()) { |
| VALIDATION_LOG << "Failed to create empty texture."; |
| } |
| } |
| |
| 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()}; |
| auto options_no_msaa_no_depth_stencil = ContentContextOptions{ |
| .sample_count = SampleCount::kCount1, |
| .primitive_type = PrimitiveType::kTriangleStrip, |
| .color_attachment_pixel_format = |
| context_->GetCapabilities()->GetDefaultColorFormat(), |
| .has_depth_stencil_attachments = false}; |
| const auto supports_decal = static_cast<Scalar>( |
| context_->GetCapabilities()->SupportsDecalSamplerAddressMode()); |
| |
| // Futures for the following pipelines may block in case the first frame is |
| // rendered without the pipelines being ready. Put pipelines that are more |
| // likely to be used first. |
| { |
| pipelines_->glyph_atlas.CreateDefault( |
| *context_, options, |
| {static_cast<Scalar>( |
| GetContext()->GetCapabilities()->GetDefaultGlyphAtlasFormat() == |
| PixelFormat::kA8UNormInt)}); |
| pipelines_->solid_fill.CreateDefault(*context_, options); |
| pipelines_->texture.CreateDefault(*context_, options); |
| pipelines_->fast_gradient.CreateDefault(*context_, options); |
| pipelines_->line.CreateDefault(*context_, options); |
| pipelines_->circle.CreateDefault(*context_, options); |
| |
| if (context_->GetCapabilities()->SupportsSSBO()) { |
| pipelines_->linear_gradient_ssbo_fill.CreateDefault(*context_, options); |
| pipelines_->radial_gradient_ssbo_fill.CreateDefault(*context_, options); |
| pipelines_->conical_gradient_ssbo_fill.CreateDefault(*context_, options, |
| {3.0}); |
| pipelines_->conical_gradient_ssbo_fill_radial.CreateDefault( |
| *context_, options, {1.0}); |
| pipelines_->conical_gradient_ssbo_fill_strip.CreateDefault( |
| *context_, options, {2.0}); |
| pipelines_->conical_gradient_ssbo_fill_strip_and_radial.CreateDefault( |
| *context_, options, {0.0}); |
| pipelines_->sweep_gradient_ssbo_fill.CreateDefault(*context_, options); |
| } else { |
| pipelines_->linear_gradient_uniform_fill.CreateDefault(*context_, |
| options); |
| pipelines_->radial_gradient_uniform_fill.CreateDefault(*context_, |
| options); |
| pipelines_->conical_gradient_uniform_fill.CreateDefault(*context_, |
| options); |
| pipelines_->conical_gradient_uniform_fill_radial.CreateDefault(*context_, |
| options); |
| pipelines_->conical_gradient_uniform_fill_strip.CreateDefault(*context_, |
| options); |
| pipelines_->conical_gradient_uniform_fill_strip_and_radial.CreateDefault( |
| *context_, options); |
| pipelines_->sweep_gradient_uniform_fill.CreateDefault(*context_, options); |
| |
| pipelines_->linear_gradient_fill.CreateDefault(*context_, options); |
| pipelines_->radial_gradient_fill.CreateDefault(*context_, options); |
| pipelines_->conical_gradient_fill.CreateDefault(*context_, options); |
| pipelines_->conical_gradient_fill_radial.CreateDefault(*context_, |
| options); |
| pipelines_->conical_gradient_fill_strip.CreateDefault(*context_, options); |
| pipelines_->conical_gradient_fill_strip_and_radial.CreateDefault( |
| *context_, options); |
| pipelines_->sweep_gradient_fill.CreateDefault(*context_, options); |
| } |
| |
| /// 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 = ColorWriteMaskBits::kNone; |
| } |
| clip_pipeline_descriptor->SetColorAttachmentDescriptors( |
| std::move(clip_color_attachments)); |
| pipelines_->clip.SetDefault( |
| options, |
| std::make_unique<ClipPipeline>(*context_, clip_pipeline_descriptor)); |
| pipelines_->texture_downsample.CreateDefault( |
| *context_, options_no_msaa_no_depth_stencil); |
| pipelines_->texture_downsample_bounded.CreateDefault( |
| *context_, options_no_msaa_no_depth_stencil); |
| pipelines_->rrect_blur.CreateDefault(*context_, options_trianglestrip); |
| pipelines_->rsuperellipse_blur.CreateDefault(*context_, |
| options_trianglestrip); |
| pipelines_->texture_strict_src.CreateDefault(*context_, options); |
| pipelines_->tiled_texture.CreateDefault(*context_, options, |
| {supports_decal}); |
| pipelines_->gaussian_blur.CreateDefault( |
| *context_, options_no_msaa_no_depth_stencil, {supports_decal}); |
| pipelines_->border_mask_blur.CreateDefault(*context_, |
| options_trianglestrip); |
| pipelines_->color_matrix_color_filter.CreateDefault(*context_, |
| options_trianglestrip); |
| pipelines_->shadow_vertices_.CreateDefault(*context_, options); |
| pipelines_->vertices_uber_1_.CreateDefault(*context_, options, |
| {supports_decal}); |
| pipelines_->vertices_uber_2_.CreateDefault(*context_, options, |
| {supports_decal}); |
| |
| const std::array<std::vector<Scalar>, 15> porter_duff_constants = |
| GetPorterDuffSpecConstants(supports_decal); |
| pipelines_->clear_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[0]); |
| pipelines_->source_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[1]); |
| pipelines_->destination_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[2]); |
| pipelines_->source_over_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[3]); |
| pipelines_->destination_over_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[4]); |
| pipelines_->source_in_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[5]); |
| pipelines_->destination_in_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[6]); |
| pipelines_->source_out_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[7]); |
| pipelines_->destination_out_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[8]); |
| pipelines_->source_a_top_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[9]); |
| pipelines_->destination_a_top_blend.CreateDefault( |
| *context_, options_trianglestrip, porter_duff_constants[10]); |
| pipelines_->xor_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[11]); |
| pipelines_->plus_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[12]); |
| pipelines_->modulate_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[13]); |
| pipelines_->screen_blend.CreateDefault(*context_, options_trianglestrip, |
| porter_duff_constants[14]); |
| } |
| |
| if (context_->GetCapabilities()->SupportsFramebufferFetch()) { |
| pipelines_->framebuffer_blend_color.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kColor), supports_decal}); |
| pipelines_->framebuffer_blend_colorburn.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kColorBurn), supports_decal}); |
| pipelines_->framebuffer_blend_colordodge.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kColorDodge), supports_decal}); |
| pipelines_->framebuffer_blend_darken.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kDarken), supports_decal}); |
| pipelines_->framebuffer_blend_difference.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kDifference), supports_decal}); |
| pipelines_->framebuffer_blend_exclusion.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kExclusion), supports_decal}); |
| pipelines_->framebuffer_blend_hardlight.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kHardLight), supports_decal}); |
| pipelines_->framebuffer_blend_hue.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kHue), supports_decal}); |
| pipelines_->framebuffer_blend_lighten.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kLighten), supports_decal}); |
| pipelines_->framebuffer_blend_luminosity.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kLuminosity), supports_decal}); |
| pipelines_->framebuffer_blend_multiply.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kMultiply), supports_decal}); |
| pipelines_->framebuffer_blend_overlay.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kOverlay), supports_decal}); |
| pipelines_->framebuffer_blend_saturation.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kSaturation), supports_decal}); |
| pipelines_->framebuffer_blend_screen.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kScreen), supports_decal}); |
| pipelines_->framebuffer_blend_softlight.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal}); |
| } else { |
| pipelines_->blend_color.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kColor), supports_decal}); |
| pipelines_->blend_colorburn.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kColorBurn), supports_decal}); |
| pipelines_->blend_colordodge.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kColorDodge), supports_decal}); |
| pipelines_->blend_darken.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kDarken), supports_decal}); |
| pipelines_->blend_difference.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kDifference), supports_decal}); |
| pipelines_->blend_exclusion.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kExclusion), supports_decal}); |
| pipelines_->blend_hardlight.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kHardLight), supports_decal}); |
| pipelines_->blend_hue.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kHue), supports_decal}); |
| pipelines_->blend_lighten.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kLighten), supports_decal}); |
| pipelines_->blend_luminosity.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kLuminosity), supports_decal}); |
| pipelines_->blend_multiply.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kMultiply), supports_decal}); |
| pipelines_->blend_overlay.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kOverlay), supports_decal}); |
| pipelines_->blend_saturation.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kSaturation), supports_decal}); |
| pipelines_->blend_screen.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kScreen), supports_decal}); |
| pipelines_->blend_softlight.CreateDefault( |
| *context_, options_trianglestrip, |
| {static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal}); |
| } |
| |
| pipelines_->morphology_filter.CreateDefault(*context_, options_trianglestrip, |
| {supports_decal}); |
| pipelines_->linear_to_srgb_filter.CreateDefault(*context_, |
| options_trianglestrip); |
| pipelines_->srgb_to_linear_filter.CreateDefault(*context_, |
| options_trianglestrip); |
| pipelines_->yuv_to_rgb_filter.CreateDefault(*context_, options_trianglestrip); |
| |
| if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) { |
| #if defined(IMPELLER_ENABLE_OPENGLES) && !defined(FML_OS_MACOSX) && \ |
| !defined(FML_OS_EMSCRIPTEN) |
| // GLES only shader that is unsupported on macOS and web. |
| pipelines_->tiled_texture_external.CreateDefault(*context_, options); |
| pipelines_->tiled_texture_uv_external.CreateDefault(*context_, options); |
| #endif // !defined(FML_OS_MACOSX) |
| |
| #if defined(IMPELLER_ENABLE_OPENGLES) |
| pipelines_->texture_downsample_gles.CreateDefault(*context_, |
| options_trianglestrip); |
| #endif // IMPELLER_ENABLE_OPENGLES |
| } |
| |
| is_valid_ = true; |
| InitializeCommonlyUsedShadersIfNeeded(); |
| } |
| |
| ContentContext::~ContentContext() = default; |
| |
| bool ContentContext::IsValid() const { |
| return is_valid_; |
| } |
| |
| std::shared_ptr<Texture> ContentContext::GetEmptyTexture() const { |
| return empty_texture_; |
| } |
| |
| fml::StatusOr<RenderTarget> ContentContext::MakeSubpass( |
| std::string_view label, |
| ISize texture_size, |
| const std::shared_ptr<CommandBuffer>& command_buffer, |
| const SubpassCallback& subpass_callback, |
| bool msaa_enabled, |
| bool depth_stencil_enabled, |
| int32_t mip_count) const { |
| const std::shared_ptr<Context>& context = GetContext(); |
| RenderTarget subpass_target; |
| |
| std::optional<RenderTarget::AttachmentConfig> depth_stencil_config = |
| depth_stencil_enabled ? RenderTarget::kDefaultStencilAttachmentConfig |
| : std::optional<RenderTarget::AttachmentConfig>(); |
| |
| if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) { |
| subpass_target = GetRenderTargetCache()->CreateOffscreenMSAA( |
| /*context=*/*context, |
| /*size=*/texture_size, |
| /*mip_count=*/mip_count, |
| /*label=*/label, |
| /*color_attachment_config=*/ |
| RenderTarget::kDefaultColorAttachmentConfigMSAA, |
| /*stencil_attachment_config=*/depth_stencil_config, |
| /*existing_color_msaa_texture=*/nullptr, |
| /*existing_color_resolve_texture=*/nullptr, |
| /*existing_depth_stencil_texture=*/nullptr, |
| /*target_pixel_format=*/std::nullopt); |
| } else { |
| subpass_target = GetRenderTargetCache()->CreateOffscreen( |
| *context, texture_size, |
| /*mip_count=*/mip_count, label, |
| RenderTarget::kDefaultColorAttachmentConfig, depth_stencil_config); |
| } |
| return MakeSubpass(label, subpass_target, command_buffer, subpass_callback); |
| } |
| |
| fml::StatusOr<RenderTarget> ContentContext::MakeSubpass( |
| std::string_view label, |
| const RenderTarget& subpass_target, |
| const std::shared_ptr<CommandBuffer>& command_buffer, |
| const SubpassCallback& subpass_callback) const { |
| const std::shared_ptr<Context>& context = GetContext(); |
| |
| auto subpass_texture = subpass_target.GetRenderTargetTexture(); |
| if (!subpass_texture) { |
| return fml::Status(fml::StatusCode::kUnknown, ""); |
| } |
| |
| auto sub_renderpass = command_buffer->CreateRenderPass(subpass_target); |
| if (!sub_renderpass) { |
| return fml::Status(fml::StatusCode::kUnknown, ""); |
| } |
| sub_renderpass->SetLabel(label); |
| |
| if (!subpass_callback(*this, *sub_renderpass)) { |
| return fml::Status(fml::StatusCode::kUnknown, ""); |
| } |
| |
| if (!sub_renderpass->EncodeCommands()) { |
| return fml::Status(fml::StatusCode::kUnknown, ""); |
| } |
| |
| const std::shared_ptr<Texture>& target_texture = |
| subpass_target.GetRenderTargetTexture(); |
| if (target_texture->GetMipCount() > 1) { |
| fml::Status mipmap_status = |
| AddMipmapGeneration(command_buffer, context, target_texture); |
| if (!mipmap_status.ok()) { |
| return mipmap_status; |
| } |
| } |
| |
| return subpass_target; |
| } |
| |
| Tessellator& ContentContext::GetTessellator() const { |
| return *tessellator_; |
| } |
| |
| std::shared_ptr<Context> ContentContext::GetContext() const { |
| return context_; |
| } |
| |
| const Capabilities& ContentContext::GetDeviceCapabilities() const { |
| return *context_->GetCapabilities(); |
| } |
| |
| PipelineRef ContentContext::GetCachedRuntimeEffectPipeline( |
| const std::string& unique_entrypoint_name, |
| const ContentContextOptions& options, |
| const std::function<std::shared_ptr<Pipeline<PipelineDescriptor>>()>& |
| create_callback) const { |
| RuntimeEffectPipelineKey key{unique_entrypoint_name, options}; |
| auto it = runtime_effect_pipelines_.find(key); |
| if (it == runtime_effect_pipelines_.end()) { |
| it = runtime_effect_pipelines_.insert(it, {key, create_callback()}); |
| } |
| return raw_ptr(it->second); |
| } |
| |
| void ContentContext::ClearCachedRuntimeEffectPipeline( |
| const std::string& unique_entrypoint_name) const { |
| #ifdef IMPELLER_DEBUG |
| // destroying in-use pipleines is a validation error. |
| const auto& idle_waiter = GetContext()->GetIdleWaiter(); |
| if (idle_waiter) { |
| idle_waiter->WaitIdle(); |
| } |
| #endif // IMPELLER_DEBUG |
| for (auto it = runtime_effect_pipelines_.begin(); |
| it != runtime_effect_pipelines_.end();) { |
| if (it->first.unique_entrypoint_name == unique_entrypoint_name) { |
| it = runtime_effect_pipelines_.erase(it); |
| } else { |
| it++; |
| } |
| } |
| } |
| |
| void ContentContext::ResetTransientsBuffers() { |
| data_host_buffer_->Reset(); |
| |
| // We should only reset the indexes host buffer if it is actually different |
| // from the data host buffer. Otherwise we'll end up resetting the same host |
| // buffer twice. |
| if (data_host_buffer_ != indexes_host_buffer_) { |
| indexes_host_buffer_->Reset(); |
| } |
| } |
| |
| void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const { |
| GetContext()->InitializeCommonlyUsedShadersIfNeeded(); |
| } |
| |
| PipelineRef ContentContext::GetFastGradientPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->fast_gradient, opts); |
| } |
| |
| PipelineRef ContentContext::GetLinearGradientFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->linear_gradient_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetLinearGradientUniformFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->linear_gradient_uniform_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetRadialGradientUniformFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->radial_gradient_uniform_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetSweepGradientUniformFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->sweep_gradient_uniform_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetLinearGradientSSBOFillPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsSSBO()); |
| return GetPipeline(this, pipelines_->linear_gradient_ssbo_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetRadialGradientSSBOFillPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsSSBO()); |
| return GetPipeline(this, pipelines_->radial_gradient_ssbo_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetConicalGradientUniformFillPipeline( |
| ContentContextOptions opts, |
| ConicalKind kind) const { |
| switch (kind) { |
| case ConicalKind::kConical: |
| return GetPipeline(this, pipelines_->conical_gradient_uniform_fill, opts); |
| case ConicalKind::kRadial: |
| return GetPipeline(this, pipelines_->conical_gradient_uniform_fill_radial, |
| opts); |
| case ConicalKind::kStrip: |
| return GetPipeline(this, pipelines_->conical_gradient_uniform_fill_strip, |
| opts); |
| case ConicalKind::kStripAndRadial: |
| return GetPipeline( |
| this, pipelines_->conical_gradient_uniform_fill_strip_and_radial, |
| opts); |
| } |
| } |
| |
| PipelineRef ContentContext::GetConicalGradientSSBOFillPipeline( |
| ContentContextOptions opts, |
| ConicalKind kind) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsSSBO()); |
| switch (kind) { |
| case ConicalKind::kConical: |
| return GetPipeline(this, pipelines_->conical_gradient_ssbo_fill, opts); |
| case ConicalKind::kRadial: |
| return GetPipeline(this, pipelines_->conical_gradient_ssbo_fill_radial, |
| opts); |
| case ConicalKind::kStrip: |
| return GetPipeline(this, pipelines_->conical_gradient_ssbo_fill_strip, |
| opts); |
| case ConicalKind::kStripAndRadial: |
| return GetPipeline( |
| this, pipelines_->conical_gradient_ssbo_fill_strip_and_radial, opts); |
| } |
| } |
| |
| PipelineRef ContentContext::GetSweepGradientSSBOFillPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsSSBO()); |
| return GetPipeline(this, pipelines_->sweep_gradient_ssbo_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetRadialGradientFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->radial_gradient_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetConicalGradientFillPipeline( |
| ContentContextOptions opts, |
| ConicalKind kind) const { |
| switch (kind) { |
| case ConicalKind::kConical: |
| return GetPipeline(this, pipelines_->conical_gradient_fill, opts); |
| case ConicalKind::kRadial: |
| return GetPipeline(this, pipelines_->conical_gradient_fill_radial, opts); |
| case ConicalKind::kStrip: |
| return GetPipeline(this, pipelines_->conical_gradient_fill_strip, opts); |
| case ConicalKind::kStripAndRadial: |
| return GetPipeline( |
| this, pipelines_->conical_gradient_fill_strip_and_radial, opts); |
| } |
| } |
| |
| PipelineRef ContentContext::GetRRectBlurPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->rrect_blur, opts); |
| } |
| |
| PipelineRef ContentContext::GetRSuperellipseBlurPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->rsuperellipse_blur, opts); |
| } |
| |
| PipelineRef ContentContext::GetSweepGradientFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->sweep_gradient_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetSolidFillPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->solid_fill, opts); |
| } |
| |
| PipelineRef ContentContext::GetTexturePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->texture, opts); |
| } |
| |
| PipelineRef ContentContext::GetTextureStrictSrcPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->texture_strict_src, opts); |
| } |
| |
| PipelineRef ContentContext::GetTiledTexturePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->tiled_texture, opts); |
| } |
| |
| PipelineRef ContentContext::GetGaussianBlurPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->gaussian_blur, opts); |
| } |
| |
| PipelineRef ContentContext::GetBorderMaskBlurPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->border_mask_blur, opts); |
| } |
| |
| PipelineRef ContentContext::GetMorphologyFilterPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->morphology_filter, opts); |
| } |
| |
| PipelineRef ContentContext::GetColorMatrixColorFilterPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->color_matrix_color_filter, opts); |
| } |
| |
| PipelineRef ContentContext::GetLinearToSrgbFilterPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->linear_to_srgb_filter, opts); |
| } |
| |
| PipelineRef ContentContext::GetSrgbToLinearFilterPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->srgb_to_linear_filter, opts); |
| } |
| |
| PipelineRef ContentContext::GetClipPipeline(ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->clip, opts); |
| } |
| |
| PipelineRef ContentContext::GetGlyphAtlasPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->glyph_atlas, opts); |
| } |
| |
| PipelineRef ContentContext::GetYUVToRGBFilterPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->yuv_to_rgb_filter, opts); |
| } |
| |
| PipelineRef ContentContext::GetPorterDuffPipeline( |
| BlendMode mode, |
| ContentContextOptions opts) const { |
| switch (mode) { |
| case BlendMode::kClear: |
| return GetClearBlendPipeline(opts); |
| case BlendMode::kSrc: |
| return GetSourceBlendPipeline(opts); |
| case BlendMode::kDst: |
| return GetDestinationBlendPipeline(opts); |
| case BlendMode::kSrcOver: |
| return GetSourceOverBlendPipeline(opts); |
| case BlendMode::kDstOver: |
| return GetDestinationOverBlendPipeline(opts); |
| case BlendMode::kSrcIn: |
| return GetSourceInBlendPipeline(opts); |
| case BlendMode::kDstIn: |
| return GetDestinationInBlendPipeline(opts); |
| case BlendMode::kSrcOut: |
| return GetSourceOutBlendPipeline(opts); |
| case BlendMode::kDstOut: |
| return GetDestinationOutBlendPipeline(opts); |
| case BlendMode::kSrcATop: |
| return GetSourceATopBlendPipeline(opts); |
| case BlendMode::kDstATop: |
| return GetDestinationATopBlendPipeline(opts); |
| case BlendMode::kXor: |
| return GetXorBlendPipeline(opts); |
| case BlendMode::kPlus: |
| return GetPlusBlendPipeline(opts); |
| case BlendMode::kModulate: |
| return GetModulateBlendPipeline(opts); |
| case BlendMode::kScreen: |
| return GetScreenBlendPipeline(opts); |
| case BlendMode::kOverlay: |
| case BlendMode::kDarken: |
| case BlendMode::kLighten: |
| case BlendMode::kColorDodge: |
| case BlendMode::kColorBurn: |
| case BlendMode::kHardLight: |
| case BlendMode::kSoftLight: |
| case BlendMode::kDifference: |
| case BlendMode::kExclusion: |
| case BlendMode::kMultiply: |
| case BlendMode::kHue: |
| case BlendMode::kSaturation: |
| case BlendMode::kColor: |
| case BlendMode::kLuminosity: |
| VALIDATION_LOG << "Invalid porter duff blend mode " |
| << BlendModeToString(mode); |
| return GetClearBlendPipeline(opts); |
| break; |
| } |
| } |
| |
| PipelineRef ContentContext::GetClearBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->clear_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetSourceBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->source_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetDestinationBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->destination_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetSourceOverBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->source_over_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetDestinationOverBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->destination_over_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetSourceInBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->source_in_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetDestinationInBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->destination_in_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetSourceOutBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->source_out_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetDestinationOutBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->destination_out_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetSourceATopBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->source_a_top_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetDestinationATopBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->destination_a_top_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetXorBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->xor_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetPlusBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->plus_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetModulateBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->modulate_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetScreenBlendPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->screen_blend, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendColorPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_color, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendColorBurnPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_colorburn, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendColorDodgePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_colordodge, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendDarkenPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_darken, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendDifferencePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_difference, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendExclusionPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_exclusion, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendHardLightPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_hardlight, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendHuePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_hue, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendLightenPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_lighten, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendLuminosityPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_luminosity, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendMultiplyPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_multiply, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendOverlayPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_overlay, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendSaturationPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_saturation, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendScreenPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_screen, opts); |
| } |
| |
| PipelineRef ContentContext::GetBlendSoftLightPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->blend_softlight, opts); |
| } |
| |
| PipelineRef ContentContext::GetDownsamplePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->texture_downsample, opts); |
| } |
| |
| PipelineRef ContentContext::GetDownsampleBoundedPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->texture_downsample_bounded, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendColorPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_color, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendColorBurnPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_colorburn, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendColorDodgePipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_colordodge, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendDarkenPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_darken, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendDifferencePipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_difference, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendExclusionPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_exclusion, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendHardLightPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_hardlight, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendHuePipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_hue, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendLightenPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_lighten, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendLuminosityPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_luminosity, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendMultiplyPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_multiply, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendOverlayPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_overlay, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendSaturationPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_saturation, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendScreenPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_screen, opts); |
| } |
| |
| PipelineRef ContentContext::GetFramebufferBlendSoftLightPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetDeviceCapabilities().SupportsFramebufferFetch()); |
| return GetPipeline(this, pipelines_->framebuffer_blend_softlight, opts); |
| } |
| |
| PipelineRef ContentContext::GetDrawShadowVerticesPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->shadow_vertices_, opts); |
| } |
| |
| PipelineRef ContentContext::GetDrawVerticesUberPipeline( |
| BlendMode blend_mode, |
| ContentContextOptions opts) const { |
| if (blend_mode <= BlendMode::kHardLight) { |
| return GetPipeline(this, pipelines_->vertices_uber_1_, opts); |
| } else { |
| return GetPipeline(this, pipelines_->vertices_uber_2_, opts); |
| } |
| } |
| |
| PipelineRef ContentContext::GetCirclePipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->circle, opts); |
| } |
| |
| PipelineRef ContentContext::GetLinePipeline(ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->line, opts); |
| } |
| |
| #ifdef IMPELLER_ENABLE_OPENGLES |
| |
| #if !defined(FML_OS_EMSCRIPTEN) |
| PipelineRef ContentContext::GetTiledTextureUvExternalPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetContext()->GetBackendType() == Context::BackendType::kOpenGLES); |
| return GetPipeline(this, pipelines_->tiled_texture_uv_external, opts); |
| } |
| |
| PipelineRef ContentContext::GetTiledTextureExternalPipeline( |
| ContentContextOptions opts) const { |
| FML_DCHECK(GetContext()->GetBackendType() == Context::BackendType::kOpenGLES); |
| return GetPipeline(this, pipelines_->tiled_texture_external, opts); |
| } |
| #endif |
| |
| PipelineRef ContentContext::GetDownsampleTextureGlesPipeline( |
| ContentContextOptions opts) const { |
| return GetPipeline(this, pipelines_->texture_downsample_gles, opts); |
| } |
| |
| #endif // IMPELLER_ENABLE_OPENGLES |
| |
| } // namespace impeller |