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/testing/testing.h"
9 #include "gmock/gmock.h"
14 #include "impeller/entity/runtime_effect.vert.h"
21 
22 namespace impeller {
23 namespace testing {
24 
27 
28 TEST_P(RuntimeStageTest, CanReadValidBlob) {
29  const std::shared_ptr<fml::Mapping> fixture =
30  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
31  ASSERT_TRUE(fixture);
32  ASSERT_GT(fixture->GetSize(), 0u);
33  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
34  auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
35  ASSERT_TRUE(stage->IsValid());
36  ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
37 }
38 
39 TEST_P(RuntimeStageTest, CanRejectInvalidBlob) {
40  ScopedValidationDisable disable_validation;
41  const std::shared_ptr<fml::Mapping> fixture =
42  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
43  ASSERT_TRUE(fixture);
44  auto junk_allocation = std::make_shared<Allocation>();
45  ASSERT_TRUE(junk_allocation->Truncate(fixture->GetSize(), false));
46  // Not meant to be secure. Just reject obviously bad blobs using magic
47  // numbers.
48  ::memset(junk_allocation->GetBuffer(), 127, junk_allocation->GetLength());
50  CreateMappingFromAllocation(junk_allocation));
51  ASSERT_FALSE(stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]);
52 }
53 
54 TEST_P(RuntimeStageTest, CanReadUniforms) {
55  const std::shared_ptr<fml::Mapping> fixture =
56  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
57  ASSERT_TRUE(fixture);
58  ASSERT_GT(fixture->GetSize(), 0u);
59  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
60  auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
61 
62  ASSERT_TRUE(stage->IsValid());
63  switch (GetBackend()) {
66  ASSERT_EQ(stage->GetUniforms().size(), 17u);
67  {
68  auto uni = stage->GetUniform("u_color");
69  ASSERT_NE(uni, nullptr);
70  EXPECT_EQ(uni->dimensions.rows, 4u);
71  EXPECT_EQ(uni->dimensions.cols, 1u);
72  EXPECT_EQ(uni->location, 0u);
73  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
74  }
75  {
76  auto uni = stage->GetUniform("u_alpha");
77  ASSERT_NE(uni, nullptr);
78  EXPECT_EQ(uni->dimensions.rows, 1u);
79  EXPECT_EQ(uni->dimensions.cols, 1u);
80  EXPECT_EQ(uni->location, 1u);
81  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
82  }
83  {
84  auto uni = stage->GetUniform("u_sparkle_color");
85  ASSERT_NE(uni, nullptr);
86  EXPECT_EQ(uni->dimensions.rows, 4u);
87  EXPECT_EQ(uni->dimensions.cols, 1u);
88  EXPECT_EQ(uni->location, 2u);
89  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
90  }
91  {
92  auto uni = stage->GetUniform("u_sparkle_alpha");
93  ASSERT_NE(uni, nullptr);
94  EXPECT_EQ(uni->dimensions.rows, 1u);
95  EXPECT_EQ(uni->dimensions.cols, 1u);
96  EXPECT_EQ(uni->location, 3u);
97  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
98  }
99  {
100  auto uni = stage->GetUniform("u_blur");
101  ASSERT_NE(uni, nullptr);
102  EXPECT_EQ(uni->dimensions.rows, 1u);
103  EXPECT_EQ(uni->dimensions.cols, 1u);
104  EXPECT_EQ(uni->location, 4u);
105  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
106  }
107  {
108  auto uni = stage->GetUniform("u_radius_scale");
109  ASSERT_NE(uni, nullptr);
110  EXPECT_EQ(uni->dimensions.rows, 1u);
111  EXPECT_EQ(uni->dimensions.cols, 1u);
112  EXPECT_EQ(uni->location, 6u);
113  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
114  }
115  {
116  auto uni = stage->GetUniform("u_max_radius");
117  ASSERT_NE(uni, nullptr);
118  EXPECT_EQ(uni->dimensions.rows, 1u);
119  EXPECT_EQ(uni->dimensions.cols, 1u);
120  EXPECT_EQ(uni->location, 7u);
121  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
122  }
123  {
124  auto uni = stage->GetUniform("u_resolution_scale");
125  ASSERT_NE(uni, nullptr);
126  EXPECT_EQ(uni->dimensions.rows, 2u);
127  EXPECT_EQ(uni->dimensions.cols, 1u);
128  EXPECT_EQ(uni->location, 8u);
129  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
130  }
131  {
132  auto uni = stage->GetUniform("u_noise_scale");
133  ASSERT_NE(uni, nullptr);
134  EXPECT_EQ(uni->dimensions.rows, 2u);
135  EXPECT_EQ(uni->dimensions.cols, 1u);
136  EXPECT_EQ(uni->location, 9u);
137  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
138  }
139  {
140  auto uni = stage->GetUniform("u_noise_phase");
141  ASSERT_NE(uni, nullptr);
142  EXPECT_EQ(uni->dimensions.rows, 1u);
143  EXPECT_EQ(uni->dimensions.cols, 1u);
144  EXPECT_EQ(uni->location, 10u);
145  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
146  }
147 
148  {
149  auto uni = stage->GetUniform("u_circle1");
150  ASSERT_NE(uni, nullptr);
151  EXPECT_EQ(uni->dimensions.rows, 2u);
152  EXPECT_EQ(uni->dimensions.cols, 1u);
153  EXPECT_EQ(uni->location, 11u);
154  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
155  }
156  {
157  auto uni = stage->GetUniform("u_circle2");
158  ASSERT_NE(uni, nullptr);
159  EXPECT_EQ(uni->dimensions.rows, 2u);
160  EXPECT_EQ(uni->dimensions.cols, 1u);
161  EXPECT_EQ(uni->location, 12u);
162  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
163  }
164  {
165  auto uni = stage->GetUniform("u_circle3");
166  ASSERT_NE(uni, nullptr);
167  EXPECT_EQ(uni->dimensions.rows, 2u);
168  EXPECT_EQ(uni->dimensions.cols, 1u);
169  EXPECT_EQ(uni->location, 13u);
170  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
171  }
172  {
173  auto uni = stage->GetUniform("u_rotation1");
174  ASSERT_NE(uni, nullptr);
175  EXPECT_EQ(uni->dimensions.rows, 2u);
176  EXPECT_EQ(uni->dimensions.cols, 1u);
177  EXPECT_EQ(uni->location, 14u);
178  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
179  }
180  {
181  auto uni = stage->GetUniform("u_rotation2");
182  ASSERT_NE(uni, nullptr);
183  EXPECT_EQ(uni->dimensions.rows, 2u);
184  EXPECT_EQ(uni->dimensions.cols, 1u);
185  EXPECT_EQ(uni->location, 15u);
186  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
187  }
188  {
189  auto uni = stage->GetUniform("u_rotation3");
190  ASSERT_NE(uni, nullptr);
191  EXPECT_EQ(uni->dimensions.rows, 2u);
192  EXPECT_EQ(uni->dimensions.cols, 1u);
193  EXPECT_EQ(uni->location, 16u);
194  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
195  }
196  break;
197  }
199  EXPECT_EQ(stage->GetUniforms().size(), 1u);
200  auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
201  ASSERT_TRUE(uni);
202  EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
203  EXPECT_EQ(uni->struct_float_count, 32u);
204 
205  // There are 36 4 byte chunks in the UBO: 32 for the 32 floats, and 4 for
206  // padding. Initialize a vector as if they'll all be floats, then manually
207  // set the few padding bytes. If the shader changes, the padding locations
208  // will change as well. For example, if `u_alpha` was moved to the end,
209  // three bytes of padding could potentially be dropped - or if some of the
210  // scalar floats were changed to vec2 or vec4s, or if any vec3s are
211  // introduced.
212  // This means 36 * 4 = 144 bytes total.
213 
214  EXPECT_EQ(uni->GetSize(), 144u);
215  std::vector<uint8_t> layout(uni->GetSize() / sizeof(float), 1);
216  layout[5] = 0;
217  layout[6] = 0;
218  layout[7] = 0;
219  layout[23] = 0;
220 
221  EXPECT_THAT(uni->struct_layout, ::testing::ElementsAreArray(layout));
222  break;
223  }
224  }
225 }
226 
227 TEST_P(RuntimeStageTest, CanRegisterStage) {
228  const std::shared_ptr<fml::Mapping> fixture =
229  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
230  ASSERT_TRUE(fixture);
231  ASSERT_GT(fixture->GetSize(), 0u);
232  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
233  auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
234  ASSERT_TRUE(stage->IsValid());
235  std::promise<bool> registration;
236  auto future = registration.get_future();
237  auto library = GetContext()->GetShaderLibrary();
238  library->RegisterFunction(
239  stage->GetEntrypoint(), //
240  ToShaderStage(stage->GetShaderStage()), //
241  stage->GetCodeMapping(), //
242  fml::MakeCopyable([reg = std::move(registration)](bool result) mutable {
243  reg.set_value(result);
244  }));
245  ASSERT_TRUE(future.get());
246  {
247  auto function =
248  library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
249  ASSERT_NE(function, nullptr);
250  }
251 
252  // Check if unregistering works.
253 
254  library->UnregisterFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
255  {
256  auto function =
257  library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
258  ASSERT_EQ(function, nullptr);
259  }
260 }
261 
262 TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
263  auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
264  auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
265 
266  ASSERT_TRUE(stage);
267  ASSERT_NE(stage, nullptr);
268  ASSERT_TRUE(RegisterStage(*stage));
269  auto library = GetContext()->GetShaderLibrary();
270  using VS = RuntimeEffectVertexShader;
271  PipelineDescriptor desc;
272  desc.SetLabel("Runtime Stage InkSparkle");
273  desc.AddStageEntrypoint(
274  library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
275  desc.AddStageEntrypoint(
276  library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment));
277  auto vertex_descriptor = std::make_shared<VertexDescriptor>();
278  vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
279  VS::kInterleavedBufferLayout);
280 
281  std::array<DescriptorSetLayout, 2> descriptor_set_layouts = {
282  VS::kDescriptorSetLayouts[0],
284  .binding = 64u,
285  .descriptor_type = DescriptorType::kUniformBuffer,
286  .shader_stage = ShaderStage::kFragment,
287  },
288  };
289  vertex_descriptor->RegisterDescriptorSetLayouts(descriptor_set_layouts);
290 
291  desc.SetVertexDescriptor(std::move(vertex_descriptor));
293  color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
296  desc.SetColorAttachmentDescriptor(0u, color0);
297  desc.SetStencilAttachmentDescriptors(stencil0);
298  const auto stencil_fmt =
299  GetContext()->GetCapabilities()->GetDefaultStencilFormat();
300  desc.SetStencilPixelFormat(stencil_fmt);
301  auto pipeline = GetContext()->GetPipelineLibrary()->GetPipeline(desc).Get();
302  ASSERT_NE(pipeline, nullptr);
303 }
304 
305 TEST_P(RuntimeStageTest, ContainsExpectedShaderTypes) {
306  auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
307  // Right now, SkSL gets implicitly bundled regardless of what the build rule
308  // for this test requested. After
309  // https://github.com/flutter/flutter/issues/138919, this may require a build
310  // rule change or a new test.
311  EXPECT_TRUE(stages[RuntimeStageBackend::kSkSL]);
312 
313  EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]);
314  EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]);
315  EXPECT_TRUE(stages[RuntimeStageBackend::kVulkan]);
316 }
317 
318 } // namespace testing
319 } // namespace impeller
impeller::PipelineDescriptor
Definition: pipeline_descriptor.h:24
impeller::kFloat
@ kFloat
Definition: runtime_types.h:23
impeller::PlaygroundBackend::kVulkan
@ kVulkan
impeller::DescriptorSetLayout
Definition: shader_types.h:162
impeller::StencilAttachmentDescriptor::stencil_compare
CompareFunction stencil_compare
Definition: formats.h:598
impeller::RuntimeStageBackend::kVulkan
@ kVulkan
impeller::PipelineDescriptor::SetStencilAttachmentDescriptors
PipelineDescriptor & SetStencilAttachmentDescriptors(std::optional< StencilAttachmentDescriptor > front_and_back)
Definition: pipeline_descriptor.cc:157
allocation.h
impeller::PipelineDescriptor::SetColorAttachmentDescriptor
PipelineDescriptor & SetColorAttachmentDescriptor(size_t index, ColorAttachmentDescriptor desc)
Definition: pipeline_descriptor.cc:110
impeller::PipelineDescriptor::SetStencilPixelFormat
PipelineDescriptor & SetStencilPixelFormat(PixelFormat format)
Definition: pipeline_descriptor.cc:145
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:60
impeller::RuntimeStage::kVulkanUBOName
static const char * kVulkanUBOName
Definition: runtime_stage.h:21
impeller::PlaygroundBackend::kMetal
@ kMetal
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::VS
SolidFillVertexShader VS
Definition: stroke_path_geometry.cc:15
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
impeller::RuntimeShaderStage::kFragment
@ kFragment
runtime_stage.h
impeller::ColorAttachmentDescriptor::format
PixelFormat format
Definition: formats.h:501
impeller::ShaderStage::kFragment
@ kFragment
impeller::RuntimeStagePlayground
Definition: runtime_stage_playground.h:14
impeller::kStruct
@ kStruct
Definition: runtime_types.h:25
impeller::DescriptorType::kUniformBuffer
@ kUniformBuffer
pipeline_library.h
impeller::PipelineDescriptor::AddStageEntrypoint
PipelineDescriptor & AddStageEntrypoint(std::shared_ptr< const ShaderFunction > function)
Definition: pipeline_descriptor.cc:81
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::RuntimeStageBackend::kSkSL
@ kSkSL
impeller::ShaderStage::kVertex
@ kVertex
runtime_stage_playground.h
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderMaskBlurHugeSigma)
Definition: aiks_blur_unittests.cc:23
impeller::StencilAttachmentDescriptor
Definition: formats.h:592
pipeline_descriptor.h
impeller::PipelineDescriptor::SetLabel
PipelineDescriptor & SetLabel(std::string label)
Definition: pipeline_descriptor.cc:71
impeller::ScopedValidationDisable
Definition: validation.h:38
shader_types.h
impeller::RuntimeStageBackend::kMetal
@ kMetal
impeller
Definition: aiks_blur_unittests.cc:20
impeller::DescriptorSetLayout::binding
uint32_t binding
Definition: shader_types.h:163
impeller::PipelineDescriptor::SetVertexDescriptor
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
Definition: pipeline_descriptor.cc:96
impeller::ColorAttachmentDescriptor
Describe the color attachment that will be used with this pipeline.
Definition: formats.h:500