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