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