| // Copyright 2020 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <memory> |
| #include <unordered_set> |
| |
| #include "gmock/gmock.h" |
| |
| #include "src/tint/lang/core/fluent_types.h" // IWYU pragma: export |
| #include "src/tint/lang/wgsl/inspector/entry_point.h" |
| #include "src/tint/lang/wgsl/inspector/inspector.h" |
| #include "src/tint/lang/wgsl/reader/reader.h" |
| |
| using namespace tint::core::number_suffixes; // NOLINT |
| using namespace tint::core::fluent_types; // NOLINT |
| |
| namespace tint::inspector { |
| namespace { |
| |
| /// Utility class for building programs in inspector tests |
| class TestHelper { |
| public: |
| /// Create a Program with Inspector from the provided WGSL shader. |
| /// Should only be called once per test and cannot be used with Build. |
| /// @param shader a WGSL shader |
| /// @returns a reference to the Inspector for the built Program. |
| Inspector& Initialize(std::string shader) { |
| if (inspector_) { |
| return *inspector_; |
| } |
| |
| wgsl::reader::Options options; |
| options.allowed_features = wgsl::AllowedFeatures::Everything(); |
| file_ = std::make_unique<Source::File>("test", shader); |
| program_ = std::make_unique<Program>(wgsl::reader::Parse(file_.get(), options)); |
| if (!program_->IsValid()) { |
| ADD_FAILURE() << program_->Diagnostics(); |
| } |
| inspector_ = std::make_unique<Inspector>(*program_); |
| return *inspector_; |
| } |
| |
| protected: |
| /// File created from input shader and used to create Program. |
| std::unique_ptr<Source::File> file_; |
| /// Program created by this runner. |
| std::unique_ptr<Program> program_; |
| /// Inspector for |program_| |
| std::unique_ptr<Inspector> inspector_; |
| }; |
| |
| class InspectorTest : public TestHelper, public testing::Test {}; |
| |
| template <typename T> |
| class InspectorTestWithParam : public TestHelper, public testing::TestWithParam<T> {}; |
| |
| using InspectorGetEntryPointTest = InspectorTest; |
| using InspectorOverridesTest = InspectorTest; |
| using InspectorGetConstantNameToIdMapTest = InspectorTest; |
| using InspectorGetResourceBindingsTest = InspectorTest; |
| using InspectorGetUsedExtensionNamesTest = InspectorTest; |
| using InspectorGetBlendSrcTest = InspectorTest; |
| using InspectorSubgroupMatrixTest = InspectorTest; |
| using InspectorTextureTest = InspectorTest; |
| |
| // This is a catch all for shaders that have demonstrated regressions/crashes in the wild. |
| using InspectorRegressionTest = InspectorTest; |
| |
| TEST_F(InspectorGetEntryPointTest, NoFunctions) { |
| auto* src = R"( |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| EXPECT_EQ(0u, result.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, NoEntryPoints) { |
| auto* src = R"( |
| fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| EXPECT_EQ(0u, result.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OneEntryPoint) { |
| auto* src = R"( |
| @fragment fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ("foo", result[0].name); |
| EXPECT_EQ(PipelineStage::kFragment, result[0].stage); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) { |
| auto* src = R"( |
| @fragment fn foo() {} |
| @compute @workgroup_size(1i) fn bar() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(2u, result.size()); |
| EXPECT_EQ("foo", result[0].name); |
| EXPECT_EQ(PipelineStage::kFragment, result[0].stage); |
| EXPECT_EQ("bar", result[1].name); |
| EXPECT_EQ(PipelineStage::kCompute, result[1].stage); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) { |
| auto* src = R"( |
| fn func() {} |
| |
| @compute @workgroup_size(1i) |
| fn foo() { func(); } |
| |
| @fragment fn bar() { func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| EXPECT_FALSE(inspector.has_error()); |
| |
| ASSERT_EQ(2u, result.size()); |
| EXPECT_EQ("foo", result[0].name); |
| EXPECT_EQ(PipelineStage::kCompute, result[0].stage); |
| EXPECT_EQ("bar", result[1].name); |
| EXPECT_EQ(PipelineStage::kFragment, result[1].stage); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) { |
| auto* src = R"( |
| @compute @workgroup_size(8i, 2i, 1i) fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| auto workgroup_size = result[0].workgroup_size; |
| ASSERT_TRUE(workgroup_size.has_value()); |
| EXPECT_EQ(8u, workgroup_size->x); |
| EXPECT_EQ(2u, workgroup_size->y); |
| EXPECT_EQ(1u, workgroup_size->z); |
| } |
| |
| // Test that immediate_data_size is zero if there are no immediate data. |
| TEST_F(InspectorGetEntryPointTest, ImmediateDataSizeNone) { |
| auto* src = R"( |
| @fragment fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].immediate_data_size); |
| } |
| |
| // Test that immediate_data_size is 4 (bytes) if there is a single F32 immediate data. |
| TEST_F(InspectorGetEntryPointTest, ImmediateDataSizeOneWord) { |
| auto* src = R"( |
| enable chromium_experimental_immediate; |
| |
| var<immediate> pc: f32; |
| |
| @fragment fn foo() { _ = pc; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(4u, result[0].immediate_data_size); |
| } |
| |
| // Test that immediate_data_size is 12 (bytes) if there is a struct containing one |
| // each of i32, f32 and u32. |
| TEST_F(InspectorGetEntryPointTest, ImmediateDataSizeThreeWords) { |
| auto* src = R"( |
| enable chromium_experimental_immediate; |
| |
| struct S { |
| a: i32, |
| b: f32, |
| c: u32, |
| } |
| var<immediate> pc : S; |
| |
| @fragment fn foo() { _ = pc; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(12u, result[0].immediate_data_size); |
| } |
| |
| // Test that immediate_data_size is 4 (bytes) if there are two immediate data, |
| // one used by the entry point containing an f32, and one unused by the entry |
| // point containing a struct of size 12 bytes. |
| TEST_F(InspectorGetEntryPointTest, ImmediateDataSizeTwoConstants) { |
| auto* src = R"( |
| enable chromium_experimental_immediate; |
| |
| struct S { |
| a: i32, |
| b: f32, |
| c: u32, |
| } |
| var<immediate> unused : S; |
| var<immediate> pc: f32; |
| |
| @fragment fn foo() { _ = pc; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| // Check that the result only includes the single f32 immediate data. |
| EXPECT_EQ(4u, result[0].immediate_data_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) { |
| auto* src = R"( |
| @compute @workgroup_size(8i, 2i, 1i) |
| fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| auto workgroup_size = result[0].workgroup_size; |
| ASSERT_TRUE(workgroup_size.has_value()); |
| EXPECT_EQ(8u, workgroup_size->x); |
| EXPECT_EQ(2u, workgroup_size->y); |
| EXPECT_EQ(1u, workgroup_size->z); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, WorkgroupStorageSizeEmpty) { |
| auto* src = R"( |
| @compute @workgroup_size(1i) |
| fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].workgroup_storage_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, WorkgroupStorageSizeSimple) { |
| auto* src = R"( |
| var<workgroup> wg_f32: f32; |
| var<workgroup> wg_i32: i32; |
| |
| fn f32_func() { _ = wg_f32; } |
| fn i32_func() { _ = wg_i32; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| f32_func(); |
| i32_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(32u, result[0].workgroup_storage_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, WorkgroupStorageSizeCompoundTypes) { |
| auto* src = R"( |
| // This struct should occupy 68 bytes. |
| struct WgStruct { |
| a: i32, |
| b: array<i32, 16>, |
| } |
| var<workgroup> wg_struct_var: WgStruct; |
| |
| fn wg_struct_func() { _ = wg_struct_var.a; } |
| |
| // Plus another 4 bytes from this other workgroup-class f32. |
| var<workgroup> wg_f32: f32; |
| fn f32_func() { _ = wg_f32; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| wg_struct_func(); |
| f32_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(96u, result[0].workgroup_storage_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, WorkgroupStorageSizeAlignmentPadding) { |
| auto* src = R"( |
| // vec3<f32> has an alignment of 16 but a size of 12. We leverage this to test |
| // that our padded size calculation for workgroup storage is accurate. |
| var<workgroup> wg_vec3: vec3f; |
| |
| fn wg_func() { _ = wg_vec3; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| wg_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(16u, result[0].workgroup_storage_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, WorkgroupStorageSizeStructAlignment) { |
| auto* src = R"( |
| // Per WGSL spec, a struct's size is the offset its last member plus the size |
| // of its last member, rounded up to the alignment of its largest member. So |
| // here the struct is expected to occupy 1024 bytes of workgroup storage. |
| struct WgStruct { |
| @align(1024i) a: f32, |
| } |
| var<workgroup> wg_struct_var: WgStruct; |
| |
| fn wg_struct_func() { _ = wg_struct_var.a; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| wg_struct_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(1024u, result[0].workgroup_storage_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, NoInOutVariables) { |
| auto* src = R"( |
| fn func() {} |
| @fragment fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].input_variables.size()); |
| EXPECT_EQ(0u, result[0].output_variables.size()); |
| } |
| |
| std::string GetType(ComponentType component, CompositionType composition) { |
| std::string comp; |
| switch (component) { |
| case ComponentType::kF32: |
| comp = "f32"; |
| break; |
| case ComponentType::kI32: |
| comp = "i32"; |
| break; |
| case ComponentType::kU32: |
| comp = "u32"; |
| break; |
| case ComponentType::kF16: |
| comp = "f16"; |
| break; |
| case ComponentType::kUnknown: |
| TINT_UNREACHABLE(); |
| } |
| |
| uint32_t n; |
| switch (composition) { |
| case CompositionType::kScalar: |
| return comp; |
| case CompositionType::kVec2: |
| n = 2; |
| break; |
| case CompositionType::kVec3: |
| n = 3; |
| break; |
| case CompositionType::kVec4: |
| n = 4; |
| break; |
| default: |
| TINT_UNREACHABLE(); |
| } |
| return std::string("vec") + std::to_string(n) + "<" + comp + ">"; |
| } |
| |
| typedef std::tuple<inspector::ComponentType, inspector::CompositionType> |
| InspectorGetEntryPointComponentAndCompositionTestParams; |
| using InspectorGetEntryPointComponentAndCompositionTest = |
| InspectorTestWithParam<InspectorGetEntryPointComponentAndCompositionTestParams>; |
| |
| TEST_P(InspectorGetEntryPointComponentAndCompositionTest, Test) { |
| ComponentType component; |
| CompositionType composition; |
| std::tie(component, composition) = GetParam(); |
| |
| std::string src = ""; |
| if (component == ComponentType::kF16) { |
| src += "enable f16;\n"; |
| } |
| |
| auto tint_type = GetType(component, composition); |
| src += R"( |
| @fragment |
| fn foo(@location(0u) @interpolate(flat) in_var: )" + |
| tint_type + ") -> @location(0) " + tint_type + R"( { |
| return in_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| ASSERT_EQ(1u, result[0].input_variables.size()); |
| EXPECT_EQ("in_var", result[0].input_variables[0].name); |
| EXPECT_EQ("in_var", result[0].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].input_variables[0].attributes.location); |
| EXPECT_EQ(component, result[0].input_variables[0].component_type); |
| |
| ASSERT_EQ(1u, result[0].output_variables.size()); |
| EXPECT_EQ("<retval>", result[0].output_variables[0].name); |
| EXPECT_EQ("", result[0].output_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.location); |
| EXPECT_EQ(component, result[0].output_variables[0].component_type); |
| } |
| INSTANTIATE_TEST_SUITE_P(InspectorGetEntryPointTest, |
| InspectorGetEntryPointComponentAndCompositionTest, |
| testing::Combine(testing::Values(ComponentType::kF32, |
| ComponentType::kI32, |
| ComponentType::kU32, |
| ComponentType::kF16), |
| testing::Values(CompositionType::kScalar, |
| CompositionType::kVec2, |
| CompositionType::kVec3, |
| CompositionType::kVec4))); |
| |
| TEST_F(InspectorGetEntryPointTest, MultipleInOutVariables) { |
| auto* src = R"( |
| enable chromium_experimental_framebuffer_fetch; |
| |
| @fragment |
| fn foo(@location(0u) @interpolate(flat) in_var0: u32, @location(1u) @interpolate(flat) in_var1: u32, @color(2u) in_var4: u32) -> @location(0u) u32 { |
| return in_var0; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| ASSERT_EQ(3u, result[0].input_variables.size()); |
| EXPECT_EQ("in_var0", result[0].input_variables[0].name); |
| EXPECT_EQ("in_var0", result[0].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].input_variables[0].attributes.location); |
| EXPECT_EQ(std::nullopt, result[0].input_variables[0].attributes.color); |
| EXPECT_EQ(InterpolationType::kFlat, result[0].input_variables[0].interpolation_type); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[0].component_type); |
| EXPECT_EQ("in_var1", result[0].input_variables[1].name); |
| EXPECT_EQ("in_var1", result[0].input_variables[1].variable_name); |
| EXPECT_EQ(1u, result[0].input_variables[1].attributes.location); |
| EXPECT_EQ(std::nullopt, result[0].input_variables[1].attributes.color); |
| EXPECT_EQ(InterpolationType::kFlat, result[0].input_variables[1].interpolation_type); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[1].component_type); |
| EXPECT_EQ("in_var4", result[0].input_variables[2].name); |
| EXPECT_EQ("in_var4", result[0].input_variables[2].variable_name); |
| EXPECT_EQ(std::nullopt, result[0].input_variables[2].attributes.location); |
| EXPECT_EQ(2u, result[0].input_variables[2].attributes.color); |
| EXPECT_EQ(InterpolationType::kPerspective, result[0].input_variables[2].interpolation_type); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[2].component_type); |
| |
| ASSERT_EQ(1u, result[0].output_variables.size()); |
| EXPECT_EQ("<retval>", result[0].output_variables[0].name); |
| EXPECT_EQ("", result[0].output_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.location); |
| EXPECT_EQ(std::nullopt, result[0].output_variables[0].attributes.color); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[0].component_type); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) { |
| auto* src = R"( |
| @fragment |
| fn foo(@location(0u) @interpolate(flat) in_var_foo: u32) -> @location(0) u32 { |
| return in_var_foo; |
| } |
| |
| @fragment |
| fn bar(@location(0u) @interpolate(flat) in_var_bar: u32) -> @location(1u) u32 { |
| return in_var_bar; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(2u, result.size()); |
| |
| ASSERT_EQ(1u, result[0].input_variables.size()); |
| EXPECT_EQ("in_var_foo", result[0].input_variables[0].name); |
| EXPECT_EQ("in_var_foo", result[0].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].input_variables[0].attributes.location); |
| EXPECT_EQ(InterpolationType::kFlat, result[0].input_variables[0].interpolation_type); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[0].component_type); |
| |
| ASSERT_EQ(1u, result[0].output_variables.size()); |
| EXPECT_EQ("<retval>", result[0].output_variables[0].name); |
| EXPECT_EQ("", result[0].output_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[0].component_type); |
| |
| ASSERT_EQ(1u, result[1].input_variables.size()); |
| EXPECT_EQ("in_var_bar", result[1].input_variables[0].name); |
| EXPECT_EQ("in_var_bar", result[1].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[1].input_variables[0].attributes.location); |
| EXPECT_EQ(InterpolationType::kFlat, result[1].input_variables[0].interpolation_type); |
| EXPECT_EQ(ComponentType::kU32, result[1].input_variables[0].component_type); |
| |
| ASSERT_EQ(1u, result[1].output_variables.size()); |
| EXPECT_EQ("<retval>", result[1].output_variables[0].name); |
| EXPECT_EQ("", result[1].output_variables[0].variable_name); |
| EXPECT_EQ(1u, result[1].output_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[1].output_variables[0].component_type); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, BuiltInsNotStageVariables) { |
| auto* src = R"( |
| @fragment |
| fn foo(@builtin(sample_index) in_var0: u32, @location(0u) in_var1: f32) -> @builtin(frag_depth) f32 { |
| return in_var1; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| ASSERT_EQ(1u, result[0].input_variables.size()); |
| EXPECT_EQ("in_var1", result[0].input_variables[0].name); |
| EXPECT_EQ("in_var1", result[0].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].input_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kF32, result[0].input_variables[0].component_type); |
| |
| ASSERT_EQ(0u, result[0].output_variables.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, InOutStruct) { |
| auto* src = R"( |
| struct Interface { |
| @location(0u) @interpolate(flat) a: u32, |
| @location(1u) @interpolate(flat) b: u32, |
| } |
| @fragment |
| fn foo(param: Interface) -> Interface { |
| return param; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| ASSERT_EQ(2u, result[0].input_variables.size()); |
| EXPECT_EQ("param.a", result[0].input_variables[0].name); |
| EXPECT_EQ("a", result[0].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].input_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[0].component_type); |
| EXPECT_EQ("param.b", result[0].input_variables[1].name); |
| EXPECT_EQ("b", result[0].input_variables[1].variable_name); |
| EXPECT_EQ(1u, result[0].input_variables[1].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[1].component_type); |
| |
| ASSERT_EQ(2u, result[0].output_variables.size()); |
| EXPECT_EQ("<retval>.a", result[0].output_variables[0].name); |
| EXPECT_EQ("a", result[0].output_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[0].component_type); |
| EXPECT_EQ("<retval>.b", result[0].output_variables[1].name); |
| EXPECT_EQ("b", result[0].output_variables[1].variable_name); |
| EXPECT_EQ(1u, result[0].output_variables[1].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[1].component_type); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutSharedStruct) { |
| auto* src = R"( |
| struct Interface { |
| @location(0u) @interpolate(flat) a: u32, |
| @location(1u) @interpolate(flat) b: u32, |
| } |
| @fragment |
| fn foo() -> Interface { |
| return Interface(); |
| } |
| |
| @fragment |
| fn bar(param: Interface) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(2u, result.size()); |
| |
| ASSERT_EQ(0u, result[0].input_variables.size()); |
| |
| ASSERT_EQ(2u, result[0].output_variables.size()); |
| EXPECT_EQ("<retval>.a", result[0].output_variables[0].name); |
| EXPECT_EQ("a", result[0].output_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[0].component_type); |
| EXPECT_EQ("<retval>.b", result[0].output_variables[1].name); |
| EXPECT_EQ("b", result[0].output_variables[1].variable_name); |
| EXPECT_EQ(1u, result[0].output_variables[1].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[1].component_type); |
| |
| ASSERT_EQ(2u, result[1].input_variables.size()); |
| EXPECT_EQ("param.a", result[1].input_variables[0].name); |
| EXPECT_EQ("a", result[1].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[1].input_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[1].input_variables[0].component_type); |
| EXPECT_EQ("param.b", result[1].input_variables[1].name); |
| EXPECT_EQ("b", result[1].input_variables[1].variable_name); |
| EXPECT_EQ(1u, result[1].input_variables[1].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[1].input_variables[1].component_type); |
| |
| ASSERT_EQ(0u, result[1].output_variables.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, MixInOutVariablesAndStruct) { |
| auto* src = R"( |
| struct struct_a { |
| @location(0u) @interpolate(flat) a: u32, |
| @location(1u) @interpolate(flat) b: u32, |
| } |
| struct struct_b { |
| @location(2u) @interpolate(flat) a: u32, |
| } |
| |
| @fragment |
| fn foo(param_a: struct_a, param_b: struct_b, @location(3u) param_c: f32, @location(4u) param_d: f32) -> struct_a { |
| return param_a; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| ASSERT_EQ(5u, result[0].input_variables.size()); |
| EXPECT_EQ("param_a.a", result[0].input_variables[0].name); |
| EXPECT_EQ("a", result[0].input_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].input_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[0].component_type); |
| EXPECT_EQ("param_a.b", result[0].input_variables[1].name); |
| EXPECT_EQ("b", result[0].input_variables[1].variable_name); |
| EXPECT_EQ(1u, result[0].input_variables[1].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[1].component_type); |
| EXPECT_EQ("param_b.a", result[0].input_variables[2].name); |
| EXPECT_EQ("a", result[0].input_variables[2].variable_name); |
| EXPECT_EQ(2u, result[0].input_variables[2].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].input_variables[2].component_type); |
| EXPECT_EQ("param_c", result[0].input_variables[3].name); |
| EXPECT_EQ("param_c", result[0].input_variables[3].variable_name); |
| EXPECT_EQ(3u, result[0].input_variables[3].attributes.location); |
| EXPECT_EQ(ComponentType::kF32, result[0].input_variables[3].component_type); |
| EXPECT_EQ("param_d", result[0].input_variables[4].name); |
| EXPECT_EQ("param_d", result[0].input_variables[4].variable_name); |
| EXPECT_EQ(4u, result[0].input_variables[4].attributes.location); |
| EXPECT_EQ(ComponentType::kF32, result[0].input_variables[4].component_type); |
| |
| ASSERT_EQ(2u, result[0].output_variables.size()); |
| EXPECT_EQ("<retval>.a", result[0].output_variables[0].name); |
| EXPECT_EQ("a", result[0].output_variables[0].variable_name); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[0].component_type); |
| EXPECT_EQ("<retval>.b", result[0].output_variables[1].name); |
| EXPECT_EQ("b", result[0].output_variables[1].variable_name); |
| EXPECT_EQ(1u, result[0].output_variables[1].attributes.location); |
| EXPECT_EQ(ComponentType::kU32, result[0].output_variables[1].component_type); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideUnreferenced) { |
| auto* src = R"( |
| override foo: f32; |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].overrides.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByEntryPoint) { |
| auto* src = R"( |
| override foo: f32; |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { _ = foo; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("foo", result[0].overrides[0].name); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByCallee) { |
| auto* src = R"( |
| override foo: f32; |
| |
| fn callee_func() { _ = foo; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| callee_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("foo", result[0].overrides[0].name); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideSomeReferenced) { |
| auto* src = R"( |
| @id(1) override foo: f32; |
| @id(2) override bar: f32; |
| |
| fn callee_fn() { _ = foo; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| callee_fn(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("foo", result[0].overrides[0].name); |
| EXPECT_EQ(1, result[0].overrides[0].id.value); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedIndirectly) { |
| auto* src = R"( |
| override foo: f32; |
| override bar: f32 = 2 * foo; |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { _ = bar; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(2u, result[0].overrides.size()); |
| EXPECT_EQ("bar", result[0].overrides[0].name); |
| EXPECT_TRUE(result[0].overrides[0].is_initialized); |
| EXPECT_EQ("foo", result[0].overrides[1].name); |
| EXPECT_FALSE(result[0].overrides[1].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedIndirectly_ViaPrivateInitializer) { |
| auto* src = R"( |
| override foo: f32; |
| var<private> bar: f32 = 2 * foo; |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { _ = bar; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("foo", result[0].overrides[0].name); |
| EXPECT_FALSE(result[0].overrides[0].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedIndirectly_MultipleEntryPoints) { |
| auto* src = R"( |
| override foo1: f32; |
| override bar1: f32 = 2 * foo1; |
| @compute @workgroup_size(1i) |
| fn ep_func1() { _ = bar1; } |
| |
| override foo2: f32; |
| override bar2: f32 = 2 * foo2; |
| @compute @workgroup_size(1i) |
| fn ep_func2() { _ = bar2; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(2u, result.size()); |
| |
| ASSERT_EQ(2u, result[0].overrides.size()); |
| EXPECT_EQ("bar1", result[0].overrides[0].name); |
| EXPECT_TRUE(result[0].overrides[0].is_initialized); |
| EXPECT_EQ("foo1", result[0].overrides[1].name); |
| EXPECT_FALSE(result[0].overrides[1].is_initialized); |
| |
| ASSERT_EQ(2u, result[1].overrides.size()); |
| EXPECT_EQ("bar2", result[1].overrides[0].name); |
| EXPECT_TRUE(result[1].overrides[0].is_initialized); |
| EXPECT_EQ("foo2", result[1].overrides[1].name); |
| EXPECT_FALSE(result[1].overrides[1].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByAttribute) { |
| auto* src = R"( |
| override wgsize: u32; |
| @compute @workgroup_size(wgsize) |
| fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("wgsize", result[0].overrides[0].name); |
| EXPECT_FALSE(result[0].overrides[0].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByAttributeIndirectly) { |
| auto* src = R"( |
| override foo: u32; |
| override bar: u32 = 2 * foo; |
| @compute @workgroup_size(2 * bar) |
| fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(2u, result[0].overrides.size()); |
| EXPECT_EQ("bar", result[0].overrides[0].name); |
| EXPECT_TRUE(result[0].overrides[0].is_initialized); |
| EXPECT_EQ("foo", result[0].overrides[1].name); |
| EXPECT_FALSE(result[0].overrides[1].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByArraySize) { |
| auto* src = R"( |
| override size: u32; |
| var<workgroup> v: array<f32, size>; |
| |
| @compute @workgroup_size(1i) |
| fn ep() { _ = v[0]; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("size", result[0].overrides[0].name); |
| EXPECT_FALSE(result[0].overrides[0].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByArraySizeIndirectly) { |
| auto* src = R"( |
| override foo: u32; |
| override bar: u32 = 2 * foo; |
| var<workgroup> v: array<f32, 2 * bar>; |
| |
| @compute @workgroup_size(1i) |
| fn ep() { _ = v[0]; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(2u, result[0].overrides.size()); |
| EXPECT_EQ("bar", result[0].overrides[0].name); |
| EXPECT_TRUE(result[0].overrides[0].is_initialized); |
| EXPECT_EQ("foo", result[0].overrides[1].name); |
| EXPECT_FALSE(result[0].overrides[1].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideReferencedByArraySizeViaAlias) { |
| auto* src = R"( |
| override foo: u32; |
| override bar: u32 = foo; |
| alias MyArray = array<f32, 2 * bar>; |
| |
| override zoo: u32; |
| alias MyArrayUnused = array<f32, 2 * zoo>; |
| |
| var<workgroup> v: MyArray; |
| |
| @compute @workgroup_size(1i) |
| fn ep() { _ = v[0]; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(2u, result[0].overrides.size()); |
| EXPECT_EQ("bar", result[0].overrides[0].name); |
| EXPECT_TRUE(result[0].overrides[0].is_initialized); |
| EXPECT_EQ("foo", result[0].overrides[1].name); |
| EXPECT_FALSE(result[0].overrides[1].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideTypes) { |
| auto* src = R"( |
| enable f16; |
| |
| override bool_var: bool; |
| override float_var: f32; |
| override u32_var: u32; |
| override i32_var: i32; |
| override f16_var: f16; |
| |
| fn bool_func() { _ = bool_var; } |
| fn float_func() { _ = float_var; } |
| fn u32_func() { _ = u32_var; } |
| fn i32_func() { _ = i32_var; } |
| fn f16_func() { _ = f16_var; } |
| |
| @compute @workgroup_size(1) |
| fn ep_func() { |
| bool_func(); |
| float_func(); |
| u32_func(); |
| i32_func(); |
| f16_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(5u, result[0].overrides.size()); |
| EXPECT_EQ("bool_var", result[0].overrides[0].name); |
| EXPECT_EQ(inspector::Override::Type::kBool, result[0].overrides[0].type); |
| EXPECT_EQ("float_var", result[0].overrides[1].name); |
| EXPECT_EQ(inspector::Override::Type::kFloat32, result[0].overrides[1].type); |
| EXPECT_EQ("u32_var", result[0].overrides[2].name); |
| EXPECT_EQ(inspector::Override::Type::kUint32, result[0].overrides[2].type); |
| EXPECT_EQ("i32_var", result[0].overrides[3].name); |
| EXPECT_EQ(inspector::Override::Type::kInt32, result[0].overrides[3].type); |
| EXPECT_EQ("f16_var", result[0].overrides[4].name); |
| EXPECT_EQ(inspector::Override::Type::kFloat16, result[0].overrides[4].type); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideInitialized) { |
| auto* src = R"( |
| override foo: f32 = 0f; |
| @compute @workgroup_size(1i) |
| fn ep_func() { _ = foo; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("foo", result[0].overrides[0].name); |
| EXPECT_TRUE(result[0].overrides[0].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideUninitialized) { |
| auto* src = R"( |
| override foo: f32; |
| @compute @workgroup_size(1i) |
| fn ep_func() { _ = foo; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].overrides.size()); |
| EXPECT_EQ("foo", result[0].overrides[0].name); |
| |
| EXPECT_FALSE(result[0].overrides[0].is_initialized); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OverrideNumericIDSpecified) { |
| auto* src = R"( |
| override foo_no_id: f32; |
| @id(1234) override foo_id: f32; |
| |
| fn no_id_func() { _ = foo_no_id; } |
| fn id_func() { _ = foo_id; } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func() { |
| no_id_func(); |
| id_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(2u, result[0].overrides.size()); |
| EXPECT_EQ("foo_no_id", result[0].overrides[0].name); |
| EXPECT_EQ("foo_id", result[0].overrides[1].name); |
| EXPECT_EQ(1234, result[0].overrides[1].id.value); |
| |
| EXPECT_FALSE(result[0].overrides[0].is_id_specified); |
| EXPECT_TRUE(result[0].overrides[1].is_id_specified); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, NonOverrideSkipped) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| } |
| @binding(0) @group(0) var<uniform> foo_ub: foo_type; |
| fn ub_func() { _ = foo_ub.a; } |
| |
| @fragment |
| fn ep_func() { |
| ub_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].overrides.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, BuiltinNotReferenced) { |
| auto* src = R"( |
| @fragment |
| fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_FALSE(result[0].input_sample_mask_used); |
| EXPECT_FALSE(result[0].output_sample_mask_used); |
| EXPECT_FALSE(result[0].front_facing_used); |
| EXPECT_FALSE(result[0].sample_index_used); |
| EXPECT_FALSE(result[0].num_workgroups_used); |
| EXPECT_FALSE(result[0].frag_depth_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, InputSampleMaskSimpleReferenced) { |
| auto* src = R"( |
| @fragment |
| fn ep_func(@builtin(sample_mask) in_var: u32) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].input_sample_mask_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, InputSampleMaskStructReferenced) { |
| auto* src = R"( |
| struct in_struct { |
| @builtin(sample_mask) inner_position: u32, |
| } |
| |
| @fragment |
| fn ep_func(in_var: in_struct) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].input_sample_mask_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OutputSampleMaskSimpleReferenced) { |
| auto* src = R"( |
| @fragment |
| fn ep_func(@builtin(sample_mask) in_var: u32) -> @builtin(sample_mask) u32 { |
| return in_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].output_sample_mask_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, OutputSampleMaskStructReferenced) { |
| auto* src = R"( |
| struct out_struct { |
| @builtin(sample_mask) inner_sample_mask: u32, |
| } |
| |
| @fragment |
| fn ep_func() -> out_struct { |
| var out_var: out_struct; |
| return out_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].output_sample_mask_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, FrontFacingSimpleReferenced) { |
| auto* src = R"( |
| @fragment |
| fn ep_func(@builtin(front_facing) in_var: bool) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].front_facing_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, FrontFacingStructReferenced) { |
| auto* src = R"( |
| struct in_struct { |
| @builtin(front_facing) inner_position: bool, |
| } |
| @fragment |
| fn ep_func(in_var: in_struct) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].front_facing_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, SampleIndexSimpleReferenced) { |
| auto* src = R"( |
| @fragment |
| fn ep_func(@builtin(sample_index) in_var: u32) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].sample_index_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, SampleIndexStructReferenced) { |
| auto* src = R"( |
| struct in_struct { |
| @builtin(sample_index) inner_position: u32, |
| } |
| |
| @fragment |
| fn ep_func(in_var: in_struct) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].sample_index_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, NumWorkgroupsSimpleReferenced) { |
| auto* src = R"( |
| @compute @workgroup_size(1i) |
| fn ep_func(@builtin(num_workgroups) in_var: vec3u) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].num_workgroups_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, NumWorkgroupsStructReferenced) { |
| auto* src = R"( |
| struct in_struct { |
| @builtin(num_workgroups) inner_position: vec3u, |
| } |
| |
| @compute @workgroup_size(1i) |
| fn ep_func(in_var: in_struct) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].num_workgroups_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, FragDepthSimpleReferenced) { |
| auto* src = R"( |
| @fragment |
| fn ep_func() -> @builtin(frag_depth) f32 { |
| return 0f; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].frag_depth_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, FragDepthStructReferenced) { |
| auto* src = R"( |
| struct out_struct { |
| @builtin(frag_depth) inner_frag_depth: f32, |
| } |
| @fragment |
| fn ep_func() -> out_struct { |
| var out_var: out_struct; |
| return out_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].frag_depth_used); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, ClipDistancesReferenced) { |
| auto* src = R"( |
| enable clip_distances; |
| |
| struct out_struct { |
| @builtin(clip_distances) inner_clip_distances: array<f32, 8>, |
| @builtin(position) inner_position: vec4f, |
| } |
| |
| @vertex |
| fn ep_func() -> out_struct { |
| var out_var: out_struct; |
| return out_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].clip_distances_size.has_value()); |
| EXPECT_EQ(8u, *result[0].clip_distances_size); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, ClipDistancesNotReferenced) { |
| auto* src = R"( |
| struct out_struct { |
| @builtin(position) inner_position: vec4f, |
| } |
| @vertex |
| fn ep_func() -> out_struct { |
| var out_var : out_struct; |
| return out_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_FALSE(result[0].clip_distances_size.has_value()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, ImplicitInterpolate) { |
| auto* src = R"( |
| struct in_struct { |
| @location(0) struct_inner: f32, |
| } |
| @fragment |
| fn ep_func(in_var: in_struct) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].input_variables.size()); |
| EXPECT_EQ(InterpolationType::kPerspective, result[0].input_variables[0].interpolation_type); |
| EXPECT_EQ(InterpolationSampling::kCenter, result[0].input_variables[0].interpolation_sampling); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, PixelLocalMemberDefault) { |
| auto* src = R"( |
| @fragment |
| fn foo() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].pixel_local_members.size()); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, PixelLocalMemberTypes) { |
| auto* src = R"( |
| enable chromium_experimental_pixel_local; |
| |
| struct Ure { |
| toto: u32, |
| titi: f32, |
| tata: i32, |
| tonton: u32, // Check having the same type multiple times |
| } |
| |
| var<pixel_local> pls: Ure; |
| @fragment fn foo() { _ = pls; } |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(4u, result[0].pixel_local_members.size()); |
| ASSERT_EQ(PixelLocalMemberType::kU32, result[0].pixel_local_members[0]); |
| ASSERT_EQ(PixelLocalMemberType::kF32, result[0].pixel_local_members[1]); |
| ASSERT_EQ(PixelLocalMemberType::kI32, result[0].pixel_local_members[2]); |
| ASSERT_EQ(PixelLocalMemberType::kU32, result[0].pixel_local_members[3]); |
| } |
| |
| struct InspectorGetEntryPointInterpolateTestParams { |
| std::string in; |
| inspector::InterpolationType out_type; |
| inspector::InterpolationSampling out_sampling; |
| }; |
| using InspectorGetEntryPointInterpolateTest = |
| InspectorTestWithParam<InspectorGetEntryPointInterpolateTestParams>; |
| |
| TEST_P(InspectorGetEntryPointInterpolateTest, Test) { |
| auto& params = GetParam(); |
| auto src = R"( |
| struct in_struct { |
| )" + params.in + |
| R"( @location(0) struct_inner: f32, |
| } |
| @fragment fn ep_func(in_var: in_struct) {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(1u, result[0].input_variables.size()); |
| EXPECT_EQ(params.out_type, result[0].input_variables[0].interpolation_type); |
| EXPECT_EQ(params.out_sampling, result[0].input_variables[0].interpolation_sampling); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| InspectorGetEntryPointTest, |
| InspectorGetEntryPointInterpolateTest, |
| testing::Values( |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(perspective, center)", |
| InterpolationType::kPerspective, |
| InterpolationSampling::kCenter}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(perspective, centroid)", |
| InterpolationType::kPerspective, |
| InterpolationSampling::kCentroid}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(perspective, sample)", |
| InterpolationType::kPerspective, |
| InterpolationSampling::kSample}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(perspective)", |
| InterpolationType::kPerspective, |
| InterpolationSampling::kCenter}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(linear, center)", |
| InterpolationType::kLinear, |
| InterpolationSampling::kCenter}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(linear, centroid)", |
| InterpolationType::kLinear, |
| InterpolationSampling::kCentroid}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(linear, sample)", |
| InterpolationType::kLinear, |
| InterpolationSampling::kSample}, |
| InspectorGetEntryPointInterpolateTestParams{ |
| "@interpolate(linear)", InterpolationType::kLinear, InterpolationSampling::kCenter}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(flat)", InterpolationType::kFlat, |
| InterpolationSampling::kFirst}, |
| InspectorGetEntryPointInterpolateTestParams{ |
| "@interpolate(flat, first)", InterpolationType::kFlat, InterpolationSampling::kFirst}, |
| InspectorGetEntryPointInterpolateTestParams{"@interpolate(flat, either)", |
| InterpolationType::kFlat, |
| InterpolationSampling::kEither})); |
| |
| TEST_F(InspectorOverridesTest, NoOverrides) { |
| auto* src = R"( |
| @compute @workgroup_size(1i) |
| fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.Overrides(); |
| EXPECT_TRUE(result.empty()); |
| } |
| |
| TEST_F(InspectorOverridesTest, Multiple) { |
| auto* src = R"( |
| @id(1) override foo: f32; |
| @id(2) override bar: f32; |
| |
| fn callee_func() { _ = foo; } |
| @compute @workgroup_size(1i) fn ep_func() { |
| callee_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.Overrides(); |
| ASSERT_EQ(2u, result.size()); |
| |
| auto& ep = result[0]; |
| EXPECT_EQ(ep.name, "foo"); |
| EXPECT_EQ(ep.id.value, 1); |
| EXPECT_FALSE(ep.is_initialized); |
| EXPECT_TRUE(ep.is_id_specified); |
| |
| ep = result[1]; |
| EXPECT_EQ(ep.name, "bar"); |
| EXPECT_EQ(ep.id.value, 2); |
| EXPECT_FALSE(ep.is_initialized); |
| EXPECT_TRUE(ep.is_id_specified); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, HasTextureLoadWithDepthTexture) { |
| std::string shader = R"( |
| @group(0) @binding(0) var td : texture_depth_2d; |
| @group(0) @binding(1) var tdm : texture_depth_multisampled_2d; |
| @group(0) @binding(2) var t : texture_2d<f32>; |
| @group(0) @binding(3) var s : sampler; |
| |
| @compute @workgroup_size(1) fn load_texture_depth() { |
| _ = textureLoad(td, vec2(0), 0); |
| } |
| @compute @workgroup_size(1) fn load_texture_depth_multisample() { |
| _ = textureLoad(td, vec2(0), 0); |
| } |
| @compute @workgroup_size(1) fn load_texture_2d() { |
| _ = textureLoad(t, vec2(0), 0); |
| } |
| @fragment fn sample_texture_depth() -> @location(0) u32 { |
| _ = textureSample(td, s, vec2(0)); |
| return 0; |
| } |
| fn load_texture_depth_arg(tex : texture_depth_2d) { |
| _ = textureLoad(tex, vec2(0), 0); |
| } |
| @compute @workgroup_size(1) fn load_texture_depth_in_function() { |
| load_texture_depth_arg(td); |
| } |
| )"; |
| Inspector& inspector = Initialize(shader); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| EXPECT_TRUE(inspector.GetEntryPoint("load_texture_depth").has_texture_load_with_depth_texture); |
| EXPECT_TRUE(inspector.GetEntryPoint("load_texture_depth_multisample") |
| .has_texture_load_with_depth_texture); |
| EXPECT_FALSE(inspector.GetEntryPoint("load_texture_2d").has_texture_load_with_depth_texture); |
| EXPECT_FALSE( |
| inspector.GetEntryPoint("sample_texture_depth").has_texture_load_with_depth_texture); |
| EXPECT_TRUE(inspector.GetEntryPoint("load_texture_depth_in_function") |
| .has_texture_load_with_depth_texture); |
| } |
| |
| TEST_F(InspectorGetEntryPointTest, HasDepthTextureWithNonComparisonSampler) { |
| std::string shader = R"( |
| @group(0) @binding(0) var td : texture_depth_2d; |
| @group(0) @binding(1) var s : sampler; |
| @group(0) @binding(2) var cs : sampler_comparison; |
| |
| @fragment fn sample_texture_depth() -> @location(0) u32 { |
| _ = textureSample(td, s, vec2(0)); |
| return 0; |
| } |
| @fragment fn comparison_sample_texture_depth() -> @location(0) u32 { |
| _ = textureSampleCompare(td, cs, vec2(0), 0.5); |
| return 0; |
| } |
| @fragment fn gather_texture_depth() -> @location(0) u32 { |
| _ = textureGather(td, s, vec2(0)); |
| return 0; |
| } |
| @fragment fn comparison_gather_texture_depth() -> @location(0) u32 { |
| _ = textureGatherCompare(td, cs, vec2(0), 0.5); |
| return 0; |
| } |
| @fragment fn sample_level_texture_depth() -> @location(0) u32 { |
| _ = textureSampleLevel(td, s, vec2(0), 0); |
| return 0; |
| } |
| @fragment fn comparison_sample_level_texture_depth() -> @location(0) u32 { |
| _ = textureSampleCompareLevel(td, cs, vec2(0), 0.5); |
| return 0; |
| } |
| |
| fn sample_texture_depth_arg(tex : texture_depth_2d) { |
| _ = textureSample(tex, s, vec2(0)); |
| } |
| @fragment fn sample_texture_depth_in_function() -> @location(0) u32 { |
| sample_texture_depth_arg(td); |
| return 0; |
| } |
| )"; |
| Inspector& inspector = Initialize(shader); |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| EXPECT_TRUE(inspector.GetEntryPoint("sample_texture_depth") |
| .has_depth_texture_with_non_comparison_sampler); |
| EXPECT_FALSE(inspector.GetEntryPoint("comparison_sample_texture_depth") |
| .has_depth_texture_with_non_comparison_sampler); |
| EXPECT_TRUE(inspector.GetEntryPoint("gather_texture_depth") |
| .has_depth_texture_with_non_comparison_sampler); |
| EXPECT_FALSE(inspector.GetEntryPoint("comparison_gather_texture_depth") |
| .has_depth_texture_with_non_comparison_sampler); |
| EXPECT_TRUE(inspector.GetEntryPoint("sample_level_texture_depth") |
| .has_depth_texture_with_non_comparison_sampler); |
| EXPECT_FALSE(inspector.GetEntryPoint("comparison_sample_level_texture_depth") |
| .has_depth_texture_with_non_comparison_sampler); |
| EXPECT_TRUE(inspector.GetEntryPoint("sample_texture_depth_in_function") |
| .has_depth_texture_with_non_comparison_sampler); |
| } |
| |
| TEST_F(InspectorGetConstantNameToIdMapTest, WithAndWithoutIds) { |
| auto* src = R"( |
| @id(1) override v1: f32; |
| @id(20) override v20: f32; |
| @id(300) override v300: f32; |
| override a: f32; |
| override b: f32; |
| override c: f32; |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetNamedOverrideIds(); |
| ASSERT_EQ(6u, result.size()); |
| |
| ASSERT_TRUE(result.count("v1")); |
| EXPECT_EQ(result["v1"].value, 1u); |
| |
| ASSERT_TRUE(result.count("v20")); |
| EXPECT_EQ(result["v20"].value, 20u); |
| |
| ASSERT_TRUE(result.count("v300")); |
| EXPECT_EQ(result["v300"].value, 300u); |
| |
| ASSERT_TRUE(result.count("a")); |
| EXPECT_EQ(result["a"].value, 0); |
| |
| ASSERT_TRUE(result.count("b")); |
| EXPECT_EQ(result["b"].value, 2); |
| |
| ASSERT_TRUE(result.count("c")); |
| EXPECT_EQ(result["c"].value, 3); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, Empty) { |
| auto* src = R"( |
| @fragment fn ep_func() {} |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(0u, result.size()); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, Simple) { |
| auto* src = R"( |
| struct ub_type { |
| a: i32, |
| } |
| @group(0) @binding(0) var<uniform> ub_var: ub_type; |
| fn ub_func() { _ = ub_var.a; } |
| |
| struct sb_type { |
| a: i32, |
| } |
| @group(1) @binding(0) var<storage, read_write> sb_var: sb_type; |
| fn sb_func() { _ = sb_var.a; } |
| |
| struct rosb_type { |
| a: i32, |
| } |
| @group(1) @binding(1) var<storage, read> rosb_var: rosb_type; |
| fn rosb_func() { _ = rosb_var.a; } |
| |
| @group(2) @binding(0) var s_texture : texture_1d<f32>; |
| @group(3) @binding(0) var s_var: sampler; |
| var<private> s_coords: f32; |
| fn s_func() { |
| let sampler_result = textureSample(s_texture, s_var, s_coords); |
| } |
| |
| @group(3) @binding(1) var cs_texture : texture_depth_2d; |
| @group(3) @binding(2) var cs_var: sampler_comparison; |
| var<private> cs_coords: vec2f; |
| var<private> cs_depth: f32; |
| fn cs_func() { |
| let sampler_result = textureSampleCompare(cs_texture, cs_var, cs_coords, cs_depth); |
| } |
| |
| @group(3) @binding(3) var depth_ms_texture : texture_depth_multisampled_2d; |
| fn depth_ms_func() { |
| _ = depth_ms_texture; |
| } |
| |
| @group(4) @binding(0) var st_var: texture_storage_2d<r32uint, write>; |
| fn st_func() { let dim = textureDimensions(st_var); } |
| |
| @group(4) @binding(1) var ba_s: binding_array<texture_2d<f32>, 3>; |
| fn ba_s_func() { let dim = textureDimensions(ba_s[2]); } |
| |
| @fragment |
| fn ep_func() { |
| ub_func(); |
| sb_func(); |
| rosb_func(); |
| s_func(); |
| cs_func(); |
| depth_ms_func(); |
| st_func(); |
| ba_s_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(10u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_FALSE(result[0].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[1].resource_type); |
| EXPECT_EQ(1u, result[1].bind_group); |
| EXPECT_EQ(0u, result[1].binding); |
| EXPECT_FALSE(result[1].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer, result[2].resource_type); |
| EXPECT_EQ(1u, result[2].bind_group); |
| EXPECT_EQ(1u, result[2].binding); |
| EXPECT_FALSE(result[2].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampledTexture, result[3].resource_type); |
| EXPECT_EQ(2u, result[3].bind_group); |
| EXPECT_EQ(0u, result[3].binding); |
| EXPECT_FALSE(result[3].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampler, result[4].resource_type); |
| EXPECT_EQ(3u, result[4].bind_group); |
| EXPECT_EQ(0u, result[4].binding); |
| EXPECT_FALSE(result[4].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kDepthTexture, result[5].resource_type); |
| EXPECT_EQ(3u, result[5].bind_group); |
| EXPECT_EQ(1u, result[5].binding); |
| EXPECT_FALSE(result[5].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kComparisonSampler, result[6].resource_type); |
| EXPECT_EQ(3u, result[6].bind_group); |
| EXPECT_EQ(2u, result[6].binding); |
| EXPECT_FALSE(result[6].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kDepthMultisampledTexture, result[7].resource_type); |
| EXPECT_EQ(3u, result[7].bind_group); |
| EXPECT_EQ(3u, result[7].binding); |
| EXPECT_FALSE(result[7].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kWriteOnlyStorageTexture, result[8].resource_type); |
| EXPECT_EQ(4u, result[8].bind_group); |
| EXPECT_EQ(0u, result[8].binding); |
| EXPECT_FALSE(result[8].array_size.has_value()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampledTexture, result[9].resource_type); |
| EXPECT_EQ(4u, result[9].bind_group); |
| EXPECT_EQ(1u, result[9].binding); |
| EXPECT_TRUE(result[9].array_size.has_value()); |
| EXPECT_EQ(3u, result[9].array_size.value()); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, InputAttachment) { |
| auto* src = R"( |
| enable chromium_internal_input_attachments; |
| |
| @group(0) @binding(1) @input_attachment_index(3) var input_tex1: input_attachment<f32>; |
| @group(4) @binding(3) @input_attachment_index(1) var input_tex2: input_attachment<i32>; |
| |
| fn f1() -> vec4f { return inputAttachmentLoad(input_tex1); } |
| fn f2() -> vec4i { return inputAttachmentLoad(input_tex2); } |
| |
| @fragment |
| fn main() { |
| f1(); |
| f2(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(2u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kInputAttachment, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(1u, result[0].binding); |
| EXPECT_EQ(3u, result[0].input_attachment_index); |
| EXPECT_EQ(inspector::ResourceBinding::SampledKind::kFloat, result[0].sampled_kind); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kInputAttachment, result[1].resource_type); |
| EXPECT_EQ(4u, result[1].bind_group); |
| EXPECT_EQ(3u, result[1].binding); |
| EXPECT_EQ(1u, result[1].input_attachment_index); |
| EXPECT_EQ(inspector::ResourceBinding::SampledKind::kSInt, result[1].sampled_kind); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, MissingEntryPoint) { |
| auto* src = R"()"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_TRUE(inspector.has_error()); |
| std::string error = inspector.error(); |
| EXPECT_TRUE(error.find("not found") != std::string::npos); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, NonEntryPointFunc) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| } |
| @group(0) @binding(0) var<uniform> foo_ub: foo_type; |
| fn ub_func() { _ = foo_ub.a; } |
| |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ub_func"); |
| std::string error = inspector.error(); |
| EXPECT_TRUE(error.find("not an entry point") != std::string::npos); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_Simple_NonStruct) { |
| auto* src = R"( |
| @group(0) @binding(0) var<uniform> foo_ub: i32; |
| fn ub_func() { _ = foo_ub; } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(4u, result[0].size); |
| EXPECT_EQ(4u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_Simple_Struct) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| } |
| @group(0) @binding(0) var<uniform> foo_ub: foo_type; |
| fn ub_func() { _ = foo_ub.a; } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(4u, result[0].size); |
| EXPECT_EQ(4u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_MultipleMembers) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| b: u32, |
| c: f32, |
| } |
| @group(0) @binding(0) var<uniform> foo_ub: foo_type; |
| fn ub_func() { |
| _ = foo_ub.a; |
| _ = foo_ub.b; |
| _ = foo_ub.c; |
| } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_ContainingPadding) { |
| auto* src = R"( |
| struct foo_type { |
| a: vec3f, |
| } |
| @group(0) @binding(0) var<uniform> foo_ub: foo_type; |
| fn ub_func() { _ = foo_ub.a; } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(16u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_NonStructVec3) { |
| auto* src = R"( |
| @group(0) @binding(0) var<uniform> foo_ub: vec3f; |
| fn ub_func() { _ = foo_ub; } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_Multiple) { |
| auto* src = R"( |
| struct ub_type { |
| a: i32, |
| b: u32, |
| c: f32, |
| } |
| @group(0) @binding(0) var<uniform> ub_foo: ub_type; |
| @group(0) @binding(1) var<uniform> ub_bar: ub_type; |
| @group(2) @binding(0) var<uniform> ub_baz: ub_type; |
| fn ub_foo_func() { _ = ub_foo.a; _ = ub_foo.b; _ = ub_foo.c; } |
| fn ub_bar_func() { _ = ub_bar.a; _ = ub_bar.b; _ = ub_bar.c; } |
| fn ub_baz_func() { _ = ub_baz.a; _ = ub_baz.b; _ = ub_baz.c; } |
| |
| @fragment fn ep_func() { |
| ub_foo_func(); |
| ub_bar_func(); |
| ub_baz_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(3u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[1].resource_type); |
| EXPECT_EQ(0u, result[1].bind_group); |
| EXPECT_EQ(1u, result[1].binding); |
| EXPECT_EQ(12u, result[1].size); |
| EXPECT_EQ(12u, result[1].size_no_padding); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[2].resource_type); |
| EXPECT_EQ(2u, result[2].bind_group); |
| EXPECT_EQ(0u, result[2].binding); |
| EXPECT_EQ(12u, result[2].size); |
| EXPECT_EQ(12u, result[2].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, UniformBuffer_ContainingArray) { |
| // Manually create uniform buffer to make sure it had a valid layout (array |
| // with elem stride of 16, and that is 16-byte aligned within the struct) |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| @align(16) b: array<vec4i, 4>, |
| } |
| @group(0) @binding(0) var<uniform> foo_ub: foo_type; |
| fn ub_func() { _ = foo_ub.a; } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kUniformBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(80u, result[0].size); |
| EXPECT_EQ(80u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_Simple_NonStruct) { |
| auto* src = R"( |
| @group(0) @binding(0) var<storage, read_write> foo_sb: i32; |
| fn sb_func() { _ = foo_sb; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(4u, result[0].size); |
| EXPECT_EQ(4u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_Simple_Struct) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| } |
| @group(0) @binding(0) var<storage, read_write> foo_sb: foo_type; |
| fn sb_func() { _ = foo_sb.a; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(4u, result[0].size); |
| EXPECT_EQ(4u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_MultipleMembers) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| b: u32, |
| c: f32 |
| } |
| @group(0) @binding(0) var<storage, read_write> foo_sb: foo_type; |
| fn sb_func() { _ = foo_sb.a; _ = foo_sb.b; _ = foo_sb.c; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_Multiple) { |
| auto* src = R"( |
| struct sb_type { |
| a: i32, |
| b: u32, |
| c: f32, |
| } |
| @group(0) @binding(0) var<storage, read_write> sb_foo: sb_type; |
| @group(0) @binding(1) var<storage, read_write> sb_bar: sb_type; |
| @group(2) @binding(0) var<storage, read_write> sb_baz: sb_type; |
| fn sb_foo_func() { _ = sb_foo.a; _ = sb_foo.b; _ = sb_foo.c; } |
| fn sb_bar_func() { _ = sb_bar.a; _ = sb_bar.b; _ = sb_bar.c; } |
| fn sb_baz_func() { _ = sb_baz.a; _ = sb_baz.b; _ = sb_baz.c; } |
| |
| @fragment |
| fn ep_func() { |
| sb_foo_func(); |
| sb_bar_func(); |
| sb_baz_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(3u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[1].resource_type); |
| EXPECT_EQ(0u, result[1].bind_group); |
| EXPECT_EQ(1u, result[1].binding); |
| EXPECT_EQ(12u, result[1].size); |
| EXPECT_EQ(12u, result[1].size_no_padding); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[2].resource_type); |
| EXPECT_EQ(2u, result[2].bind_group); |
| EXPECT_EQ(0u, result[2].binding); |
| EXPECT_EQ(12u, result[2].size); |
| EXPECT_EQ(12u, result[2].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_ContainingArray) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| b: array<u32, 4>, |
| } |
| @group(0) @binding(0) var<storage, read_write> foo_sb: foo_type; |
| fn sb_func() { _ = foo_sb.a; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(20u, result[0].size); |
| EXPECT_EQ(20u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_ContainingRuntimeArray) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| b: array<u32>, |
| } |
| @group(0) @binding(0) var<storage, read_write> foo_sb: foo_type; |
| fn sb_func() { _ = foo_sb.a; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(8u, result[0].size); |
| EXPECT_EQ(8u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_ContainingPadding) { |
| auto* src = R"( |
| struct foo_type { |
| a: vec3f, |
| } |
| @group(0) @binding(0) var<storage, read_write> foo_sb: foo_type; |
| fn sb_func() { _ = foo_sb.a; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(16u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_NonStructVec3) { |
| auto* src = R"( |
| @group(0) @binding(0) var<storage, read_write> foo_ub: vec3f; |
| fn ub_func() { _ = foo_ub; } |
| @fragment fn ep_func() { ub_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_ReadOnlySimple) { |
| auto* src = R"( |
| struct foo_type { |
| a: i32, |
| } |
| @group(0) @binding(0) var<storage, read> foo_sb: foo_type; |
| fn sb_func() { _ = foo_sb.a; } |
| @fragment fn ep_func() { sb_func(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(4u, result[0].size); |
| EXPECT_EQ(4u, result[0].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, StorageBuffer_MultipleROAndRW) { |
| auto* src = R"( |
| struct sb_type { |
| a: i32, |
| b: u32, |
| c: f32, |
| } |
| @group(0) @binding(0) var<storage, read> sb_foo: sb_type; |
| @group(0) @binding(1) var<storage, read_write> sb_bar: sb_type; |
| @group(2) @binding(0) var<storage, read> sb_baz: sb_type; |
| fn sb_foo_func() { _ = sb_foo.a; _ = sb_foo.b; _ = sb_foo.c; } |
| fn sb_bar_func() { _ = sb_bar.a; _ = sb_bar.b; _ = sb_bar.c; } |
| fn sb_baz_func() { _ = sb_baz.a; _ = sb_baz.b; _ = sb_baz.c; } |
| @fragment fn ep_func() { |
| sb_foo_func(); |
| sb_bar_func(); |
| sb_baz_func(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(3u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(12u, result[0].size); |
| EXPECT_EQ(12u, result[0].size_no_padding); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kStorageBuffer, result[1].resource_type); |
| EXPECT_EQ(0u, result[1].bind_group); |
| EXPECT_EQ(1u, result[1].binding); |
| EXPECT_EQ(12u, result[1].size); |
| EXPECT_EQ(12u, result[1].size_no_padding); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kReadOnlyStorageBuffer, result[2].resource_type); |
| EXPECT_EQ(2u, result[2].bind_group); |
| EXPECT_EQ(0u, result[2].binding); |
| EXPECT_EQ(12u, result[2].size); |
| EXPECT_EQ(12u, result[2].size_no_padding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, Sampler_Simple) { |
| auto* src = R"( |
| @group(0) @binding(0) var foo_sampler: sampler; |
| @group(0) @binding(1) var foo_texture: texture_1d<f32>; |
| var<private> foo_coords: f32; |
| @fragment fn ep() { |
| _ = textureSample(foo_texture, foo_sampler, foo_coords); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(2u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampler, result[1].resource_type); |
| EXPECT_EQ(0u, result[1].bind_group); |
| EXPECT_EQ(0u, result[1].binding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, Sampler_InFunction) { |
| auto* src = R"( |
| @group(0) @binding(0) var foo_sampler: sampler; |
| @group(0) @binding(1) var foo_texture: texture_1d<f32>; |
| var<private> foo_coords: f32; |
| @fragment fn ep_func() { |
| _ = textureSample(foo_texture, foo_sampler, foo_coords); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep_func"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(2u, result.size()); |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampler, result[1].resource_type); |
| EXPECT_EQ(0u, result[1].bind_group); |
| EXPECT_EQ(0u, result[1].binding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, Sampler_Comparison) { |
| auto* src = R"( |
| @group(0) @binding(0) var foo_sampler: sampler_comparison; |
| @group(0) @binding(1) var foo_texture: texture_depth_2d; |
| var<private> foo_coords: vec2f; |
| var<private> foo_depth: f32; |
| @fragment fn ep() { |
| _ = textureSampleCompare(foo_texture, foo_sampler, foo_coords, foo_depth); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(2u, result.size()); |
| EXPECT_EQ(ResourceBinding::ResourceType::kComparisonSampler, result[1].resource_type); |
| EXPECT_EQ(0u, result[1].bind_group); |
| EXPECT_EQ(0u, result[1].binding); |
| } |
| |
| std::string CoordsType(core::type::TextureDimension dim, std::string_view name) { |
| switch (dim) { |
| case core::type::TextureDimension::k1d: |
| return std::string(name); |
| case core::type::TextureDimension::k2d: |
| case core::type::TextureDimension::k2dArray: |
| return "vec2<" + std::string(name) + ">"; |
| case core::type::TextureDimension::k3d: |
| case core::type::TextureDimension::kCube: |
| case core::type::TextureDimension::kCubeArray: |
| return "vec3<" + std::string(name) + ">"; |
| default: |
| break; |
| } |
| TINT_UNREACHABLE(); |
| } |
| |
| struct SampledTextureTestParams { |
| core::type::TextureDimension type_dim; |
| inspector::ResourceBinding::TextureDimension inspector_dim; |
| inspector::ResourceBinding::SampledKind sampled_kind; |
| }; |
| using InspectorGetResourceBindingsTest_WithSampledTextureParams = |
| InspectorTestWithParam<SampledTextureTestParams>; |
| TEST_P(InspectorGetResourceBindingsTest_WithSampledTextureParams, TextureSample) { |
| auto& params = GetParam(); |
| |
| auto src = R"( |
| @group(0) @binding(0) var foo_texture: texture_)" + |
| std::string(ToString(params.type_dim)) + |
| R"(<f32>; |
| @group(0) @binding(1) var foo_sampler: sampler; |
| var<private> foo_coords: )" + |
| CoordsType(params.type_dim, "f32") + R"(; |
| @fragment fn ep() { |
| _ = textureSample(foo_texture, foo_sampler, foo_coords); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(2u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampledTexture, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(params.inspector_dim, result[0].dim); |
| EXPECT_EQ(params.sampled_kind, result[0].sampled_kind); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| InspectorGetResourceBindingsTest, |
| InspectorGetResourceBindingsTest_WithSampledTextureParams, |
| testing::Values(SampledTextureTestParams{core::type::TextureDimension::k1d, |
| inspector::ResourceBinding::TextureDimension::k1d, |
| inspector::ResourceBinding::SampledKind::kFloat}, |
| SampledTextureTestParams{core::type::TextureDimension::k2d, |
| inspector::ResourceBinding::TextureDimension::k2d, |
| inspector::ResourceBinding::SampledKind::kFloat}, |
| SampledTextureTestParams{core::type::TextureDimension::k3d, |
| inspector::ResourceBinding::TextureDimension::k3d, |
| inspector::ResourceBinding::SampledKind::kFloat}, |
| SampledTextureTestParams{core::type::TextureDimension::kCube, |
| inspector::ResourceBinding::TextureDimension::kCube, |
| inspector::ResourceBinding::SampledKind::kFloat})); |
| |
| using ArraySampledTextureTestParams = SampledTextureTestParams; |
| using InspectorGetResourceBindingsTest_WithArraySampledTextureParams = |
| InspectorTestWithParam<ArraySampledTextureTestParams>; |
| TEST_P(InspectorGetResourceBindingsTest_WithArraySampledTextureParams, TextureSample) { |
| auto& params = GetParam(); |
| auto src = R"( |
| @group(0) @binding(0) var foo_texture: texture_)" + |
| std::string(ToString(params.type_dim)) + |
| R"(<f32>; |
| @group(0) @binding(1) var foo_sampler: sampler; |
| var<private> foo_coords: )" + |
| CoordsType(params.type_dim, "f32") + R"(; |
| var<private> foo_array_index: i32; |
| @fragment fn ep() { |
| _ = textureSample(foo_texture, foo_sampler, foo_coords, foo_array_index); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(2u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampledTexture, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(GetParam().inspector_dim, result[0].dim); |
| EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind); |
| } |
| |
| using MultisampledTextureTestParams = SampledTextureTestParams; |
| using InspectorGetResourceBindingsTest_WithMultisampledTextureParams = |
| InspectorTestWithParam<MultisampledTextureTestParams>; |
| INSTANTIATE_TEST_SUITE_P( |
| InspectorGetResourceBindingsTest, |
| InspectorGetResourceBindingsTest_WithArraySampledTextureParams, |
| testing::Values( |
| ArraySampledTextureTestParams{core::type::TextureDimension::k2dArray, |
| inspector::ResourceBinding::TextureDimension::k2dArray, |
| inspector::ResourceBinding::SampledKind::kFloat}, |
| ArraySampledTextureTestParams{core::type::TextureDimension::kCubeArray, |
| inspector::ResourceBinding::TextureDimension::kCubeArray, |
| inspector::ResourceBinding::SampledKind::kFloat})); |
| |
| std::string BaseType(ResourceBinding::SampledKind sampled_kind) { |
| switch (sampled_kind) { |
| case ResourceBinding::SampledKind::kFloat: |
| return "f32"; |
| case ResourceBinding::SampledKind::kSInt: |
| return "i32"; |
| case ResourceBinding::SampledKind::kUInt: |
| return "u32"; |
| default: |
| TINT_UNREACHABLE(); |
| } |
| } |
| |
| TEST_P(InspectorGetResourceBindingsTest_WithMultisampledTextureParams, TextureLoad) { |
| auto& params = GetParam(); |
| auto src = R"( |
| @group(0) @binding(0) var foo_texture: texture_multisampled_)" + |
| std::string(ToString(params.type_dim)) + "<" + BaseType(params.sampled_kind) + R"(>; |
| var<private> foo_coords: )" + |
| CoordsType(params.type_dim, "i32") + R"(; |
| var<private> foo_sample_index: i32; |
| @fragment fn ep() { |
| _ = textureLoad(foo_texture, foo_coords, foo_sample_index); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(ResourceBinding::ResourceType::kMultisampledTexture, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(GetParam().inspector_dim, result[0].dim); |
| EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| InspectorGetResourceBindingsTest, |
| InspectorGetResourceBindingsTest_WithMultisampledTextureParams, |
| testing::Values(MultisampledTextureTestParams{core::type::TextureDimension::k2d, |
| inspector::ResourceBinding::TextureDimension::k2d, |
| inspector::ResourceBinding::SampledKind::kFloat}, |
| MultisampledTextureTestParams{core::type::TextureDimension::k2d, |
| inspector::ResourceBinding::TextureDimension::k2d, |
| inspector::ResourceBinding::SampledKind::kSInt}, |
| MultisampledTextureTestParams{core::type::TextureDimension::k2d, |
| inspector::ResourceBinding::TextureDimension::k2d, |
| inspector::ResourceBinding::SampledKind::kUInt})); |
| |
| using DimensionParams = std::tuple<core::type::TextureDimension, ResourceBinding::TextureDimension>; |
| using TexelFormatParams = |
| std::tuple<core::TexelFormat, ResourceBinding::TexelFormat, ResourceBinding::SampledKind>; |
| using StorageTextureTestParams = std::tuple<DimensionParams, TexelFormatParams, core::Access>; |
| using InspectorGetResourceBindingsTest_WithStorageTextureParams = |
| InspectorTestWithParam<StorageTextureTestParams>; |
| TEST_P(InspectorGetResourceBindingsTest_WithStorageTextureParams, Simple) { |
| DimensionParams dim_params; |
| TexelFormatParams format_params; |
| core::Access access; |
| std::tie(dim_params, format_params, access) = GetParam(); |
| |
| core::type::TextureDimension dim; |
| ResourceBinding::TextureDimension expected_dim; |
| std::tie(dim, expected_dim) = dim_params; |
| |
| core::TexelFormat format; |
| ResourceBinding::TexelFormat expected_format; |
| ResourceBinding::SampledKind expected_kind; |
| std::tie(format, expected_format, expected_kind) = format_params; |
| |
| ResourceBinding::ResourceType expectedResourceType; |
| switch (access) { |
| case core::Access::kWrite: |
| expectedResourceType = ResourceBinding::ResourceType::kWriteOnlyStorageTexture; |
| break; |
| case core::Access::kRead: |
| expectedResourceType = ResourceBinding::ResourceType::kReadOnlyStorageTexture; |
| break; |
| case core::Access::kReadWrite: |
| expectedResourceType = ResourceBinding::ResourceType::kReadWriteStorageTexture; |
| break; |
| case core::Access::kUndefined: |
| ASSERT_TRUE(false); |
| break; |
| } |
| |
| auto src = R"( |
| @group(0) @binding(0) var st_var: texture_storage_)" + |
| std::string(ToString(dim)) + "<" + std::string(ToString(format)) + ", " + |
| std::string(ToString(access)) + R"(>; |
| @fragment fn ep() { _ = textureDimensions(st_var); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(expectedResourceType, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(expected_dim, result[0].dim); |
| EXPECT_EQ(expected_format, result[0].image_format); |
| EXPECT_EQ(expected_kind, result[0].sampled_kind); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| InspectorGetResourceBindingsTest, |
| InspectorGetResourceBindingsTest_WithStorageTextureParams, |
| testing::Combine( |
| testing::Values(std::make_tuple(core::type::TextureDimension::k1d, |
| ResourceBinding::TextureDimension::k1d), |
| std::make_tuple(core::type::TextureDimension::k2d, |
| ResourceBinding::TextureDimension::k2d), |
| std::make_tuple(core::type::TextureDimension::k2dArray, |
| ResourceBinding::TextureDimension::k2dArray), |
| std::make_tuple(core::type::TextureDimension::k3d, |
| ResourceBinding::TextureDimension::k3d)), |
| testing::Values(std::make_tuple(core::TexelFormat::kR32Float, |
| ResourceBinding::TexelFormat::kR32Float, |
| ResourceBinding::SampledKind::kFloat), |
| std::make_tuple(core::TexelFormat::kR32Sint, |
| ResourceBinding::TexelFormat::kR32Sint, |
| ResourceBinding::SampledKind::kSInt), |
| std::make_tuple(core::TexelFormat::kR32Uint, |
| ResourceBinding::TexelFormat::kR32Uint, |
| ResourceBinding::SampledKind::kUInt), |
| std::make_tuple(core::TexelFormat::kRg32Float, |
| ResourceBinding::TexelFormat::kRg32Float, |
| ResourceBinding::SampledKind::kFloat), |
| std::make_tuple(core::TexelFormat::kRg32Sint, |
| ResourceBinding::TexelFormat::kRg32Sint, |
| ResourceBinding::SampledKind::kSInt), |
| std::make_tuple(core::TexelFormat::kRg32Uint, |
| ResourceBinding::TexelFormat::kRg32Uint, |
| ResourceBinding::SampledKind::kUInt), |
| std::make_tuple(core::TexelFormat::kRgba16Float, |
| ResourceBinding::TexelFormat::kRgba16Float, |
| ResourceBinding::SampledKind::kFloat), |
| std::make_tuple(core::TexelFormat::kRgba16Sint, |
| ResourceBinding::TexelFormat::kRgba16Sint, |
| ResourceBinding::SampledKind::kSInt), |
| std::make_tuple(core::TexelFormat::kRgba16Uint, |
| ResourceBinding::TexelFormat::kRgba16Uint, |
| ResourceBinding::SampledKind::kUInt), |
| std::make_tuple(core::TexelFormat::kRgba32Float, |
| ResourceBinding::TexelFormat::kRgba32Float, |
| ResourceBinding::SampledKind::kFloat), |
| std::make_tuple(core::TexelFormat::kRgba32Sint, |
| ResourceBinding::TexelFormat::kRgba32Sint, |
| ResourceBinding::SampledKind::kSInt), |
| std::make_tuple(core::TexelFormat::kRgba32Uint, |
| ResourceBinding::TexelFormat::kRgba32Uint, |
| ResourceBinding::SampledKind::kUInt), |
| std::make_tuple(core::TexelFormat::kRgba8Sint, |
| ResourceBinding::TexelFormat::kRgba8Sint, |
| ResourceBinding::SampledKind::kSInt), |
| std::make_tuple(core::TexelFormat::kRgba8Snorm, |
| ResourceBinding::TexelFormat::kRgba8Snorm, |
| ResourceBinding::SampledKind::kFloat), |
| std::make_tuple(core::TexelFormat::kRgba8Uint, |
| ResourceBinding::TexelFormat::kRgba8Uint, |
| ResourceBinding::SampledKind::kUInt), |
| std::make_tuple(core::TexelFormat::kRgba8Unorm, |
| ResourceBinding::TexelFormat::kRgba8Unorm, |
| ResourceBinding::SampledKind::kFloat)), |
| testing::Values(core::Access::kRead, core::Access::kWrite, core::Access::kReadWrite))); |
| |
| struct DepthTextureTestParams { |
| core::type::TextureDimension type_dim; |
| inspector::ResourceBinding::TextureDimension inspector_dim; |
| }; |
| using InspectorGetResourceBindingsTest_WithDepthTextureParams = |
| InspectorTestWithParam<DepthTextureTestParams>; |
| TEST_P(InspectorGetResourceBindingsTest_WithDepthTextureParams, TextureDimensions) { |
| auto& params = GetParam(); |
| auto src = R"( |
| @group(0) @binding(0) var dt: texture_depth_)" + |
| std::string(ToString(params.type_dim)) + R"(; |
| @fragment fn ep() { |
| _ = textureDimensions(dt); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kDepthTexture, result[0].resource_type); |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(params.inspector_dim, result[0].dim); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| InspectorGetResourceBindingsTest, |
| InspectorGetResourceBindingsTest_WithDepthTextureParams, |
| testing::Values(DepthTextureTestParams{core::type::TextureDimension::k2d, |
| inspector::ResourceBinding::TextureDimension::k2d}, |
| DepthTextureTestParams{core::type::TextureDimension::k2dArray, |
| inspector::ResourceBinding::TextureDimension::k2dArray}, |
| DepthTextureTestParams{core::type::TextureDimension::kCube, |
| inspector::ResourceBinding::TextureDimension::kCube}, |
| DepthTextureTestParams{ |
| core::type::TextureDimension::kCubeArray, |
| inspector::ResourceBinding::TextureDimension::kCubeArray})); |
| |
| TEST_F(InspectorGetResourceBindingsTest, DepthMultisampledTexture_TextureDimensions) { |
| auto* src = R"( |
| @group(0) @binding(0) var tex: texture_depth_multisampled_2d; |
| @fragment fn ep() { |
| _ = textureDimensions(tex); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kDepthMultisampledTexture, result[0].resource_type); |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| EXPECT_EQ(ResourceBinding::TextureDimension::k2d, result[0].dim); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, ExternalTexture) { |
| auto* src = R"( |
| @group(0) @binding(0) var et: texture_external; |
| @fragment fn ep() { _ = textureDimensions(et); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| EXPECT_EQ(ResourceBinding::ResourceType::kExternalTexture, result[0].resource_type); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(0u, result[0].binding); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, BindingArray_Simple) { |
| auto* src = R"( |
| @group(0) @binding(1) var toto: binding_array<texture_2d<f32>, 5>; |
| @fragment fn ep() { |
| _ = textureDimensions(toto[3]); |
| } |
| )"; |
| |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampledTexture, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(1u, result[0].binding); |
| EXPECT_TRUE(result[0].array_size.has_value()); |
| EXPECT_EQ(5u, result[0].array_size.value()); |
| } |
| |
| TEST_F(InspectorGetResourceBindingsTest, BindingArray_InFunction) { |
| auto* src = R"( |
| @group(0) @binding(1) var toto: binding_array<texture_2d<f32>, 5>; |
| fn f() { |
| _ = textureDimensions(toto[3]); |
| } |
| @fragment fn ep() { |
| f(); |
| } |
| )"; |
| |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetResourceBindings("ep"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| |
| EXPECT_EQ(ResourceBinding::ResourceType::kSampledTexture, result[0].resource_type); |
| EXPECT_EQ(0u, result[0].bind_group); |
| EXPECT_EQ(1u, result[0].binding); |
| EXPECT_TRUE(result[0].array_size.has_value()); |
| EXPECT_EQ(5u, result[0].array_size.value()); |
| } |
| |
| class InspectorGetSamplerTextureUsesTest : public TestHelper, public testing::Test { |
| public: |
| using ResultExpectation = std::initializer_list<SamplerTexturePair>; |
| |
| size_t SizeOf(const ResultExpectation& expectation) { return expectation.size(); } |
| size_t SizeOf(const std::vector<SamplerTexturePair>& result) { return result.size(); } |
| |
| // ValidateEqual checks that the expected and actual SamplerTexturePair list contain same pairs |
| // and both are deduplicated. |
| template <typename T, typename U> |
| void ValidateEqual(const T& expected, const U& actual) { |
| ASSERT_EQ(SizeOf(expected), SizeOf(actual)); |
| std::unordered_set<SamplerTexturePair> pairSet; |
| // Insert all pairs in the expected into the set. |
| for (const auto& pair : expected) { |
| // Expectation should be deduplicated, so every insertion should take place. |
| EXPECT_TRUE(pairSet.insert(pair).second) |
| << "Duplicated SamplerTexturePair found: Sampler: (" |
| << pair.sampler_binding_point.group << ", " << pair.sampler_binding_point.binding |
| << "), " |
| << "Texture: (" << pair.texture_binding_point.group << ", " |
| << pair.texture_binding_point.binding << ")"; |
| } |
| // Check that each SamplerTexturePair in the actual is in the set and occurs only once. |
| for (const auto& pair : actual) { |
| EXPECT_TRUE(pairSet.erase(pair) == 1) |
| << "Unexpected SamplerTexturePair: Sampler: (" << pair.sampler_binding_point.group |
| << ", " << pair.sampler_binding_point.binding << "), " |
| << "Texture: (" << pair.texture_binding_point.group << ", " |
| << pair.texture_binding_point.binding << ")"; |
| } |
| } |
| |
| constexpr static BindingPoint non_sampler_placeholder{123u, 654u}; |
| }; |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, None) { |
| std::string shader = R"( |
| @fragment |
| fn main() { |
| })"; |
| |
| ResultExpectation expected = {}; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| // Regression test for crbug.com/dawn/380433758. |
| TEST_F(InspectorGetSamplerTextureUsesTest, DiamondSampler) { |
| std::string shader = R"( |
| fn sample(t: texture_2d<f32>, s: sampler) -> vec4f { |
| return textureSampleLevel(t, s, vec2f(0), 0); |
| } |
| |
| fn useCombos0() -> vec4f { |
| return sample(tex0_0, smp0_0); |
| } |
| |
| fn useCombos1() -> vec4f { |
| return sample(tex1_15, smp0_0); |
| } |
| |
| @group(0) @binding(0) var tex0_0: texture_2d<f32>; |
| @group(0) @binding(1) var tex1_15: texture_2d<f32>; |
| @group(0) @binding(2) var smp0_0: sampler; |
| |
| @vertex fn vs() -> @builtin(position) vec4f { |
| return useCombos0(); |
| } |
| |
| @fragment fn fs() -> @location(0) vec4f { |
| return vec4f(useCombos1()); |
| })"; |
| |
| ResultExpectation expected_vs = { |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 0}}, |
| }; |
| ResultExpectation expected_fs = { |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 1}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("vs"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_vs, result); |
| } |
| { |
| auto result = inspector.GetSamplerTextureUses("fs"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_fs, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("vs", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_vs, result); |
| } |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("fs", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_fs, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, DiamondSampler2) { |
| std::string shader = R"( |
| fn sample(t: texture_2d<f32>, s: sampler) -> vec4f { |
| return textureSampleLevel(t, s, vec2f(0), 0); |
| } |
| |
| fn useCombos0() -> vec4f { |
| return sample(tex0_0, smp0_0); |
| } |
| |
| fn useCombos1(t: texture_2d<f32>) -> vec4f { |
| return sample(t, smp0_0); |
| } |
| |
| fn useCombos2() -> vec4f { |
| return useCombos1(tex1_15); |
| } |
| |
| @group(0) @binding(0) var tex0_0: texture_2d<f32>; |
| @group(0) @binding(1) var tex1_15: texture_2d<f32>; |
| @group(0) @binding(2) var smp0_0: sampler; |
| |
| @vertex fn vs() -> @builtin(position) vec4f { |
| return useCombos0(); |
| } |
| |
| @fragment fn fs() -> @location(0) vec4f { |
| return vec4f(useCombos2()); |
| })"; |
| |
| ResultExpectation expected_vs = { |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 0}}, |
| }; |
| ResultExpectation expected_fs = { |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 1}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("vs"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_vs, result); |
| } |
| { |
| auto result = inspector.GetSamplerTextureUses("fs"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_fs, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("vs", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_vs, result); |
| } |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("fs", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_fs, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, DiamondSampler3) { |
| std::string shader = R"( |
| fn sample(t: texture_2d<f32>, s: sampler) -> vec4f { |
| return textureSampleLevel(t, s, vec2f(0), 0); |
| } |
| |
| fn useCombos0() -> vec4f { |
| return sample(tex0_0, smp0_0); |
| } |
| |
| fn useCombos1(t: texture_2d<f32>) -> vec4f { |
| return sample(t, smp0_0); |
| } |
| |
| fn useCombos2() -> vec4f { |
| return useCombos1(tex1_15); |
| } |
| |
| @group(0) @binding(0) var tex0_0: texture_2d<f32>; |
| @group(0) @binding(1) var tex1_15: texture_2d<f32>; |
| @group(0) @binding(2) var smp0_0: sampler; |
| |
| @vertex fn vs() -> @builtin(position) vec4f { |
| _ = useCombos0(); |
| return useCombos2(); |
| } |
| |
| @fragment fn fs() -> @location(0) vec4f { |
| return vec4f(useCombos2()); |
| })"; |
| |
| ResultExpectation expected_vs = { |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 0}}, |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 1}}, |
| }; |
| ResultExpectation expected_fs = { |
| {/* Sampler */ BindingPoint{0, 2}, /* Texture */ BindingPoint{0, 1}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("vs"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_vs, result); |
| } |
| { |
| auto result = inspector.GetSamplerTextureUses("fs"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_fs, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("vs", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_vs, result); |
| } |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("fs", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_fs, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, Simple) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return textureSample(myTexture, mySampler, fragUV) * fragPosition; |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, UnknownEntryPoint) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return textureSample(myTexture, mySampler, fragUV) * fragPosition; |
| })"; |
| |
| { |
| Inspector& inspector = Initialize(shader); |
| inspector.GetSamplerTextureUses("foo"); |
| ASSERT_TRUE(inspector.has_error()) << inspector.error(); |
| } |
| { |
| Inspector& inspector = Initialize(shader); |
| inspector.GetSamplerAndNonSamplerTextureUses("foo", non_sampler_placeholder); |
| ASSERT_TRUE(inspector.has_error()) << inspector.error(); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, MultipleCalls) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return textureSample(myTexture, mySampler, fragUV) * fragPosition; |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result_0 = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| auto result_1 = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(result_0, result_1); |
| } |
| |
| { |
| auto result_0 = |
| inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| auto result_1 = |
| inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| ValidateEqual(result_0, result_1); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, BothIndirect) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return textureSample(t, s, uv); |
| } |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return doSample(myTexture, mySampler, fragUV) * fragPosition; |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, SamplerIndirect) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| fn doSample(s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return textureSample(myTexture, s, uv); |
| } |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return doSample(mySampler, fragUV) * fragPosition; |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, TextureIndirect) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| fn doSample(t: texture_2d<f32>, uv: vec2<f32>) -> vec4<f32> { |
| return textureSample(t, mySampler, uv); |
| } |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return doSample(myTexture, fragUV) * fragPosition; |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, TextureFromBindingArray) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTextures: binding_array<texture_2d<f32>, 3>; |
| |
| @fragment fn main() { |
| _ = textureSample(myTextures[1], mySampler, vec2(0)); |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, TextureFromBindingArrayPassedAsParameter) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTextures: binding_array<texture_2d<f32>, 3>; |
| |
| fn f(ts : binding_array<texture_2d<f32>, 3>) { |
| _ = textureSample(ts[1], mySampler, vec2(0)); |
| } |
| @fragment fn main() { |
| f(myTextures); |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, TextureParameterFromBindingArray) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTextures: binding_array<texture_2d<f32>, 3>; |
| |
| fn f(t : texture_2d<f32>) { |
| _ = textureSample(t, mySampler, vec2(0)); |
| } |
| @fragment fn main() { |
| f(myTextures[1]); |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, NeitherIndirect) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| fn doSample(uv: vec2<f32>) -> vec4<f32> { |
| return textureSample(myTexture, mySampler, uv); |
| } |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return doSample(fragUV) * fragPosition; |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, Complex) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| |
| fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return textureSample(t, s, uv); |
| } |
| |
| fn X(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return doSample(t, s, uv); |
| } |
| |
| fn Y(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return doSample(t, s, uv); |
| } |
| |
| fn Z(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return X(t, s, uv) + Y(t, s, uv); |
| } |
| |
| @fragment |
| fn via_call(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return Z(myTexture, mySampler, fragUV) * fragPosition; |
| } |
| |
| @fragment |
| fn via_ptr(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return textureSample(myTexture, mySampler, fragUV) + fragPosition; |
| } |
| |
| @fragment |
| fn direct(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return textureSample(myTexture, mySampler, fragUV) + fragPosition; |
| })"; |
| |
| ResultExpectation expected = { |
| {/* Sampler */ BindingPoint{0, 1}, /* Texture */ BindingPoint{0, 2}}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("via_call"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| { |
| auto result = inspector.GetSamplerTextureUses("via_ptr"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| { |
| auto result = inspector.GetSamplerTextureUses("direct"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| |
| { |
| auto result = |
| inspector.GetSamplerAndNonSamplerTextureUses("via_call", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| { |
| auto result = |
| inspector.GetSamplerAndNonSamplerTextureUses("via_ptr", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| { |
| auto result = |
| inspector.GetSamplerAndNonSamplerTextureUses("direct", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected, result); |
| } |
| } |
| |
| TEST_F(InspectorGetSamplerTextureUsesTest, SamplerAndNonSamplerTexture) { |
| std::string shader = R"( |
| @group(0) @binding(1) var sampler0: sampler; |
| @group(0) @binding(3) var sampler1: sampler; |
| @group(0) @binding(2) var texture0: texture_2d<f32>; |
| @group(2) @binding(1) var texture1: texture_2d<f32>; |
| // Storage texture should not be included in the result. |
| @group(2) @binding(3) var texture2: texture_storage_2d<r32float, read_write>; |
| @group(2) @binding(4) var external0 : texture_external; |
| @group(2) @binding(5) var external1 : texture_external; |
| @group(2) @binding(6) var texture_array : binding_array<texture_2d<f32>, 5>; |
| |
| const loadStoreCoords = vec2<u32>(0u, 0u); |
| |
| fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| _ = textureLoad(t, loadStoreCoords, 0u); |
| return textureSample(t, s, uv); |
| } |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| // Usage with a sampler |
| _ = textureSample(texture1, sampler0, fragUV); |
| |
| // Non-sampler texture usage. |
| _ = textureLoad(texture1, loadStoreCoords, 0u); |
| |
| // Both sampler and non-sampler usage but inside a function. |
| _ = doSample(texture0, sampler0, fragUV); |
| |
| // Using texture0 with sampler0 again, should be deduplicated in the result. |
| _ = textureSample(texture0, sampler0, fragUV); |
| |
| // Storage texture should not be included in the result. |
| _ = textureLoad(texture2, loadStoreCoords); |
| textureStore(texture2, loadStoreCoords, fragPosition); |
| |
| // Usages of texture_external with and without samplers |
| _ = textureSampleBaseClampToEdge(external0, sampler0, fragUV); |
| _ = textureLoad(external1, vec2(0, 0)); |
| |
| // Usages of binding_array with and without samplers |
| _ = textureSample(texture_array[2], sampler0, fragUV); |
| _ = textureLoad(texture_array[1], vec2(0, 0), 0); |
| |
| // Another usage with a sampler. |
| return textureSample(texture0, sampler1, fragUV) + fragPosition; |
| } |
| )"; |
| |
| constexpr BindingPoint sampler_0 = {0, 1}; |
| constexpr BindingPoint sampler_1 = {0, 3}; |
| constexpr BindingPoint texture_0 = {0, 2}; |
| constexpr BindingPoint texture_1 = {2, 1}; |
| constexpr BindingPoint external_0 = {2, 4}; |
| constexpr BindingPoint external_1 = {2, 5}; |
| constexpr BindingPoint texture_array = {2, 6}; |
| // Storage texture texture2 should not be included in the result. |
| |
| ResultExpectation expected_sampler_only = { |
| {/* Sampler */ sampler_0, /* Texture */ texture_1}, |
| {/* Sampler */ sampler_0, /* Texture */ texture_0}, |
| {/* Sampler */ sampler_1, /* Texture */ texture_0}, |
| {/* Sampler */ sampler_0, /* Texture */ external_0}, |
| {/* Sampler */ sampler_0, /* Texture */ texture_array}, |
| }; |
| ResultExpectation expected_sampler_and_non_sampler = { |
| {/* Sampler */ sampler_0, /* Texture */ texture_1}, |
| {/* Sampler */ non_sampler_placeholder, /* Texture */ texture_1}, |
| {/* Sampler */ non_sampler_placeholder, /* Texture */ texture_0}, |
| {/* Sampler */ sampler_0, /* Texture */ texture_0}, |
| {/* Sampler */ sampler_1, /* Texture */ texture_0}, |
| {/* Sampler */ sampler_0, /* Texture */ external_0}, |
| {/* Sampler */ non_sampler_placeholder, /* Texture */ external_1}, |
| {/* Sampler */ sampler_0, /* Texture */ texture_array}, |
| {/* Sampler */ non_sampler_placeholder, /* Texture */ texture_array}, |
| }; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| { |
| auto result = inspector.GetSamplerTextureUses("main"); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_sampler_only, result); |
| } |
| |
| { |
| auto result = inspector.GetSamplerAndNonSamplerTextureUses("main", non_sampler_placeholder); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ValidateEqual(expected_sampler_and_non_sampler, result); |
| } |
| } |
| |
| // Test calling GetUsedExtensionNames on a empty shader. |
| TEST_F(InspectorGetUsedExtensionNamesTest, Empty) { |
| std::string shader = ""; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| auto result = inspector.GetUsedExtensionNames(); |
| EXPECT_EQ(result.size(), 0u); |
| } |
| |
| // Test calling GetUsedExtensionNames on a shader with no extension. |
| TEST_F(InspectorGetUsedExtensionNamesTest, None) { |
| std::string shader = R"( |
| @fragment |
| fn main() { |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| auto result = inspector.GetUsedExtensionNames(); |
| EXPECT_EQ(result.size(), 0u); |
| } |
| |
| // Test calling GetUsedExtensionNames on a shader with valid extension. |
| TEST_F(InspectorGetUsedExtensionNamesTest, Simple) { |
| std::string shader = R"( |
| enable f16; |
| |
| @fragment |
| fn main() { |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| auto result = inspector.GetUsedExtensionNames(); |
| EXPECT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0], "f16"); |
| } |
| |
| // Test calling GetUsedExtensionNames on a shader with a extension enabled for |
| // multiple times. |
| TEST_F(InspectorGetUsedExtensionNamesTest, Duplicated) { |
| std::string shader = R"( |
| enable f16; |
| enable f16; |
| |
| @fragment |
| fn main() { |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| |
| auto result = inspector.GetUsedExtensionNames(); |
| EXPECT_EQ(result.size(), 1u); |
| EXPECT_EQ(result[0], "f16"); |
| } |
| |
| // Crash was occuring in ::GenerateSamplerTargets, when |
| // ::GetSamplerTextureUses was called. |
| TEST_F(InspectorRegressionTest, tint967) { |
| std::string shader = R"( |
| @group(0) @binding(1) var mySampler: sampler; |
| @group(0) @binding(2) var myTexture: texture_2d<f32>; |
| |
| fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> { |
| return textureSample(t, s, uv); |
| } |
| |
| @fragment |
| fn main(@location(0) fragUV: vec2<f32>, |
| @location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> { |
| return doSample(myTexture, mySampler, fragUV) * fragPosition; |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| inspector.GetSamplerTextureUses("main"); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLevelInEP) { |
| std::string shader = R"( |
| @group(2) @binding(3) var myTexture: texture_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num = textureNumLevels(myTexture); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type); |
| EXPECT_EQ(2u, info[0].group); |
| EXPECT_EQ(3u, info[0].binding); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLevelInBindingArray) { |
| std::string shader = R"( |
| @group(2) @binding(3) var myTextures : binding_array<texture_2d<f32>, 4>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num = textureNumLevels(myTextures[2]); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type); |
| EXPECT_EQ(2u, info[0].group); |
| EXPECT_EQ(3u, info[0].binding); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLevelInEPNoDups) { |
| std::string shader = R"( |
| @group(0) @binding(0) var myTexture: texture_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureNumLevels(myTexture); |
| let num2 = textureNumLevels(myTexture); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLevelInEPMultiple) { |
| std::string shader = R"( |
| @group(2) @binding(3) var tex1: texture_2d<f32>; |
| @group(1) @binding(2) var tex2: texture_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureNumLevels(tex1); |
| let num2 = textureNumLevels(tex2); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(2u, info.size()); |
| |
| Inspector::LevelSampleInfo info1 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 1, |
| /*binding*/ 2, |
| }; |
| Inspector::LevelSampleInfo info2 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 2, |
| /*binding*/ 3, |
| }; |
| EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2)); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureSamplesInEP) { |
| std::string shader = R"( |
| @group(2) @binding(3) var myTexture: texture_multisampled_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num = textureNumSamples(myTexture); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[0].type); |
| EXPECT_EQ(2u, info[0].group); |
| EXPECT_EQ(3u, info[0].binding); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureSamplesInEPNoDups) { |
| std::string shader = R"( |
| @group(0) @binding(0) var myTexture: texture_multisampled_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureNumSamples(myTexture); |
| let num2 = textureNumSamples(myTexture); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureSamplesInEPMultiple) { |
| std::string shader = R"( |
| @group(2) @binding(3) var tex1: texture_multisampled_2d<f32>; |
| @group(1) @binding(2) var tex2: texture_multisampled_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureNumSamples(tex1); |
| let num2 = textureNumSamples(tex2); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(2u, info.size()); |
| |
| Inspector::LevelSampleInfo info1 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumSamples, |
| /*group*/ 1, |
| /*binding*/ 2, |
| }; |
| Inspector::LevelSampleInfo info2 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumSamples, |
| /*group*/ 2, |
| /*binding*/ 3, |
| }; |
| EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2)); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLoadInEP) { |
| std::string shader = R"( |
| @group(2) @binding(3) var tex1: texture_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureLoad(tex1, vec2(0, 0), 0); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| |
| EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type); |
| EXPECT_EQ(2u, info[0].group); |
| EXPECT_EQ(3u, info[0].binding); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLoadInBindingArray) { |
| std::string shader = R"( |
| @group(2) @binding(3) var textures: binding_array<texture_2d<f32>, 4>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureLoad(textures[1], vec2(0, 0), 0); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(1u, info.size()); |
| |
| EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type); |
| EXPECT_EQ(2u, info[0].group); |
| EXPECT_EQ(3u, info[0].binding); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLoadMultisampledInEP) { |
| std::string shader = R"( |
| @group(2) @binding(3) var tex1: texture_multisampled_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureLoad(tex1, vec2(0, 0), 0); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(0u, info.size()); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureLoadMultipleInEP) { |
| std::string shader = R"( |
| @group(2) @binding(3) var tex1: texture_2d<f32>; |
| @group(1) @binding(4) var tex2: texture_multisampled_2d<f32>; |
| @group(0) @binding(1) var tex3: texture_2d<f32>; |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| let num1 = textureLoad(tex1, vec2(0, 0), 0); |
| let num2 = textureLoad(tex2, vec2(0, 0), 0); |
| let num3 = textureLoad(tex3, vec2(0, 0), 0); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(2u, info.size()); |
| |
| Inspector::LevelSampleInfo info1 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 0, |
| /*binding*/ 1, |
| }; |
| Inspector::LevelSampleInfo info2 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 2, |
| /*binding*/ 3, |
| }; |
| EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2)); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureInSubfunction) { |
| std::string shader = R"( |
| @group(2) @binding(3) var tex1: texture_2d<f32>; |
| @group(1) @binding(4) var tex2: texture_multisampled_2d<f32>; |
| @group(1) @binding(3) var tex3: texture_2d<f32>; |
| |
| fn b(tx1: texture_2d<f32>, tx2: texture_multisampled_2d<f32>, tx3: texture_2d<f32>, tx4: texture_2d<f32>) { |
| let v1 = textureNumLevels(tx1); |
| let v2 = textureNumSamples(tx2); |
| let v3 = textureLoad(tx3, vec2(0, 0), 0); |
| let v4 = textureNumLevels(tx4); |
| } |
| |
| fn a(tx1: texture_2d<f32>, tx2: texture_multisampled_2d<f32>, tx3: texture_2d<f32>) { |
| b(tx1, tx2, tx3, tx1); |
| } |
| |
| @compute @workgroup_size(1) |
| fn main() { |
| a(tex1, tex2, tex3); |
| })"; |
| |
| Inspector& inspector = Initialize(shader); |
| auto info = inspector.GetTextureQueries("main"); |
| |
| ASSERT_EQ(3u, info.size()); |
| |
| Inspector::LevelSampleInfo info1 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 1, |
| /*binding*/ 3, |
| }; |
| Inspector::LevelSampleInfo info2 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 2, |
| /*binding*/ 3, |
| }; |
| Inspector::LevelSampleInfo info3 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumSamples, |
| /*group*/ 1, |
| /*binding*/ 4, |
| }; |
| EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2, info3)); |
| } |
| |
| TEST_F(InspectorTextureTest, TextureMultipleEPs) { |
| std::string shader = R"( |
| @group(0) @binding(0) var<storage, read_write> dstBuf : array<u32>; |
| @group(0) @binding(1) var tex1 : texture_2d_array<f32>; |
| @group(0) @binding(4) var tex2 : texture_multisampled_2d<f32>; |
| @group(1) @binding(3) var tex3 : texture_2d_array<f32>; |
| |
| @compute @workgroup_size(1, 1, 1) fn main1() { |
| dstBuf[0] = textureNumLayers(tex1); |
| dstBuf[1] = textureNumLevels(tex1); |
| dstBuf[2] = textureNumSamples(tex2); |
| dstBuf[3] = textureNumLevels(tex3); |
| } |
| |
| @compute @workgroup_size(1, 1, 1) fn main2() { |
| dstBuf[0] = textureNumLayers(tex1); |
| dstBuf[1] = textureNumLevels(tex1); |
| dstBuf[2] = textureNumSamples(tex2); |
| } |
| )"; |
| Inspector& inspector = Initialize(shader); |
| { |
| auto info = inspector.GetTextureQueries("main1"); |
| ASSERT_EQ(3u, info.size()); |
| |
| Inspector::LevelSampleInfo info1 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 1, |
| /*binding*/ 3, |
| }; |
| Inspector::LevelSampleInfo info2 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 0, |
| /*binding*/ 1, |
| }; |
| Inspector::LevelSampleInfo info3 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumSamples, |
| /*group*/ 0, |
| /*binding*/ 4, |
| }; |
| EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2, info3)); |
| } |
| { |
| auto info = inspector.GetTextureQueries("main2"); |
| ASSERT_EQ(2u, info.size()); |
| |
| Inspector::LevelSampleInfo info1 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumLevels, |
| /*group*/ 0, |
| /*binding*/ 1, |
| }; |
| Inspector::LevelSampleInfo info2 = { |
| /*type */ Inspector::TextureQueryType::kTextureNumSamples, |
| /*group*/ 0, |
| /*binding*/ 4, |
| }; |
| EXPECT_THAT(info, testing::UnorderedElementsAre(info1, info2)); |
| } |
| } |
| |
| TEST_F(InspectorGetBlendSrcTest, Basic) { |
| auto* src = R"( |
| enable dual_source_blending; |
| |
| struct out_struct { |
| @location(0u) @blend_src(0u) output_color: vec4f, |
| @location(0u) @blend_src(1u) output_blend: vec4f, |
| } |
| |
| @fragment |
| fn ep_func() -> out_struct { |
| var out_var: out_struct; |
| return out_var; |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| |
| ASSERT_EQ(1u, result.size()); |
| ASSERT_EQ(2u, result[0].output_variables.size()); |
| EXPECT_EQ(0u, result[0].output_variables[0].attributes.blend_src); |
| EXPECT_EQ(1u, result[0].output_variables[1].attributes.blend_src); |
| } |
| |
| TEST_F(InspectorSubgroupMatrixTest, DirectUse) { |
| auto* src = R"( |
| enable chromium_experimental_subgroup_matrix; |
| |
| @compute @workgroup_size(1) fn foo() { |
| _ = subgroup_matrix_result<f32, 8, 8>(); |
| } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].uses_subgroup_matrix); |
| } |
| |
| TEST_F(InspectorSubgroupMatrixTest, IndirectUse) { |
| auto* src = R"( |
| enable chromium_experimental_subgroup_matrix; |
| |
| fn foo() { _ = subgroup_matrix_result<f32, 8, 8>(); } |
| @compute @workgroup_size(1) fn main() { foo(); } |
| )"; |
| Inspector& inspector = Initialize(src); |
| |
| auto result = inspector.GetEntryPoints(); |
| ASSERT_FALSE(inspector.has_error()) << inspector.error(); |
| |
| ASSERT_EQ(1u, result.size()); |
| EXPECT_TRUE(result[0].uses_subgroup_matrix); |
| } |
| |
| } // namespace |
| |
| static std::ostream& operator<<(std::ostream& out, const Inspector::TextureQueryType& ty) { |
| switch (ty) { |
| case Inspector::TextureQueryType::kTextureNumLevels: |
| out << "textureNumLevels"; |
| break; |
| case Inspector::TextureQueryType::kTextureNumSamples: |
| out << "textureNumSamples"; |
| break; |
| } |
| return out; |
| } |
| |
| } // namespace tint::inspector |