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"
20 
21 namespace impeller {
22 namespace testing {
23 
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());
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));
269  color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
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