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 <cstddef>
6#include <future>
7
10#include "gmock/gmock.h"
11#include "gtest/gtest.h"
16#include "impeller/entity/runtime_effect.vert.h"
23#include "impeller/runtime_stage/runtime_stage_flatbuffers.h"
25#include "runtime_stage_types_flatbuffers.h"
26#include "third_party/abseil-cpp/absl/status/status_matchers.h"
27
28namespace impeller {
29namespace testing {
30
33
34TEST_P(RuntimeStageTest, CanReadValidBlob) {
35 const std::shared_ptr<fml::Mapping> fixture =
36 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
37 ASSERT_TRUE(fixture);
38 ASSERT_GT(fixture->GetSize(), 0u);
39 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
40 ABSL_ASSERT_OK(stages);
41 auto stage = stages.value()[GetRuntimeStageBackend()];
42 ASSERT_TRUE(stage);
43 ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
44}
45
46TEST_P(RuntimeStageTest, RejectInvalidFormatVersion) {
47 flatbuffers::FlatBufferBuilder builder;
48 fb::RuntimeStagesBuilder stages_builder(builder);
49 stages_builder.add_format_version(0);
50 auto stages = stages_builder.Finish();
51 builder.Finish(stages, fb::RuntimeStagesIdentifier());
52 auto mapping = std::make_shared<fml::NonOwnedMapping>(
53 builder.GetBufferPointer(), builder.GetSize());
54 auto runtime_stages = RuntimeStage::DecodeRuntimeStages(mapping);
55 EXPECT_FALSE(runtime_stages.ok());
56 EXPECT_EQ(runtime_stages.status().code(), absl::StatusCode::kInvalidArgument);
57}
58
59TEST_P(RuntimeStageTest, CanRejectInvalidBlob) {
60 ScopedValidationDisable disable_validation;
61 const std::shared_ptr<fml::Mapping> fixture =
62 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
63 ASSERT_TRUE(fixture);
64 auto junk_allocation = std::make_shared<Allocation>();
65 ASSERT_TRUE(junk_allocation->Truncate(Bytes{fixture->GetSize()}, false));
66 // Not meant to be secure. Just reject obviously bad blobs using magic
67 // numbers.
68 ::memset(junk_allocation->GetBuffer(), 127,
69 junk_allocation->GetLength().GetByteSize());
71 CreateMappingFromAllocation(junk_allocation));
72 ASSERT_FALSE(stages.ok());
73}
74
75TEST_P(RuntimeStageTest, CanReadUniforms) {
76 const std::shared_ptr<fml::Mapping> fixture =
78 "all_supported_uniforms.frag.iplr");
79 ASSERT_TRUE(fixture);
80 ASSERT_GT(fixture->GetSize(), 0u);
81 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
82 ABSL_ASSERT_OK(stages);
83 auto stage = stages.value()[GetRuntimeStageBackend()];
84
85 ASSERT_TRUE(stage);
86 switch (GetBackend()) {
88 [[fallthrough]];
90 [[fallthrough]];
92 ASSERT_EQ(stage->GetUniforms().size(), 14u);
93 {
94 // uFloat
95 auto uni = stage->GetUniform("uFloat");
96 ASSERT_NE(uni, nullptr);
97 EXPECT_EQ(uni->dimensions.rows, 1u);
98 EXPECT_EQ(uni->dimensions.cols, 1u);
99 EXPECT_EQ(uni->location, 0u);
100 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
101 EXPECT_TRUE(uni->padding_layout.empty());
102 }
103 {
104 // uVec2
105 auto uni = stage->GetUniform("uVec2");
106 ASSERT_NE(uni, nullptr);
107 EXPECT_EQ(uni->dimensions.rows, 2u);
108 EXPECT_EQ(uni->dimensions.cols, 1u);
109 EXPECT_EQ(uni->location, 1u);
110 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
111 EXPECT_TRUE(uni->padding_layout.empty());
112 }
113 {
114 // uVec3
115 auto uni = stage->GetUniform("uVec3");
116 ASSERT_NE(uni, nullptr);
117 EXPECT_EQ(uni->dimensions.rows, 3u);
118 EXPECT_EQ(uni->dimensions.cols, 1u);
119 EXPECT_EQ(uni->location, 2u);
120 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
121 auto padding = uni->padding_layout;
122 if (GetBackend() == PlaygroundBackend::kMetal ||
123 GetBackend() == PlaygroundBackend::kMetalSDF) {
124 EXPECT_EQ(padding.size(), 4u);
125 EXPECT_EQ(padding[0], RuntimePaddingType::kFloat);
126 EXPECT_EQ(padding[1], RuntimePaddingType::kFloat);
127 EXPECT_EQ(padding[2], RuntimePaddingType::kFloat);
129 } else {
130 EXPECT_TRUE(padding.empty());
131 }
132 }
133 {
134 // uVec4
135 auto uni = stage->GetUniform("uVec4");
136 ASSERT_NE(uni, nullptr);
137 EXPECT_EQ(uni->dimensions.rows, 4u);
138 EXPECT_EQ(uni->dimensions.cols, 1u);
139 EXPECT_EQ(uni->location, 3u);
140 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
141 EXPECT_TRUE(uni->padding_layout.empty());
142 }
143 {
144 // uMat2
145 auto uni = stage->GetUniform("uMat2");
146 ASSERT_NE(uni, nullptr);
147 EXPECT_EQ(uni->dimensions.rows, 2u);
148 EXPECT_EQ(uni->dimensions.cols, 2u);
149 EXPECT_EQ(uni->location, 4u);
150 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
151 EXPECT_TRUE(uni->padding_layout.empty());
152 }
153 {
154 // uMat3
155 auto uni = stage->GetUniform("uMat3");
156 ASSERT_NE(uni, nullptr);
157 EXPECT_EQ(uni->dimensions.rows, 3u);
158 EXPECT_EQ(uni->dimensions.cols, 3u);
159 EXPECT_EQ(uni->location, 5u);
160 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
161 }
162 {
163 // uMat4
164 auto uni = stage->GetUniform("uMat4");
165 ASSERT_NE(uni, nullptr);
166 EXPECT_EQ(uni->dimensions.rows, 4u);
167 EXPECT_EQ(uni->dimensions.cols, 4u);
168 EXPECT_EQ(uni->location, 6u);
169 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
170 EXPECT_TRUE(uni->padding_layout.empty());
171 }
172 {
173 // uFloatArray
174 auto uni = stage->GetUniform("uFloatArray");
175 ASSERT_NE(uni, nullptr);
176 EXPECT_EQ(uni->dimensions.rows, 1u);
177 EXPECT_EQ(uni->dimensions.cols, 1u);
178 EXPECT_EQ(uni->location, 7u);
179 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
180 EXPECT_TRUE(uni->padding_layout.empty());
181 }
182 {
183 auto uni = stage->GetUniform("uVec2Array");
184 ASSERT_NE(uni, nullptr);
185 EXPECT_EQ(uni->dimensions.rows, 2u);
186 EXPECT_EQ(uni->dimensions.cols, 1u);
187 EXPECT_EQ(uni->location, 9u);
188 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
189 EXPECT_TRUE(uni->padding_layout.empty());
190 }
191 {
192 // uVec3Array
193 auto uni = stage->GetUniform("uVec3Array");
194 ASSERT_NE(uni, nullptr);
195 EXPECT_EQ(uni->dimensions.rows, 3u);
196 EXPECT_EQ(uni->dimensions.cols, 1u);
197 EXPECT_EQ(uni->location, 11u);
198 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
199 }
200 {
201 // uVec4Array
202 auto uni = stage->GetUniform("uVec4Array");
203 ASSERT_NE(uni, nullptr);
204 EXPECT_EQ(uni->dimensions.rows, 4u);
205 EXPECT_EQ(uni->dimensions.cols, 1u);
206 EXPECT_EQ(uni->location, 13u);
207 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
208 EXPECT_TRUE(uni->padding_layout.empty());
209 }
210 {
211 // uMat2Array
212 auto uni = stage->GetUniform("uMat2Array");
213 ASSERT_NE(uni, nullptr);
214 EXPECT_EQ(uni->dimensions.rows, 2u);
215 EXPECT_EQ(uni->dimensions.cols, 2u);
216 EXPECT_EQ(uni->location, 15u);
217 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
218 EXPECT_TRUE(uni->padding_layout.empty());
219 }
220 {
221 // uMat3Array
222 auto uni = stage->GetUniform("uMat3Array");
223 ASSERT_NE(uni, nullptr);
224 EXPECT_EQ(uni->dimensions.rows, 3u);
225 EXPECT_EQ(uni->dimensions.cols, 3u);
226 EXPECT_EQ(uni->location, 17u);
227 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
228 }
229 {
230 // uMat4Array
231 auto uni = stage->GetUniform("uMat4Array");
232 ASSERT_NE(uni, nullptr);
233 EXPECT_EQ(uni->dimensions.rows, 4u);
234 EXPECT_EQ(uni->dimensions.cols, 4u);
235 EXPECT_EQ(uni->location, 19u);
236 EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
237 EXPECT_TRUE(uni->padding_layout.empty());
238 }
239 break;
240 }
242 EXPECT_EQ(stage->GetUniforms().size(), 1u);
243 const RuntimeUniformDescription* uni =
244 stage->GetUniform(RuntimeStage::kVulkanUBOName);
245 ASSERT_TRUE(uni);
246 EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
247 EXPECT_EQ(uni->struct_float_count, 26u);
248
249 EXPECT_EQ(uni->GetGPUSize(), 640u);
250 std::vector<RuntimePaddingType> layout(uni->GetGPUSize() / sizeof(float),
252 // uFloat and uVec2 are packed into a vec4 with 1 byte of padding between.
254 // uVec3 is packed as a vec4 with 1 byte of padding.
256 // uMat2 is packed as two vec4s, with the last 2 bytes of each being
257 // padding.
258 layout[14] = RuntimePaddingType::kPadding;
259 layout[15] = RuntimePaddingType::kPadding;
260 layout[18] = RuntimePaddingType::kPadding;
261 layout[19] = RuntimePaddingType::kPadding;
262 // uMat3 is packed as 3 vec4s, with the last byte of each being padding
263 layout[23] = RuntimePaddingType::kPadding;
264 layout[27] = RuntimePaddingType::kPadding;
265 layout[31] = RuntimePaddingType::kPadding;
266 // uFloatArray is packed as 2 vec4s, with the last 3 bytes of each
267 // being padding.
268 layout[49] = RuntimePaddingType::kPadding;
269 layout[50] = RuntimePaddingType::kPadding;
270 layout[51] = RuntimePaddingType::kPadding;
271 layout[53] = RuntimePaddingType::kPadding;
272 layout[54] = RuntimePaddingType::kPadding;
273 layout[55] = RuntimePaddingType::kPadding;
274 // uVec2Array is packed as 2 vec4s, with 2 bytes of padding at the end of
275 // each.
276 layout[58] = RuntimePaddingType::kPadding;
277 layout[59] = RuntimePaddingType::kPadding;
278 layout[62] = RuntimePaddingType::kPadding;
279 layout[63] = RuntimePaddingType::kPadding;
280 // uVec3Array is packed as 2 vec4s, with the last byte of each as padding.
281 layout[67] = RuntimePaddingType::kPadding;
282 layout[71] = RuntimePaddingType::kPadding;
283 // uVec4Array has no padding.
284 // uMat2Array[2] is packed as 4 vec4s, With the last 2 bytes of each being
285 // padding.
286 layout[82] = RuntimePaddingType::kPadding;
287 layout[83] = RuntimePaddingType::kPadding;
288 layout[86] = RuntimePaddingType::kPadding;
289 layout[87] = RuntimePaddingType::kPadding;
290 layout[90] = RuntimePaddingType::kPadding;
291 layout[91] = RuntimePaddingType::kPadding;
292 layout[94] = RuntimePaddingType::kPadding;
293 layout[95] = RuntimePaddingType::kPadding;
294 // uMat3Array[2] is packed as 6 vec4s, with the last byte of each being
295 // padding.
296 layout[99] = RuntimePaddingType::kPadding;
297 layout[103] = RuntimePaddingType::kPadding;
298 layout[107] = RuntimePaddingType::kPadding;
299 layout[111] = RuntimePaddingType::kPadding;
300 layout[115] = RuntimePaddingType::kPadding;
301 layout[119] = RuntimePaddingType::kPadding;
302 // uMat4Array[2] is packed as 8 vec4s with no padding.
303 layout[152] = RuntimePaddingType::kPadding;
304 layout[153] = RuntimePaddingType::kPadding;
305 layout[154] = RuntimePaddingType::kPadding;
306 layout[155] = RuntimePaddingType::kPadding;
307 layout[156] = RuntimePaddingType::kPadding;
308 layout[157] = RuntimePaddingType::kPadding;
309 layout[158] = RuntimePaddingType::kPadding;
310 layout[159] = RuntimePaddingType::kPadding;
311
312 EXPECT_THAT(uni->padding_layout, ::testing::ElementsAreArray(layout));
313
314 std::vector<std::pair<std::string, unsigned int>> expected_uniforms = {
315 {"uFloat", 4}, {"uVec2", 8}, {"uVec3", 12},
316 {"uVec4", 16}, {"uMat2", 16}, {"uMat3", 36},
317 {"uMat4", 64}, {"uFloatArray", 8}, {"uVec2Array", 16},
318 {"uVec3Array", 24}, {"uVec4Array", 32}, {"uMat2Array", 32},
319 {"uMat3Array", 72}, {"uMat4Array", 128}};
320
321 ASSERT_EQ(uni->struct_fields.size(), expected_uniforms.size());
322
323 for (size_t i = 0; i < expected_uniforms.size(); ++i) {
324 const auto& element = uni->struct_fields[i];
325 const auto& expected = expected_uniforms[i];
326
327 EXPECT_EQ(element.name, expected.first) << "index: " << i;
328 EXPECT_EQ(element.byte_size, expected.second) << "index: " << i;
329 }
330 break;
331 }
332 }
333}
334
335TEST_P(RuntimeStageTest, CanReadUniformsSamplerBeforeUBO) {
336 if (GetBackend() != PlaygroundBackend::kVulkan) {
337 GTEST_SKIP() << "Test only relevant for Vulkan";
338 }
339 const std::shared_ptr<fml::Mapping> fixture =
341 "uniforms_and_sampler_1.frag.iplr");
342 ASSERT_TRUE(fixture);
343 ASSERT_GT(fixture->GetSize(), 0u);
344 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
345 ABSL_ASSERT_OK(stages);
346 auto stage = stages.value()[GetRuntimeStageBackend()];
347
348 EXPECT_EQ(stage->GetUniforms().size(), 2u);
349 auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
350 ASSERT_TRUE(uni);
351 // Struct must be offset at 65.
352 EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
353 EXPECT_EQ(uni->binding, 65u);
354 // Sampler should be offset at 64 but due to current bug
355 // has offset of 0, the correct offset is computed at runtime.
356 auto sampler_uniform = stage->GetUniform("u_texture");
357 EXPECT_EQ(sampler_uniform->type, RuntimeUniformType::kSampledImage);
358 EXPECT_EQ(sampler_uniform->binding, 64u);
359}
360
361TEST_P(RuntimeStageTest, CanReadUniformsSamplerAfterUBO) {
362 if (GetBackend() != PlaygroundBackend::kVulkan) {
363 GTEST_SKIP() << "Test only relevant for Vulkan";
364 }
365 const std::shared_ptr<fml::Mapping> fixture =
367 "uniforms_and_sampler_2.frag.iplr");
368 ASSERT_TRUE(fixture);
369 ASSERT_GT(fixture->GetSize(), 0u);
370 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
371 ABSL_ASSERT_OK(stages);
372 auto stage = stages.value()[GetRuntimeStageBackend()];
373
374 EXPECT_EQ(stage->GetUniforms().size(), 2u);
375 auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
376 ASSERT_TRUE(uni);
377 // Struct must be offset at 45.
378 EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
379 EXPECT_EQ(uni->binding, 64u);
380 // Sampler should be offset at 64 but due to current bug
381 // has offset of 0, the correct offset is computed at runtime.
382 auto sampler_uniform = stage->GetUniform("u_texture");
383 EXPECT_EQ(sampler_uniform->type, RuntimeUniformType::kSampledImage);
384 EXPECT_EQ(sampler_uniform->binding, 65u);
385}
386
387TEST_P(RuntimeStageTest, CanRegisterStage) {
388 const std::shared_ptr<fml::Mapping> fixture =
389 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
390 ASSERT_TRUE(fixture);
391 ASSERT_GT(fixture->GetSize(), 0u);
392 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
393 ABSL_ASSERT_OK(stages);
394 auto stage = stages.value()[GetRuntimeStageBackend()];
395 ASSERT_TRUE(stage);
396 std::promise<bool> registration;
397 auto future = registration.get_future();
398 auto library = GetContext()->GetShaderLibrary();
399 library->RegisterFunction(
400 stage->GetEntrypoint(), //
401 ToShaderStage(stage->GetShaderStage()), //
402 stage->GetCodeMapping(), //
403 fml::MakeCopyable([reg = std::move(registration)](bool result) mutable {
404 reg.set_value(result);
405 }));
406 ASSERT_TRUE(future.get());
407 {
408 auto function =
409 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
410 ASSERT_NE(function, nullptr);
411 }
412
413 // Check if unregistering works.
414
415 library->UnregisterFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
416 {
417 auto function =
418 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
419 ASSERT_EQ(function, nullptr);
420 }
421}
422
423TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
424 auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
425 ABSL_ASSERT_OK(stages_result);
426 auto stage = stages_result.value()[GetRuntimeStageBackend()];
427
428 ASSERT_TRUE(stage);
429 ASSERT_NE(stage, nullptr);
430 ASSERT_TRUE(RegisterStage(*stage));
431 auto library = GetContext()->GetShaderLibrary();
432 using VS = RuntimeEffectVertexShader;
434 desc.SetLabel("Runtime Stage InkSparkle");
436 library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
438 library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment));
439 auto vertex_descriptor = std::make_shared<VertexDescriptor>();
440 vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
441 VS::kInterleavedBufferLayout);
442
443 std::array<DescriptorSetLayout, 2> descriptor_set_layouts = {
444 VS::kDescriptorSetLayouts[0],
446 .binding = 64u,
447 .descriptor_type = DescriptorType::kUniformBuffer,
448 .shader_stage = ShaderStage::kFragment,
449 },
450 };
451 vertex_descriptor->RegisterDescriptorSetLayouts(descriptor_set_layouts);
452
453 desc.SetVertexDescriptor(std::move(vertex_descriptor));
455 color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
458 desc.SetColorAttachmentDescriptor(0u, color0);
459 desc.SetStencilAttachmentDescriptors(stencil0);
460 const auto stencil_fmt =
461 GetContext()->GetCapabilities()->GetDefaultStencilFormat();
462 desc.SetStencilPixelFormat(stencil_fmt);
463 auto pipeline = GetContext()->GetPipelineLibrary()->GetPipeline(desc).Get();
464 ASSERT_NE(pipeline, nullptr);
465}
466
467TEST_P(RuntimeStageTest, ContainsExpectedShaderTypes) {
468 auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
469 ABSL_ASSERT_OK(stages_result);
470 auto stages = stages_result.value();
471 EXPECT_TRUE(stages[RuntimeStageBackend::kSkSL]);
472 EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]);
473 EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]);
474 EXPECT_TRUE(stages[RuntimeStageBackend::kVulkan]);
475}
476
477TEST_P(RuntimeStageTest, ContainsExpectedShaderTypesNoSksl) {
478 auto stages_result =
479 OpenAssetAsRuntimeStage("runtime_stage_simple_no_sksl.frag.iplr");
480 ABSL_ASSERT_OK(stages_result);
481 auto stages = stages_result.value();
482 EXPECT_FALSE(stages[RuntimeStageBackend::kSkSL]);
483 EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]);
484 EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]);
485 EXPECT_TRUE(stages[RuntimeStageBackend::kVulkan]);
486}
487
488TEST(ShaderKeyTest, MakeUserScopedNameProducesScopedString) {
490 "assets/foo.frag", "main"),
491 "re:assets/foo.frag:main");
492 EXPECT_EQ(
495 "packages/pkg_a/assets/shaders.shaderbundle",
496 "solid_fill_fragment_main"),
497 "fg:packages/pkg_a/assets/shaders.shaderbundle:solid_fill_fragment_main");
498}
499
500TEST(ShaderKeyTest, MakeUserScopedNameContainsColonSeparator) {
501 // The colon separator is what makes user-scoped names unspoofable from
502 // engine-internal entrypoints, since impellerc-generated entrypoints are
503 // valid identifiers and cannot contain ':'.
504 std::string scoped = ShaderKey::MakeUserScopedName(
505 ShaderKey::kScopeRuntimeEffect, "asset", "entry");
506 EXPECT_NE(scoped.find(':'), std::string::npos);
507}
508
509TEST(ShaderKeyTest, MakeUserScopedNameDifferentLibraryIdsDoNotCollide) {
510 // Two user shaders that share an entrypoint name but come from different
511 // logical sources must produce different registry keys, so they cannot
512 // overwrite each other in the shared shader library.
513 std::string a = ShaderKey::MakeUserScopedName(
514 ShaderKey::kScopeFlutterGPU, "packages/pkg_a/bundle", "main");
515 std::string b = ShaderKey::MakeUserScopedName(
516 ShaderKey::kScopeFlutterGPU, "packages/pkg_b/bundle", "main");
517 EXPECT_NE(a, b);
518}
519
520TEST(ShaderKeyTest, MakeUserScopedNameDifferentScopesDoNotCollide) {
521 // A FragmentProgram and a Flutter GPU shader can independently share an
522 // asset path and an entrypoint and must still produce different keys.
523 std::string runtime_effect = ShaderKey::MakeUserScopedName(
524 ShaderKey::kScopeRuntimeEffect, "asset", "main");
525 std::string flutter_gpu = ShaderKey::MakeUserScopedName(
526 ShaderKey::kScopeFlutterGPU, "asset", "main");
527 EXPECT_NE(runtime_effect, flutter_gpu);
528}
529
530TEST(ShaderKeyTest, MakeUserScopedNameHandlesLongInputs) {
531 // The scoped name is used solely as a `std::string` key in the per-process
532 // shader library registry; there is no fixed length limit. Confirm that
533 // long inputs (e.g. deeply-nested package asset paths) round-trip through
534 // the builder without truncation.
535 const std::string long_library_id(4096, 'a');
536 const std::string long_entrypoint(2048, 'b');
537 std::string scoped = ShaderKey::MakeUserScopedName(
538 ShaderKey::kScopeFlutterGPU, long_library_id, long_entrypoint);
539 EXPECT_EQ(scoped.size(), ShaderKey::kScopeFlutterGPU.size() + 1 +
540 long_library_id.size() + 1 +
541 long_entrypoint.size());
542 EXPECT_EQ(scoped.substr(0, 3), "fg:");
543 EXPECT_EQ(scoped.substr(3, long_library_id.size()), long_library_id);
544 EXPECT_EQ(scoped.substr(3 + long_library_id.size(), 1), ":");
545 EXPECT_EQ(scoped.substr(3 + long_library_id.size() + 1), long_entrypoint);
546}
547
548TEST_P(RuntimeStageTest, RuntimeStageHasUniqueLibraryIdByDefault) {
549 // Two stages decoded independently must get distinct fallback library ids
550 // so that programmatically-constructed stages cannot collide with each
551 // other in the shared shader library.
552 const std::shared_ptr<fml::Mapping> fixture =
553 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
554 ASSERT_TRUE(fixture);
555 auto stages_a = RuntimeStage::DecodeRuntimeStages(fixture);
556 ABSL_ASSERT_OK(stages_a);
557 auto stages_b = RuntimeStage::DecodeRuntimeStages(fixture);
558 ABSL_ASSERT_OK(stages_b);
559 auto stage_a = stages_a.value()[GetRuntimeStageBackend()];
560 auto stage_b = stages_b.value()[GetRuntimeStageBackend()];
561 ASSERT_TRUE(stage_a);
562 ASSERT_TRUE(stage_b);
563 EXPECT_FALSE(stage_a->GetLibraryId().empty());
564 EXPECT_FALSE(stage_b->GetLibraryId().empty());
565 EXPECT_NE(stage_a->GetLibraryId(), stage_b->GetLibraryId());
566}
567
568TEST_P(RuntimeStageTest, RuntimeStageLibraryIdCanBeOverridden) {
569 // FragmentProgram::initFromAsset overrides the fallback id with the asset
570 // path so that hot reload of the same asset evicts and replaces the same
571 // registry slot rather than leaking a new one.
572 const std::shared_ptr<fml::Mapping> fixture =
573 flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
574 ASSERT_TRUE(fixture);
575 auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
576 ABSL_ASSERT_OK(stages);
577 auto stage = stages.value()[GetRuntimeStageBackend()];
578 ASSERT_TRUE(stage);
579 stage->SetLibraryId("packages/my_pkg/assets/shader.frag");
580 EXPECT_EQ(stage->GetLibraryId(), "packages/my_pkg/assets/shader.frag");
581}
582
583} // namespace testing
584} // 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:51
Vector2 padding
The halo padding in source space.
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
TEST(FrameTimingsRecorderTest, RecordVsync)
internal::CopyableLambda< T > MakeCopyable(T lambda)
TEST_P(AiksTest, DrawAtlasNoColor)
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
size_t GetGPUSize() const
Computes the total number of bytes that this uniform requires for representation in the GPU.
std::vector< StructField > struct_fields
std::vector< RuntimePaddingType > padding_layout
static constexpr std::string_view kScopeFlutterGPU
Scope tag for Flutter GPU user shader bundles.
Definition shader_key.h:59
static std::string MakeUserScopedName(std::string_view scope, std::string_view library_id, std::string_view entrypoint)
Definition shader_key.cc:19
static constexpr std::string_view kScopeRuntimeEffect
Definition shader_key.h:56