blob: 357c07c3a9272f12103092460b081d7c5c6e2232 [file] [log] [blame]
// Copyright 2024 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 "gmock/gmock.h"
#include "src/tint/lang/wgsl/resolver/resolver.h"
#include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
namespace tint::resolver {
using namespace tint::core::fluent_types; // NOLINT
using namespace tint::core::number_suffixes; // NOLINT
namespace {
constexpr size_t kMaxClipDistancesSize = 8;
using ResolverClipDistancesExtensionTest = ResolverTest;
// Using a clip_distances builtin attribute without WGSL extension `clip_distances` enabled should
// fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesWithoutExtensionError) {
Structure("Outputs", Vector{
Member("a", ty.array<f32, kMaxClipDistancesSize>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: use of '@builtin(clip_distances)' requires enabling extension 'clip_distances')");
}
// Using a clip_distances builtin attribute on i32 scalar should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnI32Error) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.i32(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on a vector of i32 should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnI32VecError) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.vec4<i32>(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on i32 array should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnI32ArrayError) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs", Vector{
Member("a", ty.array<i32, kMaxClipDistancesSize>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on u32 scalar should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnU32Error) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.u32(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on a vector of u32 should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnU32VecError) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.vec4<u32>(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on u32 array should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnU32ArrayError) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs", Vector{
Member("a", ty.array<u32, kMaxClipDistancesSize>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on f16 scalar should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF16Error) {
Enable(wgsl::Extension::kClipDistances);
Enable(wgsl::Extension::kF16);
Structure("Outputs",
Vector{
Member("a", ty.f16(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on a vector of f16 should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF16VecError) {
Enable(wgsl::Extension::kClipDistances);
Enable(wgsl::Extension::kF16);
Structure("Outputs",
Vector{
Member("a", ty.vec4<f16>(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on f16 array should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF16ArrayError) {
Enable(wgsl::Extension::kClipDistances);
Enable(wgsl::Extension::kF16);
Structure("Outputs", Vector{
Member("a", ty.array<f16, kMaxClipDistancesSize>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on f32 scalar should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF32Error) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.f32(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on a vector of f32 should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF32VecError) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on f32 unsized array should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF32UnsizedArrayError) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs",
Vector{
Member("a", ty.array<f32>(), Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute on f32 array with size == 1 should pass.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF32ArraySize1Success) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs", Vector{
Member("a", ty.array<f32, 1>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_TRUE(r()->Resolve());
}
// Using a clip_distances builtin attribute on f32 array with size == kMaxClipDistancesSize should
// pass.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF32ArraySize8Success) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs", Vector{
Member("a", ty.array<f32, kMaxClipDistancesSize>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_TRUE(r()->Resolve());
}
// Using a clip_distances builtin attribute on f32 array with size > kMaxClipDistancesSize should
// fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesOnF32ArrayWithTooLargeSizeFail) {
Enable(wgsl::Extension::kClipDistances);
Structure("Outputs", Vector{
Member("a", ty.array<f32, kMaxClipDistancesSize + 1>(),
Vector{Builtin(core::BuiltinValue::kClipDistances)}),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(error: store type of '@builtin(clip_distances)' must be 'array<f32, N>' (N <= 8))");
}
// Using a clip_distances builtin attribute in fragment shader inputs should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesInFragmentShaderInputFail) {
Enable(wgsl::Extension::kClipDistances);
auto* s = Structure(
"MyInputs", Vector{Member("clipDistances", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{56, 78}}, core::BuiltinValue::kClipDistances),
})});
Func("fragmentShader",
Vector{
Param("arg", ty.Of(s)),
},
ty.f32(),
Vector{
Return(1_f),
},
Vector{
Stage(ast::PipelineStage::kFragment),
},
Vector{
Location(0_a),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: '@builtin(clip_distances)' cannot be used for fragment shader input
note: while analyzing entry point 'fragmentShader')");
}
// Using a clip_distances builtin attribute in fragment shader outputs should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesInFragmentShaderOutputFail) {
Enable(wgsl::Extension::kClipDistances);
auto* s =
Structure("MyOutputs",
Vector{Member("clipDistances", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
})});
Func("fragmentShader", tint::Empty, ty.Of(s),
Vector{
Return(Call(ty.Of(s))),
},
Vector{
Stage(ast::PipelineStage::kFragment),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: '@builtin(clip_distances)' cannot be used for fragment shader output
note: while analyzing entry point 'fragmentShader')");
}
// Using a clip_distances builtin attribute in compute shader inputs should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesInComputeShaderInputFail) {
Enable(wgsl::Extension::kClipDistances);
auto* s = Structure(
"MyInputs", Vector{Member("clipDistances", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
})});
Func("computeShader",
Vector{
Param("arg", ty.Of(s)),
},
ty.void_(), tint::Empty,
Vector{
Stage(ast::PipelineStage::kCompute),
},
tint::Empty);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: '@builtin(clip_distances)' cannot be used for compute shader input
note: while analyzing entry point 'computeShader')");
}
// Using a clip_distances builtin attribute in compute shader outputs should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesInComputeShaderOutputFail) {
Enable(wgsl::Extension::kClipDistances);
auto* s =
Structure("MyOutputs",
Vector{Member("clipDistances", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
})});
Func("computeShader", tint::Empty, ty.Of(s),
Vector{
Return(Call(ty.Of(s))),
},
Vector{
Stage(ast::PipelineStage::kCompute),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: '@builtin(clip_distances)' cannot be used for compute shader output
note: while analyzing entry point 'computeShader')");
}
// Using a clip_distances builtin attribute in vertex shader inputs should fail.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesInVertexShaderInputFail) {
Enable(wgsl::Extension::kClipDistances);
auto* s = Structure(
"MyInputs", Vector{Member("clipDistances", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
})});
Func("vertexShader",
Vector{
Param("arg", ty.Of(s)),
},
ty.vec4<f32>(),
Vector{
Return(Call(ty.vec4<f32>())),
},
Vector{
Stage(ast::PipelineStage::kVertex),
},
Vector{
Builtin(core::BuiltinValue::kPosition),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: '@builtin(clip_distances)' cannot be used for vertex shader input
note: while analyzing entry point 'vertexShader')");
}
// Using a clip_distances builtin attribute in vertex shader outputs should success.
TEST_F(ResolverClipDistancesExtensionTest, UseClipDistancesInVertexShaderOutputSuccess) {
Enable(wgsl::Extension::kClipDistances);
auto* s = Structure(
"MyOutputs",
Vector{Member("clipDistances", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
}),
Member("position", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)})});
Func("vertexShader", tint::Empty, ty.Of(s),
Vector{
Return(Call(ty.Of(s))),
},
Vector{
Stage(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve());
}
// Declaring a clip_distances builtin attribute more than once should fail.
TEST_F(ResolverClipDistancesExtensionTest, DuplicateClipDistancesDeclaration) {
Enable(wgsl::Extension::kClipDistances);
auto* s = Structure(
"MyOutputs",
Vector{Member("clipDistances1", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
}),
Member("clipDistances2", ty.array<f32, kMaxClipDistancesSize / 2>(),
Vector{
Builtin(Source{{12, 34}}, core::BuiltinValue::kClipDistances),
}),
Member("position", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)})});
Func("vertexShader", tint::Empty, ty.Of(s),
Vector{
Return(Call(ty.Of(s))),
},
Vector{
Stage(ast::PipelineStage::kVertex),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(error: '@builtin(clip_distances)' appears multiple times as pipeline output
note: while analyzing entry point 'vertexShader')");
}
} // namespace
} // namespace tint::resolver