validation: Validate interpolation attributes

They are only valid on entry point parameters and return types, and
struct members. They must only be used on floating point scalar and
vector types. If the interpolation type is flat, the sampling type
must not be specified.

Bug: tint:746
Change-Id: Iab17816bc9947a74593a5937bdf513ac9ec664f1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56241
Auto-Submit: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
James Price 2021-06-28 23:04:43 +00:00
parent cb0309ed6c
commit 989a8e4d62
4 changed files with 174 additions and 2 deletions

View File

@ -36,6 +36,7 @@
#include "src/ast/float_literal.h" #include "src/ast/float_literal.h"
#include "src/ast/i32.h" #include "src/ast/i32.h"
#include "src/ast/if_statement.h" #include "src/ast/if_statement.h"
#include "src/ast/interpolate_decoration.h"
#include "src/ast/loop_statement.h" #include "src/ast/loop_statement.h"
#include "src/ast/matrix.h" #include "src/ast/matrix.h"
#include "src/ast/member_accessor_expression.h" #include "src/ast/member_accessor_expression.h"
@ -1925,6 +1926,26 @@ class ProgramBuilder {
return create<ast::BuiltinDecoration>(source_, builtin); return create<ast::BuiltinDecoration>(source_, builtin);
} }
/// Creates an ast::InterpolateDecoration
/// @param source the source information
/// @param type the interpolation type
/// @param sampling the interpolation sampling
/// @returns the interpolate decoration pointer
ast::InterpolateDecoration* Interpolate(const Source& source,
ast::InterpolationType type,
ast::InterpolationSampling sampling) {
return create<ast::InterpolateDecoration>(source, type, sampling);
}
/// Creates an ast::InterpolateDecoration
/// @param type the interpolation type
/// @param sampling the interpolation sampling
/// @returns the interpolate decoration pointer
ast::InterpolateDecoration* Interpolate(ast::InterpolationType type,
ast::InterpolationSampling sampling) {
return create<ast::InterpolateDecoration>(source_, type, sampling);
}
/// Creates an ast::LocationDecoration /// Creates an ast::LocationDecoration
/// @param source the source information /// @param source the source information
/// @param location the location value /// @param location the location value

View File

@ -61,6 +61,7 @@ enum class DecorationKind {
kBinding, kBinding,
kBuiltin, kBuiltin,
kGroup, kGroup,
kInterpolate,
kLocation, kLocation,
kOverride, kOverride,
kOffset, kOffset,
@ -102,6 +103,10 @@ static ast::DecorationList createDecorations(const Source& source,
return {builder.Builtin(source, ast::Builtin::kPosition)}; return {builder.Builtin(source, ast::Builtin::kPosition)};
case DecorationKind::kGroup: case DecorationKind::kGroup:
return {builder.create<ast::GroupDecoration>(source, 1u)}; return {builder.create<ast::GroupDecoration>(source, 1u)};
case DecorationKind::kInterpolate:
return {builder.Interpolate(source, ast::InterpolationType::kLinear,
ast::InterpolationSampling::kCenter),
builder.Location(0)};
case DecorationKind::kLocation: case DecorationKind::kLocation:
return {builder.Location(source, 1)}; return {builder.Location(source, 1)};
case DecorationKind::kOverride: case DecorationKind::kOverride:
@ -150,6 +155,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -185,6 +191,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true},
TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -247,6 +254,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -282,6 +290,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true},
TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -335,6 +344,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -369,6 +379,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -408,7 +419,7 @@ TEST_P(StructMemberDecorationTest, IsValid) {
createDecorations(Source{{12, 34}}, *this, params.kind))}); createDecorations(Source{{12, 34}}, *this, params.kind))});
} else { } else {
members.push_back( members.push_back(
{Member("a", ty.i32(), {Member("a", ty.f32(),
createDecorations(Source{{12, 34}}, *this, params.kind))}); createDecorations(Source{{12, 34}}, *this, params.kind))});
} }
@ -431,6 +442,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true},
TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, true}, TestParams{DecorationKind::kOffset, true},
@ -492,6 +504,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -542,6 +555,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, true}, TestParams{DecorationKind::kOverride, true},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -590,6 +604,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -957,5 +972,101 @@ TEST_F(WorkgroupDecoration, DuplicateDecoration) {
} // namespace } // namespace
} // namespace WorkgroupDecorationTests } // namespace WorkgroupDecorationTests
namespace InterpolateTests {
namespace {
using InterpolateTest = ResolverTest;
struct Params {
ast::InterpolationType type;
ast::InterpolationSampling sampling;
bool should_pass;
};
struct TestWithParams : ResolverTestWithParam<Params> {};
using InterpolateParameterTest = TestWithParams;
TEST_P(InterpolateParameterTest, All) {
auto& params = GetParam();
Func("main",
ast::VariableList{Param(
"a", ty.f32(),
{Location(0),
Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
ty.void_(), {},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
if (params.should_pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: flat interpolation attribute must not have a "
"sampling parameter");
}
}
INSTANTIATE_TEST_SUITE_P(
ResolverDecorationValidationTest,
InterpolateParameterTest,
testing::Values(Params{ast::InterpolationType::kPerspective,
ast::InterpolationSampling::kNone, true},
Params{ast::InterpolationType::kPerspective,
ast::InterpolationSampling::kCenter, true},
Params{ast::InterpolationType::kPerspective,
ast::InterpolationSampling::kCentroid, true},
Params{ast::InterpolationType::kPerspective,
ast::InterpolationSampling::kSample, true},
Params{ast::InterpolationType::kLinear,
ast::InterpolationSampling::kNone, true},
Params{ast::InterpolationType::kLinear,
ast::InterpolationSampling::kCenter, true},
Params{ast::InterpolationType::kLinear,
ast::InterpolationSampling::kCentroid, true},
Params{ast::InterpolationType::kLinear,
ast::InterpolationSampling::kSample, true},
// flat interpolation must not have a sampling type
Params{ast::InterpolationType::kFlat,
ast::InterpolationSampling::kNone, true},
Params{ast::InterpolationType::kFlat,
ast::InterpolationSampling::kCenter, false},
Params{ast::InterpolationType::kFlat,
ast::InterpolationSampling::kCentroid, false},
Params{ast::InterpolationType::kFlat,
ast::InterpolationSampling::kSample, false}));
TEST_F(InterpolateTest, Parameter_NotFloatingPoint) {
Func("main",
ast::VariableList{
Param("a", ty.i32(),
{Location(0),
Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
ast::InterpolationSampling::kNone)})},
ty.void_(), {},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of interpolate attribute must be floating "
"point scalar or vector");
}
TEST_F(InterpolateTest, ReturnType_NotFloatingPoint) {
Func(
"main", {}, ty.i32(), {Return(1)},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
{Location(0), Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
ast::InterpolationSampling::kNone)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of interpolate attribute must be floating "
"point scalar or vector");
}
} // namespace
} // namespace InterpolateTests
} // namespace resolver } // namespace resolver
} // namespace tint } // namespace tint

View File

@ -30,6 +30,7 @@
#include "src/ast/fallthrough_statement.h" #include "src/ast/fallthrough_statement.h"
#include "src/ast/if_statement.h" #include "src/ast/if_statement.h"
#include "src/ast/internal_decoration.h" #include "src/ast/internal_decoration.h"
#include "src/ast/interpolate_decoration.h"
#include "src/ast/loop_statement.h" #include "src/ast/loop_statement.h"
#include "src/ast/matrix.h" #include "src/ast/matrix.h"
#include "src/ast/override_decoration.h" #include "src/ast/override_decoration.h"
@ -719,7 +720,8 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
} }
} else { } else {
bool is_shader_io_decoration = bool is_shader_io_decoration =
deco->IsAnyOf<ast::BuiltinDecoration, ast::LocationDecoration>(); deco->IsAnyOf<ast::BuiltinDecoration, ast::InterpolateDecoration,
ast::LocationDecoration>();
bool has_io_storage_class = bool has_io_storage_class =
info->storage_class == ast::StorageClass::kInput || info->storage_class == ast::StorageClass::kInput ||
info->storage_class == ast::StorageClass::kOutput; info->storage_class == ast::StorageClass::kOutput;
@ -947,6 +949,10 @@ bool Resolver::ValidateParameter(const ast::Function* func,
if (!ValidateBuiltinDecoration(builtin, info->type)) { if (!ValidateBuiltinDecoration(builtin, info->type)) {
return false; return false;
} }
} else if (auto* interpolate = deco->As<ast::InterpolateDecoration>()) {
if (!ValidateInterpolateDecoration(interpolate, info->type)) {
return false;
}
} else if (!deco->IsAnyOf<ast::LocationDecoration, } else if (!deco->IsAnyOf<ast::LocationDecoration,
ast::InternalDecoration>() && ast::InternalDecoration>() &&
!(IsValidationDisabled( !(IsValidationDisabled(
@ -1015,6 +1021,29 @@ bool Resolver::ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
return true; return true;
} }
bool Resolver::ValidateInterpolateDecoration(
const ast::InterpolateDecoration* deco,
const sem::Type* storage_type) {
auto* type = storage_type->UnwrapRef();
if (!type->is_float_scalar_or_vector()) {
AddError(
"store type of interpolate attribute must be floating point scalar or "
"vector",
deco->source());
return false;
}
if (deco->type() == ast::InterpolationType::kFlat &&
deco->sampling() != ast::InterpolationSampling::kNone) {
AddError("flat interpolation attribute must not have a sampling parameter",
deco->source());
return false;
}
return true;
}
bool Resolver::ValidateFunction(const ast::Function* func, bool Resolver::ValidateFunction(const ast::Function* func,
const FunctionInfo* info) { const FunctionInfo* info) {
auto func_it = symbol_to_function_.find(func->symbol()); auto func_it = symbol_to_function_.find(func->symbol());
@ -1101,6 +1130,10 @@ bool Resolver::ValidateFunction(const ast::Function* func,
if (!ValidateBuiltinDecoration(builtin, info->return_type)) { if (!ValidateBuiltinDecoration(builtin, info->return_type)) {
return false; return false;
} }
} else if (auto* interpolate = deco->As<ast::InterpolateDecoration>()) {
if (!ValidateInterpolateDecoration(interpolate, info->return_type)) {
return false;
}
} else if (!deco->Is<ast::LocationDecoration>()) { } else if (!deco->Is<ast::LocationDecoration>()) {
AddError("decoration is not valid for entry point return types", AddError("decoration is not valid for entry point return types",
deco->source()); deco->source());
@ -3364,6 +3397,7 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
for (auto* deco : member->Declaration()->decorations()) { for (auto* deco : member->Declaration()->decorations()) {
if (!(deco->Is<ast::BuiltinDecoration>() || if (!(deco->Is<ast::BuiltinDecoration>() ||
deco->Is<ast::InterpolateDecoration>() ||
deco->Is<ast::LocationDecoration>() || deco->Is<ast::LocationDecoration>() ||
deco->Is<ast::StructMemberOffsetDecoration>() || deco->Is<ast::StructMemberOffsetDecoration>() ||
deco->Is<ast::StructMemberSizeDecoration>() || deco->Is<ast::StructMemberSizeDecoration>() ||
@ -3376,6 +3410,10 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
if (!ValidateBuiltinDecoration(builtin, member->Type())) { if (!ValidateBuiltinDecoration(builtin, member->Type())) {
return false; return false;
} }
} else if (auto* interpolate = deco->As<ast::InterpolateDecoration>()) {
if (!ValidateInterpolateDecoration(interpolate, member->Type())) {
return false;
}
} }
} }

View File

@ -276,6 +276,8 @@ class Resolver {
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info); bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info); bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);
bool ValidateGlobalVariable(const VariableInfo* var); bool ValidateGlobalVariable(const VariableInfo* var);
bool ValidateInterpolateDecoration(const ast::InterpolateDecoration* deco,
const sem::Type* storage_type);
bool ValidateMatrix(const sem::Matrix* matirx_type, const Source& source); bool ValidateMatrix(const sem::Matrix* matirx_type, const Source& source);
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor, bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Matrix* matrix_type); const sem::Matrix* matrix_type);