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