diff --git a/src/ast/intrinsic.cc b/src/ast/intrinsic.cc index 226581b596..164632ae0e 100644 --- a/src/ast/intrinsic.cc +++ b/src/ast/intrinsic.cc @@ -37,9 +37,16 @@ bool IsFloatClassificationIntrinsic(const std::string& name) { name == "is_normal"; } +bool IsTextureOperationIntrinsic(const std::string& name) { + return name == "texture_load" || name == "texture_sample" || + name == "texture_sample_level" || name == "texture_sample_bias" || + name == "texture_sample_compare"; +} + bool IsIntrinsic(const std::string& name) { return IsDerivative(name) || name == "all" || name == "any" || - IsFloatClassificationIntrinsic(name) || name == "dot" || + IsFloatClassificationIntrinsic(name) || + IsTextureOperationIntrinsic(name) || name == "dot" || name == "outer_product" || name == "select"; } diff --git a/src/ast/intrinsic.h b/src/ast/intrinsic.h index b11a73c1ab..1196db9c17 100644 --- a/src/ast/intrinsic.h +++ b/src/ast/intrinsic.h @@ -41,6 +41,11 @@ bool IsDerivative(const std::string& name); /// @returns true if the given |name| is a float intrinsic bool IsFloatClassificationIntrinsic(const std::string& name); +/// Determines if the given |name| is a texture operation intrinsic +/// @param name the name to check +/// @returns true if the given |name| is a texture operation intrinsic +bool IsTextureOperationIntrinsic(const std::string& name); + /// Determines if the given |name| is an intrinsic /// @param name the name to check /// @returns true if the given |name| is an intrinsic diff --git a/src/type_determiner.cc b/src/type_determiner.cc index 9f2e90df56..04ec17b30d 100644 --- a/src/type_determiner.cc +++ b/src/type_determiner.cc @@ -44,7 +44,10 @@ #include "src/ast/type/i32_type.h" #include "src/ast/type/matrix_type.h" #include "src/ast/type/pointer_type.h" +#include "src/ast/type/sampled_texture_type.h" +#include "src/ast/type/storage_texture_type.h" #include "src/ast/type/struct_type.h" +#include "src/ast/type/texture_type.h" #include "src/ast/type/u32_type.h" #include "src/ast/type/vector_type.h" #include "src/ast/type_constructor_expression.h" @@ -540,6 +543,7 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* expr) { return true; } +// TODO(tommek): Update names to camel case bool TypeDeterminer::DetermineIntrinsic(const std::string& name, ast::CallExpression* expr) { if (ast::intrinsic::IsDerivative(name)) { @@ -584,6 +588,46 @@ bool TypeDeterminer::DetermineIntrinsic(const std::string& name, } return true; } + if (ast::intrinsic::IsTextureOperationIntrinsic(name)) { + uint32_t num_of_params = + (name == "texture_load" || name == "texture_sample") ? 3 : 4; + if (expr->params().size() != num_of_params) { + set_error(expr->source(), + "incorrect number of parameters for " + name + ", got " + + std::to_string(expr->params().size()) + " and expected " + + std::to_string(num_of_params)); + return false; + } + + if (name == "texture_sample_compare") { + expr->func()->set_result_type( + ctx_.type_mgr().Get(std::make_unique())); + return true; + } + + auto& texture_param = expr->params()[0]; + if (!DetermineResultType(texture_param.get())) { + return false; + } + if (!texture_param->result_type()->UnwrapPtrIfNeeded()->IsTexture()) { + set_error(expr->source(), "invalid first argument for " + name); + return false; + } + ast::type::TextureType* texture = + texture_param->result_type()->UnwrapPtrIfNeeded()->AsTexture(); + + if (!texture->IsStorage() && !texture->IsSampled()) { + set_error(expr->source(), "invalid texture for " + name); + return false; + } + + expr->func()->set_result_type( + ctx_.type_mgr().Get(std::make_unique( + texture->IsStorage() ? texture->AsStorage()->type() + : texture->AsSampled()->type(), + 4))); + return true; + } if (name == "dot") { expr->func()->set_result_type( ctx_.type_mgr().Get(std::make_unique())); diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc index 092e947452..0ba43ef732 100644 --- a/src/type_determiner_test.cc +++ b/src/type_determiner_test.cc @@ -46,11 +46,16 @@ #include "src/ast/type/alias_type.h" #include "src/ast/type/array_type.h" #include "src/ast/type/bool_type.h" +#include "src/ast/type/depth_texture_type.h" #include "src/ast/type/f32_type.h" #include "src/ast/type/i32_type.h" #include "src/ast/type/matrix_type.h" #include "src/ast/type/pointer_type.h" +#include "src/ast/type/sampled_texture_type.h" +#include "src/ast/type/sampler_type.h" +#include "src/ast/type/storage_texture_type.h" #include "src/ast/type/struct_type.h" +#include "src/ast/type/texture_type.h" #include "src/ast/type/u32_type.h" #include "src/ast/type/vector_type.h" #include "src/ast/type_constructor_expression.h" @@ -80,6 +85,7 @@ class TypeDeterminerHelper { TypeDeterminer* td() const { return td_.get(); } ast::Module* mod() { return &mod_; } + Context* ctx() { return &ctx_; } private: Context ctx_; @@ -1801,6 +1807,382 @@ INSTANTIATE_TEST_SUITE_P( Intrinsic_FloatMethod, testing::Values("is_inf", "is_nan", "is_finite", "is_normal")); +enum class TextureType { kF32, kI32, kU32 }; +inline std::ostream& operator<<(std::ostream& out, TextureType data) { + if (data == TextureType::kF32) { + out << "f32"; + } else if (data == TextureType::kI32) { + out << "i32"; + } else { + out << "u32"; + } + return out; +} + +struct TextureTestParams { + ast::type::TextureDimension dim; + TextureType type = TextureType::kF32; + ast::type::ImageFormat format = ast::type::ImageFormat::kR16Float; +}; +inline std::ostream& operator<<(std::ostream& out, TextureTestParams data) { + out << data.dim << "_" << data.type; + return out; +} + +class Intrinsic_TextureOperation + : public TypeDeterminerTestWithParam { + public: + std::unique_ptr get_coords_type( + ast::type::TextureDimension dim, + ast::type::Type* type) { + if (dim == ast::type::TextureDimension::k1d) { + if (type->IsI32()) { + return std::make_unique(); + } else if (type->IsU32()) { + return std::make_unique(); + } else { + return std::make_unique(); + } + } else if (dim == ast::type::TextureDimension::k1dArray || + dim == ast::type::TextureDimension::k2d || + dim == ast::type::TextureDimension::k2dMs) { + return std::make_unique(type, 2); + } else if (dim == ast::type::TextureDimension::kCubeArray) { + return std::make_unique(type, 4); + } else { + return std::make_unique(type, 3); + } + } + + void add_call_param(std::string name, + ast::type::Type* type, + ast::ExpressionList* call_params) { + auto var = + std::make_unique(name, ast::StorageClass::kNone, type); + mod()->AddGlobalVariable(std::move(var)); + call_params->push_back(std::make_unique(name)); + } + + std::unique_ptr subtype(TextureType type) { + if (type == TextureType::kF32) { + return std::make_unique(); + } + if (type == TextureType::kI32) { + return std::make_unique(); + } + return std::make_unique(); + } +}; + +using Intrinsic_StorageTextureOperation = Intrinsic_TextureOperation; +TEST_P(Intrinsic_StorageTextureOperation, TextureLoadRo) { + auto dim = GetParam().dim; + auto type = GetParam().type; + auto format = GetParam().format; + + ast::type::I32Type i32; + auto coords_type = get_coords_type(dim, &i32); + + ast::type::Type* texture_type = + ctx()->type_mgr().Get(std::make_unique( + dim, ast::type::StorageAccess::kRead, format)); + + ast::ExpressionList call_params; + + add_call_param("texture", texture_type, &call_params); + add_call_param("coords", coords_type.get(), &call_params); + add_call_param("lod", &i32, &call_params); + + ast::CallExpression expr( + std::make_unique("texture_load"), + std::move(call_params)); + + EXPECT_TRUE(td()->Determine()); + EXPECT_TRUE(td()->DetermineResultType(&expr)); + + ASSERT_NE(expr.result_type(), nullptr); + ASSERT_TRUE(expr.result_type()->IsVector()); + if (type == TextureType::kF32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32()); + } else if (type == TextureType::kI32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32()); + } else { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32()); + } + EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u); +} + +INSTANTIATE_TEST_SUITE_P( + TypeDeterminerTest, + Intrinsic_StorageTextureOperation, + testing::Values( + TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kF32, + ast::type::ImageFormat::kR16Float}, + TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kI32, + ast::type::ImageFormat::kR16Sint}, + TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kU32, + ast::type::ImageFormat::kR8Unorm}, + TextureTestParams{ast::type::TextureDimension::k1dArray, + TextureType::kF32, ast::type::ImageFormat::kR16Float}, + TextureTestParams{ast::type::TextureDimension::k1dArray, + TextureType::kI32, ast::type::ImageFormat::kR16Sint}, + TextureTestParams{ast::type::TextureDimension::k1dArray, + TextureType::kU32, ast::type::ImageFormat::kR8Unorm}, + TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kF32, + ast::type::ImageFormat::kR16Float}, + TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kI32, + ast::type::ImageFormat::kR16Sint}, + TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kU32, + ast::type::ImageFormat::kR8Unorm}, + TextureTestParams{ast::type::TextureDimension::k2dArray, + TextureType::kF32, ast::type::ImageFormat::kR16Float}, + TextureTestParams{ast::type::TextureDimension::k2dArray, + TextureType::kI32, ast::type::ImageFormat::kR16Sint}, + TextureTestParams{ast::type::TextureDimension::k2dArray, + TextureType::kU32, ast::type::ImageFormat::kR8Unorm}, + TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kF32, + ast::type::ImageFormat::kR16Float}, + TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kI32, + ast::type::ImageFormat::kR16Sint}, + TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kU32, + ast::type::ImageFormat::kR8Unorm})); + +using Intrinsic_SampledTextureOperation = Intrinsic_TextureOperation; +TEST_P(Intrinsic_SampledTextureOperation, TextureLoadSampled) { + auto dim = GetParam().dim; + auto type = GetParam().type; + + ast::type::I32Type i32; + std::unique_ptr s = subtype(type); + auto coords_type = get_coords_type(dim, &i32); + auto texture_type = + std::make_unique(dim, s.get()); + + ast::ExpressionList call_params; + + add_call_param("texture", texture_type.get(), &call_params); + add_call_param("coords", coords_type.get(), &call_params); + add_call_param("lod", &i32, &call_params); + + ast::CallExpression expr( + std::make_unique("texture_load"), + std::move(call_params)); + + EXPECT_TRUE(td()->Determine()); + EXPECT_TRUE(td()->DetermineResultType(&expr)); + + ASSERT_NE(expr.result_type(), nullptr); + ASSERT_TRUE(expr.result_type()->IsVector()); + if (type == TextureType::kF32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32()); + } else if (type == TextureType::kI32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32()); + } else { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32()); + } + EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u); +} + +TEST_P(Intrinsic_SampledTextureOperation, TextureSample) { + auto dim = GetParam().dim; + auto type = GetParam().type; + + auto s = subtype(type); + ast::type::F32Type f32; + auto sampler_type = std::make_unique( + ast::type::SamplerKind::kSampler); + auto coords_type = get_coords_type(dim, &f32); + auto texture_type = + std::make_unique(dim, s.get()); + + ast::ExpressionList call_params; + + add_call_param("texture", texture_type.get(), &call_params); + add_call_param("sampler", sampler_type.get(), &call_params); + add_call_param("coords", coords_type.get(), &call_params); + + ast::CallExpression expr( + std::make_unique("texture_sample"), + std::move(call_params)); + + EXPECT_TRUE(td()->Determine()); + EXPECT_TRUE(td()->DetermineResultType(&expr)); + + ASSERT_NE(expr.result_type(), nullptr); + ASSERT_TRUE(expr.result_type()->IsVector()); + if (type == TextureType::kF32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32()); + } else if (type == TextureType::kI32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32()); + } else { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32()); + } + EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u); +} + +TEST_P(Intrinsic_SampledTextureOperation, TextureSampleLevel) { + auto dim = GetParam().dim; + auto type = GetParam().type; + + ast::type::F32Type f32; + auto s = subtype(type); + auto sampler_type = std::make_unique( + ast::type::SamplerKind::kSampler); + auto coords_type = get_coords_type(dim, &f32); + auto texture_type = + std::make_unique(dim, s.get()); + + ast::ExpressionList call_params; + + add_call_param("texture", texture_type.get(), &call_params); + add_call_param("sampler", sampler_type.get(), &call_params); + add_call_param("coords", coords_type.get(), &call_params); + add_call_param("lod", &f32, &call_params); + + ast::CallExpression expr( + std::make_unique("texture_sample_level"), + std::move(call_params)); + + EXPECT_TRUE(td()->Determine()); + EXPECT_TRUE(td()->DetermineResultType(&expr)); + + ASSERT_NE(expr.result_type(), nullptr); + ASSERT_TRUE(expr.result_type()->IsVector()); + if (type == TextureType::kF32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32()); + } else if (type == TextureType::kI32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32()); + } else { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32()); + } + EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u); +} + +TEST_P(Intrinsic_SampledTextureOperation, TextureSampleBias) { + auto dim = GetParam().dim; + auto type = GetParam().type; + + ast::type::F32Type f32; + auto s = subtype(type); + auto sampler_type = std::make_unique( + ast::type::SamplerKind::kSampler); + auto coords_type = get_coords_type(dim, &f32); + auto texture_type = + std::make_unique(dim, s.get()); + + ast::ExpressionList call_params; + + add_call_param("texture", texture_type.get(), &call_params); + add_call_param("sampler", sampler_type.get(), &call_params); + add_call_param("coords", coords_type.get(), &call_params); + add_call_param("bias", &f32, &call_params); + + ast::CallExpression expr( + std::make_unique("texture_sample_bias"), + std::move(call_params)); + + EXPECT_TRUE(td()->Determine()); + EXPECT_TRUE(td()->DetermineResultType(&expr)); + + ASSERT_NE(expr.result_type(), nullptr); + ASSERT_TRUE(expr.result_type()->IsVector()); + if (type == TextureType::kF32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32()); + } else if (type == TextureType::kI32) { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32()); + } else { + EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32()); + } + EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u); +} + +INSTANTIATE_TEST_SUITE_P( + TypeDeterminerTest, + Intrinsic_SampledTextureOperation, + testing::Values( + TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::k1dArray, + TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k1dArray, + TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k1dArray, + TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::k2dArray, + TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k2dArray, + TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k2dArray, + TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::k2dMs, + TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k2dMs, + TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k2dMs, + TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::k2dMsArray, + TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k2dMsArray, + TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k2dMsArray, + TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::kCube, + TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::kCube, + TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::kCube, + TextureType::kU32}, + TextureTestParams{ast::type::TextureDimension::kCubeArray, + TextureType::kF32}, + TextureTestParams{ast::type::TextureDimension::kCubeArray, + TextureType::kI32}, + TextureTestParams{ast::type::TextureDimension::kCubeArray, + TextureType::kU32})); + +using Intrinsic_DepthTextureOperation = Intrinsic_TextureOperation; +TEST_P(Intrinsic_DepthTextureOperation, TextureSampleCompare) { + auto dim = GetParam().dim; + + ast::type::F32Type f32; + auto sampler_type = std::make_unique( + ast::type::SamplerKind::kComparisonSampler); + auto coords_type = get_coords_type(dim, &f32); + auto texture_type = std::make_unique(dim); + + ast::ExpressionList call_params; + + add_call_param("texture", texture_type.get(), &call_params); + add_call_param("sampler_comparison", sampler_type.get(), &call_params); + add_call_param("coords", coords_type.get(), &call_params); + add_call_param("depth_reference", &f32, &call_params); + + ast::CallExpression expr( + std::make_unique("texture_sample_compare"), + std::move(call_params)); + + EXPECT_TRUE(td()->Determine()); + EXPECT_TRUE(td()->DetermineResultType(&expr)); + + ASSERT_NE(expr.result_type(), nullptr); + EXPECT_TRUE(expr.result_type()->IsF32()); +} + +INSTANTIATE_TEST_SUITE_P( + TypeDeterminerTest, + Intrinsic_DepthTextureOperation, + testing::Values(TextureTestParams{ast::type::TextureDimension::k2d}, + TextureTestParams{ast::type::TextureDimension::k2dArray}, + TextureTestParams{ast::type::TextureDimension::kCube}, + TextureTestParams{ + ast::type::TextureDimension::kCubeArray})); + TEST_F(TypeDeterminerTest, Intrinsic_Dot) { ast::type::F32Type f32; ast::type::VectorType vec3(&f32, 3);