Flutter Impeller
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>
6 #include "flutter/testing/testing.h"
7 #include "gtest/gtest.h"
13 
14 namespace impeller {
15 namespace compiler {
16 namespace testing {
17 
18 TEST(CompilerTest, Defines) {
19  std::shared_ptr<const fml::Mapping> fixture =
20  flutter::testing::OpenFixtureAsMapping("check_gles_definition.frag");
21 
22  SourceOptions options;
25  options.entry_point_name = "main";
27 
28  Reflector::Options reflector_options;
30  Compiler compiler = Compiler(fixture, options, reflector_options);
31 
32  // Should fail as the shader has a compilation error in it.
33  EXPECT_EQ(compiler.GetSPIRVAssembly(), nullptr);
34 
35  // Should succeed as the compilation error is ifdef'd out.
38  Compiler compiler_2 = Compiler(fixture, options, reflector_options);
39  EXPECT_NE(compiler_2.GetSPIRVAssembly(), nullptr);
40 }
41 
42 TEST(CompilerTest, ShaderKindMatchingIsSuccessful) {
43  ASSERT_EQ(SourceTypeFromFileName("hello.vert"), SourceType::kVertexShader);
44  ASSERT_EQ(SourceTypeFromFileName("hello.frag"), SourceType::kFragmentShader);
45  ASSERT_EQ(SourceTypeFromFileName("hello.comp"), SourceType::kComputeShader);
46  ASSERT_EQ(SourceTypeFromFileName("hello.msl"), SourceType::kUnknown);
47  ASSERT_EQ(SourceTypeFromFileName("hello.glsl"), SourceType::kUnknown);
48 }
49 
50 TEST_P(CompilerTest, CanCompile) {
51  if (GetParam() == TargetPlatform::kSkSL) {
52  GTEST_SKIP() << "Not supported with SkSL";
53  }
54  ASSERT_TRUE(CanCompileAndReflect("sample.vert"));
55  ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader));
56  ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader,
58 }
59 
60 TEST_P(CompilerTest, CanCompileHLSL) {
61  if (GetParam() == TargetPlatform::kSkSL) {
62  GTEST_SKIP() << "Not supported with SkSL";
63  }
64  ASSERT_TRUE(CanCompileAndReflect(
65  "simple.vert.hlsl", SourceType::kVertexShader, SourceLanguage::kHLSL));
66 }
67 
68 TEST_P(CompilerTest, CanCompileHLSLWithMultipleStages) {
69  if (GetParam() == TargetPlatform::kSkSL) {
70  GTEST_SKIP() << "Not supported with SkSL";
71  }
72  ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
74  SourceLanguage::kHLSL, "VertexShader"));
75  ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
77  SourceLanguage::kHLSL, "FragmentShader"));
78 }
79 
80 TEST_P(CompilerTest, CanCompileComputeShader) {
81  if (!TargetPlatformIsMetal(GetParam())) {
82  GTEST_SKIP()
83  << "Only enabled on Metal backends till ES 3.2 support is added.";
84  }
85  ASSERT_TRUE(CanCompileAndReflect("sample.comp"));
86  ASSERT_TRUE(CanCompileAndReflect("sample.comp", SourceType::kComputeShader));
87 }
88 
89 TEST_P(CompilerTest, MustFailDueToExceedingResourcesLimit) {
90  if (GetParam() == TargetPlatform::kSkSL) {
91  GTEST_SKIP() << "Not supported with SkSL";
92  }
93  ScopedValidationDisable disable_validation;
94  ASSERT_FALSE(
95  CanCompileAndReflect("resources_limit.vert", SourceType::kVertexShader));
96 }
97 
98 TEST_P(CompilerTest, MustFailDueToMultipleLocationPerStructMember) {
99  if (GetParam() == TargetPlatform::kSkSL) {
100  GTEST_SKIP() << "Not supported with SkSL";
101  }
102  ScopedValidationDisable disable_validation;
103  ASSERT_FALSE(CanCompileAndReflect("struct_def_bug.vert"));
104 }
105 
106 TEST_P(CompilerTest, BindingBaseForFragShader) {
107  if (!TargetPlatformIsVulkan(GetParam())) {
108  GTEST_SKIP();
109  }
110 
111  ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader));
112  ASSERT_TRUE(CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
113 
114  auto get_binding = [&](const char* fixture) -> uint32_t {
115  auto json_fd = GetReflectionJson(fixture);
116  nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
117  return shader_json["buffers"][0]["binding"].get<uint32_t>();
118  };
119 
120  auto vert_uniform_binding = get_binding("sample.vert");
121  auto frag_uniform_binding = get_binding("sample.frag");
122 
123  ASSERT_GT(frag_uniform_binding, vert_uniform_binding);
124 }
125 
126 namespace {
127 struct UniformInfo {
128  std::string uniform_name;
129  uint32_t location;
130  std::string type_name;
131  uint32_t columns;
132  uint32_t vec_size;
133 
134  static UniformInfo fromJson(const nlohmann::json& json) {
135  return {
136  .uniform_name = json["name"].get<std::string>(),
137  .location = json["location"].get<uint32_t>(),
138  .type_name = json["type"]["type_name"].get<std::string>(),
139  .columns = json["type"]["columns"].get<uint32_t>(),
140  .vec_size = json["type"]["vec_size"].get<uint32_t>(),
141  };
142  }
143 
144  static UniformInfo Sampler(const std::string& name, uint32_t location) {
145  return UniformInfo{
146  .uniform_name = name,
147  .location = location,
148  .type_name = "ShaderType::kSampledImage",
149  .columns = 1u,
150  .vec_size = 1u,
151  };
152  }
153  static UniformInfo Float(const std::string& name, uint32_t location) {
154  return FloatInfo(name, location, 1u, 1u);
155  }
156  static UniformInfo Vec2(const std::string& name, uint32_t location) {
157  return FloatInfo(name, location, 1u, 2u);
158  }
159  static UniformInfo Vec3(const std::string& name, uint32_t location) {
160  return FloatInfo(name, location, 1u, 3u);
161  }
162  static UniformInfo Vec4(const std::string& name, uint32_t location) {
163  return FloatInfo(name, location, 1u, 4u);
164  }
165  static UniformInfo Mat4(const std::string& name, uint32_t location) {
166  return FloatInfo(name, location, 4u, 4u);
167  }
168 
169  constexpr bool operator==(const UniformInfo& other) const {
170  return (uniform_name == other.uniform_name && //
171  location == other.location && //
172  type_name == other.type_name && //
173  columns == other.columns && //
174  vec_size == other.vec_size);
175  }
176 
177  private:
178  static UniformInfo FloatInfo(const std::string& name,
179  uint32_t location,
180  uint32_t columns,
181  uint32_t vec_size) {
182  return UniformInfo{
183  .uniform_name = name,
184  .location = location,
185  .type_name = "ShaderType::kFloat",
186  .columns = columns,
187  .vec_size = vec_size,
188  };
189  }
190 };
191 
192 inline std::ostream& operator<<(std::ostream& out, const UniformInfo& info) {
193  out << "UniformInfo {" << std::endl
194  << " uniform_name: " << info.uniform_name << std::endl
195  << " location: " << info.location << std::endl
196  << " type_name: " << info.type_name << std::endl
197  << " columns: " << info.columns << std::endl
198  << " vec_size: " << info.vec_size << std::endl
199  << "}";
200  return out;
201 }
202 } // namespace
203 
204 TEST_P(CompilerTestRuntime, UniformsAppearInJson) {
205  ASSERT_TRUE(CanCompileAndReflect("sample_with_uniforms.frag",
208 
209  auto json_fd = GetReflectionJson("sample_with_uniforms.frag");
210  ASSERT_TRUE(json_fd);
211  nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
212  auto sampler_list = shader_json["sampled_images"];
213  auto float_list = shader_json["uniforms"];
214  ASSERT_EQ(sampler_list.size(), 2u);
215  ASSERT_EQ(float_list.size(), 6u);
216 
217  {
218  // clang-format off
219  std::array expected_infos = {
220  UniformInfo::Sampler("uFirstSampler", 1u),
221  UniformInfo::Sampler("uSampler", 7u),
222  };
223  // clang-format on
224  ASSERT_EQ(sampler_list.size(), expected_infos.size());
225  for (size_t i = 0; i < expected_infos.size(); i++) {
226  EXPECT_EQ(UniformInfo::fromJson(sampler_list[i]), expected_infos[i])
227  << "index: " << i;
228  }
229  }
230 
231  {
232  // clang-format off
233  std::array expected_infos = {
234  UniformInfo::Float("uFirstFloat", 0u),
235  UniformInfo::Float("uFloat", 2u),
236  UniformInfo::Vec2("uVec2", 3u),
237  UniformInfo::Vec3("uVec3", 4u),
238  UniformInfo::Vec4("uVec4", 5u),
239  UniformInfo::Mat4("uMat4", 6u),
240  };
241  // clang-format on
242  ASSERT_EQ(float_list.size(), expected_infos.size());
243  for (size_t i = 0; i < expected_infos.size(); i++) {
244  EXPECT_EQ(UniformInfo::fromJson(float_list[i]), expected_infos[i])
245  << "index: " << i;
246  }
247  }
248 }
249 
250 TEST_P(CompilerTestRuntime, PositionedUniformsAppearInJson) {
251  ASSERT_TRUE(CanCompileAndReflect("sample_with_positioned_uniforms.frag",
254 
255  auto json_fd = GetReflectionJson("sample_with_positioned_uniforms.frag");
256  ASSERT_TRUE(json_fd);
257  nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
258  auto sampler_list = shader_json["sampled_images"];
259  auto float_list = shader_json["uniforms"];
260  ASSERT_EQ(sampler_list.size(), 3u);
261  ASSERT_EQ(float_list.size(), 7u);
262 
263  {
264  // clang-format off
265  std::array expected_infos = {
266  UniformInfo::Sampler("uSamplerNotPositioned1", 1u),
267  UniformInfo::Sampler("uSampler", 0u),
268  UniformInfo::Sampler("uSamplerNotPositioned2", 3u),
269  };
270  // clang-format on
271  ASSERT_EQ(sampler_list.size(), expected_infos.size());
272  for (size_t i = 0; i < expected_infos.size(); i++) {
273  EXPECT_EQ(UniformInfo::fromJson(sampler_list[i]), expected_infos[i])
274  << "index: " << i;
275  }
276  }
277 
278  {
279  // clang-format off
280  std::array expected_infos = {
281  UniformInfo::Float("uFloatNotPositioned1", 0u),
282  UniformInfo::Float("uFloat", 6u),
283  UniformInfo::Vec2("uVec2", 5u),
284  UniformInfo::Vec3("uVec3", 3u),
285  UniformInfo::Vec4("uVec4", 2u),
286  UniformInfo::Mat4("uMat4", 1u),
287  UniformInfo::Float("uFloatNotPositioned2", 2u),
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 
298 TEST_P(CompilerTest, UniformsHaveBindingAndSet) {
299  if (GetParam() == TargetPlatform::kSkSL) {
300  GTEST_SKIP() << "Not supported with SkSL";
301  }
302  ASSERT_TRUE(CanCompileAndReflect("sample_with_binding.vert",
304  ASSERT_TRUE(CanCompileAndReflect("sample.frag", SourceType::kFragmentShader));
305 
306  struct binding_and_set {
307  uint32_t binding;
308  uint32_t set;
309  };
310 
311  auto get_binding = [&](const char* fixture) -> binding_and_set {
312  auto json_fd = GetReflectionJson(fixture);
313  nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
314  uint32_t binding = shader_json["buffers"][0]["binding"].get<uint32_t>();
315  uint32_t set = shader_json["buffers"][0]["set"].get<uint32_t>();
316  return {binding, set};
317  };
318 
319  auto vert_uniform_binding = get_binding("sample_with_binding.vert");
320  auto frag_uniform_binding = get_binding("sample.frag");
321 
322  ASSERT_EQ(frag_uniform_binding.set, 0u);
323  ASSERT_EQ(vert_uniform_binding.set, 3u);
324  ASSERT_EQ(vert_uniform_binding.binding, 17u);
325 }
326 
327 TEST_P(CompilerTestSkSL, SkSLTextureLookUpOrderOfOperations) {
328  ASSERT_TRUE(
329  CanCompileAndReflect("texture_lookup.frag", SourceType::kFragmentShader));
330 
331  auto shader = GetShaderFile("texture_lookup.frag", GetParam());
332  std::string_view shader_mapping(
333  reinterpret_cast<const char*>(shader->GetMapping()), shader->GetSize());
334 
335  constexpr std::string_view expected =
336  "textureA.eval(textureA_size * ( vec2(1.0) + flutter_FragCoord.xy));";
337 
338  EXPECT_NE(shader_mapping.find(expected), std::string::npos);
339 }
340 
341 TEST_P(CompilerTestSkSL, CanCompileStructs) {
342  ASSERT_TRUE(CanCompileAndReflect("struct_internal.frag",
344 }
345 
346 #define INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
347  INSTANTIATE_TEST_SUITE_P( \
348  suite_name, CompilerTest, \
349  ::testing::Values(TargetPlatform::kOpenGLES, \
350  TargetPlatform::kOpenGLDesktop, \
351  TargetPlatform::kMetalDesktop, \
352  TargetPlatform::kMetalIOS, TargetPlatform::kSkSL), \
353  [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
354  return TargetPlatformToString(info.param); \
355  });
356 
358 
359 #define INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
360  INSTANTIATE_TEST_SUITE_P( \
361  suite_name, CompilerTestRuntime, \
362  ::testing::Values(TargetPlatform::kRuntimeStageMetal), \
363  [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
364  return TargetPlatformToString(info.param); \
365  });
366 
368 
369 #define INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
370  INSTANTIATE_TEST_SUITE_P( \
371  suite_name, CompilerTestSkSL, ::testing::Values(TargetPlatform::kSkSL), \
372  [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
373  return TargetPlatformToString(info.param); \
374  });
375 
377 
378 } // namespace testing
379 } // namespace compiler
380 } // namespace impeller
std::shared_ptr< fml::Mapping > GetSPIRVAssembly() const
Definition: compiler.cc:486
uint32_t vec_size
std::string type_name
uint32_t columns
std::string uniform_name
uint32_t location
INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite)
INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite)
TEST_P(CompilerTest, CanCompile)
INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite)
TEST(CompilerTest, Defines)
SourceType SourceTypeFromFileName(const std::filesystem::path &file_name)
Definition: types.cc:17
bool TargetPlatformIsMetal(TargetPlatform platform)
Definition: types.cc:277
bool TargetPlatformIsVulkan(TargetPlatform platform)
Definition: types.cc:296
std::ostream & operator<<(std::ostream &out, const impeller::Arc &a)
Definition: arc.h:141