Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e8debc0

Browse files
authored
[Impeller] cleaned up semantics for RenderPipelineT and added docstrings (#52237)
I just tried to capture all thing things I found confusing about these and clean them up. We were mixing up Pipeline and RenderPipelineT in our generics arguments and RenderPipelineT wasn't clear since it was named like a template parameter but was a runtime type. Summary of edits: - RenderPipelineT -> RenderPipelineHandle - Added docstrings for RenderPipelineHandle and Variants - cleaned up generics parameters to ContentContext methods and Variants - replaced references that called the variant's default pipeline handle a "prototype" since it clashed with places that just call it the "default". ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 908f619 commit e8debc0

3 files changed

Lines changed: 168 additions & 130 deletions

File tree

impeller/entity/contents/content_context.h

Lines changed: 142 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -96,161 +96,178 @@ namespace impeller {
9696

9797
#ifdef IMPELLER_DEBUG
9898
using CheckerboardPipeline =
99-
RenderPipelineT<CheckerboardVertexShader, CheckerboardFragmentShader>;
99+
RenderPipelineHandle<CheckerboardVertexShader, CheckerboardFragmentShader>;
100100
#endif // IMPELLER_DEBUG
101101

102102
using LinearGradientFillPipeline =
103-
RenderPipelineT<GradientFillVertexShader, LinearGradientFillFragmentShader>;
103+
RenderPipelineHandle<GradientFillVertexShader,
104+
LinearGradientFillFragmentShader>;
104105
using SolidFillPipeline =
105-
RenderPipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
106+
RenderPipelineHandle<SolidFillVertexShader, SolidFillFragmentShader>;
106107
using RadialGradientFillPipeline =
107-
RenderPipelineT<GradientFillVertexShader, RadialGradientFillFragmentShader>;
108+
RenderPipelineHandle<GradientFillVertexShader,
109+
RadialGradientFillFragmentShader>;
108110
using ConicalGradientFillPipeline =
109-
RenderPipelineT<GradientFillVertexShader,
110-
ConicalGradientFillFragmentShader>;
111+
RenderPipelineHandle<GradientFillVertexShader,
112+
ConicalGradientFillFragmentShader>;
111113
using SweepGradientFillPipeline =
112-
RenderPipelineT<GradientFillVertexShader, SweepGradientFillFragmentShader>;
114+
RenderPipelineHandle<GradientFillVertexShader,
115+
SweepGradientFillFragmentShader>;
113116
using LinearGradientSSBOFillPipeline =
114-
RenderPipelineT<GradientFillVertexShader,
115-
LinearGradientSsboFillFragmentShader>;
117+
RenderPipelineHandle<GradientFillVertexShader,
118+
LinearGradientSsboFillFragmentShader>;
116119
using ConicalGradientSSBOFillPipeline =
117-
RenderPipelineT<GradientFillVertexShader,
118-
ConicalGradientSsboFillFragmentShader>;
120+
RenderPipelineHandle<GradientFillVertexShader,
121+
ConicalGradientSsboFillFragmentShader>;
119122
using RadialGradientSSBOFillPipeline =
120-
RenderPipelineT<GradientFillVertexShader,
121-
RadialGradientSsboFillFragmentShader>;
123+
RenderPipelineHandle<GradientFillVertexShader,
124+
RadialGradientSsboFillFragmentShader>;
122125
using SweepGradientSSBOFillPipeline =
123-
RenderPipelineT<GradientFillVertexShader,
124-
SweepGradientSsboFillFragmentShader>;
126+
RenderPipelineHandle<GradientFillVertexShader,
127+
SweepGradientSsboFillFragmentShader>;
125128
using RRectBlurPipeline =
126-
RenderPipelineT<RrectBlurVertexShader, RrectBlurFragmentShader>;
127-
using BlendPipeline = RenderPipelineT<BlendVertexShader, BlendFragmentShader>;
129+
RenderPipelineHandle<RrectBlurVertexShader, RrectBlurFragmentShader>;
130+
using BlendPipeline =
131+
RenderPipelineHandle<BlendVertexShader, BlendFragmentShader>;
128132
using TexturePipeline =
129-
RenderPipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
133+
RenderPipelineHandle<TextureFillVertexShader, TextureFillFragmentShader>;
130134
using TextureStrictSrcPipeline =
131-
RenderPipelineT<TextureFillVertexShader,
132-
TextureFillStrictSrcFragmentShader>;
133-
using PositionUVPipeline =
134-
RenderPipelineT<TextureFillVertexShader, TiledTextureFillFragmentShader>;
135+
RenderPipelineHandle<TextureFillVertexShader,
136+
TextureFillStrictSrcFragmentShader>;
137+
using PositionUVPipeline = RenderPipelineHandle<TextureFillVertexShader,
138+
TiledTextureFillFragmentShader>;
135139
using TiledTexturePipeline =
136-
RenderPipelineT<TextureFillVertexShader, TiledTextureFillFragmentShader>;
140+
RenderPipelineHandle<TextureFillVertexShader,
141+
TiledTextureFillFragmentShader>;
137142
using KernelDecalPipeline =
138-
RenderPipelineT<KernelVertexShader, KernelDecalFragmentShader>;
143+
RenderPipelineHandle<KernelVertexShader, KernelDecalFragmentShader>;
139144
using KernelPipeline =
140-
RenderPipelineT<KernelVertexShader, KernelNodecalFragmentShader>;
145+
RenderPipelineHandle<KernelVertexShader, KernelNodecalFragmentShader>;
141146
using BorderMaskBlurPipeline =
142-
RenderPipelineT<BorderMaskBlurVertexShader, BorderMaskBlurFragmentShader>;
147+
RenderPipelineHandle<BorderMaskBlurVertexShader,
148+
BorderMaskBlurFragmentShader>;
143149
using MorphologyFilterPipeline =
144-
RenderPipelineT<MorphologyFilterVertexShader,
145-
MorphologyFilterFragmentShader>;
150+
RenderPipelineHandle<MorphologyFilterVertexShader,
151+
MorphologyFilterFragmentShader>;
146152
using ColorMatrixColorFilterPipeline =
147-
RenderPipelineT<FilterVertexShader, ColorMatrixColorFilterFragmentShader>;
153+
RenderPipelineHandle<FilterVertexShader,
154+
ColorMatrixColorFilterFragmentShader>;
148155
using LinearToSrgbFilterPipeline =
149-
RenderPipelineT<FilterVertexShader, LinearToSrgbFilterFragmentShader>;
156+
RenderPipelineHandle<FilterVertexShader, LinearToSrgbFilterFragmentShader>;
150157
using SrgbToLinearFilterPipeline =
151-
RenderPipelineT<FilterVertexShader, SrgbToLinearFilterFragmentShader>;
158+
RenderPipelineHandle<FilterVertexShader, SrgbToLinearFilterFragmentShader>;
152159
using GlyphAtlasPipeline =
153-
RenderPipelineT<GlyphAtlasVertexShader, GlyphAtlasFragmentShader>;
160+
RenderPipelineHandle<GlyphAtlasVertexShader, GlyphAtlasFragmentShader>;
154161
using GlyphAtlasColorPipeline =
155-
RenderPipelineT<GlyphAtlasVertexShader, GlyphAtlasColorFragmentShader>;
162+
RenderPipelineHandle<GlyphAtlasVertexShader, GlyphAtlasColorFragmentShader>;
156163
using PorterDuffBlendPipeline =
157-
RenderPipelineT<PorterDuffBlendVertexShader, PorterDuffBlendFragmentShader>;
158-
using ClipPipeline = RenderPipelineT<ClipVertexShader, ClipFragmentShader>;
164+
RenderPipelineHandle<PorterDuffBlendVertexShader,
165+
PorterDuffBlendFragmentShader>;
166+
using ClipPipeline = RenderPipelineHandle<ClipVertexShader, ClipFragmentShader>;
159167

160168
using GeometryColorPipeline =
161-
RenderPipelineT<PositionColorVertexShader, VerticesFragmentShader>;
169+
RenderPipelineHandle<PositionColorVertexShader, VerticesFragmentShader>;
162170
using YUVToRGBFilterPipeline =
163-
RenderPipelineT<FilterVertexShader, YuvToRgbFilterFragmentShader>;
171+
RenderPipelineHandle<FilterVertexShader, YuvToRgbFilterFragmentShader>;
164172

165173
// Advanced blends
166-
using BlendColorPipeline =
167-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
174+
using BlendColorPipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
175+
AdvancedBlendFragmentShader>;
168176
using BlendColorBurnPipeline =
169-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
177+
RenderPipelineHandle<AdvancedBlendVertexShader,
178+
AdvancedBlendFragmentShader>;
170179
using BlendColorDodgePipeline =
171-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
172-
using BlendDarkenPipeline =
173-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
180+
RenderPipelineHandle<AdvancedBlendVertexShader,
181+
AdvancedBlendFragmentShader>;
182+
using BlendDarkenPipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
183+
AdvancedBlendFragmentShader>;
174184
using BlendDifferencePipeline =
175-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
185+
RenderPipelineHandle<AdvancedBlendVertexShader,
186+
AdvancedBlendFragmentShader>;
176187
using BlendExclusionPipeline =
177-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
188+
RenderPipelineHandle<AdvancedBlendVertexShader,
189+
AdvancedBlendFragmentShader>;
178190
using BlendHardLightPipeline =
179-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
180-
using BlendHuePipeline =
181-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
182-
using BlendLightenPipeline =
183-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
191+
RenderPipelineHandle<AdvancedBlendVertexShader,
192+
AdvancedBlendFragmentShader>;
193+
using BlendHuePipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
194+
AdvancedBlendFragmentShader>;
195+
using BlendLightenPipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
196+
AdvancedBlendFragmentShader>;
184197
using BlendLuminosityPipeline =
185-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
186-
using BlendMultiplyPipeline =
187-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
188-
using BlendOverlayPipeline =
189-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
198+
RenderPipelineHandle<AdvancedBlendVertexShader,
199+
AdvancedBlendFragmentShader>;
200+
using BlendMultiplyPipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
201+
AdvancedBlendFragmentShader>;
202+
using BlendOverlayPipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
203+
AdvancedBlendFragmentShader>;
190204
using BlendSaturationPipeline =
191-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
192-
using BlendScreenPipeline =
193-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
205+
RenderPipelineHandle<AdvancedBlendVertexShader,
206+
AdvancedBlendFragmentShader>;
207+
using BlendScreenPipeline = RenderPipelineHandle<AdvancedBlendVertexShader,
208+
AdvancedBlendFragmentShader>;
194209
using BlendSoftLightPipeline =
195-
RenderPipelineT<AdvancedBlendVertexShader, AdvancedBlendFragmentShader>;
210+
RenderPipelineHandle<AdvancedBlendVertexShader,
211+
AdvancedBlendFragmentShader>;
196212
// Framebuffer Advanced Blends
197213
using FramebufferBlendColorPipeline =
198-
RenderPipelineT<FramebufferBlendVertexShader,
199-
FramebufferBlendFragmentShader>;
214+
RenderPipelineHandle<FramebufferBlendVertexShader,
215+
FramebufferBlendFragmentShader>;
200216
using FramebufferBlendColorBurnPipeline =
201-
RenderPipelineT<FramebufferBlendVertexShader,
202-
FramebufferBlendFragmentShader>;
217+
RenderPipelineHandle<FramebufferBlendVertexShader,
218+
FramebufferBlendFragmentShader>;
203219
using FramebufferBlendColorDodgePipeline =
204-
RenderPipelineT<FramebufferBlendVertexShader,
205-
FramebufferBlendFragmentShader>;
220+
RenderPipelineHandle<FramebufferBlendVertexShader,
221+
FramebufferBlendFragmentShader>;
206222
using FramebufferBlendDarkenPipeline =
207-
RenderPipelineT<FramebufferBlendVertexShader,
208-
FramebufferBlendFragmentShader>;
223+
RenderPipelineHandle<FramebufferBlendVertexShader,
224+
FramebufferBlendFragmentShader>;
209225
using FramebufferBlendDifferencePipeline =
210-
RenderPipelineT<FramebufferBlendVertexShader,
211-
FramebufferBlendFragmentShader>;
226+
RenderPipelineHandle<FramebufferBlendVertexShader,
227+
FramebufferBlendFragmentShader>;
212228
using FramebufferBlendExclusionPipeline =
213-
RenderPipelineT<FramebufferBlendVertexShader,
214-
FramebufferBlendFragmentShader>;
229+
RenderPipelineHandle<FramebufferBlendVertexShader,
230+
FramebufferBlendFragmentShader>;
215231
using FramebufferBlendHardLightPipeline =
216-
RenderPipelineT<FramebufferBlendVertexShader,
217-
FramebufferBlendFragmentShader>;
232+
RenderPipelineHandle<FramebufferBlendVertexShader,
233+
FramebufferBlendFragmentShader>;
218234
using FramebufferBlendHuePipeline =
219-
RenderPipelineT<FramebufferBlendVertexShader,
220-
FramebufferBlendFragmentShader>;
235+
RenderPipelineHandle<FramebufferBlendVertexShader,
236+
FramebufferBlendFragmentShader>;
221237
using FramebufferBlendLightenPipeline =
222-
RenderPipelineT<FramebufferBlendVertexShader,
223-
FramebufferBlendFragmentShader>;
238+
RenderPipelineHandle<FramebufferBlendVertexShader,
239+
FramebufferBlendFragmentShader>;
224240
using FramebufferBlendLuminosityPipeline =
225-
RenderPipelineT<FramebufferBlendVertexShader,
226-
FramebufferBlendFragmentShader>;
241+
RenderPipelineHandle<FramebufferBlendVertexShader,
242+
FramebufferBlendFragmentShader>;
227243
using FramebufferBlendMultiplyPipeline =
228-
RenderPipelineT<FramebufferBlendVertexShader,
229-
FramebufferBlendFragmentShader>;
244+
RenderPipelineHandle<FramebufferBlendVertexShader,
245+
FramebufferBlendFragmentShader>;
230246
using FramebufferBlendOverlayPipeline =
231-
RenderPipelineT<FramebufferBlendVertexShader,
232-
FramebufferBlendFragmentShader>;
247+
RenderPipelineHandle<FramebufferBlendVertexShader,
248+
FramebufferBlendFragmentShader>;
233249
using FramebufferBlendSaturationPipeline =
234-
RenderPipelineT<FramebufferBlendVertexShader,
235-
FramebufferBlendFragmentShader>;
250+
RenderPipelineHandle<FramebufferBlendVertexShader,
251+
FramebufferBlendFragmentShader>;
236252
using FramebufferBlendScreenPipeline =
237-
RenderPipelineT<FramebufferBlendVertexShader,
238-
FramebufferBlendFragmentShader>;
253+
RenderPipelineHandle<FramebufferBlendVertexShader,
254+
FramebufferBlendFragmentShader>;
239255
using FramebufferBlendSoftLightPipeline =
240-
RenderPipelineT<FramebufferBlendVertexShader,
241-
FramebufferBlendFragmentShader>;
256+
RenderPipelineHandle<FramebufferBlendVertexShader,
257+
FramebufferBlendFragmentShader>;
242258

243259
/// Geometry Pipelines
244260
using PointsComputeShaderPipeline = ComputePipelineBuilder<PointsComputeShader>;
245261
using UvComputeShaderPipeline = ComputePipelineBuilder<UvComputeShader>;
246262

247263
#ifdef IMPELLER_ENABLE_OPENGLES
248264
using TextureExternalPipeline =
249-
RenderPipelineT<TextureFillVertexShader, TextureFillExternalFragmentShader>;
265+
RenderPipelineHandle<TextureFillVertexShader,
266+
TextureFillExternalFragmentShader>;
250267

251268
using TiledTextureExternalPipeline =
252-
RenderPipelineT<TextureFillVertexShader,
253-
TiledTextureFillExternalFragmentShader>;
269+
RenderPipelineHandle<TextureFillVertexShader,
270+
TiledTextureFillExternalFragmentShader>;
254271
#endif // IMPELLER_ENABLE_OPENGLES
255272

256273
// A struct used to isolate command buffer storage from the content
@@ -836,43 +853,56 @@ class ContentContext {
836853
RuntimeEffectPipelineKey::Equal>
837854
runtime_effect_pipelines_;
838855

839-
template <class PipelineT>
856+
/// Holds multiple Pipelines associated with the same PipelineHandle types.
857+
///
858+
/// For example, it may have multiple
859+
/// RenderPipelineHandle<SolidFillVertexShader, SolidFillFragmentShader>
860+
/// instances for different blend modes. From them you can access the
861+
/// Pipeline.
862+
///
863+
/// See also:
864+
/// - impeller::ContentContextOptions - options from which variants are
865+
/// created.
866+
/// - impeller::Pipeline::CreateVariant
867+
/// - impeller::RenderPipelineHandle<> - The type of objects this typically
868+
/// contains.
869+
template <class PipelineHandleT>
840870
class Variants {
841871
public:
842872
Variants() = default;
843873

844874
void Set(const ContentContextOptions& options,
845-
std::unique_ptr<PipelineT> pipeline) {
875+
std::unique_ptr<PipelineHandleT> pipeline) {
846876
pipelines_[options] = std::move(pipeline);
847877
}
848878

849879
void SetDefault(const ContentContextOptions& options,
850-
std::unique_ptr<PipelineT> pipeline) {
880+
std::unique_ptr<PipelineHandleT> pipeline) {
851881
default_options_ = options;
852882
Set(options, std::move(pipeline));
853883
}
854884

855885
void CreateDefault(const Context& context,
856886
const ContentContextOptions& options,
857887
const std::initializer_list<Scalar>& constants = {}) {
858-
auto desc =
859-
PipelineT::Builder::MakeDefaultPipelineDescriptor(context, constants);
888+
auto desc = PipelineHandleT::Builder::MakeDefaultPipelineDescriptor(
889+
context, constants);
860890
if (!desc.has_value()) {
861891
VALIDATION_LOG << "Failed to create default pipeline.";
862892
return;
863893
}
864894
options.ApplyToPipelineDescriptor(*desc);
865-
SetDefault(options, std::make_unique<PipelineT>(context, desc));
895+
SetDefault(options, std::make_unique<PipelineHandleT>(context, desc));
866896
}
867897

868-
PipelineT* Get(const ContentContextOptions& options) const {
898+
PipelineHandleT* Get(const ContentContextOptions& options) const {
869899
if (auto found = pipelines_.find(options); found != pipelines_.end()) {
870900
return found->second.get();
871901
}
872902
return nullptr;
873903
}
874904

875-
PipelineT* GetDefault() const {
905+
PipelineHandleT* GetDefault() const {
876906
if (!default_options_.has_value()) {
877907
return nullptr;
878908
}
@@ -884,7 +914,7 @@ class ContentContext {
884914
private:
885915
std::optional<ContentContextOptions> default_options_;
886916
std::unordered_map<ContentContextOptions,
887-
std::unique_ptr<PipelineT>,
917+
std::unique_ptr<PipelineHandleT>,
888918
ContentContextOptions::Hash,
889919
ContentContextOptions::Equal>
890920
pipelines_;
@@ -1004,9 +1034,10 @@ class ContentContext {
10041034
return pipeline->WaitAndGet();
10051035
}
10061036

1007-
template <class TypedPipeline>
1008-
TypedPipeline* CreateIfNeeded(Variants<TypedPipeline>& container,
1009-
ContentContextOptions opts) const {
1037+
template <class RenderPipelineHandleT>
1038+
RenderPipelineHandleT* CreateIfNeeded(
1039+
Variants<RenderPipelineHandleT>& container,
1040+
ContentContextOptions opts) const {
10101041
if (!IsValid()) {
10111042
return nullptr;
10121043
}
@@ -1015,17 +1046,17 @@ class ContentContext {
10151046
opts.wireframe = true;
10161047
}
10171048

1018-
if (TypedPipeline* found = container.Get(opts)) {
1049+
if (RenderPipelineHandleT* found = container.Get(opts)) {
10191050
return found;
10201051
}
10211052

1022-
TypedPipeline* prototype = container.GetDefault();
1053+
RenderPipelineHandleT* default_handle = container.GetDefault();
10231054

1024-
// The prototype must always be initialized in the constructor.
1025-
FML_CHECK(prototype != nullptr);
1055+
// The default must always be initialized in the constructor.
1056+
FML_CHECK(default_handle != nullptr);
10261057

10271058
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
1028-
prototype->WaitAndGet();
1059+
default_handle->WaitAndGet();
10291060
if (!pipeline) {
10301061
return nullptr;
10311062
}
@@ -1037,8 +1068,8 @@ class ContentContext {
10371068
desc.SetLabel(
10381069
SPrintF("%s V#%zu", desc.GetLabel().c_str(), variants_count));
10391070
});
1040-
std::unique_ptr<TypedPipeline> variant =
1041-
std::make_unique<TypedPipeline>(std::move(variant_future));
1071+
std::unique_ptr<RenderPipelineHandleT> variant =
1072+
std::make_unique<RenderPipelineHandleT>(std::move(variant_future));
10421073
container.Set(opts, std::move(variant));
10431074
return container.Get(opts);
10441075
}

0 commit comments

Comments
 (0)