Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
compiler_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 <cstring>
7#include "gtest/gtest.h"
13
14namespace impeller {
15namespace compiler {
16namespace testing {
17
19 CompilerSuite,
21 ::testing::Values(TargetPlatform::kOpenGLES,
26 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) {
27 return TargetPlatformToString(info.param);
28 });
29
31 CompilerSuite,
33 ::testing::Values(TargetPlatform::kRuntimeStageMetal,
38 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) {
39 return TargetPlatformToString(info.param);
40 });
41
43 CompilerSuite,
45 ::testing::Values(TargetPlatform::kSkSL),
46 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) {
47 return TargetPlatformToString(info.param);
48 });
49
51 CompilerSuite,
53 ::testing::Values(TargetPlatform::kUnknown),
54 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) {
55 return TargetPlatformToString(info.param);
56 });
57
58TEST(CompilerTest, Defines) {
59 std::shared_ptr<const fml::Mapping> fixture =
60 flutter::testing::OpenFixtureAsMapping("check_gles_definition.frag");
61
62 SourceOptions options;
65 options.entry_point_name = "main";
67
68 Reflector::Options reflector_options;
70 Compiler compiler = Compiler(fixture, options, reflector_options);
71
72 // Should fail as the shader has a compilation error in it.
73 EXPECT_EQ(compiler.GetSPIRVAssembly(), nullptr);
74
75 // Should succeed as the compilation error is ifdef'd out.
78 Compiler compiler_2 = Compiler(fixture, options, reflector_options);
79 EXPECT_NE(compiler_2.GetSPIRVAssembly(), nullptr);
80}
81
82TEST(CompilerTest, ShaderKindMatchingIsSuccessful) {
83 ASSERT_EQ(SourceTypeFromFileName("hello.vert"), SourceType::kVertexShader);
85 ASSERT_EQ(SourceTypeFromFileName("hello.comp"), SourceType::kComputeShader);
86 ASSERT_EQ(SourceTypeFromFileName("hello.msl"), SourceType::kUnknown);
87 ASSERT_EQ(SourceTypeFromFileName("hello.glsl"), SourceType::kUnknown);
88}
89
90TEST_P(CompilerTest, CanCompile) {
91 ASSERT_TRUE(CanCompileAndReflect("sample.vert"));
92 ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader));
93 ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader,
95}
96
97TEST_P(CompilerTest, CanCompileHLSL) {
98 ASSERT_TRUE(CanCompileAndReflect(
100}
101
102TEST_P(CompilerTest, CanCompileHLSLWithMultipleStages) {
103 ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
105 SourceLanguage::kHLSL, "VertexShader"));
106 ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
108 SourceLanguage::kHLSL, "FragmentShader"));
109}
110
111TEST_P(CompilerTest, CanCompileComputeShader) {
112 if (!TargetPlatformIsMetal(GetParam())) {
113 GTEST_SKIP()
114 << "Only enabled on Metal backends till ES 3.2 support is added.";
115 }
116 ASSERT_TRUE(CanCompileAndReflect("sample.comp"));
117 ASSERT_TRUE(CanCompileAndReflect("sample.comp", SourceType::kComputeShader));
118}
119
120TEST_P(CompilerTest, MustFailDueToExceedingResourcesLimit) {
121 ScopedValidationDisable disable_validation;
122 ASSERT_FALSE(
123 CanCompileAndReflect("resources_limit.vert", SourceType::kVertexShader));
124}
125
126TEST_P(CompilerTest, MustFailDueToMultipleLocationPerStructMember) {
127 ScopedValidationDisable disable_validation;
128 ASSERT_FALSE(CanCompileAndReflect("struct_def_bug.vert"));
129}
130
131TEST_P(CompilerTest, UniformBlockInstanceNameCanonicalizedForGL) {
132 if (!TargetPlatformIsOpenGL(GetParam())) {
133 GTEST_SKIP() << "Only GL targets lower uniform blocks to flat structs.";
134 }
135 // `mat2_test.frag` has `uniform Params { ... } uParams;`. Without
136 // canonicalization, BufferBindingsGLES cannot resolve `uParams.uMat2`.
137 ASSERT_TRUE(
138 CanCompileAndReflect("mat2_test.frag", SourceType::kFragmentShader));
139 auto sl_source = GetCompiler()->GetSLShaderSource();
140 ASSERT_NE(sl_source, nullptr);
141 const std::string source(
142 reinterpret_cast<const char*>(sl_source->GetMapping()),
143 sl_source->GetSize());
144 EXPECT_EQ(source.find("uParams."), std::string::npos) << source;
145 EXPECT_NE(source.find("_Params."), std::string::npos) << source;
146}
147
148TEST_P(CompilerTest, BindingBaseForFragShader) {
149 if (!TargetPlatformIsVulkan(GetParam())) {
150 GTEST_SKIP();
151 }
152
153 ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader));
154 ASSERT_TRUE(CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
155
156 auto get_binding = [&](const char* fixture) -> uint32_t {
157 auto json_fd = GetReflectionJson(fixture);
158 nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
159 return shader_json["buffers"][0]["binding"].get<uint32_t>();
160 };
161
162 auto vert_uniform_binding = get_binding("sample.vert");
163 auto frag_uniform_binding = get_binding("sample.frag");
164
165 ASSERT_GT(frag_uniform_binding, vert_uniform_binding);
166}
167
168namespace {
169struct UniformInfo {
170 std::string uniform_name;
171 uint32_t location;
172 std::string type_name;
173 uint32_t columns;
174 uint32_t vec_size;
175
176 static UniformInfo fromJson(const nlohmann::json& json) {
177 return {
178 .uniform_name = json["name"].get<std::string>(),
179 .location = json["location"].get<uint32_t>(),
180 .type_name = json["type"]["type_name"].get<std::string>(),
181 .columns = json["type"]["columns"].get<uint32_t>(),
182 .vec_size = json["type"]["vec_size"].get<uint32_t>(),
183 };
184 }
185
186 static UniformInfo Sampler(const std::string& name, uint32_t location) {
187 return UniformInfo{
188 .uniform_name = name,
189 .location = location,
190 .type_name = "ShaderType::kSampledImage",
191 .columns = 1u,
192 .vec_size = 1u,
193 };
194 }
195 static UniformInfo Float(const std::string& name, uint32_t location) {
196 return FloatInfo(name, location, 1u, 1u);
197 }
198 static UniformInfo Vec2(const std::string& name, uint32_t location) {
199 return FloatInfo(name, location, 1u, 2u);
200 }
201 static UniformInfo Vec3(const std::string& name, uint32_t location) {
202 return FloatInfo(name, location, 1u, 3u);
203 }
204 static UniformInfo Vec4(const std::string& name, uint32_t location) {
205 return FloatInfo(name, location, 1u, 4u);
206 }
207 static UniformInfo Mat4(const std::string& name, uint32_t location) {
208 return FloatInfo(name, location, 4u, 4u);
209 }
210
211 constexpr bool operator==(const UniformInfo& other) const {
212 return (uniform_name == other.uniform_name && //
213 location == other.location && //
214 type_name == other.type_name && //
215 columns == other.columns && //
216 vec_size == other.vec_size);
217 }
218
219 private:
220 static UniformInfo FloatInfo(const std::string& name,
221 uint32_t location,
222 uint32_t columns,
223 uint32_t vec_size) {
224 return UniformInfo{
225 .uniform_name = name,
226 .location = location,
227 .type_name = "ShaderType::kFloat",
228 .columns = columns,
229 .vec_size = vec_size,
230 };
231 }
232};
233
234inline std::ostream& operator<<(std::ostream& out, const UniformInfo& info) {
235 out << "UniformInfo {" << std::endl
236 << " uniform_name: " << info.uniform_name << std::endl
237 << " location: " << info.location << std::endl
238 << " type_name: " << info.type_name << std::endl
239 << " columns: " << info.columns << std::endl
240 << " vec_size: " << info.vec_size << std::endl
241 << "}";
242 return out;
243}
244} // namespace
245
246TEST_P(CompilerTestRuntime, UniformsAppearInJson) {
247 if (GetParam() == TargetPlatform::kRuntimeStageVulkan) {
248 // TODO(https://github.com/flutter/flutter/issues/182578): Investigate why
249 // this does not pass.
250 GTEST_SKIP() << "Not supported with Vulkan";
251 }
252
253 ASSERT_TRUE(CanCompileAndReflect("sample_with_uniforms.frag",
256
257 auto json_fd = GetReflectionJson("sample_with_uniforms.frag");
258 ASSERT_TRUE(json_fd);
259 nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
260 auto sampler_list = shader_json["sampled_images"];
261 auto float_list = shader_json["uniforms"];
262 ASSERT_EQ(sampler_list.size(), 2u);
263 ASSERT_EQ(float_list.size(), 6u);
264
265 {
266 // clang-format off
267 std::array expected_infos = {
268 UniformInfo::Sampler("uFirstSampler", 1u),
269 UniformInfo::Sampler("uSampler", 7u),
270 };
271 // clang-format on
272 ASSERT_EQ(sampler_list.size(), expected_infos.size());
273 for (size_t i = 0; i < expected_infos.size(); i++) {
274 EXPECT_EQ(UniformInfo::fromJson(sampler_list[i]), expected_infos[i])
275 << "index: " << i;
276 }
277 }
278
279 {
280 // clang-format off
281 std::array expected_infos = {
282 UniformInfo::Float("uFirstFloat", 0u),
283 UniformInfo::Float("uFloat", 2u),
284 UniformInfo::Vec2("uVec2", 3u),
285 UniformInfo::Vec3("uVec3", 4u),
286 UniformInfo::Vec4("uVec4", 5u),
287 UniformInfo::Mat4("uMat4", 6u),
288 };
289 // clang-format on
290 ASSERT_EQ(float_list.size(), expected_infos.size());
291 for (size_t i = 0; i < expected_infos.size(); i++) {
292 EXPECT_EQ(UniformInfo::fromJson(float_list[i]), expected_infos[i])
293 << "index: " << i;
294 }
295 }
296}
297
298TEST_P(CompilerTestRuntime, PositionedUniformsAppearInJson) {
299 if (GetParam() == TargetPlatform::kRuntimeStageVulkan) {
300 // TODO(https://github.com/flutter/flutter/issues/182578): Investigate why
301 // this does not pass.
302 GTEST_SKIP() << "Not supported with Vulkan";
303 }
304
305 ASSERT_TRUE(CanCompileAndReflect("sample_with_positioned_uniforms.frag",
308
309 auto json_fd = GetReflectionJson("sample_with_positioned_uniforms.frag");
310 ASSERT_TRUE(json_fd);
311 nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
312 auto sampler_list = shader_json["sampled_images"];
313 auto float_list = shader_json["uniforms"];
314 ASSERT_EQ(sampler_list.size(), 3u);
315 ASSERT_EQ(float_list.size(), 7u);
316
317 {
318 // clang-format off
319 std::array expected_infos = {
320 UniformInfo::Sampler("uSamplerNotPositioned1", 1u),
321 UniformInfo::Sampler("uSampler", 0u),
322 UniformInfo::Sampler("uSamplerNotPositioned2", 3u),
323 };
324 // clang-format on
325 ASSERT_EQ(sampler_list.size(), expected_infos.size());
326 for (size_t i = 0; i < expected_infos.size(); i++) {
327 EXPECT_EQ(UniformInfo::fromJson(sampler_list[i]), expected_infos[i])
328 << "index: " << i;
329 }
330 }
331
332 {
333 // clang-format off
334 std::array expected_infos = {
335 UniformInfo::Float("uFloatNotPositioned1", 0u),
336 UniformInfo::Float("uFloat", 6u),
337 UniformInfo::Vec2("uVec2", 5u),
338 UniformInfo::Vec3("uVec3", 3u),
339 UniformInfo::Vec4("uVec4", 2u),
340 UniformInfo::Mat4("uMat4", 1u),
341 UniformInfo::Float("uFloatNotPositioned2", 2u),
342 };
343 // clang-format on
344 ASSERT_EQ(float_list.size(), expected_infos.size());
345 for (size_t i = 0; i < expected_infos.size(); i++) {
346 EXPECT_EQ(UniformInfo::fromJson(float_list[i]), expected_infos[i])
347 << "index: " << i;
348 }
349 }
350}
351
352TEST_P(CompilerTest, UniformsHaveBindingAndSet) {
353 if (GetParam() == TargetPlatform::kSkSL) {
354 GTEST_SKIP() << "Not supported with SkSL";
355 }
356 ASSERT_TRUE(CanCompileAndReflect("sample_with_binding.vert",
358 ASSERT_TRUE(CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
359
360 struct binding_and_set {
361 uint32_t binding;
362 uint32_t set;
363 };
364
365 auto get_binding = [&](const char* fixture) -> binding_and_set {
366 auto json_fd = GetReflectionJson(fixture);
367 nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
368 uint32_t binding = shader_json["buffers"][0]["binding"].get<uint32_t>();
369 uint32_t set = shader_json["buffers"][0]["set"].get<uint32_t>();
370 return {binding, set};
371 };
372
373 auto vert_uniform_binding = get_binding("sample_with_binding.vert");
374 auto frag_uniform_binding = get_binding("sample.frag");
375
376 ASSERT_EQ(frag_uniform_binding.set, 0u);
377 ASSERT_EQ(vert_uniform_binding.set, 3u);
378 ASSERT_EQ(vert_uniform_binding.binding, 17u);
379}
380
381TEST_P(CompilerTestSkSL, SkSLTextureLookUpOrderOfOperations) {
382 ASSERT_TRUE(
383 CanCompileAndReflect("texture_lookup.frag", SourceType::kFragmentShader));
384
385 auto shader = GetShaderFile("texture_lookup.frag", GetParam());
386 std::string_view shader_mapping(
387 reinterpret_cast<const char*>(shader->GetMapping()), shader->GetSize());
388
389 constexpr std::string_view expected =
390 "textureA.eval(textureA_size * ( vec2(1.0) + flutter_FragCoord.xy));";
391
392 EXPECT_NE(shader_mapping.find(expected), std::string::npos);
393}
394
395TEST_P(CompilerTestSkSL, CanCompileStructs) {
396 ASSERT_TRUE(CanCompileAndReflect("struct_internal.frag",
398}
399
400TEST_P(CompilerTestSkSL, FailsToCompileDueToArrayInitializerWithConstants) {
401 auto expected_err =
402 "There was a compiler error: SkSL does not support array initializers: "
403 "array_initializer_with_constants.frag:6";
404
405 EXPECT_EXIT(CanCompileAndReflect("array_initializer_with_constants.frag",
407 ::testing::ExitedWithCode(1), expected_err);
408}
409
410TEST_P(CompilerTestSkSL, FailsToCompileDueToArrayInitializerWithVariables) {
411 auto expected_err =
412 "There was a compiler error: SkSL does not support array initializers: "
413 "array_initializer_with_variables.frag:12";
414
415 EXPECT_EXIT(CanCompileAndReflect("array_initializer_with_variables.frag",
417 ::testing::ExitedWithCode(1), expected_err);
418}
419
420TEST_P(CompilerTestSkSL, FailsToCompileDueToArrayAssignment) {
421 // Does not EXIT because the backend SkSL compiler doesn't detect the invalid
422 // SkSL. Returns false because the Impeller Compiler's post-compile validation
423 // fails.
424 ASSERT_FALSE(CanCompileAndReflect("array_assignment.frag",
426
427 const Compiler* compiler = GetCompiler();
428 ASSERT_TRUE(compiler);
429 // Truncated error message.
430 ASSERT_STREQ(
431 compiler->GetErrorMessages().c_str(),
432 "\"array_assignment.frag\": \n"
433 "Compiled to invalid SkSL:\n"
434 " // This SkSL shader is autogenerated by spirv-cross.\n"
435 " \n"
436 " float4 flutter_FragCoord;\n"
437 " \n"
438 " vec4 frag_color;\n"
439 " \n"
440 "... (truncated 16 lines)\n"
441 "SkSL Error:\n"
442 " error: 12: initializers are not permitted on arrays (or structs "
443 "containing arrays)\n"
444 " float more_nums[2] = nums;\n"
445 " ^^^^\n"
446 " 1 error\n");
447 // Full error message.
448 ASSERT_STREQ(compiler->GetVerboseErrorMessages().c_str(),
449 "\"array_assignment.frag\": \n"
450 "Compiled to invalid SkSL:\n"
451 "// This SkSL shader is autogenerated by spirv-cross.\n"
452 "\n"
453 "float4 flutter_FragCoord;\n"
454 "\n"
455 "vec4 frag_color;\n"
456 "\n"
457 "void FLT_main()\n"
458 "{\n"
459 " float nums[2];\n"
460 " nums[0] = 1.0;\n"
461 " nums[1] = 0.5;\n"
462 " float more_nums[2] = nums;\n"
463 " frag_color = vec4(nums[0], nums[1], 1.0, 1.0);\n"
464 "}\n"
465 "\n"
466 "half4 main(float2 iFragCoord)\n"
467 "{\n"
468 " flutter_FragCoord = float4(iFragCoord, 0, 0);\n"
469 " FLT_main();\n"
470 " return frag_color;\n"
471 "}\n"
472 "\n"
473 "SkSL Error:\n"
474 "error: 12: initializers are not permitted on arrays (or "
475 "structs containing arrays)\n"
476 " float more_nums[2] = nums;\n"
477 " ^^^^\n"
478 "1 error\n"
479 "\n");
480}
481
482TEST_P(CompilerTestSkSL, CompilesWithValidArrayInitialization) {
483 ASSERT_TRUE(
484 CanCompileAndReflect("array_initialization_without_initializer.frag",
486}
487
488TEST_P(CompilerTestRuntime, Mat2Reflection) {
489 if (GetParam() == TargetPlatform::kSkSL) {
490 GTEST_SKIP() << "Not supported with SkSL";
491 }
492
493 ASSERT_TRUE(CanCompileAndReflect(
495
496 std::unique_ptr<fml::FileMapping> json_fd =
497 GetReflectionJson("mat2_test.frag");
498 ASSERT_TRUE(json_fd);
499 nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
500 nlohmann::json::value_type buffers = shader_json["buffers"];
501
502 // uParams should be in buffers.
503 ASSERT_GE(buffers.size(), 1u);
504 nlohmann::json::value_type uParams = buffers[0];
505 EXPECT_EQ(uParams["name"], "Params");
506
507 nlohmann::json::value_type members = uParams["type"]["members"];
508 // We expect 1 member (for the mat2) which is a Point (vec2) with array
509 // size 2.
510 ASSERT_EQ(members.size(), 1u);
511
512 nlohmann::json::value_type mat2Member = members[0];
513
514 // Should be Mat2 (vec2)
515 EXPECT_EQ(mat2Member["type"], "Mat2");
516
517 // Size 8 bytes (vec2)
518 EXPECT_EQ(mat2Member["size"], 8u);
519
520 // Byte length should be total array size (stride * count)
521 // Stride 16 * 2 = 32.
522 EXPECT_EQ(mat2Member["byte_length"], 32u);
523
524 // Array elements should be 2 (columns).
525 EXPECT_EQ(mat2Member["array_elements"], 2u);
526
527 // Offset 0
528 EXPECT_EQ(mat2Member["offset"], 0u);
529}
530
531TEST_P(CompilerTestUnknownPlatform, MustFailDueToUnknownPlatform) {
532 ASSERT_FALSE(
533 CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
534}
535
536} // namespace testing
537} // namespace compiler
538} // namespace impeller
std::string GetVerboseErrorMessages() const
Definition compiler.cc:620
std::shared_ptr< fml::Mapping > GetSPIRVAssembly() const
Definition compiler.cc:598
std::string GetErrorMessages() const
Definition compiler.cc:616
uint32_t vec_size
std::string type_name
uint32_t columns
std::string uniform_name
uint32_t location
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
std::ostream & operator<<(std::ostream &out, const FlutterPoint &point)
const char * name
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
std::function< ProfileSample(void)> Sampler
Sampler is run during SamplingProfiler::SampleRepeatedly. Each platform should implement its version ...
INSTANTIATE_TEST_SUITE_P(CompilerSuite, CompilerTest, ::testing::Values(TargetPlatform::kOpenGLES, TargetPlatform::kOpenGLDesktop, TargetPlatform::kMetalDesktop, TargetPlatform::kMetalIOS, TargetPlatform::kVulkan), [](const ::testing::TestParamInfo< CompilerTest::ParamType > &info) { return TargetPlatformToString(info.param);})
TEST_P(CompilerTest, CanCompile)
TEST(CompilerTest, Defines)
std::string TargetPlatformToString(TargetPlatform platform)
Definition types.cc:62
SourceType SourceTypeFromFileName(const std::filesystem::path &file_name)
Definition types.cc:17
bool TargetPlatformIsMetal(TargetPlatform platform)
Definition types.cc:258
bool TargetPlatformIsOpenGL(TargetPlatform platform)
Definition types.cc:239
bool TargetPlatformIsVulkan(TargetPlatform platform)
Definition types.cc:277