| 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"
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/entity/runtime_effect.vert.h"
15 #include "impeller/playground/playground.h"
16 #include "impeller/renderer/pipeline_descriptor.h"
17 #include "impeller/renderer/pipeline_library.h"
18 #include "impeller/renderer/shader_library.h"
19 #include "impeller/runtime_stage/runtime_stage.h"
20 #include "impeller/runtime_stage/runtime_stage_playground.h"
21
22 namespace impeller {
23 namespace testing {
24
25 using RuntimeStageTest = RuntimeStagePlayground;
26 INSTANTIATE_PLAYGROUND_SUITE(RuntimeStageTest);
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());
49 auto stages = RuntimeStage::DecodeRuntimeStages(
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()) {
64 case PlaygroundBackend::kMetal:
65 case PlaygroundBackend::kOpenGLES: {
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 }
198 case PlaygroundBackend::kVulkan: {
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],
283 DescriptorSetLayout{
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));
292 ColorAttachmentDescriptor color0;
293 color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
294 StencilAttachmentDescriptor stencil0;
295 stencil0.stencil_compare = CompareFunction::kEqual;
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