| Flutter Impeller |

runtime_stage_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <future>
6
7 #include "flutter/fml/make_copyable.h"
8 #include "flutter/impeller/fixtures/simple.vert.h"
9 #include "flutter/testing/testing.h"
10 #include "impeller/base/allocation.h"
11 #include "impeller/base/validation.h"
12 #include "impeller/core/runtime_types.h"
13 #include "impeller/core/shader_types.h"
14 #include "impeller/playground/playground.h"
15 #include "impeller/renderer/pipeline_descriptor.h"
16 #include "impeller/renderer/pipeline_library.h"
17 #include "impeller/renderer/shader_library.h"
18 #include "impeller/runtime_stage/runtime_stage.h"
19 #include "impeller/runtime_stage/runtime_stage_playground.h"
20
21 namespace impeller {
22 namespace testing {
23
24 using RuntimeStageTest = RuntimeStagePlayground;
25 INSTANTIATE_PLAYGROUND_SUITE(RuntimeStageTest);
26
27 TEST_P(RuntimeStageTest, CanReadValidBlob) {
28 if (!BackendSupportsFragmentProgram()) {
29 GTEST_SKIP_("This backend doesn't support runtime effects.");
30 }
31
32 const std::shared_ptr<fml::Mapping> fixture =
33 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
34 ASSERT_TRUE(fixture);
35 ASSERT_GT(fixture->GetSize(), 0u);
36 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
37 auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
38 ASSERT_TRUE(stage->IsValid());
39 ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
40 }
41
42 TEST_P(RuntimeStageTest, CanRejectInvalidBlob) {
43 if (!BackendSupportsFragmentProgram()) {
44 GTEST_SKIP_("This backend doesn't support runtime effects.");
45 }
46
47 ScopedValidationDisable disable_validation;
48 const std::shared_ptr<fml::Mapping> fixture =
49 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
50 ASSERT_TRUE(fixture);
51 auto junk_allocation = std::make_shared<Allocation>();
52 ASSERT_TRUE(junk_allocation->Truncate(fixture->GetSize(), false));
53 // Not meant to be secure. Just reject obviously bad blobs using magic
54 // numbers.
55 ::memset(junk_allocation->GetBuffer(), 127, junk_allocation->GetLength());
56 auto stages = RuntimeStage::DecodeRuntimeStages(
57 CreateMappingFromAllocation(junk_allocation));
58 ASSERT_FALSE(stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]);
59 }
60
61 TEST_P(RuntimeStageTest, CanReadUniforms) {
62 if (!BackendSupportsFragmentProgram()) {
63 GTEST_SKIP_("This backend doesn't support runtime effects.");
64 }
65
66 const std::shared_ptr<fml::Mapping> fixture =
67 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
68 ASSERT_TRUE(fixture);
69 ASSERT_GT(fixture->GetSize(), 0u);
70 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
71 auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
72
73 ASSERT_TRUE(stage->IsValid());
74 ASSERT_EQ(stage->GetUniforms().size(), 17u);
75 {
76 auto uni = stage->GetUniform("u_color");
77 ASSERT_NE(uni, nullptr);
78 ASSERT_EQ(uni->dimensions.rows, 4u);
79 ASSERT_EQ(uni->dimensions.cols, 1u);
80 ASSERT_EQ(uni->location, 0u);
81 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
82 }
83 {
84 auto uni = stage->GetUniform("u_alpha");
85 ASSERT_NE(uni, nullptr);
86 ASSERT_EQ(uni->dimensions.rows, 1u);
87 ASSERT_EQ(uni->dimensions.cols, 1u);
88 ASSERT_EQ(uni->location, 1u);
89 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
90 }
91 {
92 auto uni = stage->GetUniform("u_sparkle_color");
93 ASSERT_NE(uni, nullptr);
94 ASSERT_EQ(uni->dimensions.rows, 4u);
95 ASSERT_EQ(uni->dimensions.cols, 1u);
96 ASSERT_EQ(uni->location, 2u);
97 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
98 }
99 {
100 auto uni = stage->GetUniform("u_sparkle_alpha");
101 ASSERT_NE(uni, nullptr);
102 ASSERT_EQ(uni->dimensions.rows, 1u);
103 ASSERT_EQ(uni->dimensions.cols, 1u);
104 ASSERT_EQ(uni->location, 3u);
105 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
106 }
107 {
108 auto uni = stage->GetUniform("u_blur");
109 ASSERT_NE(uni, nullptr);
110 ASSERT_EQ(uni->dimensions.rows, 1u);
111 ASSERT_EQ(uni->dimensions.cols, 1u);
112 ASSERT_EQ(uni->location, 4u);
113 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
114 }
115 {
116 auto uni = stage->GetUniform("u_radius_scale");
117 ASSERT_NE(uni, nullptr);
118 ASSERT_EQ(uni->dimensions.rows, 1u);
119 ASSERT_EQ(uni->dimensions.cols, 1u);
120 ASSERT_EQ(uni->location, 6u);
121 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
122 }
123 {
124 auto uni = stage->GetUniform("u_max_radius");
125 ASSERT_NE(uni, nullptr);
126 ASSERT_EQ(uni->dimensions.rows, 1u);
127 ASSERT_EQ(uni->dimensions.cols, 1u);
128 ASSERT_EQ(uni->location, 7u);
129 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
130 }
131 {
132 auto uni = stage->GetUniform("u_resolution_scale");
133 ASSERT_NE(uni, nullptr);
134 ASSERT_EQ(uni->dimensions.rows, 2u);
135 ASSERT_EQ(uni->dimensions.cols, 1u);
136 ASSERT_EQ(uni->location, 8u);
137 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
138 }
139 {
140 auto uni = stage->GetUniform("u_noise_scale");
141 ASSERT_NE(uni, nullptr);
142 ASSERT_EQ(uni->dimensions.rows, 2u);
143 ASSERT_EQ(uni->dimensions.cols, 1u);
144 ASSERT_EQ(uni->location, 9u);
145 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
146 }
147 {
148 auto uni = stage->GetUniform("u_noise_phase");
149 ASSERT_NE(uni, nullptr);
150 ASSERT_EQ(uni->dimensions.rows, 1u);
151 ASSERT_EQ(uni->dimensions.cols, 1u);
152 ASSERT_EQ(uni->location, 10u);
153 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
154 }
155
156 {
157 auto uni = stage->GetUniform("u_circle1");
158 ASSERT_NE(uni, nullptr);
159 ASSERT_EQ(uni->dimensions.rows, 2u);
160 ASSERT_EQ(uni->dimensions.cols, 1u);
161 ASSERT_EQ(uni->location, 11u);
162 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
163 }
164 {
165 auto uni = stage->GetUniform("u_circle2");
166 ASSERT_NE(uni, nullptr);
167 ASSERT_EQ(uni->dimensions.rows, 2u);
168 ASSERT_EQ(uni->dimensions.cols, 1u);
169 ASSERT_EQ(uni->location, 12u);
170 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
171 }
172 {
173 auto uni = stage->GetUniform("u_circle3");
174 ASSERT_NE(uni, nullptr);
175 ASSERT_EQ(uni->dimensions.rows, 2u);
176 ASSERT_EQ(uni->dimensions.cols, 1u);
177 ASSERT_EQ(uni->location, 13u);
178 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
179 }
180 {
181 auto uni = stage->GetUniform("u_rotation1");
182 ASSERT_NE(uni, nullptr);
183 ASSERT_EQ(uni->dimensions.rows, 2u);
184 ASSERT_EQ(uni->dimensions.cols, 1u);
185 ASSERT_EQ(uni->location, 14u);
186 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
187 }
188 {
189 auto uni = stage->GetUniform("u_rotation2");
190 ASSERT_NE(uni, nullptr);
191 ASSERT_EQ(uni->dimensions.rows, 2u);
192 ASSERT_EQ(uni->dimensions.cols, 1u);
193 ASSERT_EQ(uni->location, 15u);
194 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
195 }
196 {
197 auto uni = stage->GetUniform("u_rotation3");
198 ASSERT_NE(uni, nullptr);
199 ASSERT_EQ(uni->dimensions.rows, 2u);
200 ASSERT_EQ(uni->dimensions.cols, 1u);
201 ASSERT_EQ(uni->location, 16u);
202 ASSERT_EQ(uni->type, RuntimeUniformType::kFloat);
203 }
204 }
205
206 TEST_P(RuntimeStageTest, CanRegisterStage) {
207 if (!BackendSupportsFragmentProgram()) {
208 GTEST_SKIP_("This backend doesn't support runtime effects.");
209 }
210
211 const std::shared_ptr<fml::Mapping> fixture =
212 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
213 ASSERT_TRUE(fixture);
214 ASSERT_GT(fixture->GetSize(), 0u);
215 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
216 auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
217 ASSERT_TRUE(stage->IsValid());
218 std::promise<bool> registration;
219 auto future = registration.get_future();
220 auto library = GetContext()->GetShaderLibrary();
221 library->RegisterFunction(
222 stage->GetEntrypoint(), //
223 ToShaderStage(stage->GetShaderStage()), //
224 stage->GetCodeMapping(), //
225 fml::MakeCopyable([reg = std::move(registration)](bool result) mutable {
226 reg.set_value(result);
227 }));
228 ASSERT_TRUE(future.get());
229 {
230 auto function =
231 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
232 ASSERT_NE(function, nullptr);
233 }
234
235 // Check if unregistering works.
236
237 library->UnregisterFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
238 {
239 auto function =
240 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
241 ASSERT_EQ(function, nullptr);
242 }
243 }
244
245 TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
246 if (!BackendSupportsFragmentProgram()) {
247 GTEST_SKIP_("This backend doesn't support runtime effects.");
248 }
249 auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
250 auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
251
252 ASSERT_TRUE(stage);
253 ASSERT_NE(stage, nullptr);
254 ASSERT_TRUE(RegisterStage(*stage));
255 auto library = GetContext()->GetShaderLibrary();
256 using VS = SimpleVertexShader;
257 PipelineDescriptor desc;
258 desc.SetLabel("Runtime Stage InkSparkle");
259 desc.AddStageEntrypoint(
260 library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
261 desc.AddStageEntrypoint(
262 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment));
263 auto vertex_descriptor = std::make_shared<VertexDescriptor>();
264 vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
265 VS::kInterleavedBufferLayout);
266
267 desc.SetVertexDescriptor(std::move(vertex_descriptor));
268 ColorAttachmentDescriptor color0;
269 color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
270 StencilAttachmentDescriptor stencil0;
271 stencil0.stencil_compare = CompareFunction::kEqual;
272 desc.SetColorAttachmentDescriptor(0u, color0);
273 desc.SetStencilAttachmentDescriptors(stencil0);
274 const auto stencil_fmt =
275 GetContext()->GetCapabilities()->GetDefaultStencilFormat();
276 desc.SetStencilPixelFormat(stencil_fmt);
277 auto pipeline = GetContext()->GetPipelineLibrary()->GetPipeline(desc).Get();
278 ASSERT_NE(pipeline, nullptr);
279 }
280
281 TEST_P(RuntimeStageTest, ContainsExpectedShaderTypes) {
282 auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
283 // Right now, SkSL gets implicitly bundled regardless of what the build rule
284 // for this test requested. After
285 // https://github.com/flutter/flutter/issues/138919, this may require a build
286 // rule change or a new test.
287 EXPECT_TRUE(stages[RuntimeStageBackend::kSkSL]);
288
289 EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]);
290 EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]);
291 // TODO(dnfield): Flip this when
292 // https://github.com/flutter/flutter/issues/122823 is fixed.
293 EXPECT_FALSE(stages[RuntimeStageBackend::kVulkan]);
294 }
295
296 } // namespace testing
297 } // namespace impeller
impeller::PipelineDescriptor
Definition: pipeline_descriptor.h:29
impeller::kFloat
@ kFloat
Definition: runtime_types.h:32
impeller::StencilAttachmentDescriptor::stencil_compare
CompareFunction stencil_compare
Definition: formats.h:593
impeller::RuntimeStageBackend::kVulkan
@ kVulkan
impeller::PipelineDescriptor::SetStencilAttachmentDescriptors
PipelineDescriptor & SetStencilAttachmentDescriptors(std::optional< StencilAttachmentDescriptor > front_and_back)
Definition: pipeline_descriptor.cc:159
allocation.h
impeller::PipelineDescriptor::SetColorAttachmentDescriptor
PipelineDescriptor & SetColorAttachmentDescriptor(size_t index, ColorAttachmentDescriptor desc)
Definition: pipeline_descriptor.cc:112
impeller::PipelineDescriptor::SetStencilPixelFormat
PipelineDescriptor & SetStencilPixelFormat(PixelFormat format)
Definition: pipeline_descriptor.cc:147
impeller::CompareFunction::kEqual
@ kEqual
Comparison test passes if new_value == current_value.
impeller::RuntimeStage::DecodeRuntimeStages
static Map DecodeRuntimeStages(const std::shared_ptr< fml::Mapping > &payload)
Definition: runtime_stage.cc:72
shader_library.h
playground.h
validation.h
impeller::CreateMappingFromAllocation
std::shared_ptr< fml::Mapping > CreateMappingFromAllocation(const std::shared_ptr< Allocation > &allocation)
Definition: allocation.cc:99
impeller::PlaygroundBackendToRuntimeStageBackend
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:35
impeller::ToShaderStage
constexpr ShaderStage ToShaderStage(RuntimeShaderStage stage)
Definition: shader_types.h:29
runtime_types.h
impeller::RuntimeStageBackend::kOpenGLES
@ kOpenGLES
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
impeller::RuntimeShaderStage::kFragment
@ kFragment
runtime_stage.h
impeller::ColorAttachmentDescriptor::format
PixelFormat format
Definition: formats.h:495
impeller::ShaderStage::kFragment
@ kFragment
impeller::RuntimeStagePlayground
Definition: runtime_stage_playground.h:14
pipeline_library.h
impeller::PipelineDescriptor::AddStageEntrypoint
PipelineDescriptor & AddStageEntrypoint(std::shared_ptr< const ShaderFunction > function)
Definition: pipeline_descriptor.cc:83
impeller::RuntimeStageBackend::kSkSL
@ kSkSL
impeller::ShaderStage::kVertex
@ kVertex
runtime_stage_playground.h
impeller::StencilAttachmentDescriptor
Definition: formats.h:587
pipeline_descriptor.h
impeller::PipelineDescriptor::SetLabel
PipelineDescriptor & SetLabel(std::string label)
Definition: pipeline_descriptor.cc:73
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderLinearGradientClamp)
Definition: aiks_gradient_unittests.cc:48
impeller::ScopedValidationDisable
Definition: validation.h:42
shader_types.h
impeller::RuntimeStageBackend::kMetal
@ kMetal
impeller
Definition: aiks_context.cc:10
impeller::PipelineDescriptor::SetVertexDescriptor
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
Definition: pipeline_descriptor.cc:98
impeller::ColorAttachmentDescriptor
Describe the color attachment that will be used with this pipeline.
Definition: formats.h:494