Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
shader_bundle_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 "gtest/gtest.h"
7
12#include "impeller/shader_bundle/shader_bundle_flatbuffers.h"
13
14namespace impeller {
15namespace compiler {
16namespace testing {
17
18const std::string kUnlitFragmentBundleConfig =
19 "\"UnlitFragment\": {\"type\": \"fragment\", \"file\": "
20 "\"shaders/flutter_gpu_unlit.frag\"}";
21const std::string kUnlitVertexBundleConfig =
22 "\"UnlitVertex\": {\"type\": \"vertex\", \"file\": "
23 "\"shaders/flutter_gpu_unlit.vert\"}";
24
25TEST(ShaderBundleTest, ParseShaderBundleConfigFailsForInvalidJSON) {
26 std::string bundle = "";
27 std::stringstream error;
28 auto result = ParseShaderBundleConfig(bundle, error);
29 ASSERT_FALSE(result.has_value());
30 ASSERT_STREQ(error.str().c_str(),
31 "The shader bundle is not a valid JSON object.\n");
32}
33
34TEST(ShaderBundleTest, ParseShaderBundleConfigFailsWhenEntryNotObject) {
35 std::string bundle = "{\"UnlitVertex\": []}";
36 std::stringstream error;
37 auto result = ParseShaderBundleConfig(bundle, error);
38 ASSERT_FALSE(result.has_value());
39 ASSERT_STREQ(
40 error.str().c_str(),
41 "Invalid shader entry \"UnlitVertex\": Entry is not a JSON object.\n");
42}
43
44TEST(ShaderBundleTest, ParseShaderBundleConfigFailsWhenMissingFile) {
45 std::string bundle = "{\"UnlitVertex\": {\"type\": \"vertex\"}}";
46 std::stringstream error;
47 auto result = ParseShaderBundleConfig(bundle, error);
48 ASSERT_FALSE(result.has_value());
49 ASSERT_STREQ(error.str().c_str(),
50 "Invalid shader entry \"UnlitVertex\": Missing required "
51 "\"file\" field.\n");
52}
53
54TEST(ShaderBundleTest, ParseShaderBundleConfigFailsWhenMissingType) {
55 std::string bundle =
56 "{\"UnlitVertex\": {\"file\": \"shaders/flutter_gpu_unlit.vert\"}}";
57 std::stringstream error;
58 auto result = ParseShaderBundleConfig(bundle, error);
59 ASSERT_FALSE(result.has_value());
60 ASSERT_STREQ(error.str().c_str(),
61 "Invalid shader entry \"UnlitVertex\": Missing required "
62 "\"type\" field.\n");
63}
64
65TEST(ShaderBundleTest, ParseShaderBundleConfigFailsForInvalidType) {
66 std::string bundle =
67 "{\"UnlitVertex\": {\"type\": \"invalid\", \"file\": "
68 "\"shaders/flutter_gpu_unlit.vert\"}}";
69 std::stringstream error;
70 auto result = ParseShaderBundleConfig(bundle, error);
71 ASSERT_FALSE(result.has_value());
72 ASSERT_STREQ(error.str().c_str(),
73 "Invalid shader entry \"UnlitVertex\": Shader type "
74 "\"invalid\" is unknown.\n");
75}
76
77TEST(ShaderBundleTest, ParseShaderBundleConfigFailsForInvalidLanguage) {
78 std::string bundle =
79 "{\"UnlitVertex\": {\"type\": \"vertex\", \"language\": \"invalid\", "
80 "\"file\": \"shaders/flutter_gpu_unlit.vert\"}}";
81 std::stringstream error;
82 auto result = ParseShaderBundleConfig(bundle, error);
83 ASSERT_FALSE(result.has_value());
84 ASSERT_STREQ(error.str().c_str(),
85 "Invalid shader entry \"UnlitVertex\": Unknown language type "
86 "\"invalid\".\n");
87}
88
89TEST(ShaderBundleTest, ParseShaderBundleConfigReturnsExpectedConfig) {
90 std::string bundle =
92 std::stringstream error;
93 auto result = ParseShaderBundleConfig(bundle, error);
94 ASSERT_TRUE(result.has_value());
95 ASSERT_STREQ(error.str().c_str(), "");
96
97 // NOLINTBEGIN(bugprone-unchecked-optional-access)
98 auto maybe_vertex = result->find("UnlitVertex");
99 auto maybe_fragment = result->find("UnlitFragment");
100 ASSERT_TRUE(maybe_vertex != result->end());
101 ASSERT_TRUE(maybe_fragment != result->end());
102 auto vertex = maybe_vertex->second;
103 auto fragment = maybe_fragment->second;
104 // NOLINTEND(bugprone-unchecked-optional-access)
105
106 EXPECT_EQ(vertex.type, SourceType::kVertexShader);
107 EXPECT_EQ(vertex.language, SourceLanguage::kGLSL);
108 EXPECT_STREQ(vertex.entry_point.c_str(), "main");
109 EXPECT_STREQ(vertex.source_file_name.c_str(),
110 "shaders/flutter_gpu_unlit.vert");
111
112 EXPECT_EQ(fragment.type, SourceType::kFragmentShader);
113 EXPECT_EQ(fragment.language, SourceLanguage::kGLSL);
114 EXPECT_STREQ(fragment.entry_point.c_str(), "main");
115 EXPECT_STREQ(fragment.source_file_name.c_str(),
116 "shaders/flutter_gpu_unlit.frag");
117}
118
119template <typename T>
120const T* FindByName(const std::vector<std::unique_ptr<T>>& collection,
121 const std::string& name) {
122 const auto maybe = std::find_if(
123 collection.begin(), collection.end(),
124 [&name](const std::unique_ptr<T>& value) { return value->name == name; });
125 if (maybe == collection.end()) {
126 return nullptr;
127 }
128 return maybe->get();
129}
130
131TEST(ShaderBundleTest, GenerateShaderBundleFlatbufferProducesCorrectResult) {
132 std::string fixtures_path = flutter::testing::GetFixturesPath();
133 std::string config =
134 "{\"UnlitFragment\": {\"type\": \"fragment\", \"file\": \"" +
135 fixtures_path +
136 "/flutter_gpu_unlit.frag\"}, \"UnlitVertex\": {\"type\": "
137 "\"vertex\", \"file\": \"" +
138 fixtures_path + "/flutter_gpu_unlit.vert\"}}";
139
140 SourceOptions options;
143
144 std::optional<fb::shaderbundle::ShaderBundleT> bundle =
145 GenerateShaderBundleFlatbuffer(config, options);
146 ASSERT_TRUE(bundle.has_value());
147
148 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
149 const auto& shaders = bundle->shaders;
150
151 const auto* vertex = FindByName(shaders, "UnlitVertex");
152 const auto* fragment = FindByName(shaders, "UnlitFragment");
153 ASSERT_NE(vertex, nullptr);
154 ASSERT_NE(fragment, nullptr);
155
156 // --------------------------------------------------------------------------
157 /// Verify vertex shader.
158 ///
159
160 EXPECT_STREQ(vertex->metal_desktop->entrypoint.c_str(),
161 "flutter_gpu_unlit_vertex_main");
162 EXPECT_EQ(vertex->metal_desktop->stage,
163 fb::shaderbundle::ShaderStage::kVertex);
164
165 // Inputs.
166 ASSERT_EQ(vertex->metal_desktop->inputs.size(), 1u);
167 const auto& v_in_position = vertex->metal_desktop->inputs[0];
168 EXPECT_STREQ(v_in_position->name.c_str(), "position");
169 EXPECT_EQ(v_in_position->location, 0u);
170 EXPECT_EQ(v_in_position->set, 0u);
171 EXPECT_EQ(v_in_position->binding, 0u);
172 EXPECT_EQ(v_in_position->type, fb::shaderbundle::InputDataType::kFloat);
173 EXPECT_EQ(v_in_position->bit_width, 32u);
174 EXPECT_EQ(v_in_position->vec_size, 2u);
175 EXPECT_EQ(v_in_position->columns, 1u);
176 EXPECT_EQ(v_in_position->offset, 0u);
177
178 // Uniforms.
179 ASSERT_EQ(vertex->metal_desktop->uniform_structs.size(), 1u);
180 const auto* vert_info =
181 FindByName(vertex->metal_desktop->uniform_structs, "VertInfo");
182 ASSERT_NE(vert_info, nullptr);
183 EXPECT_EQ(vert_info->ext_res_0, 0u);
184 EXPECT_EQ(vert_info->set, 0u);
185 EXPECT_EQ(vert_info->binding, 0u);
186 ASSERT_EQ(vert_info->fields.size(), 2u);
187 const auto& mvp = vert_info->fields[0];
188 EXPECT_STREQ(mvp->name.c_str(), "mvp");
189 EXPECT_EQ(mvp->type, fb::shaderbundle::UniformDataType::kFloat);
190 EXPECT_EQ(mvp->offset_in_bytes, 0u);
191 EXPECT_EQ(mvp->element_size_in_bytes, 64u);
192 EXPECT_EQ(mvp->total_size_in_bytes, 64u);
193 EXPECT_EQ(mvp->array_elements, 0u);
194 EXPECT_EQ(mvp->vec_size, 4u);
195 EXPECT_EQ(mvp->columns, 4u);
196 const auto& color = vert_info->fields[1];
197 EXPECT_STREQ(color->name.c_str(), "color");
198 EXPECT_EQ(color->type, fb::shaderbundle::UniformDataType::kFloat);
199 EXPECT_EQ(color->offset_in_bytes, 64u);
200 EXPECT_EQ(color->element_size_in_bytes, 16u);
201 EXPECT_EQ(color->total_size_in_bytes, 16u);
202 EXPECT_EQ(color->array_elements, 0u);
203 EXPECT_EQ(color->vec_size, 4u);
204 EXPECT_EQ(color->columns, 1u);
205
206 // --------------------------------------------------------------------------
207 /// Verify fragment shader.
208 ///
209
210 EXPECT_STREQ(fragment->metal_desktop->entrypoint.c_str(),
211 "flutter_gpu_unlit_fragment_main");
212 EXPECT_EQ(fragment->metal_desktop->stage,
213 fb::shaderbundle::ShaderStage::kFragment);
214
215 // Inputs (not recorded for fragment shaders).
216 ASSERT_EQ(fragment->metal_desktop->inputs.size(), 0u);
217
218 // Uniforms.
219 ASSERT_EQ(fragment->metal_desktop->inputs.size(), 0u);
220}
221
222TEST(ShaderBundleTest,
223 GenerateShaderBundleFlatbufferReportsSourceFilesAsDependencies) {
224 std::string fixtures_path = flutter::testing::GetFixturesPath();
225 const std::string fragment_path = fixtures_path + "/flutter_gpu_unlit.frag";
226 const std::string vertex_path = fixtures_path + "/flutter_gpu_unlit.vert";
227 std::string config =
228 "{\"UnlitFragment\": {\"type\": \"fragment\", \"file\":\"" +
229 fragment_path +
230 "\"}, \"UnlitVertex\": {\"type\": \"vertex\", \"file\": \"" +
231 vertex_path + "\"}}";
232
233 SourceOptions options;
236
237 std::set<std::string> dependencies;
238 std::optional<fb::shaderbundle::ShaderBundleT> bundle =
239 GenerateShaderBundleFlatbuffer(config, options, &dependencies);
240 ASSERT_TRUE(bundle.has_value());
241
242 // Every primary source file referenced by the bundle config should
243 // appear in the dependency set, deduplicated across the multiple
244 // target-platform compiles of each shader. The fixtures used here
245 // don't contain `#include` directives, so the dependency set
246 // contains exactly the two source files.
247 EXPECT_NE(dependencies.find(fragment_path), dependencies.end());
248 EXPECT_NE(dependencies.find(vertex_path), dependencies.end());
249}
250
251TEST(ShaderBundleTest,
252 GenerateShaderBundleFlatbufferIgnoresNullDependencyCollector) {
253 // Passing nullptr as the dependency collector is supported and is
254 // the default behaviour for callers that don't need a depfile.
255 std::string fixtures_path = flutter::testing::GetFixturesPath();
256 std::string config =
257 "{\"UnlitFragment\": {\"type\": \"fragment\", \"file\": \"" +
258 fixtures_path +
259 "/flutter_gpu_unlit.frag\"}, \"UnlitVertex\": {\"type\": \"vertex\", "
260 "\"file\": \"" +
261 fixtures_path + "/flutter_gpu_unlit.vert\"}}";
262
263 SourceOptions options;
266
267 std::optional<fb::shaderbundle::ShaderBundleT> bundle =
268 GenerateShaderBundleFlatbuffer(config, options, /*out_dependencies=*/
269 nullptr);
270 ASSERT_TRUE(bundle.has_value());
271}
272
273TEST(ShaderBundleTest, DeriveShaderFloatTypeFromDimensions) {
274 // Non-float types always map to nullopt.
275 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kSignedInt, 1, 1), std::nullopt);
277 std::nullopt);
278 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kBoolean, 1, 1), std::nullopt);
279
280 // Scalar and vector floats (columns == 1).
289
290 // Square matrices (vec_size == columns).
297
298 // Non-square matrices and unsupported shapes return nullopt.
299 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kFloat, 3, 2), std::nullopt);
300 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kFloat, 2, 3), std::nullopt);
301 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kFloat, 5, 1), std::nullopt);
302
303 // Zero values (legacy bundle defaults) map to nullopt.
304 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kFloat, 0, 0), std::nullopt);
305 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kFloat, 0, 1), std::nullopt);
306 EXPECT_EQ(DeriveShaderFloatType(ShaderType::kFloat, 1, 0), std::nullopt);
307}
308
309} // namespace testing
310} // namespace compiler
311} // namespace impeller
int32_t value
const uint8_t uint32_t uint32_t GError ** error
const char * name
Definition fuchsia.cc:50
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
const T * FindByName(const std::vector< std::unique_ptr< T > > &collection, const std::string &name)
TEST(CompilerTest, Defines)
std::optional< fb::shaderbundle::ShaderBundleT > GenerateShaderBundleFlatbuffer(const std::string &bundle_config_json, const SourceOptions &options, std::set< std::string > *out_dependencies)
Parses the JSON shader bundle configuration and invokes the compiler multiple times to produce a shad...
std::optional< ShaderBundleConfig > ParseShaderBundleConfig(const std::string &bundle_config_json, std::ostream &error_stream)
Parse a shader bundle configuration from a given JSON string.
constexpr std::optional< ShaderFloatType > DeriveShaderFloatType(ShaderType type, size_t vec_size, size_t columns)
Derive the ShaderFloatType from the base ShaderType and the (vec_size, columns) dimensions reported b...