| 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"
8 #include "impeller/base/validation.h"
9 #include "impeller/compiler/compiler.h"
10 #include "impeller/compiler/compiler_test.h"
11 #include "impeller/compiler/source_options.h"
12 #include "impeller/compiler/types.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;
23 options.source_language = SourceLanguage::kGLSL;
24 options.target_platform = TargetPlatform::kRuntimeStageGLES;
25 options.entry_point_name = "main";
26 options.type = SourceType::kFragmentShader;
27
28 Reflector::Options reflector_options;
29 reflector_options.target_platform = TargetPlatform::kRuntimeStageGLES;
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.
36 options.target_platform = TargetPlatform::kRuntimeStageVulkan;
37 reflector_options.target_platform = TargetPlatform::kRuntimeStageVulkan;
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,
57 SourceLanguage::kGLSL));
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",
73 SourceType::kVertexShader,
74 SourceLanguage::kHLSL, "VertexShader"));
75 ASSERT_TRUE(CanCompileAndReflect("multiple_stages.hlsl",
76 SourceType::kFragmentShader,
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",
206 SourceType::kFragmentShader,
207 SourceLanguage::kGLSL));
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",
252 SourceType::kFragmentShader,
253 SourceLanguage::kGLSL));
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",
303 SourceType::kVertexShader));
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",
343 SourceType::kFragmentShader));
344 }
345
346 TEST_P(CompilerTestSkSL, FailsToCompileDueToArrayInitializerWithConstants) {
347 auto expected_err =
348 "There was a compiler error: SkSL does not support array initializers: "
349 "array_initializer_with_constants.frag:6";
350
351 EXPECT_EXIT(CanCompileAndReflect("array_initializer_with_constants.frag",
352 SourceType::kFragmentShader),
353 ::testing::ExitedWithCode(1), expected_err);
354 }
355
356 TEST_P(CompilerTestSkSL, FailsToCompileDueToArrayInitializerWithVariables) {
357 auto expected_err =
358 "There was a compiler error: SkSL does not support array initializers: "
359 "array_initializer_with_variables.frag:12";
360
361 EXPECT_EXIT(CanCompileAndReflect("array_initializer_with_variables.frag",
362 SourceType::kFragmentShader),
363 ::testing::ExitedWithCode(1), expected_err);
364 }
365
366 TEST_P(CompilerTestSkSL, FailsToCompileDueToArrayAssignment) {
367 // Does not EXIT because the backend SkSL compiler doesn't detect the invalid
368 // SkSL. Returns false because the Impeller Compiler's post-compile validation
369 // fails.
370 ASSERT_FALSE(CanCompileAndReflect("array_assignment.frag",
371 SourceType::kFragmentShader));
372 }
373
374 TEST_P(CompilerTestSkSL, CompilesWithValidArrayInitialization) {
375 ASSERT_TRUE(
376 CanCompileAndReflect("array_initialization_without_initializer.frag",
377 SourceType::kFragmentShader));
378 }
379
380 #define INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
381 INSTANTIATE_TEST_SUITE_P( \
382 suite_name, CompilerTest, \
383 ::testing::Values(TargetPlatform::kOpenGLES, \
384 TargetPlatform::kOpenGLDesktop, \
385 TargetPlatform::kMetalDesktop, \
386 TargetPlatform::kMetalIOS, TargetPlatform::kSkSL), \
387 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
388 return TargetPlatformToString(info.param); \
389 });
390
391 INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite);
392
393 #define INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
394 INSTANTIATE_TEST_SUITE_P( \
395 suite_name, CompilerTestRuntime, \
396 ::testing::Values(TargetPlatform::kRuntimeStageMetal), \
397 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
398 return TargetPlatformToString(info.param); \
399 });
400
401 INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite);
402
403 #define INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \
404 INSTANTIATE_TEST_SUITE_P( \
405 suite_name, CompilerTestSkSL, ::testing::Values(TargetPlatform::kSkSL), \
406 [](const ::testing::TestParamInfo<CompilerTest::ParamType>& info) { \
407 return TargetPlatformToString(info.param); \
408 });
409
410 INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite);
411
412 TEST_P(CompilerTestRuntime, Mat2Reflection) {
413 ASSERT_TRUE(CanCompileAndReflect(
414 "mat2_test.frag", SourceType::kFragmentShader, SourceLanguage::kGLSL));
415
416 std::unique_ptr<fml::FileMapping> json_fd =
417 GetReflectionJson("mat2_test.frag");
418 ASSERT_TRUE(json_fd);
419 nlohmann::json shader_json = nlohmann::json::parse(json_fd->GetMapping());
420 nlohmann::json::value_type buffers = shader_json["buffers"];
421
422 // uParams should be in buffers.
423 ASSERT_GE(buffers.size(), 1u);
424 nlohmann::json::value_type uParams = buffers[0];
425 EXPECT_EQ(uParams["name"], "Params");
426
427 nlohmann::json::value_type members = uParams["type"]["members"];
428 // We expect 1 member (for the mat2) which is a Point (vec2) with array
429 // size 2.
430 ASSERT_EQ(members.size(), 1u);
431
432 nlohmann::json::value_type mat2Member = members[0];
433
434 // Should be Mat2 (vec2)
435 EXPECT_EQ(mat2Member["type"], "Mat2");
436
437 // Size 8 bytes (vec2)
438 EXPECT_EQ(mat2Member["size"], 8u);
439
440 // Byte length should be total array size (stride * count)
441 // Stride 16 * 2 = 32.
442 EXPECT_EQ(mat2Member["byte_length"], 32u);
443
444 // Array elements should be 2 (columns).
445 EXPECT_EQ(mat2Member["array_elements"], 2u);
446
447 // Offset 0
448 EXPECT_EQ(mat2Member["offset"], 0u);
449 }
450
451 } // namespace testing
452 } // namespace compiler
453 } // namespace impeller
impeller::compiler::Compiler
Definition: compiler.h:24
impeller::compiler::Compiler::GetSPIRVAssembly
std::shared_ptr< fml::Mapping > GetSPIRVAssembly() const
Definition: compiler.cc:502
impeller::compiler::testing::CompilerTest
Definition: compiler_test.h:46
impeller::compiler::testing::CompilerTestRuntime
Definition: compiler_test.h:50
impeller::compiler::testing::CompilerTestSkSL
Definition: compiler_test.h:48
types.h
compiler.h
compiler_test.h
vec_size
uint32_t vec_size
Definition: compiler_unittests.cc:132
type_name
std::string type_name
Definition: compiler_unittests.cc:130
columns
uint32_t columns
Definition: compiler_unittests.cc:131
uniform_name
std::string uniform_name
Definition: compiler_unittests.cc:128
location
uint32_t location
Definition: compiler_unittests.cc:129
impeller::compiler::testing::INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P
INSTANTIATE_SKSL_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite)
impeller::compiler::testing::INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P
INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite)
impeller::compiler::testing::TEST_P
TEST_P(CompilerTest, CanCompile)
Definition: compiler_unittests.cc:50
impeller::compiler::testing::INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P
INSTANTIATE_RUNTIME_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite)
impeller::compiler::testing::TEST
TEST(CompilerTest, Defines)
Definition: compiler_unittests.cc:18
impeller::compiler::SourceType::kUnknown
@ kUnknown
impeller::compiler::SourceType::kFragmentShader
@ kFragmentShader
impeller::compiler::SourceType::kVertexShader
@ kVertexShader
impeller::compiler::SourceType::kComputeShader
@ kComputeShader
impeller::compiler::SourceTypeFromFileName
SourceType SourceTypeFromFileName(const std::filesystem::path &file_name)
Definition: types.cc:17
impeller::compiler::SourceLanguage::kGLSL
@ kGLSL
impeller::compiler::SourceLanguage::kHLSL
@ kHLSL
impeller::compiler::TargetPlatformIsMetal
bool TargetPlatformIsMetal(TargetPlatform platform)
Definition: types.cc:277
impeller::compiler::TargetPlatformIsVulkan
bool TargetPlatformIsVulkan(TargetPlatform platform)
Definition: types.cc:296
impeller::compiler::TargetPlatform::kRuntimeStageVulkan
@ kRuntimeStageVulkan
impeller::compiler::TargetPlatform::kSkSL
@ kSkSL
impeller::compiler::TargetPlatform::kRuntimeStageGLES
@ kRuntimeStageGLES
impeller
Definition: allocation.cc:12
std::operator<<
std::ostream & operator<<(std::ostream &out, const impeller::Arc &a)
Definition: arc.h:141
source_options.h
impeller::ScopedValidationDisable
Definition: validation.h:56
impeller::compiler::Reflector::Options
Definition: reflector.h:162
impeller::compiler::Reflector::Options::target_platform
TargetPlatform target_platform
Definition: reflector.h:163
impeller::compiler::SourceOptions
Definition: source_options.h:20
impeller::compiler::SourceOptions::source_language
SourceLanguage source_language
Definition: source_options.h:23
impeller::compiler::SourceOptions::entry_point_name
std::string entry_point_name
Definition: source_options.h:27
impeller::compiler::SourceOptions::target_platform
TargetPlatform target_platform
Definition: source_options.h:22
impeller::compiler::SourceOptions::type
SourceType type
Definition: source_options.h:21
validation.h