mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 21:30:07 +00:00
57e1d8ebe3
convert-to-sampled-image pass converts images and/or samplers with given pairs of descriptor set and binding to sampled image. If a pair of an image and a sampler have the same pair of descriptor set and binding that is one of the given pairs, they will be converted to a sampled image. In addition, if only an image has the descriptor set and binding that is one of the given pairs, it will be converted to a sampled image as well. For example, when we have %a = OpLoad %type_2d_image %texture %b = OpLoad %type_sampler %sampler %combined = OpSampledImage %type_sampled_image %a %b %value = OpImageSampleExplicitLod %v4float %combined ... 1. If %texture and %sampler have the same descriptor set and binding %combine_texture_and_sampler = OpVaraible %ptr_type_sampled_image_Uniform ... %combined = OpLoad %type_sampled_image %combine_texture_and_sampler %value = OpImageSampleExplicitLod %v4float %combined ... 2. If %texture and %sampler have different pairs of descriptor set and binding %a = OpLoad %type_sampled_image %texture %extracted_image = OpImage %type_2d_image %a %b = OpLoad %type_sampler %sampler %combined = OpSampledImage %type_sampled_image %extracted_image %b %value = OpImageSampleExplicitLod %v4float %combined ...
354 lines
14 KiB
C++
354 lines
14 KiB
C++
// Copyright (c) 2021 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "source/opt/convert_to_sampled_image_pass.h"
|
|
#include "test/opt/pass_fixture.h"
|
|
#include "test/opt/pass_utils.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
namespace {
|
|
|
|
using testing::Eq;
|
|
using VectorOfDescriptorSetAndBindingPairs =
|
|
std::vector<DescriptorSetAndBinding>;
|
|
|
|
struct DescriptorSetAndBindingStringParsingTestCase {
|
|
const char* descriptor_set_binding_str;
|
|
bool expect_success;
|
|
VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs;
|
|
};
|
|
|
|
using DescriptorSetAndBindingStringParsingTest =
|
|
::testing::TestWithParam<DescriptorSetAndBindingStringParsingTestCase>;
|
|
|
|
TEST_P(DescriptorSetAndBindingStringParsingTest, TestCase) {
|
|
const auto& tc = GetParam();
|
|
auto actual_descriptor_set_binding_pairs =
|
|
ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
|
|
tc.descriptor_set_binding_str);
|
|
if (tc.expect_success) {
|
|
EXPECT_NE(nullptr, actual_descriptor_set_binding_pairs);
|
|
if (actual_descriptor_set_binding_pairs) {
|
|
EXPECT_THAT(*actual_descriptor_set_binding_pairs,
|
|
Eq(tc.expected_descriptor_set_binding_pairs));
|
|
}
|
|
} else {
|
|
EXPECT_EQ(nullptr, actual_descriptor_set_binding_pairs);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
ValidString, DescriptorSetAndBindingStringParsingTest,
|
|
::testing::ValuesIn(std::vector<
|
|
DescriptorSetAndBindingStringParsingTestCase>{
|
|
// 0. empty vector
|
|
{"", true, VectorOfDescriptorSetAndBindingPairs({})},
|
|
// 1. one pair
|
|
{"100:1024", true,
|
|
VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
|
|
1024}})},
|
|
// 2. two pairs
|
|
{"100:1024 200:2048", true,
|
|
VectorOfDescriptorSetAndBindingPairs(
|
|
{DescriptorSetAndBinding{100, 1024},
|
|
DescriptorSetAndBinding{200, 2048}})},
|
|
// 3. spaces between entries
|
|
{"100:1024 \n \r \t \v \f 200:2048", true,
|
|
VectorOfDescriptorSetAndBindingPairs(
|
|
{DescriptorSetAndBinding{100, 1024},
|
|
DescriptorSetAndBinding{200, 2048}})},
|
|
// 4. \t, \n, \r and spaces before spec id
|
|
{" \n \r\t \t \v \f 100:1024", true,
|
|
VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
|
|
1024}})},
|
|
// 5. \t, \n, \r and spaces after value string
|
|
{"100:1024 \n \r\t \t \v \f ", true,
|
|
VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
|
|
1024}})},
|
|
// 6. maximum spec id
|
|
{"4294967295:0", true,
|
|
VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{
|
|
4294967295, 0}})},
|
|
// 7. minimum spec id
|
|
{"0:100", true,
|
|
VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0,
|
|
100}})},
|
|
// 8. multiple entries
|
|
{"101:1 102:2 103:3 104:4 200:201 9999:1000", true,
|
|
VectorOfDescriptorSetAndBindingPairs(
|
|
{DescriptorSetAndBinding{101, 1}, DescriptorSetAndBinding{102, 2},
|
|
DescriptorSetAndBinding{103, 3}, DescriptorSetAndBinding{104, 4},
|
|
DescriptorSetAndBinding{200, 201},
|
|
DescriptorSetAndBinding{9999, 1000}})},
|
|
}));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
InvalidString, DescriptorSetAndBindingStringParsingTest,
|
|
::testing::ValuesIn(
|
|
std::vector<DescriptorSetAndBindingStringParsingTestCase>{
|
|
// 0. missing default value
|
|
{"100:", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 1. descriptor set is not an integer
|
|
{"100.0:200", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 2. descriptor set is not a number
|
|
{"something_not_a_number:1", false,
|
|
VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 3. only descriptor set number
|
|
{"100", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 4. empty descriptor set
|
|
{":3", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 5. only colon
|
|
{":", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 6. descriptor set overflow
|
|
{"4294967296:200", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 7. descriptor set less than 0
|
|
{"-1:200", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 8. nullptr
|
|
{nullptr, false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 9. only a number is invalid
|
|
{"1234", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 10. invalid entry separator
|
|
{"12:34;23:14", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 11. invalid descriptor set and default value separator
|
|
{"12@34", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 12. spaces before colon
|
|
{"100 :1024", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 13. spaces after colon
|
|
{"100: 1024", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
// 14. descriptor set represented in hex float format is invalid
|
|
{"0x3p10:200", false, VectorOfDescriptorSetAndBindingPairs{}},
|
|
}));
|
|
|
|
std::string BuildShader(const char* shader_decorate_instructions,
|
|
const char* shader_image_and_sampler_variables,
|
|
const char* shader_body) {
|
|
// Base HLSL code:
|
|
//
|
|
// SamplerState sam : register(s2);
|
|
// Texture2D <float4> texture : register(t5);
|
|
//
|
|
// float4 main() : SV_TARGET {
|
|
// return texture.SampleLevel(sam, float2(1, 2), 10, 2);
|
|
// }
|
|
std::stringstream ss;
|
|
ss << R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %out_var_SV_TARGET
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource HLSL 600
|
|
OpName %type_sampler "type.sampler"
|
|
OpName %type_2d_image "type.2d.image"
|
|
OpName %out_var_SV_TARGET "out.var.SV_TARGET"
|
|
OpName %main "main"
|
|
OpName %type_sampled_image "type.sampled.image"
|
|
OpDecorate %out_var_SV_TARGET Location 0
|
|
)";
|
|
ss << shader_decorate_instructions;
|
|
ss << R"(
|
|
%float = OpTypeFloat 32
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%v2float = OpTypeVector %float 2
|
|
%12 = OpConstantComposite %v2float %float_1 %float_2
|
|
%float_10 = OpConstant %float 10
|
|
%int = OpTypeInt 32 1
|
|
%int_2 = OpConstant %int 2
|
|
%v2int = OpTypeVector %int 2
|
|
%17 = OpConstantComposite %v2int %int_2 %int_2
|
|
%type_sampler = OpTypeSampler
|
|
%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
|
|
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
|
|
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%void = OpTypeVoid
|
|
%23 = OpTypeFunction %void
|
|
%type_sampled_image = OpTypeSampledImage %type_2d_image
|
|
)";
|
|
ss << shader_image_and_sampler_variables;
|
|
ss << R"(
|
|
%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
|
|
%main = OpFunction %void None %23
|
|
%24 = OpLabel
|
|
)";
|
|
ss << shader_body;
|
|
ss << R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
return ss.str();
|
|
}
|
|
|
|
using ConvertToSampledImageTest = PassTest<::testing::Test>;
|
|
|
|
TEST_F(ConvertToSampledImageTest, Texture2DAndSamplerToSampledImage) {
|
|
const std::string shader = BuildShader(
|
|
R"(
|
|
OpDecorate %sam DescriptorSet 0
|
|
OpDecorate %sam Binding 5
|
|
OpDecorate %texture DescriptorSet 0
|
|
OpDecorate %texture Binding 5
|
|
)",
|
|
R"(
|
|
; CHECK-NOT: OpVariable %_ptr_UniformConstant_type_2d_image
|
|
|
|
; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant
|
|
%sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
|
|
%texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
|
|
)",
|
|
R"(
|
|
; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]]
|
|
; CHECK: OpImageSampleExplicitLod %v4float [[load]]
|
|
%25 = OpLoad %type_2d_image %texture
|
|
%26 = OpLoad %type_sampler %sam
|
|
%27 = OpSampledImage %type_sampled_image %25 %26
|
|
%28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
|
|
OpStore %out_var_SV_TARGET %28
|
|
)");
|
|
|
|
auto result = SinglePassRunAndMatch<ConvertToSampledImagePass>(
|
|
shader, /* do_validate = */ true,
|
|
VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
|
|
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
TEST_F(ConvertToSampledImageTest, Texture2DToSampledImage) {
|
|
const std::string shader = BuildShader(
|
|
R"(
|
|
OpDecorate %sam DescriptorSet 0
|
|
OpDecorate %sam Binding 2
|
|
OpDecorate %texture DescriptorSet 0
|
|
OpDecorate %texture Binding 5
|
|
)",
|
|
R"(
|
|
; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant
|
|
%sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
|
|
%texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
|
|
)",
|
|
R"(
|
|
; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]]
|
|
; CHECK: [[image_extraction:%\w+]] = OpImage %type_2d_image [[load]]
|
|
; CHECK: OpSampledImage %type_sampled_image [[image_extraction]]
|
|
%25 = OpLoad %type_2d_image %texture
|
|
%26 = OpLoad %type_sampler %sam
|
|
%27 = OpSampledImage %type_sampled_image %25 %26
|
|
%28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
|
|
OpStore %out_var_SV_TARGET %28
|
|
)");
|
|
|
|
auto result = SinglePassRunAndMatch<ConvertToSampledImagePass>(
|
|
shader, /* do_validate = */ true,
|
|
VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
|
|
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
|
|
}
|
|
|
|
TEST_F(ConvertToSampledImageTest, SamplerToSampledImage) {
|
|
const std::string shader = BuildShader(
|
|
R"(
|
|
OpDecorate %sam DescriptorSet 0
|
|
OpDecorate %sam Binding 2
|
|
OpDecorate %texture DescriptorSet 0
|
|
OpDecorate %texture Binding 5
|
|
)",
|
|
R"(
|
|
%sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
|
|
%texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
|
|
)",
|
|
R"(
|
|
%25 = OpLoad %type_2d_image %texture
|
|
%26 = OpLoad %type_sampler %sam
|
|
%27 = OpSampledImage %type_sampled_image %25 %26
|
|
%28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
|
|
OpStore %out_var_SV_TARGET %28
|
|
)");
|
|
|
|
auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
|
|
shader, /* skip_nop = */ false,
|
|
VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}});
|
|
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
|
|
}
|
|
|
|
TEST_F(ConvertToSampledImageTest, TwoImagesWithDuplicatedDescriptorSetBinding) {
|
|
const std::string shader = BuildShader(
|
|
R"(
|
|
OpDecorate %sam DescriptorSet 0
|
|
OpDecorate %sam Binding 2
|
|
OpDecorate %texture0 DescriptorSet 0
|
|
OpDecorate %texture0 Binding 5
|
|
OpDecorate %texture1 DescriptorSet 0
|
|
OpDecorate %texture1 Binding 5
|
|
)",
|
|
R"(
|
|
%sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
|
|
%texture0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
|
|
%texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
|
|
)",
|
|
R"(
|
|
%25 = OpLoad %type_2d_image %texture0
|
|
%26 = OpLoad %type_sampler %sam
|
|
%27 = OpSampledImage %type_sampled_image %25 %26
|
|
%28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
|
|
OpStore %out_var_SV_TARGET %28
|
|
)");
|
|
|
|
auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
|
|
shader, /* skip_nop = */ false,
|
|
VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
|
|
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
|
|
}
|
|
|
|
TEST_F(ConvertToSampledImageTest,
|
|
TwoSamplersWithDuplicatedDescriptorSetBinding) {
|
|
const std::string shader = BuildShader(
|
|
R"(
|
|
OpDecorate %sam0 DescriptorSet 0
|
|
OpDecorate %sam0 Binding 2
|
|
OpDecorate %sam1 DescriptorSet 0
|
|
OpDecorate %sam1 Binding 2
|
|
OpDecorate %texture DescriptorSet 0
|
|
OpDecorate %texture Binding 5
|
|
)",
|
|
R"(
|
|
%sam0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
|
|
%sam1 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
|
|
%texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
|
|
)",
|
|
R"(
|
|
%25 = OpLoad %type_2d_image %texture
|
|
%26 = OpLoad %type_sampler %sam0
|
|
%27 = OpSampledImage %type_sampled_image %25 %26
|
|
%28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
|
|
OpStore %out_var_SV_TARGET %28
|
|
)");
|
|
|
|
auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
|
|
shader, /* skip_nop = */ false,
|
|
VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}});
|
|
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace opt
|
|
} // namespace spvtools
|