1 // Copyright (c) 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <vector>
16
17 #include "gmock/gmock.h"
18 #include "source/opt/convert_to_sampled_image_pass.h"
19 #include "test/opt/pass_fixture.h"
20 #include "test/opt/pass_utils.h"
21
22 namespace spvtools {
23 namespace opt {
24 namespace {
25
26 using testing::Eq;
27 using VectorOfDescriptorSetAndBindingPairs =
28 std::vector<DescriptorSetAndBinding>;
29
30 struct DescriptorSetAndBindingStringParsingTestCase {
31 const char* descriptor_set_binding_str;
32 bool expect_success;
33 VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs;
34 };
35
36 using DescriptorSetAndBindingStringParsingTest =
37 ::testing::TestWithParam<DescriptorSetAndBindingStringParsingTestCase>;
38
TEST_P(DescriptorSetAndBindingStringParsingTest,TestCase)39 TEST_P(DescriptorSetAndBindingStringParsingTest, TestCase) {
40 const auto& tc = GetParam();
41 auto actual_descriptor_set_binding_pairs =
42 ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
43 tc.descriptor_set_binding_str);
44 if (tc.expect_success) {
45 EXPECT_NE(nullptr, actual_descriptor_set_binding_pairs);
46 if (actual_descriptor_set_binding_pairs) {
47 EXPECT_THAT(*actual_descriptor_set_binding_pairs,
48 Eq(tc.expected_descriptor_set_binding_pairs));
49 }
50 } else {
51 EXPECT_EQ(nullptr, actual_descriptor_set_binding_pairs);
52 }
53 }
54
55 INSTANTIATE_TEST_SUITE_P(
56 ValidString, DescriptorSetAndBindingStringParsingTest,
57 ::testing::ValuesIn(std::vector<
58 DescriptorSetAndBindingStringParsingTestCase>{
59 // 0. empty vector
60 {"", true, VectorOfDescriptorSetAndBindingPairs({})},
61 // 1. one pair
62 {"100:1024", true,
63 VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
64 1024}})},
65 // 2. two pairs
66 {"100:1024 200:2048", true,
67 VectorOfDescriptorSetAndBindingPairs(
68 {DescriptorSetAndBinding{100, 1024},
69 DescriptorSetAndBinding{200, 2048}})},
70 // 3. spaces between entries
71 {"100:1024 \n \r \t \v \f 200:2048", true,
72 VectorOfDescriptorSetAndBindingPairs(
73 {DescriptorSetAndBinding{100, 1024},
74 DescriptorSetAndBinding{200, 2048}})},
75 // 4. \t, \n, \r and spaces before spec id
76 {" \n \r\t \t \v \f 100:1024", true,
77 VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
78 1024}})},
79 // 5. \t, \n, \r and spaces after value string
80 {"100:1024 \n \r\t \t \v \f ", true,
81 VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
82 1024}})},
83 // 6. maximum spec id
84 {"4294967295:0", true,
85 VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{
86 4294967295, 0}})},
87 // 7. minimum spec id
88 {"0:100", true,
89 VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0,
90 100}})},
91 // 8. multiple entries
92 {"101:1 102:2 103:3 104:4 200:201 9999:1000", true,
93 VectorOfDescriptorSetAndBindingPairs(
94 {DescriptorSetAndBinding{101, 1}, DescriptorSetAndBinding{102, 2},
95 DescriptorSetAndBinding{103, 3}, DescriptorSetAndBinding{104, 4},
96 DescriptorSetAndBinding{200, 201},
97 DescriptorSetAndBinding{9999, 1000}})},
98 }));
99
100 INSTANTIATE_TEST_SUITE_P(
101 InvalidString, DescriptorSetAndBindingStringParsingTest,
102 ::testing::ValuesIn(
103 std::vector<DescriptorSetAndBindingStringParsingTestCase>{
104 // 0. missing default value
105 {"100:", false, VectorOfDescriptorSetAndBindingPairs{}},
106 // 1. descriptor set is not an integer
107 {"100.0:200", false, VectorOfDescriptorSetAndBindingPairs{}},
108 // 2. descriptor set is not a number
109 {"something_not_a_number:1", false,
110 VectorOfDescriptorSetAndBindingPairs{}},
111 // 3. only descriptor set number
112 {"100", false, VectorOfDescriptorSetAndBindingPairs{}},
113 // 4. empty descriptor set
114 {":3", false, VectorOfDescriptorSetAndBindingPairs{}},
115 // 5. only colon
116 {":", false, VectorOfDescriptorSetAndBindingPairs{}},
117 // 6. descriptor set overflow
118 {"4294967296:200", false, VectorOfDescriptorSetAndBindingPairs{}},
119 // 7. descriptor set less than 0
120 {"-1:200", false, VectorOfDescriptorSetAndBindingPairs{}},
121 // 8. nullptr
122 {nullptr, false, VectorOfDescriptorSetAndBindingPairs{}},
123 // 9. only a number is invalid
124 {"1234", false, VectorOfDescriptorSetAndBindingPairs{}},
125 // 10. invalid entry separator
126 {"12:34;23:14", false, VectorOfDescriptorSetAndBindingPairs{}},
127 // 11. invalid descriptor set and default value separator
128 {"12@34", false, VectorOfDescriptorSetAndBindingPairs{}},
129 // 12. spaces before colon
130 {"100 :1024", false, VectorOfDescriptorSetAndBindingPairs{}},
131 // 13. spaces after colon
132 {"100: 1024", false, VectorOfDescriptorSetAndBindingPairs{}},
133 // 14. descriptor set represented in hex float format is invalid
134 {"0x3p10:200", false, VectorOfDescriptorSetAndBindingPairs{}},
135 }));
136
BuildShader(const char * shader_decorate_instructions,const char * shader_image_and_sampler_variables,const char * shader_body)137 std::string BuildShader(const char* shader_decorate_instructions,
138 const char* shader_image_and_sampler_variables,
139 const char* shader_body) {
140 // Base HLSL code:
141 //
142 // SamplerState sam : register(s2);
143 // Texture2D <float4> texture : register(t5);
144 //
145 // float4 main() : SV_TARGET {
146 // return texture.SampleLevel(sam, float2(1, 2), 10, 2);
147 // }
148 std::stringstream ss;
149 ss << R"(
150 OpCapability Shader
151 OpMemoryModel Logical GLSL450
152 OpEntryPoint Fragment %main "main" %out_var_SV_TARGET
153 OpExecutionMode %main OriginUpperLeft
154 OpSource HLSL 600
155 OpName %type_sampler "type.sampler"
156 OpName %type_2d_image "type.2d.image"
157 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
158 OpName %main "main"
159 OpName %type_sampled_image "type.sampled.image"
160 OpDecorate %out_var_SV_TARGET Location 0
161 )";
162 ss << shader_decorate_instructions;
163 ss << R"(
164 %float = OpTypeFloat 32
165 %float_1 = OpConstant %float 1
166 %float_2 = OpConstant %float 2
167 %v2float = OpTypeVector %float 2
168 %12 = OpConstantComposite %v2float %float_1 %float_2
169 %float_10 = OpConstant %float 10
170 %int = OpTypeInt 32 1
171 %int_2 = OpConstant %int 2
172 %v2int = OpTypeVector %int 2
173 %17 = OpConstantComposite %v2int %int_2 %int_2
174 %type_sampler = OpTypeSampler
175 %_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
176 %type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
177 %_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
178 %v4float = OpTypeVector %float 4
179 %_ptr_Output_v4float = OpTypePointer Output %v4float
180 %void = OpTypeVoid
181 %23 = OpTypeFunction %void
182 %type_sampled_image = OpTypeSampledImage %type_2d_image
183 )";
184 ss << shader_image_and_sampler_variables;
185 ss << R"(
186 %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
187 %main = OpFunction %void None %23
188 %24 = OpLabel
189 )";
190 ss << shader_body;
191 ss << R"(
192 OpReturn
193 OpFunctionEnd
194 )";
195 return ss.str();
196 }
197
198 using ConvertToSampledImageTest = PassTest<::testing::Test>;
199
TEST_F(ConvertToSampledImageTest,Texture2DAndSamplerToSampledImage)200 TEST_F(ConvertToSampledImageTest, Texture2DAndSamplerToSampledImage) {
201 const std::string shader = BuildShader(
202 R"(
203 OpDecorate %sam DescriptorSet 0
204 OpDecorate %sam Binding 5
205 OpDecorate %texture DescriptorSet 0
206 OpDecorate %texture Binding 5
207 )",
208 R"(
209 ; CHECK-NOT: OpVariable %_ptr_UniformConstant_type_2d_image
210
211 ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant
212 %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
213 %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
214 )",
215 R"(
216 ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]]
217 ; CHECK: OpImageSampleExplicitLod %v4float [[load]]
218 %25 = OpLoad %type_2d_image %texture
219 %26 = OpLoad %type_sampler %sam
220 %27 = OpSampledImage %type_sampled_image %25 %26
221 %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
222 OpStore %out_var_SV_TARGET %28
223 )");
224
225 auto result = SinglePassRunAndMatch<ConvertToSampledImagePass>(
226 shader, /* do_validate = */ true,
227 VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
228
229 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
230 }
231
TEST_F(ConvertToSampledImageTest,Texture2DToSampledImage)232 TEST_F(ConvertToSampledImageTest, Texture2DToSampledImage) {
233 const std::string shader = BuildShader(
234 R"(
235 OpDecorate %sam DescriptorSet 0
236 OpDecorate %sam Binding 2
237 OpDecorate %texture DescriptorSet 0
238 OpDecorate %texture Binding 5
239 )",
240 R"(
241 ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant
242 %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
243 %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
244 )",
245 R"(
246 ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]]
247 ; CHECK: [[image_extraction:%\w+]] = OpImage %type_2d_image [[load]]
248 ; CHECK: OpSampledImage %type_sampled_image [[image_extraction]]
249 %25 = OpLoad %type_2d_image %texture
250 %26 = OpLoad %type_sampler %sam
251 %27 = OpSampledImage %type_sampled_image %25 %26
252 %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
253 OpStore %out_var_SV_TARGET %28
254 )");
255
256 auto result = SinglePassRunAndMatch<ConvertToSampledImagePass>(
257 shader, /* do_validate = */ true,
258 VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
259
260 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
261 }
262
TEST_F(ConvertToSampledImageTest,SamplerToSampledImage)263 TEST_F(ConvertToSampledImageTest, SamplerToSampledImage) {
264 const std::string shader = BuildShader(
265 R"(
266 OpDecorate %sam DescriptorSet 0
267 OpDecorate %sam Binding 2
268 OpDecorate %texture DescriptorSet 0
269 OpDecorate %texture Binding 5
270 )",
271 R"(
272 %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
273 %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
274 )",
275 R"(
276 %25 = OpLoad %type_2d_image %texture
277 %26 = OpLoad %type_sampler %sam
278 %27 = OpSampledImage %type_sampled_image %25 %26
279 %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
280 OpStore %out_var_SV_TARGET %28
281 )");
282
283 auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
284 shader, /* skip_nop = */ false,
285 VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}});
286
287 EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
288 }
289
TEST_F(ConvertToSampledImageTest,TwoImagesWithDuplicatedDescriptorSetBinding)290 TEST_F(ConvertToSampledImageTest, TwoImagesWithDuplicatedDescriptorSetBinding) {
291 const std::string shader = BuildShader(
292 R"(
293 OpDecorate %sam DescriptorSet 0
294 OpDecorate %sam Binding 2
295 OpDecorate %texture0 DescriptorSet 0
296 OpDecorate %texture0 Binding 5
297 OpDecorate %texture1 DescriptorSet 0
298 OpDecorate %texture1 Binding 5
299 )",
300 R"(
301 %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
302 %texture0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
303 %texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
304 )",
305 R"(
306 %25 = OpLoad %type_2d_image %texture0
307 %26 = OpLoad %type_sampler %sam
308 %27 = OpSampledImage %type_sampled_image %25 %26
309 %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
310 OpStore %out_var_SV_TARGET %28
311 )");
312
313 auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
314 shader, /* skip_nop = */ false,
315 VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
316
317 EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
318 }
319
TEST_F(ConvertToSampledImageTest,TwoSamplersWithDuplicatedDescriptorSetBinding)320 TEST_F(ConvertToSampledImageTest,
321 TwoSamplersWithDuplicatedDescriptorSetBinding) {
322 const std::string shader = BuildShader(
323 R"(
324 OpDecorate %sam0 DescriptorSet 0
325 OpDecorate %sam0 Binding 2
326 OpDecorate %sam1 DescriptorSet 0
327 OpDecorate %sam1 Binding 2
328 OpDecorate %texture DescriptorSet 0
329 OpDecorate %texture Binding 5
330 )",
331 R"(
332 %sam0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
333 %sam1 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
334 %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
335 )",
336 R"(
337 %25 = OpLoad %type_2d_image %texture
338 %26 = OpLoad %type_sampler %sam0
339 %27 = OpSampledImage %type_sampled_image %25 %26
340 %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
341 OpStore %out_var_SV_TARGET %28
342 )");
343
344 auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
345 shader, /* skip_nop = */ false,
346 VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}});
347
348 EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
349 }
350
351 } // namespace
352 } // namespace opt
353 } // namespace spvtools
354