tint/writer/glsl: Fix emitting float modulo with mixing type

This patch fix the GLSL writer issue that emit only one helper function
when using both `v % s`, `s % v` and `v % vs in the shader, where `s` is
of `f32` and `v` is a vector of `f32`. Unittests are added for GLSL.

Bug: tint:1614
Change-Id: Ia89ae010341b9c88b8101cc6febab7d83c96bb17
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96085
Reviewed-by: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
Zhaoming Jiang 2022-07-15 11:05:29 +00:00 committed by Dawn LUCI CQ
parent 32bdf41ed3
commit e0ecd86e73
3 changed files with 92 additions and 2 deletions

View File

@ -500,7 +500,9 @@ bool GeneratorImpl::EmitBitwiseBoolOp(std::ostream& out, const ast::BinaryExpres
bool GeneratorImpl::EmitFloatModulo(std::ostream& out, const ast::BinaryExpression* expr) { bool GeneratorImpl::EmitFloatModulo(std::ostream& out, const ast::BinaryExpression* expr) {
std::string fn; std::string fn;
auto* ret_ty = TypeOf(expr)->UnwrapRef(); auto* ret_ty = TypeOf(expr)->UnwrapRef();
fn = utils::GetOrCreate(float_modulo_funcs_, ret_ty, [&]() -> std::string { auto* lhs_ty = TypeOf(expr->lhs)->UnwrapRef();
auto* rhs_ty = TypeOf(expr->rhs)->UnwrapRef();
fn = utils::GetOrCreate(float_modulo_funcs_, {lhs_ty, rhs_ty}, [&]() -> std::string {
TextBuffer b; TextBuffer b;
TINT_DEFER(helpers_.Append(b)); TINT_DEFER(helpers_.Append(b));

View File

@ -492,6 +492,23 @@ class GeneratorImpl : public TextGenerator {
}; };
}; };
/// The structure holding both type of two operands for a binary operator.
struct BinaryOperandType {
const sem::Type* lhs_type;
const sem::Type* rhs_type;
bool operator==(const BinaryOperandType& rhs) const {
return lhs_type == rhs.lhs_type && rhs_type == rhs.rhs_type;
}
/// Hasher is a std::hash function for BinaryOperandType
struct Hasher {
/// @param i the BinaryOperandType to hash
/// @returns the hash of `i`
inline std::size_t operator()(const BinaryOperandType& i) const {
return utils::Hash(i.lhs_type, i.rhs_type);
}
};
};
/// CallBuiltinHelper will call the builtin helper function, creating it /// CallBuiltinHelper will call the builtin helper function, creating it
/// if it hasn't been built already. If the builtin needs to be built then /// if it hasn't been built already. If the builtin needs to be built then
/// CallBuiltinHelper will generate the function signature and will call /// CallBuiltinHelper will generate the function signature and will call
@ -522,7 +539,8 @@ class GeneratorImpl : public TextGenerator {
std::unordered_map<const sem::Builtin*, std::string> builtins_; std::unordered_map<const sem::Builtin*, std::string> builtins_;
std::unordered_map<const sem::Vector*, std::string> dynamic_vector_write_; std::unordered_map<const sem::Vector*, std::string> dynamic_vector_write_;
std::unordered_map<const sem::Vector*, std::string> int_dot_funcs_; std::unordered_map<const sem::Vector*, std::string> int_dot_funcs_;
std::unordered_map<const sem::Type*, std::string> float_modulo_funcs_; std::unordered_map<BinaryOperandType, std::string, BinaryOperandType::Hasher>
float_modulo_funcs_;
std::unordered_set<const sem::Struct*> emitted_structs_; std::unordered_set<const sem::Struct*> emitted_structs_;
bool requires_oes_sample_variables_ = false; bool requires_oes_sample_variables_ = false;
bool requires_default_precision_qualifier_ = false; bool requires_default_precision_qualifier_ = false;

View File

@ -273,6 +273,76 @@ TEST_F(GlslGeneratorImplTest_Binary, ModVec3F32) {
EXPECT_EQ(out.str(), "tint_float_modulo(a, b)"); EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
} }
TEST_F(GlslGeneratorImplTest_Binary, ModVec3F32ScalarF32) {
GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
WrapInFunction(expr);
GeneratorImpl& gen = Build();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
}
TEST_F(GlslGeneratorImplTest_Binary, ModScalarF32Vec3F32) {
GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
WrapInFunction(expr);
GeneratorImpl& gen = Build();
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.error();
EXPECT_EQ(out.str(), "tint_float_modulo(a, b)");
}
TEST_F(GlslGeneratorImplTest_Binary, ModMixedVec3ScalarF32) {
GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
auto* expr_vec_mod_vec =
create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("a"));
auto* expr_vec_mod_scalar =
create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
auto* expr_scalar_mod_vec =
create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("b"), Expr("a"));
WrapInFunction(expr_vec_mod_vec, expr_vec_mod_scalar, expr_scalar_mod_vec);
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
vec3 tint_float_modulo(vec3 lhs, vec3 rhs) {
return (lhs - rhs * trunc(lhs / rhs));
}
vec3 tint_float_modulo_1(vec3 lhs, float rhs) {
return (lhs - rhs * trunc(lhs / rhs));
}
vec3 tint_float_modulo_2(float lhs, vec3 rhs) {
return (lhs - rhs * trunc(lhs / rhs));
}
vec3 a = vec3(0.0f, 0.0f, 0.0f);
float b = 0.0f;
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void test_function() {
vec3 tint_symbol = tint_float_modulo(a, a);
vec3 tint_symbol_1 = tint_float_modulo_1(a, b);
vec3 tint_symbol_2 = tint_float_modulo_2(b, a);
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Binary, Logical_Multi) { TEST_F(GlslGeneratorImplTest_Binary, Logical_Multi) {
// (a && b) || (c || d) // (a && b) || (c || d)
GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate); GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);