mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-06 22:53:35 +00:00
[hlsl-writer] Generate intrinsics.
This CL adds the beginning of intrinsic emission for the HLSL backend. The `outer_product`, `is_normal` and `select` intrinsics are currently missing. Bug: tint:7 Change-Id: Ice7a2b285eeb52041e3accd9751e127d6c5a0177 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/26927 Commit-Queue: dan sinclair <dsinclair@chromium.org> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
3739a23f24
commit
a1087ee513
1
BUILD.gn
1
BUILD.gn
@ -1073,6 +1073,7 @@ source_set("tint_unittests_hlsl_writer_src") {
|
|||||||
"src/writer/hlsl/generator_impl_function_test.cc",
|
"src/writer/hlsl/generator_impl_function_test.cc",
|
||||||
"src/writer/hlsl/generator_impl_identifier_test.cc",
|
"src/writer/hlsl/generator_impl_identifier_test.cc",
|
||||||
"src/writer/hlsl/generator_impl_if_test.cc",
|
"src/writer/hlsl/generator_impl_if_test.cc",
|
||||||
|
"src/writer/hlsl/generator_impl_intrinsic_test.cc",
|
||||||
"src/writer/hlsl/generator_impl_loop_test.cc",
|
"src/writer/hlsl/generator_impl_loop_test.cc",
|
||||||
"src/writer/hlsl/generator_impl_member_accessor_test.cc",
|
"src/writer/hlsl/generator_impl_member_accessor_test.cc",
|
||||||
"src/writer/hlsl/generator_impl_module_constant_test.cc",
|
"src/writer/hlsl/generator_impl_module_constant_test.cc",
|
||||||
|
@ -584,6 +584,7 @@ if (${TINT_BUILD_HLSL_WRITER})
|
|||||||
writer/hlsl/generator_impl_function_test.cc
|
writer/hlsl/generator_impl_function_test.cc
|
||||||
writer/hlsl/generator_impl_identifier_test.cc
|
writer/hlsl/generator_impl_identifier_test.cc
|
||||||
writer/hlsl/generator_impl_if_test.cc
|
writer/hlsl/generator_impl_if_test.cc
|
||||||
|
writer/hlsl/generator_impl_intrinsic_test.cc
|
||||||
writer/hlsl/generator_impl_loop_test.cc
|
writer/hlsl/generator_impl_loop_test.cc
|
||||||
writer/hlsl/generator_impl_member_accessor_test.cc
|
writer/hlsl/generator_impl_member_accessor_test.cc
|
||||||
writer/hlsl/generator_impl_module_constant_test.cc
|
writer/hlsl/generator_impl_module_constant_test.cc
|
||||||
|
@ -339,6 +339,49 @@ bool GeneratorImpl::EmitBreak(ast::BreakStatement*) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GeneratorImpl::generate_intrinsic_name(const std::string& name) {
|
||||||
|
if (name == "any") {
|
||||||
|
return "any";
|
||||||
|
}
|
||||||
|
if (name == "all") {
|
||||||
|
return "all";
|
||||||
|
}
|
||||||
|
if (name == "dot") {
|
||||||
|
return "dot";
|
||||||
|
}
|
||||||
|
if (name == "is_finite") {
|
||||||
|
return "isfinite";
|
||||||
|
}
|
||||||
|
if (name == "is_inf") {
|
||||||
|
return "isinf";
|
||||||
|
}
|
||||||
|
if (name == "is_nan") {
|
||||||
|
return "isnan";
|
||||||
|
}
|
||||||
|
if (name == "dpdy") {
|
||||||
|
return "ddy";
|
||||||
|
}
|
||||||
|
if (name == "dpdy_fine") {
|
||||||
|
return "ddy_fine";
|
||||||
|
}
|
||||||
|
if (name == "dpdy_coarse") {
|
||||||
|
return "ddy_coarse";
|
||||||
|
}
|
||||||
|
if (name == "dpdx") {
|
||||||
|
return "ddx";
|
||||||
|
}
|
||||||
|
if (name == "dpdx_fine") {
|
||||||
|
return "ddx_fine";
|
||||||
|
}
|
||||||
|
if (name == "dpdx_coarse") {
|
||||||
|
return "ddx_coarse";
|
||||||
|
}
|
||||||
|
if (name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse") {
|
||||||
|
return "fwidth";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
||||||
if (!expr->func()->IsIdentifier()) {
|
if (!expr->func()->IsIdentifier()) {
|
||||||
error_ = "invalid function name";
|
error_ = "invalid function name";
|
||||||
@ -347,8 +390,91 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||||||
|
|
||||||
auto* ident = expr->func()->AsIdentifier();
|
auto* ident = expr->func()->AsIdentifier();
|
||||||
if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
|
if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
|
||||||
error_ = "Intrinsics not supported in HLSL backend.";
|
const auto& params = expr->params();
|
||||||
return false;
|
if (ident->name() == "select") {
|
||||||
|
error_ = "select not supported in HLSL backend yet";
|
||||||
|
return false;
|
||||||
|
} else if (ident->name() == "is_normal") {
|
||||||
|
error_ = "is_normal not supported in HLSL backend yet";
|
||||||
|
return false;
|
||||||
|
} else if (ident->name() == "outer_product") {
|
||||||
|
error_ = "outer_product not supported yet";
|
||||||
|
return false;
|
||||||
|
// TODO(dsinclair): This gets tricky. We need to generate two variables to
|
||||||
|
// hold the outer_product expressions, but we maybe inside an expression
|
||||||
|
// ourselves. So, this will need to, possibly, output the variables
|
||||||
|
// _before_ the expression which contains the outer product.
|
||||||
|
//
|
||||||
|
// This then has the follow on, what if we have `(false &&
|
||||||
|
// outer_product())` in that case, we shouldn't evaluate the expressions
|
||||||
|
// at all because of short circuting.
|
||||||
|
//
|
||||||
|
// So .... this turns out to be hard ...
|
||||||
|
|
||||||
|
// // We create variables to hold the two parameters in case they're
|
||||||
|
// // function calls with side effects.
|
||||||
|
// auto* param0 = param[0].get();
|
||||||
|
// auto* name0 = generate_name("outer_product_expr_0");
|
||||||
|
|
||||||
|
// auto* param1 = param[1].get();
|
||||||
|
// auto* name1 = generate_name("outer_product_expr_1");
|
||||||
|
|
||||||
|
// make_indent();
|
||||||
|
// if (!EmitType(expr->result_type(), "")) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// out_ << "(";
|
||||||
|
|
||||||
|
// auto param1_type = params[1]->result_type()->UnwrapPtrIfNeeded();
|
||||||
|
// if (!param1_type->IsVector()) {
|
||||||
|
// error_ = "invalid param type in outer_product got: " +
|
||||||
|
// param1_type->type_name();
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (uint32_t i = 0; i < param1_type->AsVector()->size(); ++i) {
|
||||||
|
// if (i > 0) {
|
||||||
|
// out_ << ", ";
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!EmitExpression(params[0].get())) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// out_ << " * ";
|
||||||
|
|
||||||
|
// if (!EmitExpression(params[1].get())) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// out_ << "[" << i << "]";
|
||||||
|
// }
|
||||||
|
|
||||||
|
// out_ << ")";
|
||||||
|
} else {
|
||||||
|
auto name = generate_intrinsic_name(ident->name());
|
||||||
|
if (name.empty()) {
|
||||||
|
error_ = "unable to determine intrinsic name for intrinsic: " +
|
||||||
|
ident->name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_indent();
|
||||||
|
out_ << name << "(";
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& param : params) {
|
||||||
|
if (!first) {
|
||||||
|
out_ << ", ";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
if (!EmitExpression(param.get())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_ << ")";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ident->has_path()) {
|
if (!ident->has_path()) {
|
||||||
|
@ -197,6 +197,10 @@ class GeneratorImpl : public TextGenerator {
|
|||||||
/// @param prefix the prefix of the name to generate
|
/// @param prefix the prefix of the name to generate
|
||||||
/// @returns the name
|
/// @returns the name
|
||||||
std::string generate_name(const std::string& prefix);
|
std::string generate_name(const std::string& prefix);
|
||||||
|
/// Generates an intrinsic name from the given name
|
||||||
|
/// @param name the name to convert to an intrinsic
|
||||||
|
/// @returns the intrinsic name or blank on error
|
||||||
|
std::string generate_intrinsic_name(const std::string& name);
|
||||||
/// Converts a builtin to an attribute name
|
/// Converts a builtin to an attribute name
|
||||||
/// @param builtin the builtin to convert
|
/// @param builtin the builtin to convert
|
||||||
/// @returns the string name of the builtin or blank on error
|
/// @returns the string name of the builtin or blank on error
|
||||||
|
136
src/writer/hlsl/generator_impl_intrinsic_test.cc
Normal file
136
src/writer/hlsl/generator_impl_intrinsic_test.cc
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// Copyright 2020 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "src/ast/call_expression.h"
|
||||||
|
#include "src/ast/identifier_expression.h"
|
||||||
|
#include "src/ast/module.h"
|
||||||
|
#include "src/ast/type/f32_type.h"
|
||||||
|
#include "src/ast/type/vector_type.h"
|
||||||
|
#include "src/context.h"
|
||||||
|
#include "src/type_determiner.h"
|
||||||
|
#include "src/writer/hlsl/generator_impl.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace writer {
|
||||||
|
namespace hlsl {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using HlslGeneratorImplTest = testing::Test;
|
||||||
|
|
||||||
|
struct IntrinsicData {
|
||||||
|
const char* name;
|
||||||
|
const char* hlsl_name;
|
||||||
|
};
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
|
||||||
|
out << data.name;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
using HlslIntrinsicTest = testing::TestWithParam<IntrinsicData>;
|
||||||
|
TEST_P(HlslIntrinsicTest, Emit) {
|
||||||
|
auto param = GetParam();
|
||||||
|
|
||||||
|
ast::Module m;
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
EXPECT_EQ(g.generate_intrinsic_name(param.name), param.hlsl_name);
|
||||||
|
}
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
HlslGeneratorImplTest,
|
||||||
|
HlslIntrinsicTest,
|
||||||
|
testing::Values(IntrinsicData{"any", "any"},
|
||||||
|
IntrinsicData{"all", "all"},
|
||||||
|
IntrinsicData{"dot", "dot"},
|
||||||
|
IntrinsicData{"dpdx", "ddx"},
|
||||||
|
IntrinsicData{"dpdx_coarse", "ddx_coarse"},
|
||||||
|
IntrinsicData{"dpdx_fine", "ddx_fine"},
|
||||||
|
IntrinsicData{"dpdy", "ddy"},
|
||||||
|
IntrinsicData{"dpdy_coarse", "ddy_coarse"},
|
||||||
|
IntrinsicData{"dpdy_fine", "ddy_fine"},
|
||||||
|
IntrinsicData{"fwidth", "fwidth"},
|
||||||
|
IntrinsicData{"fwidth_coarse", "fwidth"},
|
||||||
|
IntrinsicData{"fwidth_fine", "fwidth"},
|
||||||
|
IntrinsicData{"is_finite", "isfinite"},
|
||||||
|
IntrinsicData{"is_inf", "isinf"},
|
||||||
|
IntrinsicData{"is_nan", "isnan"}));
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest, DISABLED_Intrinsic_IsNormal) {
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest, DISABLED_Intrinsic_Select) {
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest, DISABLED_Intrinsic_OuterProduct) {
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
ast::type::VectorType vec2(&f32, 2);
|
||||||
|
ast::type::VectorType vec3(&f32, 3);
|
||||||
|
|
||||||
|
auto a =
|
||||||
|
std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &vec2);
|
||||||
|
auto b =
|
||||||
|
std::make_unique<ast::Variable>("b", ast::StorageClass::kNone, &vec3);
|
||||||
|
|
||||||
|
ast::ExpressionList params;
|
||||||
|
params.push_back(std::make_unique<ast::IdentifierExpression>("a"));
|
||||||
|
params.push_back(std::make_unique<ast::IdentifierExpression>("b"));
|
||||||
|
|
||||||
|
ast::CallExpression call(
|
||||||
|
std::make_unique<ast::IdentifierExpression>("outer_product"),
|
||||||
|
std::move(params));
|
||||||
|
|
||||||
|
Context ctx;
|
||||||
|
ast::Module m;
|
||||||
|
TypeDeterminer td(&ctx, &m);
|
||||||
|
td.RegisterVariableForTesting(a.get());
|
||||||
|
td.RegisterVariableForTesting(b.get());
|
||||||
|
|
||||||
|
m.AddGlobalVariable(std::move(a));
|
||||||
|
m.AddGlobalVariable(std::move(b));
|
||||||
|
|
||||||
|
ASSERT_TRUE(td.Determine()) << td.error();
|
||||||
|
ASSERT_TRUE(td.DetermineResultType(&call)) << td.error();
|
||||||
|
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
|
||||||
|
g.increment_indent();
|
||||||
|
ASSERT_TRUE(g.EmitExpression(&call)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), " float3x2(a * b[0], a * b[1], a * b[2])");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest, Intrinsic_Bad_Name) {
|
||||||
|
ast::Module m;
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
EXPECT_EQ(g.generate_intrinsic_name("unknown name"), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest, Intrinsic_Call) {
|
||||||
|
ast::ExpressionList params;
|
||||||
|
params.push_back(std::make_unique<ast::IdentifierExpression>("param1"));
|
||||||
|
params.push_back(std::make_unique<ast::IdentifierExpression>("param2"));
|
||||||
|
|
||||||
|
ast::CallExpression call(std::make_unique<ast::IdentifierExpression>("dot"),
|
||||||
|
std::move(params));
|
||||||
|
|
||||||
|
ast::Module m;
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
g.increment_indent();
|
||||||
|
ASSERT_TRUE(g.EmitExpression(&call)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), " dot(param1, param2)");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace hlsl
|
||||||
|
} // namespace writer
|
||||||
|
} // namespace tint
|
Loading…
x
Reference in New Issue
Block a user