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 22namespace spvtools { 23namespace opt { 24namespace { 25 26using testing::Eq; 27using VectorOfDescriptorSetAndBindingPairs = 28 std::vector<DescriptorSetAndBinding>; 29 30struct DescriptorSetAndBindingStringParsingTestCase { 31 const char* descriptor_set_binding_str; 32 bool expect_success; 33 VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs; 34}; 35 36using DescriptorSetAndBindingStringParsingTest = 37 ::testing::TestWithParam<DescriptorSetAndBindingStringParsingTestCase>; 38 39TEST_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 55INSTANTIATE_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 100INSTANTIATE_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 137std::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 198using ConvertToSampledImageTest = PassTest<::testing::Test>; 199 200TEST_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 232TEST_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 263TEST_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 290TEST_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 320TEST_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