Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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"
21#include "impeller/runtime_stage/runtime_stage_flatbuffers.h"
23#include "runtime_stage_types_flatbuffers.h"
24#include "third_party/abseil-cpp/absl/status/status_matchers.h"
25
26namespace impeller {
27namespace testing {
28
31
32TEST_P(RuntimeStageTest, CanReadValidBlob) {
33 const std::shared_ptr<fml::Mapping> fixture =
34 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
35 ASSERT_TRUE(fixture);
36 ASSERT_GT(fixture->GetSize(), 0u);
37 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
38 ABSL_ASSERT_OK(stages);
39 auto stage =
40 stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
41 ASSERT_TRUE(stage);
42 ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
43}
44
45TEST_P(RuntimeStageTest, RejectInvalidFormatVersion) {
46 flatbuffers::FlatBufferBuilder builder;
47 fb::RuntimeStagesBuilder stages_builder(builder);
48 stages_builder.add_format_version(0);
49 auto stages = stages_builder.Finish();
50 builder.Finish(stages, fb::RuntimeStagesIdentifier());
51 auto mapping = std::make_shared<fml::NonOwnedMapping>(
52 builder.GetBufferPointer(), builder.GetSize());
53 auto runtime_stages = RuntimeStage::DecodeRuntimeStages(mapping);
54 EXPECT_FALSE(runtime_stages.ok());
55 EXPECT_EQ(runtime_stages.status().code(), absl::StatusCode::kInvalidArgument);
56}
57
58TEST_P(RuntimeStageTest, CanRejectInvalidBlob) {
59 ScopedValidationDisable disable_validation;
60 const std::shared_ptr<fml::Mapping> fixture =
61 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
62 ASSERT_TRUE(fixture);
63 auto junk_allocation = std::make_shared<Allocation>();
64 ASSERT_TRUE(junk_allocation->Truncate(Bytes{fixture->GetSize()}, false));
65 // Not meant to be secure. Just reject obviously bad blobs using magic
66 // numbers.
67 ::memset(junk_allocation->GetBuffer(), 127,
68 junk_allocation->GetLength().GetByteSize());
70 CreateMappingFromAllocation(junk_allocation));
71 ASSERT_FALSE(stages.ok());
72}
73
74TEST_P(RuntimeStageTest, CanReadUniforms) {
75 const std::shared_ptr<fml::Mapping> fixture =
76 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
77 ASSERT_TRUE(fixture);
78 ASSERT_GT(fixture->GetSize(), 0u);
79 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
80 ABSL_ASSERT_OK(stages);
81 auto stage =
82 stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
83
84 ASSERT_TRUE(stage);
85 switch (GetBackend()) {
87 [[fallthrough]];
89 ASSERT_EQ(stage->GetUniforms().size(), 17u);
90 {
91 auto uni = stage->GetUniform("u_color");
92 ASSERT_NE(uni, nullptr);
93 EXPECT_EQ(uni->dimensions.rows, 4u);
94 EXPECT_EQ(uni->dimensions.cols, 1u);
95 EXPECT_EQ(uni->location, 0u);
96 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
97 }
98 {
99 auto uni = stage->GetUniform("u_alpha");
100 ASSERT_NE(uni, nullptr);
101 EXPECT_EQ(uni->dimensions.rows, 1u);
102 EXPECT_EQ(uni->dimensions.cols, 1u);
103 EXPECT_EQ(uni->location, 1u);
104 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
105 }
106 {
107 auto uni = stage->GetUniform("u_sparkle_color");
108 ASSERT_NE(uni, nullptr);
109 EXPECT_EQ(uni->dimensions.rows, 4u);
110 EXPECT_EQ(uni->dimensions.cols, 1u);
111 EXPECT_EQ(uni->location, 2u);
112 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
113 }
114 {
115 auto uni = stage->GetUniform("u_sparkle_alpha");
116 ASSERT_NE(uni, nullptr);
117 EXPECT_EQ(uni->dimensions.rows, 1u);
118 EXPECT_EQ(uni->dimensions.cols, 1u);
119 EXPECT_EQ(uni->location, 3u);
120 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
121 }
122 {
123 auto uni = stage->GetUniform("u_blur");
124 ASSERT_NE(uni, nullptr);
125 EXPECT_EQ(uni->dimensions.rows, 1u);
126 EXPECT_EQ(uni->dimensions.cols, 1u);
127 EXPECT_EQ(uni->location, 4u);
128 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
129 }
130 {
131 auto uni = stage->GetUniform("u_radius_scale");
132 ASSERT_NE(uni, nullptr);
133 EXPECT_EQ(uni->dimensions.rows, 1u);
134 EXPECT_EQ(uni->dimensions.cols, 1u);
135 EXPECT_EQ(uni->location, 6u);
136 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
137 }
138 {
139 auto uni = stage->GetUniform("u_max_radius");
140 ASSERT_NE(uni, nullptr);
141 EXPECT_EQ(uni->dimensions.rows, 1u);
142 EXPECT_EQ(uni->dimensions.cols, 1u);
143 EXPECT_EQ(uni->location, 7u);
144 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
145 }
146 {
147 auto uni = stage->GetUniform("u_resolution_scale");
148 ASSERT_NE(uni, nullptr);
149 EXPECT_EQ(uni->dimensions.rows, 2u);
150 EXPECT_EQ(uni->dimensions.cols, 1u);
151 EXPECT_EQ(uni->location, 8u);
152 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
153 }
154 {
155 auto uni = stage->GetUniform("u_noise_scale");
156 ASSERT_NE(uni, nullptr);
157 EXPECT_EQ(uni->dimensions.rows, 2u);
158 EXPECT_EQ(uni->dimensions.cols, 1u);
159 EXPECT_EQ(uni->location, 9u);
160 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
161 }
162 {
163 auto uni = stage->GetUniform("u_noise_phase");
164 ASSERT_NE(uni, nullptr);
165 EXPECT_EQ(uni->dimensions.rows, 1u);
166 EXPECT_EQ(uni->dimensions.cols, 1u);
167 EXPECT_EQ(uni->location, 10u);
168 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
169 }
170
171 {
172 auto uni = stage->GetUniform("u_circle1");
173 ASSERT_NE(uni, nullptr);
174 EXPECT_EQ(uni->dimensions.rows, 2u);
175 EXPECT_EQ(uni->dimensions.cols, 1u);
176 EXPECT_EQ(uni->location, 11u);
177 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
178 }
179 {
180 auto uni = stage->GetUniform("u_circle2");
181 ASSERT_NE(uni, nullptr);
182 EXPECT_EQ(uni->dimensions.rows, 2u);
183 EXPECT_EQ(uni->dimensions.cols, 1u);
184 EXPECT_EQ(uni->location, 12u);
185 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
186 }
187 {
188 auto uni = stage->GetUniform("u_circle3");
189 ASSERT_NE(uni, nullptr);
190 EXPECT_EQ(uni->dimensions.rows, 2u);
191 EXPECT_EQ(uni->dimensions.cols, 1u);
192 EXPECT_EQ(uni->location, 13u);
193 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
194 }
195 {
196 auto uni = stage->GetUniform("u_rotation1");
197 ASSERT_NE(uni, nullptr);
198 EXPECT_EQ(uni->dimensions.rows, 2u);
199 EXPECT_EQ(uni->dimensions.cols, 1u);
200 EXPECT_EQ(uni->location, 14u);
201 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
202 }
203 {
204 auto uni = stage->GetUniform("u_rotation2");
205 ASSERT_NE(uni, nullptr);
206 EXPECT_EQ(uni->dimensions.rows, 2u);
207 EXPECT_EQ(uni->dimensions.cols, 1u);
208 EXPECT_EQ(uni->location, 15u);
209 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
210 }
211 {
212 auto uni = stage->GetUniform("u_rotation3");
213 ASSERT_NE(uni, nullptr);
214 EXPECT_EQ(uni->dimensions.rows, 2u);
215 EXPECT_EQ(uni->dimensions.cols, 1u);
216 EXPECT_EQ(uni->location, 16u);
217 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
218 }
219 break;
220 }
222 EXPECT_EQ(stage->GetUniforms().size(), 1u);
223 auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
224 ASSERT_TRUE(uni);
225 EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
226 EXPECT_EQ(uni->struct_float_count, 32u);
227
228 // There are 36 4 byte chunks in the UBO: 32 for the 32 floats, and 4 for
229 // padding. Initialize a vector as if they'll all be floats, then manually
230 // set the few padding bytes. If the shader changes, the padding locations
231 // will change as well. For example, if `u_alpha` was moved to the end,
232 // three bytes of padding could potentially be dropped - or if some of the
233 // scalar floats were changed to vec2 or vec4s, or if any vec3s are
234 // introduced.
235 // This means 36 * 4 = 144 bytes total.
236
237 EXPECT_EQ(uni->GetSize(), 144u);
238 std::vector<uint8_t> layout(uni->GetSize() / sizeof(float), 1);
239 layout[5] = 0;
240 layout[6] = 0;
241 layout[7] = 0;
242 layout[23] = 0;
243
244 EXPECT_THAT(uni->struct_layout, ::testing::ElementsAreArray(layout));
245 break;
246 }
247 }
248}
249
250TEST_P(RuntimeStageTest, CanReadUniformsSamplerBeforeUBO) {
251 if (GetBackend() != PlaygroundBackend::kVulkan) {
252 GTEST_SKIP() << "Test only relevant for Vulkan";
253 }
254 const std::shared_ptr<fml::Mapping> fixture =
256 "uniforms_and_sampler_1.frag.iplr");
257 ASSERT_TRUE(fixture);
258 ASSERT_GT(fixture->GetSize(), 0u);
259 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
260 ABSL_ASSERT_OK(stages);
261 auto stage =
262 stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
263
264 EXPECT_EQ(stage->GetUniforms().size(), 2u);
265 auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
266 ASSERT_TRUE(uni);
267 // Struct must be offset at 65.
268 EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
269 EXPECT_EQ(uni->binding, 65u);
270 // Sampler should be offset at 64 but due to current bug
271 // has offset of 0, the correct offset is computed at runtime.
272 auto sampler_uniform = stage->GetUniform("u_texture");
273 EXPECT_EQ(sampler_uniform->type, RuntimeUniformType::kSampledImage);
274 EXPECT_EQ(sampler_uniform->binding, 64u);
275}
276
277TEST_P(RuntimeStageTest, CanReadUniformsSamplerAfterUBO) {
278 if (GetBackend() != PlaygroundBackend::kVulkan) {
279 GTEST_SKIP() << "Test only relevant for Vulkan";
280 }
281 const std::shared_ptr<fml::Mapping> fixture =
283 "uniforms_and_sampler_2.frag.iplr");
284 ASSERT_TRUE(fixture);
285 ASSERT_GT(fixture->GetSize(), 0u);
286 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
287 ABSL_ASSERT_OK(stages);
288 auto stage =
289 stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
290
291 EXPECT_EQ(stage->GetUniforms().size(), 2u);
292 auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
293 ASSERT_TRUE(uni);
294 // Struct must be offset at 45.
295 EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
296 EXPECT_EQ(uni->binding, 64u);
297 // Sampler should be offset at 64 but due to current bug
298 // has offset of 0, the correct offset is computed at runtime.
299 auto sampler_uniform = stage->GetUniform("u_texture");
300 EXPECT_EQ(sampler_uniform->type, RuntimeUniformType::kSampledImage);
301 EXPECT_EQ(sampler_uniform->binding, 65u);
302}
303
304TEST_P(RuntimeStageTest, CanRegisterStage) {
305 const std::shared_ptr<fml::Mapping> fixture =
306 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
307 ASSERT_TRUE(fixture);
308 ASSERT_GT(fixture->GetSize(), 0u);
309 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
310 ABSL_ASSERT_OK(stages);
311 auto stage =
312 stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
313 ASSERT_TRUE(stage);
314 std::promise<bool> registration;
315 auto future = registration.get_future();
316 auto library = GetContext()->GetShaderLibrary();
317 library->RegisterFunction(
318 stage->GetEntrypoint(), //
319 ToShaderStage(stage->GetShaderStage()), //
320 stage->GetCodeMapping(), //
321 fml::MakeCopyable([reg = std::move(registration)](bool result) mutable {
322 reg.set_value(result);
323 }));
324 ASSERT_TRUE(future.get());
325 {
326 auto function =
327 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
328 ASSERT_NE(function, nullptr);
329 }
330
331 // Check if unregistering works.
332
333 library->UnregisterFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
334 {
335 auto function =
336 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
337 ASSERT_EQ(function, nullptr);
338 }
339}
340
341TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
342 auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
343 ABSL_ASSERT_OK(stages_result);
344 auto stage =
345 stages_result
346 .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
347
348 ASSERT_TRUE(stage);
349 ASSERT_NE(stage, nullptr);
350 ASSERT_TRUE(RegisterStage(*stage));
351 auto library = GetContext()->GetShaderLibrary();
352 using VS = RuntimeEffectVertexShader;
354 desc.SetLabel("Runtime Stage InkSparkle");
356 library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
358 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment));
359 auto vertex_descriptor = std::make_shared<VertexDescriptor>();
360 vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
361 VS::kInterleavedBufferLayout);
362
363 std::array<DescriptorSetLayout, 2> descriptor_set_layouts = {
364 VS::kDescriptorSetLayouts[0],
366 .binding = 64u,
367 .descriptor_type = DescriptorType::kUniformBuffer,
368 .shader_stage = ShaderStage::kFragment,
369 },
370 };
371 vertex_descriptor->RegisterDescriptorSetLayouts(descriptor_set_layouts);
372
373 desc.SetVertexDescriptor(std::move(vertex_descriptor));
375 color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
378 desc.SetColorAttachmentDescriptor(0u, color0);
379 desc.SetStencilAttachmentDescriptors(stencil0);
380 const auto stencil_fmt =
381 GetContext()->GetCapabilities()->GetDefaultStencilFormat();
382 desc.SetStencilPixelFormat(stencil_fmt);
383 auto pipeline = GetContext()->GetPipelineLibrary()->GetPipeline(desc).Get();
384 ASSERT_NE(pipeline, nullptr);
385}
386
387TEST_P(RuntimeStageTest, ContainsExpectedShaderTypes) {
388 auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
389 ABSL_ASSERT_OK(stages_result);
390 auto stages = stages_result.value();
391 // Right now, SkSL gets implicitly bundled regardless of what the build rule
392 // for this test requested. After
393 // https://github.com/flutter/flutter/issues/138919, this may require a build
394 // rule change or a new test.
395 EXPECT_TRUE(stages[RuntimeStageBackend::kSkSL]);
396
397 EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]);
398 EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]);
399 EXPECT_TRUE(stages[RuntimeStageBackend::kVulkan]);
400}
401
402} // namespace testing
403} // 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 absl::StatusOr< 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:522