blob: 23173a6814b934fb5c131c7ddc9726f72f795821 [file] [log] [blame]
// Copyright 2025 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 "src/tint/lang/wgsl/resolver/resolver.h"
#include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
namespace tint::resolver {
namespace {
using namespace tint::core::fluent_types; // NOLINT
using namespace tint::core::number_suffixes; // NOLINT
using ResolverTexelBufferTest = ResolverTest;
struct FormatParams {
core::TexelFormat format;
bool is_valid;
};
#define VALID_FORMAT_CASE(FMT) \
FormatParams { \
core::TexelFormat::FMT, true \
}
static constexpr FormatParams kFormatCases[] = {
VALID_FORMAT_CASE(kBgra8Unorm), VALID_FORMAT_CASE(kR32Float), VALID_FORMAT_CASE(kR32Sint),
VALID_FORMAT_CASE(kR32Uint), VALID_FORMAT_CASE(kR8Unorm), VALID_FORMAT_CASE(kRg32Float),
VALID_FORMAT_CASE(kRg32Sint), VALID_FORMAT_CASE(kRg32Uint), VALID_FORMAT_CASE(kRgba16Float),
VALID_FORMAT_CASE(kRgba16Sint), VALID_FORMAT_CASE(kRgba16Uint), VALID_FORMAT_CASE(kRgba32Float),
VALID_FORMAT_CASE(kRgba32Sint), VALID_FORMAT_CASE(kRgba32Uint), VALID_FORMAT_CASE(kRgba8Sint),
VALID_FORMAT_CASE(kRgba8Snorm), VALID_FORMAT_CASE(kRgba8Uint), VALID_FORMAT_CASE(kRgba8Unorm),
};
#undef VALID_FORMAT_CASE
using TexelBufferFormatTest = ResolverTestWithParam<FormatParams>;
// Tests that the texel_buffer format is valid or invalid based on the
// format parameter.
TEST_P(TexelBufferFormatTest, Usage) {
auto& params = GetParam();
Require(wgsl::LanguageFeature::kTexelBuffers);
auto tb = ty(Source{{12, 34}}, "texel_buffer", tint::ToString(params.format),
tint::ToString(core::Access::kRead));
GlobalVar("a", tb, Group(0_a), Binding(0_a));
if (params.is_valid) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: image format must be one of the texel formats "
"specified for texel buffers in https://gpuweb.github.io/"
"gpuweb/wgsl/#texel-formats");
}
}
INSTANTIATE_TEST_SUITE_P(ResolverTexelBufferTest,
TexelBufferFormatTest,
testing::ValuesIn(kFormatCases));
// Requires 2 template arguments: format and access
TEST_F(ResolverTexelBufferTest, MissingAccess) {
Require(wgsl::LanguageFeature::kTexelBuffers);
auto tb = ty(Source{{12, 34}}, "texel_buffer", "rgba32float");
GlobalVar("a", tb, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(12:34 error: 'texel_buffer' requires 2 template arguments)");
}
// Access must be read or read_write
TEST_F(ResolverTexelBufferTest, InvalidAccess) {
Require(wgsl::LanguageFeature::kTexelBuffers);
auto tb =
ty(Source{{12, 34}}, "texel_buffer", "rgba32float", Expr(Source{{12, 34}}, "read_only"));
GlobalVar("a", tb, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: unresolved access 'read_only'
12:34 note: Possible values: 'read', 'read_write', 'write')");
}
// Access must be read or read_write, not write
TEST_F(ResolverTexelBufferTest, WriteOnlyAccess) {
Require(wgsl::LanguageFeature::kTexelBuffers);
auto tb = ty(Source{{12, 34}}, "texel_buffer", "rgba32float", "write");
GlobalVar("a", tb, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(12:34 error: texel_buffer access must be read or read_write)");
}
// Requires the texel_buffer language feature to be enabled
TEST_F(ResolverTexelBufferTest, FeatureRequired) {
auto tb = ty(Source{{12, 34}}, "texel_buffer", "rgba32float", "read");
GlobalVar("a", tb, Group(0_a), Binding(0_a));
Resolver resolver{this, wgsl::AllowedFeatures{}};
EXPECT_FALSE(resolver.Resolve());
EXPECT_EQ(resolver.error(),
"12:34 error: use of 'texel_buffer' requires the texel_buffer language "
"feature, which is not allowed in the current environment");
}
enum class SubtypeKind {
kF16,
kI8,
kU8,
};
struct SubtypeParams {
SubtypeKind kind;
};
static constexpr SubtypeParams kInvalidSubtypeCases[] = {
{SubtypeKind::kF16},
{SubtypeKind::kI8},
{SubtypeKind::kU8},
};
using TexelBufferSubtypeTest = ResolverTestWithParam<SubtypeParams>;
// Storage subtype must be f32, i32 or u32
TEST_P(TexelBufferSubtypeTest, InvalidSubtype) {
auto& params = GetParam();
Require(wgsl::LanguageFeature::kTexelBuffers);
const core::type::Type* subtype = nullptr;
switch (params.kind) {
case SubtypeKind::kF16:
subtype = create<core::type::F16>();
break;
case SubtypeKind::kI8:
subtype = create<core::type::I8>();
break;
case SubtypeKind::kU8:
subtype = create<core::type::U8>();
break;
}
auto* tb_ty = create<core::type::TexelBuffer>(core::TexelFormat::kRgba32Float,
core::Access::kRead, subtype);
EXPECT_FALSE(v()->TexelBuffer(tb_ty, Source{{12, 34}}));
EXPECT_EQ(r()->error(),
"12:34 error: texel_buffer<format, access>: storage subtype must "
"be f32, i32 or u32");
}
INSTANTIATE_TEST_SUITE_P(ResolverTexelBufferTest,
TexelBufferSubtypeTest,
testing::ValuesIn(kInvalidSubtypeCases));
// texel_buffer used as struct member
TEST_F(ResolverTexelBufferTest, StructMember) {
Require(wgsl::LanguageFeature::kTexelBuffers);
auto* s = Structure(
"S", Vector{
Member("buf", ty(Source{{12, 34}}, "texel_buffer", "rgba32float", "read")),
});
GlobalVar("g", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(0_a),
Group(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"error: texel_buffer<rgba32float, read> cannot be used as the "
"type of a structure member");
}
// texel_buffer used as function parameter
TEST_F(ResolverTexelBufferTest, FunctionParameter) {
Require(wgsl::LanguageFeature::kTexelBuffers);
Func("f", Vector{Param("p", ty(Source{{12, 34}}, "texel_buffer", "rgba32float", "read_write"))},
ty.void_(), Empty);
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
// Global variable missing binding or group attributes
TEST_F(ResolverTexelBufferTest, MissingBinding) {
Require(wgsl::LanguageFeature::kTexelBuffers);
GlobalVar(Source{{12, 34}}, "G", ty("texel_buffer", "rgba32float", "read"));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: resource variables require '@group' and "
"'@binding' attributes");
}
} // namespace
} // namespace tint::resolver