tint: Resolve @builtin() args as expressions
This CL makes the builtin argument resolve as a shadowable enumerator expression. Bug: tint:1841 Bug: tint:1845 Change-Id: I2aaa2c63025752f25c113cf2fb38f4d91c2c16c2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/120680 Kokoro: Ben Clayton <bclayton@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
parent
21571709a2
commit
4d3ff9711e
|
@ -825,6 +825,7 @@ libtint_source_set("libtint_reader_src") {
|
||||||
|
|
||||||
libtint_source_set("libtint_spv_reader_src") {
|
libtint_source_set("libtint_spv_reader_src") {
|
||||||
sources = [
|
sources = [
|
||||||
|
"reader/spirv/attributes.h",
|
||||||
"reader/spirv/construct.cc",
|
"reader/spirv/construct.cc",
|
||||||
"reader/spirv/construct.h",
|
"reader/spirv/construct.h",
|
||||||
"reader/spirv/entry_point_info.cc",
|
"reader/spirv/entry_point_info.cc",
|
||||||
|
@ -916,6 +917,7 @@ libtint_source_set("libtint_spv_writer_src") {
|
||||||
deps = [
|
deps = [
|
||||||
":libtint_ast_src",
|
":libtint_ast_src",
|
||||||
":libtint_base_src",
|
":libtint_base_src",
|
||||||
|
":libtint_builtins_src",
|
||||||
":libtint_constant_src",
|
":libtint_constant_src",
|
||||||
":libtint_program_src",
|
":libtint_program_src",
|
||||||
":libtint_sem_src",
|
":libtint_sem_src",
|
||||||
|
@ -981,6 +983,7 @@ libtint_source_set("libtint_msl_writer_src") {
|
||||||
deps = [
|
deps = [
|
||||||
":libtint_ast_src",
|
":libtint_ast_src",
|
||||||
":libtint_base_src",
|
":libtint_base_src",
|
||||||
|
":libtint_builtins_src",
|
||||||
":libtint_constant_src",
|
":libtint_constant_src",
|
||||||
":libtint_program_src",
|
":libtint_program_src",
|
||||||
":libtint_sem_src",
|
":libtint_sem_src",
|
||||||
|
@ -1001,6 +1004,7 @@ libtint_source_set("libtint_hlsl_writer_src") {
|
||||||
deps = [
|
deps = [
|
||||||
":libtint_ast_src",
|
":libtint_ast_src",
|
||||||
":libtint_base_src",
|
":libtint_base_src",
|
||||||
|
":libtint_builtins_src",
|
||||||
":libtint_constant_src",
|
":libtint_constant_src",
|
||||||
":libtint_program_src",
|
":libtint_program_src",
|
||||||
":libtint_sem_src",
|
":libtint_sem_src",
|
||||||
|
@ -1740,6 +1744,7 @@ if (tint_build_unittests) {
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
":libtint_base_src",
|
":libtint_base_src",
|
||||||
|
":libtint_builtins_src",
|
||||||
":libtint_unittests_ast_helper",
|
":libtint_unittests_ast_helper",
|
||||||
":libtint_wgsl_reader_src",
|
":libtint_wgsl_reader_src",
|
||||||
]
|
]
|
||||||
|
@ -1781,6 +1786,7 @@ if (tint_build_unittests) {
|
||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
|
":libtint_builtins_src",
|
||||||
":libtint_wgsl_writer_src",
|
":libtint_wgsl_writer_src",
|
||||||
":tint_unittests_ast_src",
|
":tint_unittests_ast_src",
|
||||||
]
|
]
|
||||||
|
|
|
@ -575,6 +575,7 @@ endif()
|
||||||
|
|
||||||
if(${TINT_BUILD_SPV_READER})
|
if(${TINT_BUILD_SPV_READER})
|
||||||
list(APPEND TINT_LIB_SRCS
|
list(APPEND TINT_LIB_SRCS
|
||||||
|
reader/spirv/attributes.h
|
||||||
reader/spirv/construct.h
|
reader/spirv/construct.h
|
||||||
reader/spirv/construct.cc
|
reader/spirv/construct.cc
|
||||||
reader/spirv/entry_point_info.h
|
reader/spirv/entry_point_info.h
|
||||||
|
|
|
@ -25,8 +25,10 @@ namespace tint::ast {
|
||||||
BuiltinAttribute::BuiltinAttribute(ProgramID pid,
|
BuiltinAttribute::BuiltinAttribute(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
builtin::BuiltinValue b)
|
const Expression* b)
|
||||||
: Base(pid, nid, src), builtin(b) {}
|
: Base(pid, nid, src), builtin(b) {
|
||||||
|
TINT_ASSERT_PROGRAM_IDS_EQUAL(AST, b, program_id);
|
||||||
|
}
|
||||||
|
|
||||||
BuiltinAttribute::~BuiltinAttribute() = default;
|
BuiltinAttribute::~BuiltinAttribute() = default;
|
||||||
|
|
||||||
|
@ -37,7 +39,8 @@ std::string BuiltinAttribute::Name() const {
|
||||||
const BuiltinAttribute* BuiltinAttribute::Clone(CloneContext* ctx) const {
|
const BuiltinAttribute* BuiltinAttribute::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
return ctx->dst->create<BuiltinAttribute>(src, builtin);
|
auto b = ctx->Clone(builtin);
|
||||||
|
return ctx->dst->create<BuiltinAttribute>(src, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -18,7 +18,11 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "src/tint/ast/attribute.h"
|
#include "src/tint/ast/attribute.h"
|
||||||
#include "src/tint/builtin/builtin_value.h"
|
|
||||||
|
// Forward declarations
|
||||||
|
namespace tint::ast {
|
||||||
|
class Expression;
|
||||||
|
}
|
||||||
|
|
||||||
namespace tint::ast {
|
namespace tint::ast {
|
||||||
|
|
||||||
|
@ -30,7 +34,7 @@ class BuiltinAttribute final : public Castable<BuiltinAttribute, Attribute> {
|
||||||
/// @param nid the unique node identifier
|
/// @param nid the unique node identifier
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param builtin the builtin value
|
/// @param builtin the builtin value
|
||||||
BuiltinAttribute(ProgramID pid, NodeID nid, const Source& src, builtin::BuiltinValue builtin);
|
BuiltinAttribute(ProgramID pid, NodeID nid, const Source& src, const Expression* builtin);
|
||||||
~BuiltinAttribute() override;
|
~BuiltinAttribute() override;
|
||||||
|
|
||||||
/// @returns the WGSL name for the attribute
|
/// @returns the WGSL name for the attribute
|
||||||
|
@ -43,7 +47,7 @@ class BuiltinAttribute final : public Castable<BuiltinAttribute, Attribute> {
|
||||||
const BuiltinAttribute* Clone(CloneContext* ctx) const override;
|
const BuiltinAttribute* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
/// The builtin value
|
/// The builtin value
|
||||||
const builtin::BuiltinValue builtin;
|
const Expression* const builtin;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -12,7 +12,10 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "gtest/gtest-spi.h"
|
||||||
|
|
||||||
#include "src/tint/ast/test_helper.h"
|
#include "src/tint/ast/test_helper.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
|
|
||||||
namespace tint::ast {
|
namespace tint::ast {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -20,8 +23,27 @@ namespace {
|
||||||
using BuiltinAttributeTest = TestHelper;
|
using BuiltinAttributeTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(BuiltinAttributeTest, Creation) {
|
TEST_F(BuiltinAttributeTest, Creation) {
|
||||||
auto* d = create<BuiltinAttribute>(builtin::BuiltinValue::kFragDepth);
|
auto* d = Builtin(builtin::BuiltinValue::kFragDepth);
|
||||||
EXPECT_EQ(builtin::BuiltinValue::kFragDepth, d->builtin);
|
CheckIdentifier(Symbols(), d->builtin, "frag_depth");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BuiltinAttributeTest, Assert_Null_Builtin) {
|
||||||
|
EXPECT_FATAL_FAILURE(
|
||||||
|
{
|
||||||
|
ProgramBuilder b;
|
||||||
|
b.Builtin(nullptr);
|
||||||
|
},
|
||||||
|
"internal compiler error");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BuiltinAttributeTest, Assert_DifferentProgramID_Builtin) {
|
||||||
|
EXPECT_FATAL_FAILURE(
|
||||||
|
{
|
||||||
|
ProgramBuilder b1;
|
||||||
|
ProgramBuilder b2;
|
||||||
|
b1.Builtin(b2.Expr("bang"));
|
||||||
|
},
|
||||||
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "src/tint/ast/id_attribute.h"
|
#include "src/tint/ast/id_attribute.h"
|
||||||
#include "src/tint/ast/test_helper.h"
|
#include "src/tint/ast/test_helper.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
|
|
||||||
using namespace tint::number_suffixes; // NOLINT
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ namespace tint::builtin {
|
||||||
/// @param str the string to parse
|
/// @param str the string to parse
|
||||||
/// @returns the parsed enum, or BuiltinValue::kUndefined if the string could not be parsed.
|
/// @returns the parsed enum, or BuiltinValue::kUndefined if the string could not be parsed.
|
||||||
BuiltinValue ParseBuiltinValue(std::string_view str) {
|
BuiltinValue ParseBuiltinValue(std::string_view str) {
|
||||||
|
if (str == "__point_size") {
|
||||||
|
return BuiltinValue::kPointSize;
|
||||||
|
}
|
||||||
if (str == "frag_depth") {
|
if (str == "frag_depth") {
|
||||||
return BuiltinValue::kFragDepth;
|
return BuiltinValue::kFragDepth;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +74,8 @@ std::ostream& operator<<(std::ostream& out, BuiltinValue value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case BuiltinValue::kUndefined:
|
case BuiltinValue::kUndefined:
|
||||||
return out << "undefined";
|
return out << "undefined";
|
||||||
|
case BuiltinValue::kPointSize:
|
||||||
|
return out << "__point_size";
|
||||||
case BuiltinValue::kFragDepth:
|
case BuiltinValue::kFragDepth:
|
||||||
return out << "frag_depth";
|
return out << "frag_depth";
|
||||||
case BuiltinValue::kFrontFacing:
|
case BuiltinValue::kFrontFacing:
|
||||||
|
@ -85,8 +90,6 @@ std::ostream& operator<<(std::ostream& out, BuiltinValue value) {
|
||||||
return out << "local_invocation_index";
|
return out << "local_invocation_index";
|
||||||
case BuiltinValue::kNumWorkgroups:
|
case BuiltinValue::kNumWorkgroups:
|
||||||
return out << "num_workgroups";
|
return out << "num_workgroups";
|
||||||
case BuiltinValue::kPointSize:
|
|
||||||
return out << "point_size";
|
|
||||||
case BuiltinValue::kPosition:
|
case BuiltinValue::kPosition:
|
||||||
return out << "position";
|
return out << "position";
|
||||||
case BuiltinValue::kSampleIndex:
|
case BuiltinValue::kSampleIndex:
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace tint::builtin {
|
||||||
/// Builtin value defined with `@builtin(<name>)`.
|
/// Builtin value defined with `@builtin(<name>)`.
|
||||||
enum class BuiltinValue {
|
enum class BuiltinValue {
|
||||||
kUndefined,
|
kUndefined,
|
||||||
|
kPointSize,
|
||||||
kFragDepth,
|
kFragDepth,
|
||||||
kFrontFacing,
|
kFrontFacing,
|
||||||
kGlobalInvocationId,
|
kGlobalInvocationId,
|
||||||
|
@ -37,7 +38,6 @@ enum class BuiltinValue {
|
||||||
kLocalInvocationId,
|
kLocalInvocationId,
|
||||||
kLocalInvocationIndex,
|
kLocalInvocationIndex,
|
||||||
kNumWorkgroups,
|
kNumWorkgroups,
|
||||||
kPointSize, // Tint-internal enum entry - not parsed
|
|
||||||
kPosition,
|
kPosition,
|
||||||
kSampleIndex,
|
kSampleIndex,
|
||||||
kSampleMask,
|
kSampleMask,
|
||||||
|
@ -56,12 +56,11 @@ std::ostream& operator<<(std::ostream& out, BuiltinValue value);
|
||||||
BuiltinValue ParseBuiltinValue(std::string_view str);
|
BuiltinValue ParseBuiltinValue(std::string_view str);
|
||||||
|
|
||||||
constexpr const char* kBuiltinValueStrings[] = {
|
constexpr const char* kBuiltinValueStrings[] = {
|
||||||
"frag_depth", "front_facing",
|
"__point_size", "frag_depth", "front_facing",
|
||||||
"global_invocation_id", "instance_index",
|
"global_invocation_id", "instance_index", "local_invocation_id",
|
||||||
"local_invocation_id", "local_invocation_index",
|
"local_invocation_index", "num_workgroups", "position",
|
||||||
"num_workgroups", "position",
|
"sample_index", "sample_mask", "vertex_index",
|
||||||
"sample_index", "sample_mask",
|
"workgroup_id",
|
||||||
"vertex_index", "workgroup_id",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::builtin
|
} // namespace tint::builtin
|
||||||
|
|
|
@ -31,90 +31,97 @@ namespace {
|
||||||
|
|
||||||
void BuiltinValueParser(::benchmark::State& state) {
|
void BuiltinValueParser(::benchmark::State& state) {
|
||||||
const char* kStrings[] = {
|
const char* kStrings[] = {
|
||||||
"fragdeccth",
|
"_ccpoint_siz",
|
||||||
"flaget3",
|
"_3poi_ile",
|
||||||
"fVag_depth",
|
"__poiVt_size",
|
||||||
|
"__point_size",
|
||||||
|
"1_point_size",
|
||||||
|
"__pointJsqze",
|
||||||
|
"__lloint_siz77",
|
||||||
|
"frqqgppepHHh",
|
||||||
|
"fv_dcpt",
|
||||||
|
"frabGdeth",
|
||||||
"frag_depth",
|
"frag_depth",
|
||||||
"frag1depth",
|
"frag_veiith",
|
||||||
"fraJqqepth",
|
"fr8g_depWWh",
|
||||||
"fra7ll_depth",
|
"Mragxxepth",
|
||||||
"fonHHpp_facing",
|
"ggroXtfacing",
|
||||||
"fron_facg",
|
"Vot_fuciXg",
|
||||||
"frGnt_fbcin",
|
"front_fac3ng",
|
||||||
"front_facing",
|
"front_facing",
|
||||||
"front_facvnii",
|
"front_fEcing",
|
||||||
"frWWnt_faci8g",
|
"fronPPfaTTing",
|
||||||
"fxxonM_facig",
|
"ddroxxtfacing",
|
||||||
"gXobalgginvocationid",
|
"global_invocatio44_id",
|
||||||
"goVal_uvocatioX_id",
|
"global_invocaSSioVV_id",
|
||||||
"global_in3ocation_id",
|
"22loRal_invoRtion_id",
|
||||||
"global_invocation_id",
|
"global_invocation_id",
|
||||||
"global_invocation_iE",
|
"globalFinvoction_id",
|
||||||
"TTobal_invocationPPid",
|
"gloal_invocation_id",
|
||||||
"globdd_invocatioxx_id",
|
"RRlHOOaV_invoction_id",
|
||||||
"instance44index",
|
"instance_ydex",
|
||||||
"instaVVce_SSndex",
|
"instGlr77cnn_index",
|
||||||
"Rnstane_ind2Rx",
|
"instan04e_index",
|
||||||
"instance_index",
|
"instance_index",
|
||||||
"inFtanceind9x",
|
"insacoo_inex",
|
||||||
"insance_index",
|
"izstane_index",
|
||||||
"inRRancV_OOHdex",
|
"nippance_in11ex",
|
||||||
"local_nvocytion_id",
|
"local_invXXcation_id",
|
||||||
"llGcnnl_inv77catirrn_id",
|
"lIIcal_i5599ocation_inn",
|
||||||
"local_invoca4i00n_id",
|
"HHrrcal_inSSocation_Yaa",
|
||||||
"local_invocation_id",
|
"local_invocation_id",
|
||||||
"loool_nvocaton_id",
|
"lokkal_invocatini",
|
||||||
"local_inozztion_id",
|
"jocal_invocRRongid",
|
||||||
"p11cal_invocatiiin_i",
|
"local_inocatbon_i",
|
||||||
"local_invocation_iXXdex",
|
"local_injocation_index",
|
||||||
"local_invnnIIati99n55index",
|
"local_invocatio_index",
|
||||||
"localYirrHHocaationSSindex",
|
"locl_invocqtion_ndex",
|
||||||
"local_invocation_index",
|
"local_invocation_index",
|
||||||
"lkkal_invHcation_idx",
|
"localNNinvocaton_index",
|
||||||
"gRcal_invocatioj_inex",
|
"local_invocatin_ivvdx",
|
||||||
"lcal_invcbtion_index",
|
"locl_invocatioQQ_index",
|
||||||
|
"num_workgffus",
|
||||||
"num_workgroujs",
|
"num_workgroujs",
|
||||||
"num_worgroups",
|
"num_wrkgNNwoup8",
|
||||||
"nuq_orkgoups",
|
|
||||||
"num_workgroups",
|
"num_workgroups",
|
||||||
"nm_workgroNNps",
|
"numworkgroups",
|
||||||
"um_workgrovps",
|
"num_workrrroups",
|
||||||
"nQQm_orkgroups",
|
"num_worGgroups",
|
||||||
"posftrn",
|
"pFFsition",
|
||||||
"pojition",
|
"pEiio",
|
||||||
"poswNN82n",
|
"prrsitio",
|
||||||
"position",
|
"position",
|
||||||
"positon",
|
"sition",
|
||||||
"porrition",
|
"poJJDtin",
|
||||||
"pGsition",
|
"poi8i",
|
||||||
"sample_inFFex",
|
"smpke11nde",
|
||||||
"samleinex",
|
"samle_index",
|
||||||
"sample_indrr",
|
"saple_Jndex",
|
||||||
"sample_index",
|
"sample_index",
|
||||||
"sample_iex",
|
"cample_index",
|
||||||
"DaplX_JJndex",
|
"sample_indOx",
|
||||||
"8amleinde",
|
"savvKKl___inttex",
|
||||||
"saplekmak",
|
"sam8le_xx5k",
|
||||||
"samle_mask",
|
"sampqq__msk",
|
||||||
"saJple_mak",
|
"sampleqmask",
|
||||||
"sample_mask",
|
"sample_mask",
|
||||||
"sample_cask",
|
"33amOe_mas66",
|
||||||
"sample_maOk",
|
"samoott6QQmask",
|
||||||
"__attpvve_KKask",
|
"66mple_mask",
|
||||||
"vrtex5inxxe8",
|
"verzzx_in6Oxx",
|
||||||
"v__rex_qFdex",
|
"vertex_yyndex",
|
||||||
"veqqtex_idex",
|
"vetxHHZnZex",
|
||||||
"vertex_index",
|
"vertex_index",
|
||||||
"veOtx_33nde66",
|
"vWWteq_in44ex",
|
||||||
"v6ootex_indttQx",
|
"vrtex_OOndex",
|
||||||
"ver66ex_inex",
|
"hrteYooindx",
|
||||||
"worzzroup6Oxd",
|
"wogroup_i",
|
||||||
"workgroyyp_id",
|
"wokgrouF_id",
|
||||||
"wokrHHZpZid",
|
"worgrwup_id",
|
||||||
"workgroup_id",
|
"workgroup_id",
|
||||||
"wWWkgqoup44id",
|
"workGKou_if",
|
||||||
"wrkgroOOp_id",
|
"worKKgrouq_id",
|
||||||
"hrkgYooup_d",
|
"w3rkgrommp_id",
|
||||||
};
|
};
|
||||||
for (auto _ : state) {
|
for (auto _ : state) {
|
||||||
for (auto* str : kStrings) {
|
for (auto* str : kStrings) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ inline std::ostream& operator<<(std::ostream& out, Case c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr Case kValidCases[] = {
|
static constexpr Case kValidCases[] = {
|
||||||
|
{"__point_size", BuiltinValue::kPointSize},
|
||||||
{"frag_depth", BuiltinValue::kFragDepth},
|
{"frag_depth", BuiltinValue::kFragDepth},
|
||||||
{"front_facing", BuiltinValue::kFrontFacing},
|
{"front_facing", BuiltinValue::kFrontFacing},
|
||||||
{"global_invocation_id", BuiltinValue::kGlobalInvocationId},
|
{"global_invocation_id", BuiltinValue::kGlobalInvocationId},
|
||||||
|
@ -58,42 +59,45 @@ static constexpr Case kValidCases[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Case kInvalidCases[] = {
|
static constexpr Case kInvalidCases[] = {
|
||||||
{"fragdeccth", BuiltinValue::kUndefined},
|
{"_ccpoint_siz", BuiltinValue::kUndefined},
|
||||||
{"flaget3", BuiltinValue::kUndefined},
|
{"_3poi_ile", BuiltinValue::kUndefined},
|
||||||
{"fVag_depth", BuiltinValue::kUndefined},
|
{"__poiVt_size", BuiltinValue::kUndefined},
|
||||||
{"1ront_facing", BuiltinValue::kUndefined},
|
{"frag1depth", BuiltinValue::kUndefined},
|
||||||
{"front_fJcqng", BuiltinValue::kUndefined},
|
{"fraJqqepth", BuiltinValue::kUndefined},
|
||||||
{"frllnt_facin77", BuiltinValue::kUndefined},
|
{"fra7ll_depth", BuiltinValue::kUndefined},
|
||||||
{"global_invoqqtionppHid", BuiltinValue::kUndefined},
|
{"fonHHpp_facing", BuiltinValue::kUndefined},
|
||||||
{"clvbal_inocaionid", BuiltinValue::kUndefined},
|
{"fron_facg", BuiltinValue::kUndefined},
|
||||||
{"global_Gvocation_id", BuiltinValue::kUndefined},
|
{"frGnt_fbcin", BuiltinValue::kUndefined},
|
||||||
{"invtance_iniiex", BuiltinValue::kUndefined},
|
{"glvbal_iinvocation_id", BuiltinValue::kUndefined},
|
||||||
{"8nstanceWWindex", BuiltinValue::kUndefined},
|
{"gl8bal_invocation_WWd", BuiltinValue::kUndefined},
|
||||||
{"insxxanceindex", BuiltinValue::kUndefined},
|
{"Mlobal_invocaton_xxd", BuiltinValue::kUndefined},
|
||||||
{"lXcal_invoation_igg", BuiltinValue::kUndefined},
|
{"isXance_indegg", BuiltinValue::kUndefined},
|
||||||
{"Xocal_nvocatin_Vd", BuiltinValue::kUndefined},
|
{"insanc_iXVex", BuiltinValue::kUndefined},
|
||||||
{"local_invoca3ion_id", BuiltinValue::kUndefined},
|
{"instance_in3ex", BuiltinValue::kUndefined},
|
||||||
{"local_invocation_indeE", BuiltinValue::kUndefined},
|
{"local_Envocation_id", BuiltinValue::kUndefined},
|
||||||
{"loTTal_invPPcatin_index", BuiltinValue::kUndefined},
|
{"localiPPvocatioTT_id", BuiltinValue::kUndefined},
|
||||||
{"loal_invocadxxion_index", BuiltinValue::kUndefined},
|
{"localxxnvocationddid", BuiltinValue::kUndefined},
|
||||||
{"num_work44roups", BuiltinValue::kUndefined},
|
{"loca44_invocation_index", BuiltinValue::kUndefined},
|
||||||
{"num_wVVrkgSSoups", BuiltinValue::kUndefined},
|
{"local_invocSStionVVindex", BuiltinValue::kUndefined},
|
||||||
{"Rum_wokgrou2Rs", BuiltinValue::kUndefined},
|
{"locRR_invocat22n_index", BuiltinValue::kUndefined},
|
||||||
{"oFi9ion", BuiltinValue::kUndefined},
|
{"nuF_workrou9s", BuiltinValue::kUndefined},
|
||||||
{"postion", BuiltinValue::kUndefined},
|
{"numworkgroups", BuiltinValue::kUndefined},
|
||||||
{"ROOoHiiVn", BuiltinValue::kUndefined},
|
{"nuRRworVgOOHups", BuiltinValue::kUndefined},
|
||||||
{"samply_inde", BuiltinValue::kUndefined},
|
{"posytio", BuiltinValue::kUndefined},
|
||||||
{"snrrmpl77l_indGx", BuiltinValue::kUndefined},
|
{"77orritllnon", BuiltinValue::kUndefined},
|
||||||
{"00ample4index", BuiltinValue::kUndefined},
|
{"04osition", BuiltinValue::kUndefined},
|
||||||
{"smoo_mask", BuiltinValue::kUndefined},
|
{"smpe_oonde", BuiltinValue::kUndefined},
|
||||||
{"sampzemask", BuiltinValue::kUndefined},
|
{"smpl_inzzex", BuiltinValue::kUndefined},
|
||||||
{"ppaplii1_mas", BuiltinValue::kUndefined},
|
{"saiip11eindep", BuiltinValue::kUndefined},
|
||||||
{"vertex_iXXdex", BuiltinValue::kUndefined},
|
{"sample_XXask", BuiltinValue::kUndefined},
|
||||||
{"5nnertex_99IIdex", BuiltinValue::kUndefined},
|
{"samII99l55_mask", BuiltinValue::kUndefined},
|
||||||
{"verYeaaHHrrndeSS", BuiltinValue::kUndefined},
|
{"samaale_SSrHHYk", BuiltinValue::kUndefined},
|
||||||
{"workkgHo_i", BuiltinValue::kUndefined},
|
{"verkkeH_de", BuiltinValue::kUndefined},
|
||||||
{"worRgoupjid", BuiltinValue::kUndefined},
|
{"verRg_injex", BuiltinValue::kUndefined},
|
||||||
{"wrkgrupbid", BuiltinValue::kUndefined},
|
{"vrtexinbex", BuiltinValue::kUndefined},
|
||||||
|
{"workjroup_id", BuiltinValue::kUndefined},
|
||||||
|
{"wrkgroup_id", BuiltinValue::kUndefined},
|
||||||
|
{"qorkgro_id", BuiltinValue::kUndefined},
|
||||||
};
|
};
|
||||||
|
|
||||||
using BuiltinValueParseTest = testing::TestWithParam<Case>;
|
using BuiltinValueParseTest = testing::TestWithParam<Case>;
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
#include "src/tint/ast/module.h"
|
#include "src/tint/ast/module.h"
|
||||||
#include "src/tint/ast/override.h"
|
#include "src/tint/ast/override.h"
|
||||||
#include "src/tint/ast/var.h"
|
#include "src/tint/ast/var.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/builtin/extension.h"
|
#include "src/tint/builtin/extension.h"
|
||||||
|
#include "src/tint/sem/builtin_enum_expression.h"
|
||||||
#include "src/tint/sem/call.h"
|
#include "src/tint/sem/call.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
#include "src/tint/sem/module.h"
|
#include "src/tint/sem/module.h"
|
||||||
|
@ -698,11 +700,10 @@ bool Inspector::ContainsBuiltin(builtin::BuiltinValue builtin,
|
||||||
|
|
||||||
// Base case: check for builtin
|
// Base case: check for builtin
|
||||||
auto* builtin_declaration = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
|
auto* builtin_declaration = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
|
||||||
if (!builtin_declaration || builtin_declaration->builtin != builtin) {
|
if (!builtin_declaration) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return program_->Sem().Get(builtin_declaration)->Value() == builtin;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
|
std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "tint/override_id.h"
|
#include "tint/override_id.h"
|
||||||
|
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/inspector/entry_point.h"
|
#include "src/tint/inspector/entry_point.h"
|
||||||
#include "src/tint/inspector/resource_binding.h"
|
#include "src/tint/inspector/resource_binding.h"
|
||||||
#include "src/tint/inspector/scalar.h"
|
#include "src/tint/inspector/scalar.h"
|
||||||
|
|
|
@ -37,7 +37,7 @@ enum builtin_value {
|
||||||
num_workgroups
|
num_workgroups
|
||||||
sample_index
|
sample_index
|
||||||
sample_mask
|
sample_mask
|
||||||
@internal point_size
|
__point_size
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#filterable-triggering-rules
|
// https://gpuweb.github.io/gpuweb/wgsl/#filterable-triggering-rules
|
||||||
|
|
|
@ -3445,15 +3445,17 @@ class ProgramBuilder {
|
||||||
/// @param source the source information
|
/// @param source the source information
|
||||||
/// @param builtin the builtin value
|
/// @param builtin the builtin value
|
||||||
/// @returns the builtin attribute pointer
|
/// @returns the builtin attribute pointer
|
||||||
const ast::BuiltinAttribute* Builtin(const Source& source, builtin::BuiltinValue builtin) {
|
template <typename BUILTIN>
|
||||||
return create<ast::BuiltinAttribute>(source, builtin);
|
const ast::BuiltinAttribute* Builtin(const Source& source, BUILTIN&& builtin) {
|
||||||
|
return create<ast::BuiltinAttribute>(source, Expr(std::forward<BUILTIN>(builtin)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an ast::BuiltinAttribute
|
/// Creates an ast::BuiltinAttribute
|
||||||
/// @param builtin the builtin value
|
/// @param builtin the builtin value
|
||||||
/// @returns the builtin attribute pointer
|
/// @returns the builtin attribute pointer
|
||||||
const ast::BuiltinAttribute* Builtin(builtin::BuiltinValue builtin) {
|
template <typename BUILTIN>
|
||||||
return create<ast::BuiltinAttribute>(source_, builtin);
|
const ast::BuiltinAttribute* Builtin(BUILTIN&& builtin) {
|
||||||
|
return create<ast::BuiltinAttribute>(source_, Expr(std::forward<BUILTIN>(builtin)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an ast::InterpolateAttribute
|
/// Creates an ast::InterpolateAttribute
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright 2023 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.
|
||||||
|
|
||||||
|
#ifndef SRC_TINT_READER_SPIRV_ATTRIBUTES_H_
|
||||||
|
#define SRC_TINT_READER_SPIRV_ATTRIBUTES_H_
|
||||||
|
|
||||||
|
#include "src/tint/ast/attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
|
#include "src/tint/program_builder.h"
|
||||||
|
#include "src/tint/utils/enum_set.h"
|
||||||
|
#include "src/tint/utils/vector.h"
|
||||||
|
|
||||||
|
namespace tint::reader::spirv {
|
||||||
|
|
||||||
|
/// Attributes holds a vector of ast::Attribute pointers, and a enum-set of flags used to hold
|
||||||
|
/// additional metadata.
|
||||||
|
struct Attributes {
|
||||||
|
/// Flags used by #flags.
|
||||||
|
enum class Flags {
|
||||||
|
kHasBuiltinSampleMask,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Adds the attributes and flags of @p other to this.
|
||||||
|
/// @param other the other Attributes to combine into this
|
||||||
|
void Add(const Attributes& other) {
|
||||||
|
for (auto* attr : other.list) {
|
||||||
|
list.Push(attr);
|
||||||
|
}
|
||||||
|
for (auto flag : other.flags) {
|
||||||
|
flags.Add(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the attribute @p attr to the list of attributes
|
||||||
|
/// @param attr the attribute to add to this
|
||||||
|
void Add(const ast::Attribute* attr) { list.Push(attr); }
|
||||||
|
|
||||||
|
/// Adds the builtin to the attribute list, also marking any necessary flags
|
||||||
|
/// @param builder the program builder
|
||||||
|
/// @param source the source of the builtin attribute
|
||||||
|
/// @param builtin the builtin attribute to add
|
||||||
|
void Add(ProgramBuilder& builder, const Source& source, builtin::BuiltinValue builtin) {
|
||||||
|
Add(builder.Builtin(source, builtin));
|
||||||
|
if (builtin == builtin::BuiltinValue::kSampleMask) {
|
||||||
|
flags.Add(Flags::kHasBuiltinSampleMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns true if the attribute list contains an attribute with the type `T`.
|
||||||
|
template <typename T>
|
||||||
|
bool Has() const {
|
||||||
|
return ast::HasAttribute<T>(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the attribute with type `T` in the list, or nullptr if no attribute of the given
|
||||||
|
/// type exists in list.
|
||||||
|
template <typename T>
|
||||||
|
const T* Get() const {
|
||||||
|
return ast::GetAttribute<T>(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The attributes
|
||||||
|
utils::Vector<const ast::Attribute*, 8> list;
|
||||||
|
/// The additional metadata flags
|
||||||
|
utils::EnumSet<Flags> flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tint::reader::spirv
|
||||||
|
|
||||||
|
#endif // SRC_TINT_READER_SPIRV_ATTRIBUTES_H_
|
|
@ -756,15 +756,6 @@ struct LoopStatementBuilder final : public Castable<LoopStatementBuilder, Statem
|
||||||
mutable const ast::BlockStatement* continuing = nullptr;
|
mutable const ast::BlockStatement* continuing = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @param decos a list of parsed decorations
|
|
||||||
/// @returns true if the decorations include a SampleMask builtin
|
|
||||||
bool HasBuiltinSampleMask(utils::VectorRef<const ast::Attribute*> decos) {
|
|
||||||
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(decos)) {
|
|
||||||
return builtin->builtin == builtin::BuiltinValue::kSampleMask;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BlockInfo::BlockInfo(const spvtools::opt::BasicBlock& bb) : basic_block(&bb), id(bb.id()) {}
|
BlockInfo::BlockInfo(const spvtools::opt::BasicBlock& bb) : basic_block(&bb), id(bb.id()) {}
|
||||||
|
@ -956,7 +947,7 @@ bool FunctionEmitter::Emit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
builder_.Func(decl.source, decl.name, std::move(decl.params),
|
builder_.Func(decl.source, decl.name, std::move(decl.params),
|
||||||
decl.return_type->Build(builder_), body, std::move(decl.attributes));
|
decl.return_type->Build(builder_), body, std::move(decl.attributes.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep_info_ && !ep_info_->inner_name.empty()) {
|
if (ep_info_ && !ep_info_->inner_name.empty()) {
|
||||||
|
@ -994,12 +985,12 @@ const ast::BlockStatement* FunctionEmitter::MakeFunctionBody() {
|
||||||
|
|
||||||
bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
||||||
const Type* var_type,
|
const Type* var_type,
|
||||||
AttributeList* attrs,
|
|
||||||
utils::Vector<int, 8> index_prefix,
|
utils::Vector<int, 8> index_prefix,
|
||||||
const Type* tip_type,
|
const Type* tip_type,
|
||||||
const Type* forced_param_type,
|
const Type* forced_param_type,
|
||||||
ParameterList* params,
|
Attributes& attrs,
|
||||||
StatementList* statements) {
|
ParameterList& params,
|
||||||
|
StatementList& statements) {
|
||||||
// TODO(dneto): Handle structs where the locations are annotated on members.
|
// TODO(dneto): Handle structs where the locations are annotated on members.
|
||||||
tip_type = tip_type->UnwrapAlias();
|
tip_type = tip_type->UnwrapAlias();
|
||||||
if (auto* ref_type = tip_type->As<Reference>()) {
|
if (auto* ref_type = tip_type->As<Reference>()) {
|
||||||
|
@ -1015,8 +1006,8 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
||||||
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
|
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
|
||||||
for (int col = 0; col < num_columns; col++) {
|
for (int col = 0; col < num_columns; col++) {
|
||||||
index_prefix.Back() = col;
|
index_prefix.Back() = col;
|
||||||
if (!EmitPipelineInput(var_name, var_type, attrs, index_prefix, vec_ty,
|
if (!EmitPipelineInput(var_name, var_type, index_prefix, vec_ty, forced_param_type,
|
||||||
forced_param_type, params, statements)) {
|
attrs, params, statements)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1030,8 +1021,8 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
||||||
const Type* elem_ty = array_type->type;
|
const Type* elem_ty = array_type->type;
|
||||||
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
|
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
|
||||||
index_prefix.Back() = i;
|
index_prefix.Back() = i;
|
||||||
if (!EmitPipelineInput(var_name, var_type, attrs, index_prefix, elem_ty,
|
if (!EmitPipelineInput(var_name, var_type, index_prefix, elem_ty, forced_param_type,
|
||||||
forced_param_type, params, statements)) {
|
attrs, params, statements)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1042,38 +1033,36 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
||||||
index_prefix.Push(0);
|
index_prefix.Push(0);
|
||||||
for (size_t i = 0; i < members.size(); ++i) {
|
for (size_t i = 0; i < members.size(); ++i) {
|
||||||
index_prefix.Back() = static_cast<int>(i);
|
index_prefix.Back() = static_cast<int>(i);
|
||||||
AttributeList member_attrs(*attrs);
|
Attributes member_attrs(attrs);
|
||||||
if (!parser_impl_.ConvertPipelineDecorations(
|
if (!parser_impl_.ConvertPipelineDecorations(
|
||||||
struct_type,
|
struct_type,
|
||||||
parser_impl_.GetMemberPipelineDecorations(*struct_type,
|
parser_impl_.GetMemberPipelineDecorations(*struct_type,
|
||||||
static_cast<int>(i)),
|
static_cast<int>(i)),
|
||||||
&member_attrs)) {
|
member_attrs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!EmitPipelineInput(var_name, var_type, &member_attrs, index_prefix, members[i],
|
if (!EmitPipelineInput(var_name, var_type, index_prefix, members[i],
|
||||||
forced_param_type, params, statements)) {
|
forced_param_type, member_attrs, params, statements)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Copy the location as updated by nested expansion of the member.
|
// Copy the location as updated by nested expansion of the member.
|
||||||
parser_impl_.SetLocation(attrs,
|
parser_impl_.SetLocation(attrs, member_attrs.Get<ast::LocationAttribute>());
|
||||||
ast::GetAttribute<ast::LocationAttribute>(member_attrs));
|
|
||||||
}
|
}
|
||||||
return success();
|
return success();
|
||||||
},
|
},
|
||||||
[&](Default) {
|
[&](Default) {
|
||||||
const bool is_builtin = ast::HasAttribute<ast::BuiltinAttribute>(*attrs);
|
const bool is_builtin = attrs.Has<ast::BuiltinAttribute>();
|
||||||
|
|
||||||
const Type* param_type = is_builtin ? forced_param_type : tip_type;
|
const Type* param_type = is_builtin ? forced_param_type : tip_type;
|
||||||
|
|
||||||
const auto param_name = namer_.MakeDerivedName(var_name + "_param");
|
const auto param_name = namer_.MakeDerivedName(var_name + "_param");
|
||||||
// Create the parameter.
|
// Create the parameter.
|
||||||
// TODO(dneto): Note: If the parameter has non-location decorations,
|
// TODO(dneto): Note: If the parameter has non-location decorations, then those
|
||||||
// then those decoration AST nodes will be reused between multiple
|
// decoration AST nodes will be reused between multiple elements of a matrix, array, or
|
||||||
// elements of a matrix, array, or structure. Normally that's
|
// structure. Normally that's disallowed but currently the SPIR-V reader will make
|
||||||
// disallowed but currently the SPIR-V reader will make duplicates when
|
// duplicates when the entire AST is cloned at the top level of the SPIR-V reader flow.
|
||||||
// the entire AST is cloned at the top level of the SPIR-V reader flow.
|
|
||||||
// Consider rewriting this to avoid this node-sharing.
|
// Consider rewriting this to avoid this node-sharing.
|
||||||
params->Push(builder_.Param(param_name, param_type->Build(builder_), *attrs));
|
params.Push(builder_.Param(param_name, param_type->Build(builder_), attrs.list));
|
||||||
|
|
||||||
// Add a body statement to copy the parameter to the corresponding
|
// Add a body statement to copy the parameter to the corresponding
|
||||||
// private variable.
|
// private variable.
|
||||||
|
@ -1101,27 +1090,25 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_builtin && (tip_type != forced_param_type)) {
|
if (is_builtin && (tip_type != forced_param_type)) {
|
||||||
// The parameter will have the WGSL type, but we need bitcast to
|
// The parameter will have the WGSL type, but we need bitcast to the variable store
|
||||||
// the variable store type.
|
// type.
|
||||||
param_value = builder_.Bitcast(tip_type->Build(builder_), param_value);
|
param_value = builder_.Bitcast(tip_type->Build(builder_), param_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
statements->Push(builder_.Assign(store_dest, param_value));
|
statements.Push(builder_.Assign(store_dest, param_value));
|
||||||
|
|
||||||
// Increment the location attribute, in case more parameters will
|
// Increment the location attribute, in case more parameters will follow.
|
||||||
// follow.
|
|
||||||
IncrementLocation(attrs);
|
IncrementLocation(attrs);
|
||||||
|
|
||||||
return success();
|
return success();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionEmitter::IncrementLocation(AttributeList* attributes) {
|
void FunctionEmitter::IncrementLocation(Attributes& attributes) {
|
||||||
for (auto*& attr : *attributes) {
|
for (auto*& attr : attributes.list) {
|
||||||
if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
|
if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
|
||||||
// Replace this location attribute with a new one with one higher index.
|
// Replace this location attribute with a new one with one higher index.
|
||||||
// The old one doesn't leak because it's kept in the builder's AST node
|
// The old one doesn't leak because it's kept in the builder's AST node list.
|
||||||
// list.
|
|
||||||
attr = builder_.Location(
|
attr = builder_.Location(
|
||||||
loc_attr->source, AInt(loc_attr->expr->As<ast::IntLiteralExpression>()->value + 1));
|
loc_attr->source, AInt(loc_attr->expr->As<ast::IntLiteralExpression>()->value + 1));
|
||||||
}
|
}
|
||||||
|
@ -1130,12 +1117,12 @@ void FunctionEmitter::IncrementLocation(AttributeList* attributes) {
|
||||||
|
|
||||||
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
const Type* var_type,
|
const Type* var_type,
|
||||||
AttributeList* decos,
|
|
||||||
utils::Vector<int, 8> index_prefix,
|
utils::Vector<int, 8> index_prefix,
|
||||||
const Type* tip_type,
|
const Type* tip_type,
|
||||||
const Type* forced_member_type,
|
const Type* forced_member_type,
|
||||||
StructMemberList* return_members,
|
Attributes& attrs,
|
||||||
ExpressionList* return_exprs) {
|
StructMemberList& return_members,
|
||||||
|
ExpressionList& return_exprs) {
|
||||||
tip_type = tip_type->UnwrapAlias();
|
tip_type = tip_type->UnwrapAlias();
|
||||||
if (auto* ref_type = tip_type->As<Reference>()) {
|
if (auto* ref_type = tip_type->As<Reference>()) {
|
||||||
tip_type = ref_type->type;
|
tip_type = ref_type->type;
|
||||||
|
@ -1150,8 +1137,8 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
|
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
|
||||||
for (int col = 0; col < num_columns; col++) {
|
for (int col = 0; col < num_columns; col++) {
|
||||||
index_prefix.Back() = col;
|
index_prefix.Back() = col;
|
||||||
if (!EmitPipelineOutput(var_name, var_type, std::move(decos), index_prefix, vec_ty,
|
if (!EmitPipelineOutput(var_name, var_type, index_prefix, vec_ty,
|
||||||
forced_member_type, return_members, return_exprs)) {
|
forced_member_type, attrs, return_members, return_exprs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1165,8 +1152,8 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
const Type* elem_ty = array_type->type;
|
const Type* elem_ty = array_type->type;
|
||||||
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
|
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
|
||||||
index_prefix.Back() = i;
|
index_prefix.Back() = i;
|
||||||
if (!EmitPipelineOutput(var_name, var_type, std::move(decos), index_prefix, elem_ty,
|
if (!EmitPipelineOutput(var_name, var_type, index_prefix, elem_ty,
|
||||||
forced_member_type, return_members, return_exprs)) {
|
forced_member_type, attrs, return_members, return_exprs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1177,39 +1164,37 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
index_prefix.Push(0);
|
index_prefix.Push(0);
|
||||||
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||||
index_prefix.Back() = i;
|
index_prefix.Back() = i;
|
||||||
AttributeList member_attrs(*decos);
|
Attributes member_attrs(attrs);
|
||||||
if (!parser_impl_.ConvertPipelineDecorations(
|
if (!parser_impl_.ConvertPipelineDecorations(
|
||||||
struct_type, parser_impl_.GetMemberPipelineDecorations(*struct_type, i),
|
struct_type, parser_impl_.GetMemberPipelineDecorations(*struct_type, i),
|
||||||
&member_attrs)) {
|
member_attrs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!EmitPipelineOutput(var_name, var_type, &member_attrs, index_prefix,
|
if (!EmitPipelineOutput(var_name, var_type, index_prefix,
|
||||||
members[static_cast<size_t>(i)], forced_member_type,
|
members[static_cast<size_t>(i)], forced_member_type,
|
||||||
return_members, return_exprs)) {
|
member_attrs, return_members, return_exprs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Copy the location as updated by nested expansion of the member.
|
// Copy the location as updated by nested expansion of the member.
|
||||||
parser_impl_.SetLocation(decos,
|
parser_impl_.SetLocation(attrs, member_attrs.Get<ast::LocationAttribute>());
|
||||||
ast::GetAttribute<ast::LocationAttribute>(member_attrs));
|
|
||||||
}
|
}
|
||||||
return success();
|
return success();
|
||||||
},
|
},
|
||||||
[&](Default) {
|
[&](Default) {
|
||||||
const bool is_builtin = ast::HasAttribute<ast::BuiltinAttribute>(*decos);
|
const bool is_builtin = attrs.Has<ast::BuiltinAttribute>();
|
||||||
|
|
||||||
const Type* member_type = is_builtin ? forced_member_type : tip_type;
|
const Type* member_type = is_builtin ? forced_member_type : tip_type;
|
||||||
// Derive the member name directly from the variable name. They can't
|
// Derive the member name directly from the variable name. They can't
|
||||||
// collide.
|
// collide.
|
||||||
const auto member_name = namer_.MakeDerivedName(var_name);
|
const auto member_name = namer_.MakeDerivedName(var_name);
|
||||||
// Create the member.
|
// Create the member.
|
||||||
// TODO(dneto): Note: If the parameter has non-location decorations,
|
// TODO(dneto): Note: If the parameter has non-location decorations, then those
|
||||||
// then those decoration AST nodes will be reused between multiple
|
// decoration AST nodes will be reused between multiple elements of a matrix, array, or
|
||||||
// elements of a matrix, array, or structure. Normally that's
|
// structure. Normally that's disallowed but currently the SPIR-V reader will make
|
||||||
// disallowed but currently the SPIR-V reader will make duplicates when
|
// duplicates when the entire AST is cloned at the top level of the SPIR-V reader flow.
|
||||||
// the entire AST is cloned at the top level of the SPIR-V reader flow.
|
|
||||||
// Consider rewriting this to avoid this node-sharing.
|
// Consider rewriting this to avoid this node-sharing.
|
||||||
return_members->Push(
|
return_members.Push(
|
||||||
builder_.Member(member_name, member_type->Build(builder_), *decos));
|
builder_.Member(member_name, member_type->Build(builder_), attrs.list));
|
||||||
|
|
||||||
// Create an expression to evaluate the part of the variable indexed by
|
// Create an expression to evaluate the part of the variable indexed by
|
||||||
// the index_prefix.
|
// the index_prefix.
|
||||||
|
@ -1240,11 +1225,10 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
// the variable store type.
|
// the variable store type.
|
||||||
load_source = builder_.Bitcast(forced_member_type->Build(builder_), load_source);
|
load_source = builder_.Bitcast(forced_member_type->Build(builder_), load_source);
|
||||||
}
|
}
|
||||||
return_exprs->Push(load_source);
|
return_exprs.Push(load_source);
|
||||||
|
|
||||||
// Increment the location attribute, in case more parameters will
|
// Increment the location attribute, in case more parameters will follow.
|
||||||
// follow.
|
IncrementLocation(attrs);
|
||||||
IncrementLocation(decos);
|
|
||||||
|
|
||||||
return success();
|
return success();
|
||||||
});
|
});
|
||||||
|
@ -1270,8 +1254,8 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
TINT_ASSERT(Reader, opcode(var) == spv::Op::OpVariable);
|
TINT_ASSERT(Reader, opcode(var) == spv::Op::OpVariable);
|
||||||
auto* store_type = GetVariableStoreType(*var);
|
auto* store_type = GetVariableStoreType(*var);
|
||||||
auto* forced_param_type = store_type;
|
auto* forced_param_type = store_type;
|
||||||
AttributeList param_decos;
|
Attributes param_attrs;
|
||||||
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_param_type, ¶m_decos,
|
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_param_type, param_attrs,
|
||||||
true)) {
|
true)) {
|
||||||
// This occurs, and is not an error, for the PointSize builtin.
|
// This occurs, and is not an error, for the PointSize builtin.
|
||||||
if (!success()) {
|
if (!success()) {
|
||||||
|
@ -1287,18 +1271,17 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
const auto var_name = namer_.GetName(var_id);
|
const auto var_name = namer_.GetName(var_id);
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (HasBuiltinSampleMask(param_decos)) {
|
if (param_attrs.flags.Contains(Attributes::Flags::kHasBuiltinSampleMask)) {
|
||||||
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar.
|
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar.
|
||||||
// Use the first element only.
|
// Use the first element only.
|
||||||
auto* sample_mask_array_type = store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
|
auto* sample_mask_array_type = store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
|
||||||
TINT_ASSERT(Reader, sample_mask_array_type);
|
TINT_ASSERT(Reader, sample_mask_array_type);
|
||||||
ok = EmitPipelineInput(var_name, store_type, ¶m_decos, {0},
|
ok = EmitPipelineInput(var_name, store_type, {0}, sample_mask_array_type->type,
|
||||||
sample_mask_array_type->type, forced_param_type, &decl.params,
|
forced_param_type, param_attrs, decl.params, stmts);
|
||||||
&stmts);
|
|
||||||
} else {
|
} else {
|
||||||
// The normal path.
|
// The normal path.
|
||||||
ok = EmitPipelineInput(var_name, store_type, ¶m_decos, {}, store_type,
|
ok = EmitPipelineInput(var_name, store_type, {}, store_type, forced_param_type,
|
||||||
forced_param_type, &decl.params, &stmts);
|
param_attrs, decl.params, stmts);
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1330,12 +1313,12 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
// The SPIR-V gl_PerVertex variable has already been remapped to
|
// The SPIR-V gl_PerVertex variable has already been remapped to
|
||||||
// a gl_Position variable. Substitute the type.
|
// a gl_Position variable. Substitute the type.
|
||||||
const Type* param_type = ty_.Vector(ty_.F32(), 4);
|
const Type* param_type = ty_.Vector(ty_.F32(), 4);
|
||||||
AttributeList out_decos{
|
|
||||||
create<ast::BuiltinAttribute>(source, builtin::BuiltinValue::kPosition)};
|
|
||||||
|
|
||||||
const auto var_name = namer_.GetName(var_id);
|
const auto var_name = namer_.GetName(var_id);
|
||||||
return_members.Push(
|
return_members.Push(
|
||||||
builder_.Member(var_name, param_type->Build(builder_), out_decos));
|
builder_.Member(var_name, param_type->Build(builder_),
|
||||||
|
utils::Vector{
|
||||||
|
builder_.Builtin(source, builtin::BuiltinValue::kPosition),
|
||||||
|
}));
|
||||||
return_exprs.Push(builder_.Expr(var_name));
|
return_exprs.Push(builder_.Expr(var_name));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -1344,9 +1327,9 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
TINT_ASSERT(Reader, opcode(var) == spv::Op::OpVariable);
|
TINT_ASSERT(Reader, opcode(var) == spv::Op::OpVariable);
|
||||||
const Type* store_type = GetVariableStoreType(*var);
|
const Type* store_type = GetVariableStoreType(*var);
|
||||||
const Type* forced_member_type = store_type;
|
const Type* forced_member_type = store_type;
|
||||||
AttributeList out_decos;
|
Attributes out_attrs;
|
||||||
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_member_type,
|
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_member_type,
|
||||||
&out_decos, true)) {
|
out_attrs, true)) {
|
||||||
// This occurs, and is not an error, for the PointSize builtin.
|
// This occurs, and is not an error, for the PointSize builtin.
|
||||||
if (!success()) {
|
if (!success()) {
|
||||||
// But exit early if an error was logged.
|
// But exit early if an error was logged.
|
||||||
|
@ -1357,19 +1340,20 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
|
|
||||||
const auto var_name = namer_.GetName(var_id);
|
const auto var_name = namer_.GetName(var_id);
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (HasBuiltinSampleMask(out_decos)) {
|
if (out_attrs.flags.Contains(Attributes::Flags::kHasBuiltinSampleMask)) {
|
||||||
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a
|
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a
|
||||||
// scalar. Use the first element only.
|
// scalar. Use the first element only.
|
||||||
auto* sample_mask_array_type =
|
auto* sample_mask_array_type =
|
||||||
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
|
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
|
||||||
TINT_ASSERT(Reader, sample_mask_array_type);
|
TINT_ASSERT(Reader, sample_mask_array_type);
|
||||||
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {0},
|
ok = EmitPipelineOutput(var_name, store_type, {0}, sample_mask_array_type->type,
|
||||||
sample_mask_array_type->type, forced_member_type,
|
forced_member_type, out_attrs, return_members,
|
||||||
&return_members, &return_exprs);
|
return_exprs);
|
||||||
} else {
|
} else {
|
||||||
// The normal path.
|
// The normal path.
|
||||||
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {}, store_type,
|
ok =
|
||||||
forced_member_type, &return_members, &return_exprs);
|
EmitPipelineOutput(var_name, store_type, {}, store_type, forced_member_type,
|
||||||
|
out_attrs, return_members, return_exprs);
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1384,7 +1368,7 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
} else {
|
} else {
|
||||||
// Create and register the result type.
|
// Create and register the result type.
|
||||||
auto* str = create<ast::Struct>(Source{}, builder_.Ident(return_struct_sym),
|
auto* str = create<ast::Struct>(Source{}, builder_.Ident(return_struct_sym),
|
||||||
return_members, AttributeList{});
|
return_members, utils::Empty);
|
||||||
parser_impl_.AddTypeDecl(return_struct_sym, str);
|
parser_impl_.AddTypeDecl(return_struct_sym, str);
|
||||||
return_type = builder_.ty.Of(str);
|
return_type = builder_.ty.Of(str);
|
||||||
|
|
||||||
|
@ -1394,8 +1378,9 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AttributeList fn_attrs;
|
utils::Vector<const ast::Attribute*, 2> fn_attrs{
|
||||||
fn_attrs.Push(create<ast::StageAttribute>(source, ep_info_->stage));
|
create<ast::StageAttribute>(source, ep_info_->stage),
|
||||||
|
};
|
||||||
|
|
||||||
if (ep_info_->stage == ast::PipelineStage::kCompute) {
|
if (ep_info_->stage == ast::PipelineStage::kCompute) {
|
||||||
auto& size = ep_info_->workgroup_size;
|
auto& size = ep_info_->workgroup_size;
|
||||||
|
@ -1442,7 +1427,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
|
||||||
: parser_impl_.ConvertType(param->type_id());
|
: parser_impl_.ConvertType(param->type_id());
|
||||||
|
|
||||||
if (type != nullptr) {
|
if (type != nullptr) {
|
||||||
auto* ast_param = parser_impl_.MakeParameter(param->result_id(), type, AttributeList{});
|
auto* ast_param = parser_impl_.MakeParameter(param->result_id(), type, Attributes{});
|
||||||
// Parameters are treated as const declarations.
|
// Parameters are treated as const declarations.
|
||||||
ast_params.Push(ast_param);
|
ast_params.Push(ast_param);
|
||||||
// The value is accessible by name.
|
// The value is accessible by name.
|
||||||
|
@ -1458,7 +1443,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
|
||||||
decl->name = name;
|
decl->name = name;
|
||||||
decl->params = std::move(ast_params);
|
decl->params = std::move(ast_params);
|
||||||
decl->return_type = ret_ty;
|
decl->return_type = ret_ty;
|
||||||
decl->attributes.Clear();
|
decl->attributes = {};
|
||||||
|
|
||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
@ -2523,7 +2508,7 @@ bool FunctionEmitter::EmitFunctionVariables() {
|
||||||
}
|
}
|
||||||
auto* var = parser_impl_.MakeVar(inst.result_id(), builtin::AddressSpace::kUndefined,
|
auto* var = parser_impl_.MakeVar(inst.result_id(), builtin::AddressSpace::kUndefined,
|
||||||
builtin::Access::kUndefined, var_store_type, initializer,
|
builtin::Access::kUndefined, var_store_type, initializer,
|
||||||
AttributeList{});
|
Attributes{});
|
||||||
auto* var_decl_stmt = create<ast::VariableDeclStatement>(Source{}, var);
|
auto* var_decl_stmt = create<ast::VariableDeclStatement>(Source{}, var);
|
||||||
AddStatement(var_decl_stmt);
|
AddStatement(var_decl_stmt);
|
||||||
auto* var_type = ty_.Reference(var_store_type, builtin::AddressSpace::kUndefined);
|
auto* var_type = ty_.Reference(var_store_type, builtin::AddressSpace::kUndefined);
|
||||||
|
@ -3370,7 +3355,7 @@ bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
|
||||||
AddStatement(create<ast::VariableDeclStatement>(
|
AddStatement(create<ast::VariableDeclStatement>(
|
||||||
Source{},
|
Source{},
|
||||||
parser_impl_.MakeVar(id, builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
|
parser_impl_.MakeVar(id, builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
|
||||||
store_type, nullptr, AttributeList{})));
|
store_type, nullptr, Attributes{})));
|
||||||
auto* type = ty_.Reference(store_type, builtin::AddressSpace::kUndefined);
|
auto* type = ty_.Reference(store_type, builtin::AddressSpace::kUndefined);
|
||||||
identifier_types_.emplace(id, type);
|
identifier_types_.emplace(id, type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
|
#include "src/tint/reader/spirv/attributes.h"
|
||||||
#include "src/tint/reader/spirv/construct.h"
|
#include "src/tint/reader/spirv/construct.h"
|
||||||
#include "src/tint/reader/spirv/parser_impl.h"
|
#include "src/tint/reader/spirv/parser_impl.h"
|
||||||
|
|
||||||
|
@ -420,7 +421,6 @@ class StatementBuilder : public Castable<StatementBuilder, ast::Statement> {
|
||||||
|
|
||||||
/// A FunctionEmitter emits a SPIR-V function onto a Tint AST module.
|
/// A FunctionEmitter emits a SPIR-V function onto a Tint AST module.
|
||||||
class FunctionEmitter {
|
class FunctionEmitter {
|
||||||
using AttributeList = utils::Vector<const ast::Attribute*, 8>;
|
|
||||||
using StructMemberList = utils::Vector<const ast::StructMember*, 8>;
|
using StructMemberList = utils::Vector<const ast::StructMember*, 8>;
|
||||||
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
|
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
|
||||||
using ParameterList = utils::Vector<const ast::Parameter*, 8>;
|
using ParameterList = utils::Vector<const ast::Parameter*, 8>;
|
||||||
|
@ -473,32 +473,31 @@ class FunctionEmitter {
|
||||||
/// @returns false if emission failed.
|
/// @returns false if emission failed.
|
||||||
bool EmitEntryPointAsWrapper();
|
bool EmitEntryPointAsWrapper();
|
||||||
|
|
||||||
/// Creates one or more entry point input parameters corresponding to a
|
/// Creates one or more entry point input parameters corresponding to a part of an input
|
||||||
/// part of an input variable. The part of the input variable is specfied
|
/// variable. The part of the input variable is specfied by the `index_prefix`, which
|
||||||
/// by the `index_prefix`, which successively indexes into the variable.
|
/// successively indexes into the variable. Also generates the assignment statements that copy
|
||||||
/// Also generates the assignment statements that copy the input parameter
|
/// the input parameter to the corresponding part of the variable. Assumes the variable has
|
||||||
/// to the corresponding part of the variable. Assumes the variable
|
/// already been created in the Private address space.
|
||||||
/// has already been created in the Private address space.
|
|
||||||
/// @param var_name The name of the variable
|
/// @param var_name The name of the variable
|
||||||
/// @param var_type The store type of the variable
|
/// @param var_type The store type of the variable
|
||||||
/// @param decos The variable's decorations
|
/// @param attributes The variable's attributes
|
||||||
/// @param index_prefix Indices stepping into the variable, indicating
|
/// @param index_prefix Indices stepping into the variable, indicating what part of the variable
|
||||||
/// what part of the variable to populate.
|
/// to populate.
|
||||||
/// @param tip_type The type of the component inside variable, after indexing
|
/// @param tip_type The type of the component inside variable, after indexing with the indices
|
||||||
/// with the indices in `index_prefix`.
|
/// in `index_prefix`.
|
||||||
/// @param forced_param_type The type forced by WGSL, if the variable is a
|
/// @param forced_param_type The type forced by WGSL, if the variable is a builtin, otherwise
|
||||||
/// builtin, otherwise the same as var_type.
|
/// the same as var_type.
|
||||||
/// @param params The parameter list where the new parameter is appended.
|
/// @param params The parameter list where the new parameter is appended.
|
||||||
/// @param statements The statement list where the assignment is appended.
|
/// @param statements The statement list where the assignment is appended.
|
||||||
/// @returns false if emission failed
|
/// @returns false if emission failed
|
||||||
bool EmitPipelineInput(std::string var_name,
|
bool EmitPipelineInput(std::string var_name,
|
||||||
const Type* var_type,
|
const Type* var_type,
|
||||||
AttributeList* decos,
|
|
||||||
utils::Vector<int, 8> index_prefix,
|
utils::Vector<int, 8> index_prefix,
|
||||||
const Type* tip_type,
|
const Type* tip_type,
|
||||||
const Type* forced_param_type,
|
const Type* forced_param_type,
|
||||||
ParameterList* params,
|
Attributes& attributes,
|
||||||
StatementList* statements);
|
ParameterList& params,
|
||||||
|
StatementList& statements);
|
||||||
|
|
||||||
/// Creates one or more struct members from an output variable, and the
|
/// Creates one or more struct members from an output variable, and the
|
||||||
/// expressions that compute the value they contribute to the entry point
|
/// expressions that compute the value they contribute to the entry point
|
||||||
|
@ -507,31 +506,31 @@ class FunctionEmitter {
|
||||||
/// Assumes the variable has already been created in the Private address space
|
/// Assumes the variable has already been created in the Private address space
|
||||||
/// @param var_name The name of the variable
|
/// @param var_name The name of the variable
|
||||||
/// @param var_type The store type of the variable
|
/// @param var_type The store type of the variable
|
||||||
/// @param decos The variable's decorations
|
|
||||||
/// @param index_prefix Indices stepping into the variable, indicating what part of the variable
|
/// @param index_prefix Indices stepping into the variable, indicating what part of the variable
|
||||||
/// to populate.
|
/// to populate.
|
||||||
/// @param tip_type The type of the component inside variable, after indexing with the indices
|
/// @param tip_type The type of the component inside variable, after indexing with the indices
|
||||||
/// in `index_prefix`.
|
/// in `index_prefix`.
|
||||||
/// @param forced_member_type The type forced by WGSL, if the variable is a builtin, otherwise
|
/// @param forced_member_type The type forced by WGSL, if the variable is a builtin, otherwise
|
||||||
/// the same as var_type.
|
/// the same as var_type.
|
||||||
|
/// @param attributes The variable's attributes
|
||||||
/// @param return_members The struct member list where the new member is added.
|
/// @param return_members The struct member list where the new member is added.
|
||||||
/// @param return_exprs The expression list where the return expression is added.
|
/// @param return_exprs The expression list where the return expression is added.
|
||||||
/// @returns false if emission failed
|
/// @returns false if emission failed
|
||||||
bool EmitPipelineOutput(std::string var_name,
|
bool EmitPipelineOutput(std::string var_name,
|
||||||
const Type* var_type,
|
const Type* var_type,
|
||||||
AttributeList* decos,
|
|
||||||
utils::Vector<int, 8> index_prefix,
|
utils::Vector<int, 8> index_prefix,
|
||||||
const Type* tip_type,
|
const Type* tip_type,
|
||||||
const Type* forced_member_type,
|
const Type* forced_member_type,
|
||||||
StructMemberList* return_members,
|
Attributes& attributes,
|
||||||
ExpressionList* return_exprs);
|
StructMemberList& return_members,
|
||||||
|
ExpressionList& return_exprs);
|
||||||
|
|
||||||
/// Updates the attribute list, replacing an existing Location attribute
|
/// Updates the attribute list, replacing an existing Location attribute
|
||||||
/// with another having one higher location value. Does nothing if no
|
/// with another having one higher location value. Does nothing if no
|
||||||
/// location attribute exists.
|
/// location attribute exists.
|
||||||
/// Assumes the list contains at most one Location attribute.
|
/// Assumes the list contains at most one Location attribute.
|
||||||
/// @param attributes the attribute list to modify
|
/// @param attributes the attribute list to modify
|
||||||
void IncrementLocation(AttributeList* attributes);
|
void IncrementLocation(Attributes& attributes);
|
||||||
|
|
||||||
/// Create an ast::BlockStatement representing the body of the function.
|
/// Create an ast::BlockStatement representing the body of the function.
|
||||||
/// This creates the statement stack, which is non-empty for the lifetime
|
/// This creates the statement stack, which is non-empty for the lifetime
|
||||||
|
@ -976,7 +975,7 @@ class FunctionEmitter {
|
||||||
/// Function return type
|
/// Function return type
|
||||||
const Type* return_type;
|
const Type* return_type;
|
||||||
/// Function attributes
|
/// Function attributes
|
||||||
AttributeList attributes;
|
Attributes attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parse the function declaration, which comprises the name, parameters, and
|
/// Parse the function declaration, which comprises the name, parameters, and
|
||||||
|
|
|
@ -26,32 +26,146 @@ namespace {
|
||||||
|
|
||||||
const char* kWGSLReservedWords[] = {
|
const char* kWGSLReservedWords[] = {
|
||||||
// Please keep this list sorted
|
// Please keep this list sorted
|
||||||
"array", "as", "asm",
|
"array",
|
||||||
"bf16", "binding", "block",
|
"as",
|
||||||
"bool", "break", "builtin",
|
"asm",
|
||||||
"case", "cast", "compute",
|
"atomic",
|
||||||
"const", "continue", "default",
|
"bf16",
|
||||||
"discard", "do", "else",
|
"binding",
|
||||||
"elseif", "entry_point", "enum",
|
"block",
|
||||||
"f16", "f32", "fallthrough",
|
"bool",
|
||||||
"false", "fn", "for",
|
"break",
|
||||||
"fragment", "i16", "i32",
|
"builtin",
|
||||||
"i64", "i8", "if",
|
"case",
|
||||||
"image", "import", "in",
|
"cast",
|
||||||
"let", "location", "loop",
|
"compute",
|
||||||
"mat2x2", "mat2x3", "mat2x4",
|
"const",
|
||||||
"mat3x2", "mat3x3", "mat3x4",
|
"continue",
|
||||||
"mat4x2", "mat4x3", "mat4x4",
|
"default",
|
||||||
"offset", "out", "override",
|
"discard",
|
||||||
"premerge", "private", "ptr",
|
"do",
|
||||||
"regardless", "return", "set",
|
"else",
|
||||||
"storage", "struct", "switch",
|
"elseif",
|
||||||
"true", "type", "typedef",
|
"entry_point",
|
||||||
"u16", "u32", "u64",
|
"enum",
|
||||||
"u8", "uniform", "uniform_constant",
|
"f16",
|
||||||
"unless", "using", "var",
|
"f32",
|
||||||
"vec2", "vec3", "vec4",
|
"fallthrough",
|
||||||
"vertex", "void", "while",
|
"false",
|
||||||
|
"fn",
|
||||||
|
"for",
|
||||||
|
"frag_depth",
|
||||||
|
"fragment",
|
||||||
|
"front_facing",
|
||||||
|
"global_invocation_id",
|
||||||
|
"i16",
|
||||||
|
"i32",
|
||||||
|
"i64",
|
||||||
|
"i8",
|
||||||
|
"if",
|
||||||
|
"image",
|
||||||
|
"import",
|
||||||
|
"in",
|
||||||
|
"instance_index",
|
||||||
|
"let",
|
||||||
|
"local_invocation_id",
|
||||||
|
"local_invocation_index",
|
||||||
|
"location",
|
||||||
|
"loop",
|
||||||
|
"mat2x2",
|
||||||
|
"mat2x2f",
|
||||||
|
"mat2x2h",
|
||||||
|
"mat2x3",
|
||||||
|
"mat2x3f",
|
||||||
|
"mat2x3h",
|
||||||
|
"mat2x4",
|
||||||
|
"mat2x4f",
|
||||||
|
"mat2x4h",
|
||||||
|
"mat3x2",
|
||||||
|
"mat3x2f",
|
||||||
|
"mat3x2h",
|
||||||
|
"mat3x3",
|
||||||
|
"mat3x3f",
|
||||||
|
"mat3x3h",
|
||||||
|
"mat3x4",
|
||||||
|
"mat3x4f",
|
||||||
|
"mat3x4h",
|
||||||
|
"mat4x2",
|
||||||
|
"mat4x2f",
|
||||||
|
"mat4x2h",
|
||||||
|
"mat4x3",
|
||||||
|
"mat4x3f",
|
||||||
|
"mat4x3h",
|
||||||
|
"mat4x4",
|
||||||
|
"mat4x4f",
|
||||||
|
"mat4x4h",
|
||||||
|
"num_workgroups",
|
||||||
|
"offset",
|
||||||
|
"out",
|
||||||
|
"override",
|
||||||
|
"position",
|
||||||
|
"premerge",
|
||||||
|
"private",
|
||||||
|
"ptr",
|
||||||
|
"regardless",
|
||||||
|
"return",
|
||||||
|
"sample_index",
|
||||||
|
"sample_mask",
|
||||||
|
"sampler_comparison",
|
||||||
|
"sampler",
|
||||||
|
"set",
|
||||||
|
"storage",
|
||||||
|
"struct",
|
||||||
|
"switch",
|
||||||
|
"texture_1d",
|
||||||
|
"texture_2d_array",
|
||||||
|
"texture_2d",
|
||||||
|
"texture_3d",
|
||||||
|
"texture_cube_array",
|
||||||
|
"texture_cube",
|
||||||
|
"texture_depth_2d_array",
|
||||||
|
"texture_depth_2d",
|
||||||
|
"texture_depth_cube_array",
|
||||||
|
"texture_depth_cube",
|
||||||
|
"texture_depth_multisampled_2d",
|
||||||
|
"texture_external",
|
||||||
|
"texture_multisampled_2d",
|
||||||
|
"texture_storage_1d",
|
||||||
|
"texture_storage_2d_array",
|
||||||
|
"texture_storage_2d",
|
||||||
|
"texture_storage_3d",
|
||||||
|
"true",
|
||||||
|
"type",
|
||||||
|
"typedef",
|
||||||
|
"u16",
|
||||||
|
"u32",
|
||||||
|
"u64",
|
||||||
|
"u8",
|
||||||
|
"uniform_constant",
|
||||||
|
"uniform",
|
||||||
|
"unless",
|
||||||
|
"using",
|
||||||
|
"var",
|
||||||
|
"vec2",
|
||||||
|
"vec2f",
|
||||||
|
"vec2h",
|
||||||
|
"vec2i",
|
||||||
|
"vec2u",
|
||||||
|
"vec3",
|
||||||
|
"vec3f",
|
||||||
|
"vec3h",
|
||||||
|
"vec3i",
|
||||||
|
"vec3u",
|
||||||
|
"vec4",
|
||||||
|
"vec4f",
|
||||||
|
"vec4h",
|
||||||
|
"vec4i",
|
||||||
|
"vec4u",
|
||||||
|
"vertex_index",
|
||||||
|
"vertex",
|
||||||
|
"void",
|
||||||
|
"while",
|
||||||
|
"workgroup_id",
|
||||||
"workgroup",
|
"workgroup",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -461,49 +461,42 @@ std::string ParserImpl::ShowType(uint32_t type_id) {
|
||||||
return "SPIR-V type " + std::to_string(type_id);
|
return "SPIR-V type " + std::to_string(type_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParserImpl::AttributeList ParserImpl::ConvertMemberDecoration(uint32_t struct_type_id,
|
Attributes ParserImpl::ConvertMemberDecoration(uint32_t struct_type_id,
|
||||||
uint32_t member_index,
|
uint32_t member_index,
|
||||||
const Type* member_ty,
|
const Type* member_ty,
|
||||||
const Decoration& decoration) {
|
const Decoration& decoration) {
|
||||||
if (decoration.empty()) {
|
if (decoration.empty()) {
|
||||||
Fail() << "malformed SPIR-V decoration: it's empty";
|
Fail() << "malformed SPIR-V decoration: it's empty";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
Attributes out;
|
||||||
switch (static_cast<spv::Decoration>(decoration[0])) {
|
switch (static_cast<spv::Decoration>(decoration[0])) {
|
||||||
case spv::Decoration::Offset:
|
case spv::Decoration::Offset: {
|
||||||
if (decoration.size() != 2) {
|
if (decoration.size() != 2) {
|
||||||
Fail() << "malformed Offset decoration: expected 1 literal operand, has "
|
Fail() << "malformed Offset decoration: expected 1 literal operand, has "
|
||||||
<< decoration.size() - 1 << ": member " << member_index << " of "
|
<< decoration.size() - 1 << ": member " << member_index << " of "
|
||||||
<< ShowType(struct_type_id);
|
<< ShowType(struct_type_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return {
|
out.Add(builder_.MemberOffset(Source{}, AInt(decoration[1])));
|
||||||
builder_.MemberOffset(Source{}, AInt(decoration[1])),
|
break;
|
||||||
};
|
}
|
||||||
case spv::Decoration::NonReadable:
|
case spv::Decoration::NonReadable: // WGSL doesn't have a member decoration for this.
|
||||||
// WGSL doesn't have a member decoration for this. Silently drop it.
|
case spv::Decoration::NonWritable: // WGSL doesn't have a member decoration for this.
|
||||||
return {};
|
case spv::Decoration::ColMajor: // WGSL only supports column major matrices.
|
||||||
case spv::Decoration::NonWritable:
|
case spv::Decoration::RelaxedPrecision: // WGSL doesn't support relaxed precision.
|
||||||
// WGSL doesn't have a member decoration for this.
|
break;
|
||||||
return {};
|
|
||||||
case spv::Decoration::ColMajor:
|
|
||||||
// WGSL only supports column major matrices.
|
|
||||||
return {};
|
|
||||||
case spv::Decoration::RelaxedPrecision:
|
|
||||||
// WGSL doesn't support relaxed precision.
|
|
||||||
return {};
|
|
||||||
case spv::Decoration::RowMajor:
|
case spv::Decoration::RowMajor:
|
||||||
Fail() << "WGSL does not support row-major matrices: can't "
|
Fail() << "WGSL does not support row-major matrices: can't "
|
||||||
"translate member "
|
"translate member "
|
||||||
<< member_index << " of " << ShowType(struct_type_id);
|
<< member_index << " of " << ShowType(struct_type_id);
|
||||||
return {};
|
break;
|
||||||
case spv::Decoration::MatrixStride: {
|
case spv::Decoration::MatrixStride: {
|
||||||
if (decoration.size() != 2) {
|
if (decoration.size() != 2) {
|
||||||
Fail() << "malformed MatrixStride decoration: expected 1 literal "
|
Fail() << "malformed MatrixStride decoration: expected 1 literal operand, has "
|
||||||
"operand, has "
|
|
||||||
<< decoration.size() - 1 << ": member " << member_index << " of "
|
<< decoration.size() - 1 << ": member " << member_index << " of "
|
||||||
<< ShowType(struct_type_id);
|
<< ShowType(struct_type_id);
|
||||||
return {};
|
break;
|
||||||
}
|
}
|
||||||
uint32_t stride = decoration[1];
|
uint32_t stride = decoration[1];
|
||||||
auto* ty = member_ty->UnwrapAlias();
|
auto* ty = member_ty->UnwrapAlias();
|
||||||
|
@ -513,31 +506,30 @@ ParserImpl::AttributeList ParserImpl::ConvertMemberDecoration(uint32_t struct_ty
|
||||||
auto* mat = ty->As<Matrix>();
|
auto* mat = ty->As<Matrix>();
|
||||||
if (!mat) {
|
if (!mat) {
|
||||||
Fail() << "MatrixStride cannot be applied to type " << ty->String();
|
Fail() << "MatrixStride cannot be applied to type " << ty->String();
|
||||||
return {};
|
break;
|
||||||
}
|
}
|
||||||
uint32_t natural_stride = (mat->rows == 2) ? 8 : 16;
|
uint32_t natural_stride = (mat->rows == 2) ? 8 : 16;
|
||||||
if (stride == natural_stride) {
|
if (stride == natural_stride) {
|
||||||
return {}; // Decoration matches the natural stride for the matrix
|
break; // Decoration matches the natural stride for the matrix
|
||||||
}
|
}
|
||||||
if (!member_ty->Is<Matrix>()) {
|
if (!member_ty->Is<Matrix>()) {
|
||||||
Fail() << "custom matrix strides not currently supported on array of "
|
Fail() << "custom matrix strides not currently supported on array of matrices";
|
||||||
"matrices";
|
break;
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
return {
|
out.Add(create<ast::StrideAttribute>(Source{}, decoration[1]));
|
||||||
create<ast::StrideAttribute>(Source{}, decoration[1]),
|
out.Add(builder_.ASTNodes().Create<ast::DisableValidationAttribute>(
|
||||||
builder_.ASTNodes().Create<ast::DisableValidationAttribute>(
|
builder_.ID(), builder_.AllocateNodeID(),
|
||||||
builder_.ID(), builder_.AllocateNodeID(),
|
ast::DisabledValidation::kIgnoreStrideAttribute));
|
||||||
ast::DisabledValidation::kIgnoreStrideAttribute),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// TODO(dneto): Support the remaining member decorations.
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// TODO(dneto): Support the remaining member decorations.
|
||||||
|
Fail() << "unhandled member decoration: " << decoration[0] << " on member "
|
||||||
|
<< member_index << " of " << ShowType(struct_type_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Fail() << "unhandled member decoration: " << decoration[0] << " on member " << member_index
|
return out;
|
||||||
<< " of " << ShowType(struct_type_id);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserImpl::BuildInternalModule() {
|
bool ParserImpl::BuildInternalModule() {
|
||||||
|
@ -1138,7 +1130,7 @@ const Type* ParserImpl::ConvertType(uint32_t type_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_non_writable = false;
|
bool is_non_writable = false;
|
||||||
AttributeList ast_member_decorations;
|
Attributes ast_member_decorations;
|
||||||
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
|
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
|
||||||
if (IsPipelineDecoration(decoration)) {
|
if (IsPipelineDecoration(decoration)) {
|
||||||
// IO decorations are handled when emitting the entry point.
|
// IO decorations are handled when emitting the entry point.
|
||||||
|
@ -1149,11 +1141,9 @@ const Type* ParserImpl::ConvertType(uint32_t type_id,
|
||||||
// the members are non-writable.
|
// the members are non-writable.
|
||||||
is_non_writable = true;
|
is_non_writable = true;
|
||||||
} else {
|
} else {
|
||||||
auto decos =
|
auto attrs =
|
||||||
ConvertMemberDecoration(type_id, member_index, ast_member_ty, decoration);
|
ConvertMemberDecoration(type_id, member_index, ast_member_ty, decoration);
|
||||||
for (auto* deco : decos) {
|
ast_member_decorations.Add(attrs);
|
||||||
ast_member_decorations.Push(deco);
|
|
||||||
}
|
|
||||||
if (!success_) {
|
if (!success_) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1168,7 +1158,7 @@ const Type* ParserImpl::ConvertType(uint32_t type_id,
|
||||||
const auto member_name = namer_.GetMemberName(type_id, member_index);
|
const auto member_name = namer_.GetMemberName(type_id, member_index);
|
||||||
auto* ast_struct_member =
|
auto* ast_struct_member =
|
||||||
builder_.Member(Source{}, member_name, ast_member_ty->Build(builder_),
|
builder_.Member(Source{}, member_name, ast_member_ty->Build(builder_),
|
||||||
std::move(ast_member_decorations));
|
std::move(ast_member_decorations.list));
|
||||||
ast_members.Push(ast_struct_member);
|
ast_members.Push(ast_struct_member);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1368,7 +1358,7 @@ bool ParserImpl::EmitScalarSpecConstants() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ast_type && ast_expr) {
|
if (ast_type && ast_expr) {
|
||||||
AttributeList spec_id_decos;
|
Attributes spec_id_attrs;
|
||||||
for (const auto& deco : GetDecorationsFor(inst.result_id())) {
|
for (const auto& deco : GetDecorationsFor(inst.result_id())) {
|
||||||
if ((deco.size() == 2) && (deco[0] == uint32_t(spv::Decoration::SpecId))) {
|
if ((deco.size() == 2) && (deco[0] == uint32_t(spv::Decoration::SpecId))) {
|
||||||
const uint32_t id = deco[1];
|
const uint32_t id = deco[1];
|
||||||
|
@ -1378,12 +1368,12 @@ bool ParserImpl::EmitScalarSpecConstants() {
|
||||||
<< inst.result_id() << " has SpecId " << id;
|
<< inst.result_id() << " has SpecId " << id;
|
||||||
}
|
}
|
||||||
auto* cid = builder_.Id(Source{}, AInt(id));
|
auto* cid = builder_.Id(Source{}, AInt(id));
|
||||||
spec_id_decos.Push(cid);
|
spec_id_attrs.Add(cid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto* ast_var =
|
auto* ast_var =
|
||||||
MakeOverride(inst.result_id(), ast_type, ast_expr, std::move(spec_id_decos));
|
MakeOverride(inst.result_id(), ast_type, ast_expr, std::move(spec_id_attrs));
|
||||||
if (ast_var) {
|
if (ast_var) {
|
||||||
scalar_spec_constants_.insert(inst.result_id());
|
scalar_spec_constants_.insert(inst.result_id());
|
||||||
}
|
}
|
||||||
|
@ -1506,7 +1496,7 @@ bool ParserImpl::EmitModuleScopeVariables() {
|
||||||
}
|
}
|
||||||
auto ast_access = VarAccess(ast_store_type, ast_address_space);
|
auto ast_access = VarAccess(ast_store_type, ast_address_space);
|
||||||
auto* ast_var = MakeVar(var.result_id(), ast_address_space, ast_access, ast_store_type,
|
auto* ast_var = MakeVar(var.result_id(), ast_address_space, ast_access, ast_store_type,
|
||||||
ast_initializer, utils::Empty);
|
ast_initializer, Attributes{});
|
||||||
// TODO(dneto): initializers (a.k.a. initializer expression)
|
// TODO(dneto): initializers (a.k.a. initializer expression)
|
||||||
if (ast_var) {
|
if (ast_var) {
|
||||||
builder_.AST().AddGlobalVariable(ast_var);
|
builder_.AST().AddGlobalVariable(ast_var);
|
||||||
|
@ -1596,7 +1586,7 @@ const ast::Var* ParserImpl::MakeVar(uint32_t id,
|
||||||
builtin::Access access,
|
builtin::Access access,
|
||||||
const Type* storage_type,
|
const Type* storage_type,
|
||||||
const ast::Expression* initializer,
|
const ast::Expression* initializer,
|
||||||
AttributeList decorations) {
|
Attributes attrs) {
|
||||||
if (storage_type == nullptr) {
|
if (storage_type == nullptr) {
|
||||||
Fail() << "internal error: can't make ast::Variable for null type";
|
Fail() << "internal error: can't make ast::Variable for null type";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1608,14 +1598,14 @@ const ast::Var* ParserImpl::MakeVar(uint32_t id,
|
||||||
address_space = builtin::AddressSpace::kUndefined;
|
address_space = builtin::AddressSpace::kUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ConvertDecorationsForVariable(id, &storage_type, &decorations,
|
if (!ConvertDecorationsForVariable(id, &storage_type, attrs,
|
||||||
address_space != builtin::AddressSpace::kPrivate)) {
|
address_space != builtin::AddressSpace::kPrivate)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sym = builder_.Symbols().Register(namer_.Name(id));
|
auto sym = builder_.Symbols().Register(namer_.Name(id));
|
||||||
return builder_.Var(Source{}, sym, storage_type->Build(builder_), address_space, access,
|
return builder_.Var(Source{}, sym, storage_type->Build(builder_), address_space, access,
|
||||||
initializer, decorations);
|
initializer, std::move(attrs.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ast::Let* ParserImpl::MakeLet(uint32_t id,
|
const ast::Let* ParserImpl::MakeLet(uint32_t id,
|
||||||
|
@ -1628,28 +1618,27 @@ const ast::Let* ParserImpl::MakeLet(uint32_t id,
|
||||||
const ast::Override* ParserImpl::MakeOverride(uint32_t id,
|
const ast::Override* ParserImpl::MakeOverride(uint32_t id,
|
||||||
const Type* type,
|
const Type* type,
|
||||||
const ast::Expression* initializer,
|
const ast::Expression* initializer,
|
||||||
AttributeList decorations) {
|
Attributes attrs) {
|
||||||
if (!ConvertDecorationsForVariable(id, &type, &decorations, false)) {
|
if (!ConvertDecorationsForVariable(id, &type, attrs, false)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto sym = builder_.Symbols().Register(namer_.Name(id));
|
auto sym = builder_.Symbols().Register(namer_.Name(id));
|
||||||
return builder_.Override(Source{}, sym, type->Build(builder_), initializer, decorations);
|
return builder_.Override(Source{}, sym, type->Build(builder_), initializer,
|
||||||
|
std::move(attrs.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ast::Parameter* ParserImpl::MakeParameter(uint32_t id,
|
const ast::Parameter* ParserImpl::MakeParameter(uint32_t id, const Type* type, Attributes attrs) {
|
||||||
const Type* type,
|
if (!ConvertDecorationsForVariable(id, &type, attrs, false)) {
|
||||||
AttributeList decorations) {
|
|
||||||
if (!ConvertDecorationsForVariable(id, &type, &decorations, false)) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sym = builder_.Symbols().Register(namer_.Name(id));
|
auto sym = builder_.Symbols().Register(namer_.Name(id));
|
||||||
return builder_.Param(Source{}, sym, type->Build(builder_), decorations);
|
return builder_.Param(Source{}, sym, type->Build(builder_), attrs.list);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
||||||
const Type** store_type,
|
const Type** store_type,
|
||||||
AttributeList* decorations,
|
Attributes& attrs,
|
||||||
bool transfer_pipeline_io) {
|
bool transfer_pipeline_io) {
|
||||||
DecorationList non_builtin_pipeline_decorations;
|
DecorationList non_builtin_pipeline_decorations;
|
||||||
for (auto& deco : GetDecorationsFor(id)) {
|
for (auto& deco : GetDecorationsFor(id)) {
|
||||||
|
@ -1709,7 +1698,7 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (transfer_pipeline_io) {
|
if (transfer_pipeline_io) {
|
||||||
decorations->Push(create<ast::BuiltinAttribute>(Source{}, ast_builtin));
|
attrs.Add(builder_, Source{}, ast_builtin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transfer_pipeline_io && IsPipelineDecoration(deco)) {
|
if (transfer_pipeline_io && IsPipelineDecoration(deco)) {
|
||||||
|
@ -1720,19 +1709,18 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
||||||
return Fail() << "malformed DescriptorSet decoration on ID " << id
|
return Fail() << "malformed DescriptorSet decoration on ID " << id
|
||||||
<< ": has no operand";
|
<< ": has no operand";
|
||||||
}
|
}
|
||||||
decorations->Push(builder_.Group(Source{}, AInt(deco[1])));
|
attrs.Add(builder_.Group(Source{}, AInt(deco[1])));
|
||||||
}
|
}
|
||||||
if (deco[0] == uint32_t(spv::Decoration::Binding)) {
|
if (deco[0] == uint32_t(spv::Decoration::Binding)) {
|
||||||
if (deco.size() == 1) {
|
if (deco.size() == 1) {
|
||||||
return Fail() << "malformed Binding decoration on ID " << id << ": has no operand";
|
return Fail() << "malformed Binding decoration on ID " << id << ": has no operand";
|
||||||
}
|
}
|
||||||
decorations->Push(builder_.Binding(Source{}, AInt(deco[1])));
|
attrs.Add(builder_.Binding(Source{}, AInt(deco[1])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transfer_pipeline_io) {
|
if (transfer_pipeline_io) {
|
||||||
if (!ConvertPipelineDecorations(*store_type, non_builtin_pipeline_decorations,
|
if (!ConvertPipelineDecorations(*store_type, non_builtin_pipeline_decorations, attrs)) {
|
||||||
decorations)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1753,11 +1741,11 @@ DecorationList ParserImpl::GetMemberPipelineDecorations(const Struct& struct_typ
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserImpl::SetLocation(AttributeList* attributes, const ast::Attribute* replacement) {
|
void ParserImpl::SetLocation(Attributes& attributes, const ast::Attribute* replacement) {
|
||||||
if (!replacement) {
|
if (!replacement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (auto*& attribute : *attributes) {
|
for (auto*& attribute : attributes.list) {
|
||||||
if (attribute->Is<ast::LocationAttribute>()) {
|
if (attribute->Is<ast::LocationAttribute>()) {
|
||||||
// Replace this location attribute with the replacement.
|
// Replace this location attribute with the replacement.
|
||||||
// The old one doesn't leak because it's kept in the builder's AST node
|
// The old one doesn't leak because it's kept in the builder's AST node
|
||||||
|
@ -1767,13 +1755,13 @@ void ParserImpl::SetLocation(AttributeList* attributes, const ast::Attribute* re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The list didn't have a location. Add it.
|
// The list didn't have a location. Add it.
|
||||||
attributes->Push(replacement);
|
attributes.Add(replacement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
|
bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
|
||||||
const DecorationList& decorations,
|
const DecorationList& decorations,
|
||||||
AttributeList* attributes) {
|
Attributes& attributes) {
|
||||||
// Vulkan defaults to perspective-correct interpolation.
|
// Vulkan defaults to perspective-correct interpolation.
|
||||||
builtin::InterpolationType type = builtin::InterpolationType::kPerspective;
|
builtin::InterpolationType type = builtin::InterpolationType::kPerspective;
|
||||||
builtin::InterpolationSampling sampling = builtin::InterpolationSampling::kUndefined;
|
builtin::InterpolationSampling sampling = builtin::InterpolationSampling::kUndefined;
|
||||||
|
@ -1783,8 +1771,8 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
|
||||||
switch (static_cast<spv::Decoration>(deco[0])) {
|
switch (static_cast<spv::Decoration>(deco[0])) {
|
||||||
case spv::Decoration::Location:
|
case spv::Decoration::Location:
|
||||||
if (deco.size() != 2) {
|
if (deco.size() != 2) {
|
||||||
return Fail() << "malformed Location decoration on ID requires one "
|
return Fail()
|
||||||
"literal operand";
|
<< "malformed Location decoration on ID requires one literal operand";
|
||||||
}
|
}
|
||||||
SetLocation(attributes, builder_.Location(AInt(deco[1])));
|
SetLocation(attributes, builder_.Location(AInt(deco[1])));
|
||||||
if (store_type->IsIntegerScalarOrVector()) {
|
if (store_type->IsIntegerScalarOrVector()) {
|
||||||
|
@ -1821,8 +1809,7 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == builtin::InterpolationType::kFlat &&
|
if (type == builtin::InterpolationType::kFlat && !attributes.Has<ast::LocationAttribute>()) {
|
||||||
!ast::HasAttribute<ast::LocationAttribute>(*attributes)) {
|
|
||||||
// WGSL requires that '@interpolate(flat)' needs to be paired with '@location', however
|
// WGSL requires that '@interpolate(flat)' needs to be paired with '@location', however
|
||||||
// SPIR-V requires all fragment shader integer Inputs are 'flat'. If the decorations do not
|
// SPIR-V requires all fragment shader integer Inputs are 'flat'. If the decorations do not
|
||||||
// contain a spv::Decoration::Location, then make this perspective.
|
// contain a spv::Decoration::Location, then make this perspective.
|
||||||
|
@ -1834,7 +1821,7 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
|
||||||
sampling == builtin::InterpolationSampling::kUndefined) {
|
sampling == builtin::InterpolationSampling::kUndefined) {
|
||||||
// This is the default. Don't add a decoration.
|
// This is the default. Don't add a decoration.
|
||||||
} else {
|
} else {
|
||||||
attributes->Push(create<ast::InterpolateAttribute>(type, sampling));
|
attributes.Add(create<ast::InterpolateAttribute>(type, sampling));
|
||||||
}
|
}
|
||||||
|
|
||||||
return success();
|
return success();
|
||||||
|
|
|
@ -37,6 +37,7 @@ TINT_END_DISABLE_WARNING(NEWLINE_EOF);
|
||||||
|
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/reader/reader.h"
|
#include "src/tint/reader/reader.h"
|
||||||
|
#include "src/tint/reader/spirv/attributes.h"
|
||||||
#include "src/tint/reader/spirv/entry_point_info.h"
|
#include "src/tint/reader/spirv/entry_point_info.h"
|
||||||
#include "src/tint/reader/spirv/enum_converter.h"
|
#include "src/tint/reader/spirv/enum_converter.h"
|
||||||
#include "src/tint/reader/spirv/namer.h"
|
#include "src/tint/reader/spirv/namer.h"
|
||||||
|
@ -122,7 +123,6 @@ struct WorkgroupSizeInfo {
|
||||||
|
|
||||||
/// Parser implementation for SPIR-V.
|
/// Parser implementation for SPIR-V.
|
||||||
class ParserImpl : Reader {
|
class ParserImpl : Reader {
|
||||||
using AttributeList = utils::Vector<const ast::Attribute*, 8>;
|
|
||||||
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
|
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -261,7 +261,7 @@ class ParserImpl : Reader {
|
||||||
/// @returns false when the variable should not be emitted as a variable
|
/// @returns false when the variable should not be emitted as a variable
|
||||||
bool ConvertDecorationsForVariable(uint32_t id,
|
bool ConvertDecorationsForVariable(uint32_t id,
|
||||||
const Type** store_type,
|
const Type** store_type,
|
||||||
AttributeList* attributes,
|
Attributes& attributes,
|
||||||
bool transfer_pipeline_io);
|
bool transfer_pipeline_io);
|
||||||
|
|
||||||
/// Converts SPIR-V decorations for pipeline IO into AST decorations.
|
/// Converts SPIR-V decorations for pipeline IO into AST decorations.
|
||||||
|
@ -271,15 +271,15 @@ class ParserImpl : Reader {
|
||||||
/// @returns false if conversion fails
|
/// @returns false if conversion fails
|
||||||
bool ConvertPipelineDecorations(const Type* store_type,
|
bool ConvertPipelineDecorations(const Type* store_type,
|
||||||
const DecorationList& decorations,
|
const DecorationList& decorations,
|
||||||
AttributeList* attributes);
|
Attributes& attributes);
|
||||||
|
|
||||||
/// Updates the attribute list, placing a non-null location decoration into
|
/// Updates the attribute list, placing a non-null location decoration into
|
||||||
/// the list, replacing an existing one if it exists. Does nothing if the
|
/// the list, replacing an existing one if it exists. Does nothing if the
|
||||||
/// replacement is nullptr.
|
/// replacement is nullptr.
|
||||||
/// Assumes the list contains at most one Location decoration.
|
/// Assumes the list contains at most one Location decoration.
|
||||||
/// @param decos the attribute list to modify
|
/// @param attributes the attribute list to modify
|
||||||
/// @param replacement the location decoration to place into the list
|
/// @param replacement the location decoration to place into the list
|
||||||
void SetLocation(AttributeList* decos, const ast::Attribute* replacement);
|
void SetLocation(Attributes& attributes, const ast::Attribute* replacement);
|
||||||
|
|
||||||
/// Converts a SPIR-V struct member decoration into a number of AST
|
/// Converts a SPIR-V struct member decoration into a number of AST
|
||||||
/// decorations. If the decoration is recognized but deliberately dropped,
|
/// decorations. If the decoration is recognized but deliberately dropped,
|
||||||
|
@ -290,10 +290,10 @@ class ParserImpl : Reader {
|
||||||
/// @param member_ty the type of the member
|
/// @param member_ty the type of the member
|
||||||
/// @param decoration an encoded SPIR-V Decoration
|
/// @param decoration an encoded SPIR-V Decoration
|
||||||
/// @returns the AST decorations
|
/// @returns the AST decorations
|
||||||
AttributeList ConvertMemberDecoration(uint32_t struct_type_id,
|
Attributes ConvertMemberDecoration(uint32_t struct_type_id,
|
||||||
uint32_t member_index,
|
uint32_t member_index,
|
||||||
const Type* member_ty,
|
const Type* member_ty,
|
||||||
const Decoration& decoration);
|
const Decoration& decoration);
|
||||||
|
|
||||||
/// Returns a string for the given type. If the type ID is invalid,
|
/// Returns a string for the given type. If the type ID is invalid,
|
||||||
/// then the resulting string only names the type ID.
|
/// then the resulting string only names the type ID.
|
||||||
|
@ -433,7 +433,7 @@ class ParserImpl : Reader {
|
||||||
/// @param access the access
|
/// @param access the access
|
||||||
/// @param storage_type the storage type of the variable
|
/// @param storage_type the storage type of the variable
|
||||||
/// @param initializer the variable initializer
|
/// @param initializer the variable initializer
|
||||||
/// @param decorations the variable decorations
|
/// @param attributes the variable attributes
|
||||||
/// @returns a new Variable node, or null in the ignorable variable case and
|
/// @returns a new Variable node, or null in the ignorable variable case and
|
||||||
/// in the error case
|
/// in the error case
|
||||||
const ast::Var* MakeVar(uint32_t id,
|
const ast::Var* MakeVar(uint32_t id,
|
||||||
|
@ -441,7 +441,7 @@ class ParserImpl : Reader {
|
||||||
builtin::Access access,
|
builtin::Access access,
|
||||||
const Type* storage_type,
|
const Type* storage_type,
|
||||||
const ast::Expression* initializer,
|
const ast::Expression* initializer,
|
||||||
AttributeList decorations);
|
Attributes attributes);
|
||||||
|
|
||||||
/// Creates an AST 'let' node for a SPIR-V ID, including any attached decorations,.
|
/// Creates an AST 'let' node for a SPIR-V ID, including any attached decorations,.
|
||||||
/// @param id the SPIR-V result ID
|
/// @param id the SPIR-V result ID
|
||||||
|
@ -454,20 +454,20 @@ class ParserImpl : Reader {
|
||||||
/// @param id the SPIR-V result ID
|
/// @param id the SPIR-V result ID
|
||||||
/// @param type the type of the variable
|
/// @param type the type of the variable
|
||||||
/// @param initializer the variable initializer
|
/// @param initializer the variable initializer
|
||||||
/// @param decorations the variable decorations
|
/// @param attributes the variable attributes
|
||||||
/// @returns the AST 'override' node
|
/// @returns the AST 'override' node
|
||||||
const ast::Override* MakeOverride(uint32_t id,
|
const ast::Override* MakeOverride(uint32_t id,
|
||||||
const Type* type,
|
const Type* type,
|
||||||
const ast::Expression* initializer,
|
const ast::Expression* initializer,
|
||||||
AttributeList decorations);
|
Attributes attributes);
|
||||||
|
|
||||||
/// Creates an AST parameter node for a SPIR-V ID, including any attached decorations, unless
|
/// Creates an AST parameter node for a SPIR-V ID, including any attached decorations, unless
|
||||||
/// it's an ignorable builtin variable.
|
/// it's an ignorable builtin variable.
|
||||||
/// @param id the SPIR-V result ID
|
/// @param id the SPIR-V result ID
|
||||||
/// @param type the type of the parameter
|
/// @param type the type of the parameter
|
||||||
/// @param decorations the parameter decorations
|
/// @param attributes the parameter attributes
|
||||||
/// @returns the AST parameter node
|
/// @returns the AST parameter node
|
||||||
const ast::Parameter* MakeParameter(uint32_t id, const Type* type, AttributeList decorations);
|
const ast::Parameter* MakeParameter(uint32_t id, const Type* type, Attributes attributes);
|
||||||
|
|
||||||
/// Returns true if a constant expression can be generated.
|
/// Returns true if a constant expression can be generated.
|
||||||
/// @param id the SPIR-V ID of the value
|
/// @param id the SPIR-V ID of the value
|
||||||
|
|
|
@ -24,7 +24,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_IsEmpty) {
|
||||||
auto p = parser(std::vector<uint32_t>{});
|
auto p = parser(std::vector<uint32_t>{});
|
||||||
|
|
||||||
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {});
|
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_THAT(p->error(), Eq("malformed SPIR-V decoration: it's empty"));
|
EXPECT_THAT(p->error(), Eq("malformed SPIR-V decoration: it's empty"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_OffsetWithoutOperand) {
|
||||||
auto p = parser(std::vector<uint32_t>{});
|
auto p = parser(std::vector<uint32_t>{});
|
||||||
|
|
||||||
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset)});
|
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset)});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
|
EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
|
||||||
"operand, has 0: member 13 of SPIR-V type 12"));
|
"operand, has 0: member 13 of SPIR-V type 12"));
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_OffsetWithTooManyOperands) {
|
||||||
|
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset), 3, 4});
|
p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset), 3, 4});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
|
EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
|
||||||
"operand, has 2: member 13 of SPIR-V type 12"));
|
"operand, has 2: member 13 of SPIR-V type 12"));
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Offset) {
|
||||||
auto p = parser(std::vector<uint32_t>{});
|
auto p = parser(std::vector<uint32_t>{});
|
||||||
|
|
||||||
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::Offset), 8});
|
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::Offset), 8});
|
||||||
ASSERT_FALSE(result.IsEmpty());
|
ASSERT_FALSE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(result[0]->Is<ast::StructMemberOffsetAttribute>());
|
EXPECT_TRUE(result.list[0]->Is<ast::StructMemberOffsetAttribute>());
|
||||||
auto* offset_deco = result[0]->As<ast::StructMemberOffsetAttribute>();
|
auto* offset_deco = result.list[0]->As<ast::StructMemberOffsetAttribute>();
|
||||||
ASSERT_NE(offset_deco, nullptr);
|
ASSERT_NE(offset_deco, nullptr);
|
||||||
ASSERT_TRUE(offset_deco->expr->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(offset_deco->expr->Is<ast::IntLiteralExpression>());
|
||||||
EXPECT_EQ(offset_deco->expr->As<ast::IntLiteralExpression>()->value, 8u);
|
EXPECT_EQ(offset_deco->expr->As<ast::IntLiteralExpression>()->value, 8u);
|
||||||
|
@ -67,7 +67,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x2_Stride_Natural) {
|
||||||
spirv::Matrix matrix(&f32, 2, 2);
|
spirv::Matrix matrix(&f32, 2, 2);
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 8});
|
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 8});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +78,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x2_Stride_Custom) {
|
||||||
spirv::Matrix matrix(&f32, 2, 2);
|
spirv::Matrix matrix(&f32, 2, 2);
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
|
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
|
||||||
ASSERT_FALSE(result.IsEmpty());
|
ASSERT_FALSE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>());
|
EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
|
||||||
auto* stride_deco = result[0]->As<ast::StrideAttribute>();
|
auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
|
||||||
ASSERT_NE(stride_deco, nullptr);
|
ASSERT_NE(stride_deco, nullptr);
|
||||||
EXPECT_EQ(stride_deco->stride, 16u);
|
EXPECT_EQ(stride_deco->stride, 16u);
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
@ -93,7 +93,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x4_Stride_Natural) {
|
||||||
spirv::Matrix matrix(&f32, 2, 4);
|
spirv::Matrix matrix(&f32, 2, 4);
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
|
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,9 +104,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x4_Stride_Custom) {
|
||||||
spirv::Matrix matrix(&f32, 2, 4);
|
spirv::Matrix matrix(&f32, 2, 4);
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 64});
|
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 64});
|
||||||
ASSERT_FALSE(result.IsEmpty());
|
ASSERT_FALSE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>());
|
EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
|
||||||
auto* stride_deco = result[0]->As<ast::StrideAttribute>();
|
auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
|
||||||
ASSERT_NE(stride_deco, nullptr);
|
ASSERT_NE(stride_deco, nullptr);
|
||||||
EXPECT_EQ(stride_deco->stride, 64u);
|
EXPECT_EQ(stride_deco->stride, 64u);
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
@ -119,9 +119,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x3_Stride_Custom) {
|
||||||
spirv::Matrix matrix(&f32, 2, 3);
|
spirv::Matrix matrix(&f32, 2, 3);
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 32});
|
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 32});
|
||||||
ASSERT_FALSE(result.IsEmpty());
|
ASSERT_FALSE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>());
|
EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
|
||||||
auto* stride_deco = result[0]->As<ast::StrideAttribute>();
|
auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
|
||||||
ASSERT_NE(stride_deco, nullptr);
|
ASSERT_NE(stride_deco, nullptr);
|
||||||
EXPECT_EQ(stride_deco->stride, 32u);
|
EXPECT_EQ(stride_deco->stride, 32u);
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
@ -135,7 +135,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_RelaxedPrecision) {
|
||||||
|
|
||||||
auto result =
|
auto result =
|
||||||
p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::RelaxedPrecision)});
|
p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::RelaxedPrecision)});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_UnhandledDecoration) {
|
||||||
auto p = parser(std::vector<uint32_t>{});
|
auto p = parser(std::vector<uint32_t>{});
|
||||||
|
|
||||||
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {12345678});
|
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {12345678});
|
||||||
EXPECT_TRUE(result.IsEmpty());
|
EXPECT_TRUE(result.list.IsEmpty());
|
||||||
EXPECT_THAT(p->error(), Eq("unhandled member decoration: 12345678 on member "
|
EXPECT_THAT(p->error(), Eq("unhandled member decoration: 12345678 on member "
|
||||||
"13 of SPIR-V type 12"));
|
"13 of SPIR-V type 12"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2861,7 +2861,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_Direct) {
|
||||||
const auto module_str = test::ToString(p->program());
|
const auto module_str = test::ToString(p->program());
|
||||||
const std::string expected = R"(var<private> x_1 : i32;
|
const std::string expected = R"(var<private> x_1 : i32;
|
||||||
|
|
||||||
var<private> position : vec4<f32>;
|
var<private> position_1 : vec4<f32>;
|
||||||
|
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
let x_2 : i32 = x_1;
|
let x_2 : i32 = x_1;
|
||||||
|
@ -2870,14 +2870,14 @@ fn main_1() {
|
||||||
|
|
||||||
struct main_out {
|
struct main_out {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
position_1 : vec4<f32>,
|
position_1_1 : vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
||||||
x_1 = bitcast<i32>(x_1_param);
|
x_1 = bitcast<i32>(x_1_param);
|
||||||
main_1();
|
main_1();
|
||||||
return main_out(position);
|
return main_out(position_1);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(module_str, expected) << module_str;
|
EXPECT_EQ(module_str, expected) << module_str;
|
||||||
|
@ -2898,7 +2898,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_CopyObject) {
|
||||||
const auto module_str = test::ToString(p->program());
|
const auto module_str = test::ToString(p->program());
|
||||||
const std::string expected = R"(var<private> x_1 : i32;
|
const std::string expected = R"(var<private> x_1 : i32;
|
||||||
|
|
||||||
var<private> position : vec4<f32>;
|
var<private> position_1 : vec4<f32>;
|
||||||
|
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
let x_14 : ptr<private, i32> = &(x_1);
|
let x_14 : ptr<private, i32> = &(x_1);
|
||||||
|
@ -2908,14 +2908,14 @@ fn main_1() {
|
||||||
|
|
||||||
struct main_out {
|
struct main_out {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
position_1 : vec4<f32>,
|
position_1_1 : vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
||||||
x_1 = bitcast<i32>(x_1_param);
|
x_1 = bitcast<i32>(x_1_param);
|
||||||
main_1();
|
main_1();
|
||||||
return main_out(position);
|
return main_out(position_1);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(module_str, expected) << module_str;
|
EXPECT_EQ(module_str, expected) << module_str;
|
||||||
|
@ -2936,7 +2936,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_AccessChain) {
|
||||||
const auto module_str = test::ToString(p->program());
|
const auto module_str = test::ToString(p->program());
|
||||||
const std::string expected = R"(var<private> x_1 : i32;
|
const std::string expected = R"(var<private> x_1 : i32;
|
||||||
|
|
||||||
var<private> position : vec4<f32>;
|
var<private> position_1 : vec4<f32>;
|
||||||
|
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
let x_2 : i32 = x_1;
|
let x_2 : i32 = x_1;
|
||||||
|
@ -2945,14 +2945,14 @@ fn main_1() {
|
||||||
|
|
||||||
struct main_out {
|
struct main_out {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
position_1 : vec4<f32>,
|
position_1_1 : vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
||||||
x_1 = bitcast<i32>(x_1_param);
|
x_1 = bitcast<i32>(x_1_param);
|
||||||
main_1();
|
main_1();
|
||||||
return main_out(position);
|
return main_out(position_1);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(module_str, expected) << module_str;
|
EXPECT_EQ(module_str, expected) << module_str;
|
||||||
|
@ -2995,7 +2995,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_Direct) {
|
||||||
const auto module_str = test::ToString(p->program());
|
const auto module_str = test::ToString(p->program());
|
||||||
const std::string expected = R"(var<private> x_1 : u32;
|
const std::string expected = R"(var<private> x_1 : u32;
|
||||||
|
|
||||||
var<private> position : vec4<f32>;
|
var<private> position_1 : vec4<f32>;
|
||||||
|
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
let x_2 : u32 = x_1;
|
let x_2 : u32 = x_1;
|
||||||
|
@ -3004,14 +3004,14 @@ fn main_1() {
|
||||||
|
|
||||||
struct main_out {
|
struct main_out {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
position_1 : vec4<f32>,
|
position_1_1 : vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
||||||
x_1 = x_1_param;
|
x_1 = x_1_param;
|
||||||
main_1();
|
main_1();
|
||||||
return main_out(position);
|
return main_out(position_1);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(module_str, expected);
|
EXPECT_EQ(module_str, expected);
|
||||||
|
@ -3032,7 +3032,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_CopyObject) {
|
||||||
const auto module_str = test::ToString(p->program());
|
const auto module_str = test::ToString(p->program());
|
||||||
const std::string expected = R"(var<private> x_1 : u32;
|
const std::string expected = R"(var<private> x_1 : u32;
|
||||||
|
|
||||||
var<private> position : vec4<f32>;
|
var<private> position_1 : vec4<f32>;
|
||||||
|
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
let x_14 : ptr<private, u32> = &(x_1);
|
let x_14 : ptr<private, u32> = &(x_1);
|
||||||
|
@ -3042,14 +3042,14 @@ fn main_1() {
|
||||||
|
|
||||||
struct main_out {
|
struct main_out {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
position_1 : vec4<f32>,
|
position_1_1 : vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
||||||
x_1 = x_1_param;
|
x_1 = x_1_param;
|
||||||
main_1();
|
main_1();
|
||||||
return main_out(position);
|
return main_out(position_1);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(module_str, expected);
|
EXPECT_EQ(module_str, expected);
|
||||||
|
@ -3070,7 +3070,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_AccessChain) {
|
||||||
const auto module_str = test::ToString(p->program());
|
const auto module_str = test::ToString(p->program());
|
||||||
const std::string expected = R"(var<private> x_1 : u32;
|
const std::string expected = R"(var<private> x_1 : u32;
|
||||||
|
|
||||||
var<private> position : vec4<f32>;
|
var<private> position_1 : vec4<f32>;
|
||||||
|
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
let x_2 : u32 = x_1;
|
let x_2 : u32 = x_1;
|
||||||
|
@ -3079,14 +3079,14 @@ fn main_1() {
|
||||||
|
|
||||||
struct main_out {
|
struct main_out {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
position_1 : vec4<f32>,
|
position_1_1 : vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
|
||||||
x_1 = x_1_param;
|
x_1 = x_1_param;
|
||||||
main_1();
|
main_1();
|
||||||
return main_out(position);
|
return main_out(position_1);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(module_str, expected);
|
EXPECT_EQ(module_str, expected);
|
||||||
|
|
|
@ -1125,23 +1125,6 @@ Expect<builtin::InterpolationType> ParserImpl::expect_interpolation_type_name()
|
||||||
builtin::kInterpolationTypeStrings);
|
builtin::kInterpolationTypeStrings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtin_value_name
|
|
||||||
// : frag_depth
|
|
||||||
// | front_facing
|
|
||||||
// | global_invocation_id
|
|
||||||
// | instance_index
|
|
||||||
// | local_invocation_id
|
|
||||||
// | local_invocation_index
|
|
||||||
// | num_workgroups
|
|
||||||
// | position
|
|
||||||
// | sample_index
|
|
||||||
// | sample_mask
|
|
||||||
// | vertex_index
|
|
||||||
// | workgroup_id
|
|
||||||
Expect<builtin::BuiltinValue> ParserImpl::expect_builtin() {
|
|
||||||
return expect_enum("builtin", builtin::ParseBuiltinValue, builtin::kBuiltinValueStrings);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compound_statement
|
// compound_statement
|
||||||
// : attribute* BRACE_LEFT statement* BRACE_RIGHT
|
// : attribute* BRACE_LEFT statement* BRACE_RIGHT
|
||||||
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(std::string_view use) {
|
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(std::string_view use) {
|
||||||
|
@ -3020,7 +3003,7 @@ Maybe<const ast::Attribute*> ParserImpl::attribute() {
|
||||||
|
|
||||||
if (t == "builtin") {
|
if (t == "builtin") {
|
||||||
return expect_paren_block("builtin attribute", [&]() -> Result {
|
return expect_paren_block("builtin attribute", [&]() -> Result {
|
||||||
auto builtin = expect_builtin();
|
auto builtin = expect_expression("builtin name");
|
||||||
if (builtin.errored) {
|
if (builtin.errored) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
|
|
@ -462,10 +462,6 @@ class ParserImpl {
|
||||||
/// value type name.
|
/// value type name.
|
||||||
/// @returns the parsed type name
|
/// @returns the parsed type name
|
||||||
Expect<builtin::InterpolationType> expect_interpolation_type_name();
|
Expect<builtin::InterpolationType> expect_interpolation_type_name();
|
||||||
/// Parses a builtin identifier, erroring if the next token does not match a
|
|
||||||
/// valid builtin name.
|
|
||||||
/// @returns the parsed builtin.
|
|
||||||
Expect<builtin::BuiltinValue> expect_builtin();
|
|
||||||
/// Parses a `compound_statement` grammar element, erroring on parse failure.
|
/// Parses a `compound_statement` grammar element, erroring on parse failure.
|
||||||
/// @param use a description of what was being parsed if an error was raised
|
/// @param use a description of what was being parsed if an error was raised
|
||||||
/// @returns the parsed statements
|
/// @returns the parsed statements
|
||||||
|
|
|
@ -969,25 +969,6 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinMissingRParen) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinInvalidIdentifer) {
|
|
||||||
EXPECT("@builtin(1) var i : i32;",
|
|
||||||
R"(test.wgsl:1:10 error: expected builtin
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id'
|
|
||||||
@builtin(1) var i : i32;
|
|
||||||
^
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinInvalidValue) {
|
|
||||||
EXPECT("@builtin(frag_d3pth) var i : i32;",
|
|
||||||
R"(test.wgsl:1:10 error: expected builtin
|
|
||||||
Did you mean 'frag_depth'?
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id'
|
|
||||||
@builtin(frag_d3pth) var i : i32;
|
|
||||||
^^^^^^^^^^
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBindingMissingLParen) {
|
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBindingMissingLParen) {
|
||||||
EXPECT("@binding 1) var i : i32;",
|
EXPECT("@binding 1) var i : i32;",
|
||||||
R"(test.wgsl:1:10 error: expected '(' for binding attribute
|
R"(test.wgsl:1:10 error: expected '(' for binding attribute
|
||||||
|
|
|
@ -102,7 +102,8 @@ TEST_F(ParserImplTest, ParamList_Attributes) {
|
||||||
auto attrs_0 = e.value[0]->attributes;
|
auto attrs_0 = e.value[0]->attributes;
|
||||||
ASSERT_EQ(attrs_0.Length(), 1u);
|
ASSERT_EQ(attrs_0.Length(), 1u);
|
||||||
EXPECT_TRUE(attrs_0[0]->Is<ast::BuiltinAttribute>());
|
EXPECT_TRUE(attrs_0[0]->Is<ast::BuiltinAttribute>());
|
||||||
EXPECT_EQ(attrs_0[0]->As<ast::BuiltinAttribute>()->builtin, builtin::BuiltinValue::kPosition);
|
ast::CheckIdentifier(p->builder().Symbols(), attrs_0[0]->As<ast::BuiltinAttribute>()->builtin,
|
||||||
|
"position");
|
||||||
|
|
||||||
ASSERT_EQ(e.value[0]->source.range.begin.line, 1u);
|
ASSERT_EQ(e.value[0]->source.range.begin.line, 1u);
|
||||||
ASSERT_EQ(e.value[0]->source.range.begin.column, 20u);
|
ASSERT_EQ(e.value[0]->source.range.begin.column, 20u);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/tint/ast/test_helper.h"
|
||||||
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
|
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
|
||||||
|
|
||||||
namespace tint::reader::wgsl {
|
namespace tint::reader::wgsl {
|
||||||
|
@ -38,7 +39,8 @@ TEST_F(ParserImplTest, AttributeList_Parses) {
|
||||||
EXPECT_EQ(exp->value, 4u);
|
EXPECT_EQ(exp->value, 4u);
|
||||||
|
|
||||||
ASSERT_TRUE(attr_1->Is<ast::BuiltinAttribute>());
|
ASSERT_TRUE(attr_1->Is<ast::BuiltinAttribute>());
|
||||||
EXPECT_EQ(attr_1->As<ast::BuiltinAttribute>()->builtin, builtin::BuiltinValue::kPosition);
|
ast::CheckIdentifier(p->builder().Symbols(), attr_1->As<ast::BuiltinAttribute>()->builtin,
|
||||||
|
"position");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, AttributeList_Invalid) {
|
TEST_F(ParserImplTest, AttributeList_Invalid) {
|
||||||
|
@ -51,27 +53,5 @@ TEST_F(ParserImplTest, AttributeList_Invalid) {
|
||||||
EXPECT_EQ(p->error(), R"(1:2: expected attribute)");
|
EXPECT_EQ(p->error(), R"(1:2: expected attribute)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, AttributeList_InvalidValue) {
|
|
||||||
auto p = parser("@builtin(invalid)");
|
|
||||||
auto attrs = p->attribute_list();
|
|
||||||
EXPECT_TRUE(p->has_error());
|
|
||||||
EXPECT_TRUE(attrs.errored);
|
|
||||||
EXPECT_FALSE(attrs.matched);
|
|
||||||
EXPECT_TRUE(attrs.value.IsEmpty());
|
|
||||||
EXPECT_EQ(p->error(), R"(1:10: expected builtin
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ParserImplTest, AttributeList_InvalidValueSuggest) {
|
|
||||||
auto p = parser("@builtin(instanceindex)");
|
|
||||||
auto attrs = p->attribute_list();
|
|
||||||
EXPECT_TRUE(p->has_error());
|
|
||||||
EXPECT_TRUE(attrs.errored);
|
|
||||||
EXPECT_FALSE(attrs.matched);
|
|
||||||
EXPECT_TRUE(attrs.value.IsEmpty());
|
|
||||||
EXPECT_EQ(p->error(), R"(1:10: expected builtin
|
|
||||||
Did you mean 'instance_index'?
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tint::reader::wgsl
|
} // namespace tint::reader::wgsl
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/tint/ast/test_helper.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
|
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
|
||||||
|
|
||||||
namespace tint::reader::wgsl {
|
namespace tint::reader::wgsl {
|
||||||
|
@ -215,20 +217,11 @@ TEST_F(ParserImplTest, Attribute_Location_MissingInvalid) {
|
||||||
EXPECT_EQ(p->error(), "1:10: expected location expression");
|
EXPECT_EQ(p->error(), "1:10: expected location expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BuiltinData {
|
class BuiltinTest : public ParserImplTestWithParam<builtin::BuiltinValue> {};
|
||||||
const char* input;
|
|
||||||
builtin::BuiltinValue result;
|
|
||||||
};
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
|
|
||||||
out << std::string(data.input);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BuiltinTest : public ParserImplTestWithParam<BuiltinData> {};
|
|
||||||
|
|
||||||
TEST_P(BuiltinTest, Attribute_Builtin) {
|
TEST_P(BuiltinTest, Attribute_Builtin) {
|
||||||
auto params = GetParam();
|
auto str = utils::ToString(GetParam());
|
||||||
auto p = parser(std::string("builtin(") + params.input + ")");
|
auto p = parser("builtin(" + str + ")");
|
||||||
|
|
||||||
auto attr = p->attribute();
|
auto attr = p->attribute();
|
||||||
EXPECT_TRUE(attr.matched);
|
EXPECT_TRUE(attr.matched);
|
||||||
|
@ -240,11 +233,11 @@ TEST_P(BuiltinTest, Attribute_Builtin) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
|
||||||
|
|
||||||
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
|
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
|
||||||
EXPECT_EQ(builtin->builtin, params.result);
|
ast::CheckIdentifier(p->builder().Symbols(), builtin->builtin, str);
|
||||||
}
|
}
|
||||||
TEST_P(BuiltinTest, Attribute_Builtin_TrailingComma) {
|
TEST_P(BuiltinTest, Attribute_Builtin_TrailingComma) {
|
||||||
auto params = GetParam();
|
auto str = utils::ToString(GetParam());
|
||||||
auto p = parser(std::string("builtin(") + params.input + ",)");
|
auto p = parser("builtin(" + str + ",)");
|
||||||
|
|
||||||
auto attr = p->attribute();
|
auto attr = p->attribute();
|
||||||
EXPECT_TRUE(attr.matched);
|
EXPECT_TRUE(attr.matched);
|
||||||
|
@ -256,24 +249,22 @@ TEST_P(BuiltinTest, Attribute_Builtin_TrailingComma) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
|
||||||
|
|
||||||
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
|
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
|
||||||
EXPECT_EQ(builtin->builtin, params.result);
|
ast::CheckIdentifier(p->builder().Symbols(), builtin->builtin, str);
|
||||||
}
|
}
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
|
||||||
ParserImplTest,
|
BuiltinTest,
|
||||||
BuiltinTest,
|
testing::Values(builtin::BuiltinValue::kPosition,
|
||||||
testing::Values(BuiltinData{"position", builtin::BuiltinValue::kPosition},
|
builtin::BuiltinValue::kVertexIndex,
|
||||||
BuiltinData{"vertex_index", builtin::BuiltinValue::kVertexIndex},
|
builtin::BuiltinValue::kInstanceIndex,
|
||||||
BuiltinData{"instance_index", builtin::BuiltinValue::kInstanceIndex},
|
builtin::BuiltinValue::kFrontFacing,
|
||||||
BuiltinData{"front_facing", builtin::BuiltinValue::kFrontFacing},
|
builtin::BuiltinValue::kFragDepth,
|
||||||
BuiltinData{"frag_depth", builtin::BuiltinValue::kFragDepth},
|
builtin::BuiltinValue::kLocalInvocationId,
|
||||||
BuiltinData{"local_invocation_id", builtin::BuiltinValue::kLocalInvocationId},
|
builtin::BuiltinValue::kLocalInvocationIndex,
|
||||||
BuiltinData{"local_invocation_index",
|
builtin::BuiltinValue::kGlobalInvocationId,
|
||||||
builtin::BuiltinValue::kLocalInvocationIndex},
|
builtin::BuiltinValue::kWorkgroupId,
|
||||||
BuiltinData{"global_invocation_id", builtin::BuiltinValue::kGlobalInvocationId},
|
builtin::BuiltinValue::kNumWorkgroups,
|
||||||
BuiltinData{"workgroup_id", builtin::BuiltinValue::kWorkgroupId},
|
builtin::BuiltinValue::kSampleIndex,
|
||||||
BuiltinData{"num_workgroups", builtin::BuiltinValue::kNumWorkgroups},
|
builtin::BuiltinValue::kSampleMask));
|
||||||
BuiltinData{"sample_index", builtin::BuiltinValue::kSampleIndex},
|
|
||||||
BuiltinData{"sample_mask", builtin::BuiltinValue::kSampleMask}));
|
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Attribute_Builtin_MissingLeftParen) {
|
TEST_F(ParserImplTest, Attribute_Builtin_MissingLeftParen) {
|
||||||
auto p = parser("builtin position)");
|
auto p = parser("builtin position)");
|
||||||
|
@ -302,42 +293,7 @@ TEST_F(ParserImplTest, Attribute_Builtin_MissingValue) {
|
||||||
EXPECT_TRUE(attr.errored);
|
EXPECT_TRUE(attr.errored);
|
||||||
EXPECT_EQ(attr.value, nullptr);
|
EXPECT_EQ(attr.value, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), R"(1:9: expected builtin
|
EXPECT_EQ(p->error(), R"(1:9: expected expression for builtin name)");
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Attribute_Builtin_InvalidValue) {
|
|
||||||
auto p = parser("builtin(other_thingy)");
|
|
||||||
auto attr = p->attribute();
|
|
||||||
EXPECT_FALSE(attr.matched);
|
|
||||||
EXPECT_TRUE(attr.errored);
|
|
||||||
EXPECT_EQ(attr.value, nullptr);
|
|
||||||
EXPECT_TRUE(p->has_error());
|
|
||||||
EXPECT_EQ(p->error(), R"(1:9: expected builtin
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Attribute_Builtin_InvalidValueSuggest) {
|
|
||||||
auto p = parser("builtin(front_face)");
|
|
||||||
auto attr = p->attribute();
|
|
||||||
EXPECT_FALSE(attr.matched);
|
|
||||||
EXPECT_TRUE(attr.errored);
|
|
||||||
EXPECT_EQ(attr.value, nullptr);
|
|
||||||
EXPECT_TRUE(p->has_error());
|
|
||||||
EXPECT_EQ(p->error(), R"(1:9: expected builtin
|
|
||||||
Did you mean 'front_facing'?
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Attribute_Builtin_MissingInvalid) {
|
|
||||||
auto p = parser("builtin(3)");
|
|
||||||
auto attr = p->attribute();
|
|
||||||
EXPECT_FALSE(attr.matched);
|
|
||||||
EXPECT_TRUE(attr.errored);
|
|
||||||
EXPECT_EQ(attr.value, nullptr);
|
|
||||||
EXPECT_TRUE(p->has_error());
|
|
||||||
EXPECT_EQ(p->error(), R"(1:9: expected builtin
|
|
||||||
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Attribute_Interpolate_Flat) {
|
TEST_F(ParserImplTest, Attribute_Interpolate_Flat) {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "src/tint/ast/disable_validation_attribute.h"
|
#include "src/tint/ast/disable_validation_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver.h"
|
#include "src/tint/resolver/resolver.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
#include "src/tint/transform/add_block_attribute.h"
|
#include "src/tint/transform/add_block_attribute.h"
|
||||||
|
@ -221,9 +222,9 @@ TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
|
||||||
} else {
|
} else {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
if (params.kind == AttributeKind::kBuiltin) {
|
if (params.kind == AttributeKind::kBuiltin) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"12:34 error: builtin(position) cannot be used in input of "
|
r()->error(),
|
||||||
"compute pipeline stage");
|
R"(12:34 error: @builtin(position) cannot be used in input of compute pipeline stage)");
|
||||||
} else if (params.kind == AttributeKind::kInterpolate ||
|
} else if (params.kind == AttributeKind::kInterpolate ||
|
||||||
params.kind == AttributeKind::kLocation ||
|
params.kind == AttributeKind::kLocation ||
|
||||||
params.kind == AttributeKind::kInvariant) {
|
params.kind == AttributeKind::kInvariant) {
|
||||||
|
@ -314,9 +315,9 @@ TEST_P(VertexShaderParameterAttributeTest, IsValid) {
|
||||||
} else {
|
} else {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
if (params.kind == AttributeKind::kBuiltin) {
|
if (params.kind == AttributeKind::kBuiltin) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"12:34 error: builtin(position) cannot be used in input of "
|
r()->error(),
|
||||||
"vertex pipeline stage");
|
R"(12:34 error: @builtin(position) cannot be used in input of vertex pipeline stage)");
|
||||||
} else if (params.kind == AttributeKind::kInvariant) {
|
} else if (params.kind == AttributeKind::kInvariant) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: invariant attribute must only be applied to a "
|
"12:34 error: invariant attribute must only be applied to a "
|
||||||
|
@ -362,9 +363,9 @@ TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
|
||||||
} else {
|
} else {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
if (params.kind == AttributeKind::kBuiltin) {
|
if (params.kind == AttributeKind::kBuiltin) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"12:34 error: builtin(position) cannot be used in output of "
|
r()->error(),
|
||||||
"compute pipeline stage");
|
R"(12:34 error: @builtin(position) cannot be used in output of compute pipeline stage)");
|
||||||
} else if (params.kind == AttributeKind::kInterpolate ||
|
} else if (params.kind == AttributeKind::kInterpolate ||
|
||||||
params.kind == AttributeKind::kLocation ||
|
params.kind == AttributeKind::kLocation ||
|
||||||
params.kind == AttributeKind::kInvariant) {
|
params.kind == AttributeKind::kInvariant) {
|
||||||
|
@ -411,21 +412,20 @@ TEST_P(FragmentShaderReturnTypeAttributeTest, IsValid) {
|
||||||
} else {
|
} else {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
if (params.kind == AttributeKind::kBuiltin) {
|
if (params.kind == AttributeKind::kBuiltin) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"12:34 error: builtin(position) cannot be used in output of "
|
r()->error(),
|
||||||
"fragment pipeline stage");
|
R"(12:34 error: @builtin(position) cannot be used in output of fragment pipeline stage)");
|
||||||
} else if (params.kind == AttributeKind::kInvariant) {
|
} else if (params.kind == AttributeKind::kInvariant) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"12:34 error: invariant attribute must only be applied to a "
|
r()->error(),
|
||||||
"position builtin");
|
R"(12:34 error: invariant attribute must only be applied to a position builtin)");
|
||||||
} else if (params.kind == AttributeKind::kLocation) {
|
} else if (params.kind == AttributeKind::kLocation) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"34:56 error: duplicate location attribute\n"
|
R"(34:56 error: duplicate location attribute
|
||||||
"12:34 note: first attribute declared here");
|
12:34 note: first attribute declared here)");
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: attribute is not valid for entry point return "
|
R"(12:34 error: attribute is not valid for entry point return types)");
|
||||||
"types");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,12 +470,11 @@ TEST_P(VertexShaderReturnTypeAttributeTest, IsValid) {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
if (params.kind == AttributeKind::kLocation) {
|
if (params.kind == AttributeKind::kLocation) {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"34:56 error: multiple entry point IO attributes\n"
|
R"(34:56 error: multiple entry point IO attributes
|
||||||
"12:34 note: previously consumed location(1)");
|
12:34 note: previously consumed @location)");
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: attribute is not valid for entry point return "
|
R"(12:34 error: attribute is not valid for entry point return types)");
|
||||||
"types");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "src/tint/ast/call_statement.h"
|
#include "src/tint/ast/call_statement.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
|
|
||||||
using namespace tint::number_suffixes; // NOLINT
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
@ -145,7 +146,7 @@ TEST_P(ResolverBuiltinsStageTest, All_input) {
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
} else {
|
} else {
|
||||||
std::stringstream err;
|
std::stringstream err;
|
||||||
err << "12:34 error: builtin(" << params.builtin << ")";
|
err << "12:34 error: @builtin(" << params.builtin << ")";
|
||||||
err << " cannot be used in input of " << params.stage << " pipeline stage";
|
err << " cannot be used in input of " << params.stage << " pipeline stage";
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), err.str());
|
EXPECT_EQ(r()->error(), err.str());
|
||||||
|
@ -178,9 +179,9 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInput_Fail) {
|
||||||
Location(0_a),
|
Location(0_a),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"12:34 error: builtin(frag_depth) cannot be used in input of "
|
r()->error(),
|
||||||
"fragment pipeline stage");
|
"12:34 error: @builtin(frag_depth) cannot be used in input of fragment pipeline stage");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
|
||||||
|
@ -214,7 +215,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: builtin(frag_depth) cannot be used in input of "
|
"12:34 error: @builtin(frag_depth) cannot be used in input of "
|
||||||
"fragment pipeline stage\n"
|
"fragment pipeline stage\n"
|
||||||
"note: while analyzing entry point 'fragShader'");
|
"note: while analyzing entry point 'fragShader'");
|
||||||
}
|
}
|
||||||
|
@ -272,7 +273,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(position) must be 'vec4<f32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
|
||||||
|
@ -288,7 +289,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(position) must be 'vec4<f32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
|
||||||
|
@ -317,7 +318,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(frag_depth) must be 'f32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
|
||||||
|
@ -346,7 +347,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_mask) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
|
||||||
|
@ -361,7 +362,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_mask) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
|
||||||
|
@ -387,7 +388,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
|
||||||
Location(0_a),
|
Location(0_a),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_mask) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
|
||||||
|
@ -416,7 +417,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_index) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
|
||||||
|
@ -442,7 +443,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
|
||||||
Location(0_a),
|
Location(0_a),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_index) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
|
||||||
|
@ -468,7 +469,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
|
||||||
Location(0_a),
|
Location(0_a),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(position) must be 'vec4<f32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
|
||||||
|
@ -487,7 +488,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
|
||||||
Builtin(Source{{12, 34}}, builtin::BuiltinValue::kFragDepth),
|
Builtin(Source{{12, 34}}, builtin::BuiltinValue::kFragDepth),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(frag_depth) must be 'f32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
|
||||||
|
@ -512,7 +513,7 @@ TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
|
||||||
Builtin(builtin::BuiltinValue::kPosition),
|
Builtin(builtin::BuiltinValue::kPosition),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(vertex_index) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(vertex_index) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
|
TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
|
||||||
|
@ -537,7 +538,7 @@ TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
|
||||||
Builtin(builtin::BuiltinValue::kPosition),
|
Builtin(builtin::BuiltinValue::kPosition),
|
||||||
});
|
});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(instance_index) must be 'u32'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(instance_index) must be 'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
|
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
|
||||||
|
@ -657,7 +658,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: store type of builtin(workgroup_id) must be "
|
"12:34 error: store type of @builtin(workgroup_id) must be "
|
||||||
"'vec3<u32>'");
|
"'vec3<u32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +673,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_NumWorkgroupsNotVec3U32) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: store type of builtin(num_workgroups) must be "
|
"12:34 error: store type of @builtin(num_workgroups) must be "
|
||||||
"'vec3<u32>'");
|
"'vec3<u32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,7 +688,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: store type of builtin(global_invocation_id) must be "
|
"12:34 error: store type of @builtin(global_invocation_id) must be "
|
||||||
"'vec3<u32>'");
|
"'vec3<u32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +704,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: store type of builtin(local_invocation_index) must be "
|
"12:34 error: store type of @builtin(local_invocation_index) must be "
|
||||||
"'u32'");
|
"'u32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +719,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32)
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: store type of builtin(local_invocation_id) must be "
|
"12:34 error: store type of @builtin(local_invocation_id) must be "
|
||||||
"'vec3<u32>'");
|
"'vec3<u32>'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,7 +786,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(front_facing) must be 'bool'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
||||||
|
@ -814,7 +815,50 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
|
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(front_facing) must be 'bool'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(crbug.com/tint/1846): This isn't a validation test, but this sits next to other @builtin
|
||||||
|
// tests. Clean this up.
|
||||||
|
TEST_F(ResolverBuiltinsValidationTest, StructMemberAttributeMapsToSemBuiltinEnum) {
|
||||||
|
// struct S {
|
||||||
|
// @builtin(front_facing) b : bool;
|
||||||
|
// };
|
||||||
|
// @fragment
|
||||||
|
// fn f(s : S) {}
|
||||||
|
|
||||||
|
auto* builtin = Builtin(builtin::BuiltinValue::kFrontFacing);
|
||||||
|
auto* s = Structure("S", utils::Vector{
|
||||||
|
Member("f", ty.bool_(), utils::Vector{builtin}),
|
||||||
|
});
|
||||||
|
Func("f", utils::Vector{Param("b", ty.Of(s))}, ty.void_(), utils::Empty,
|
||||||
|
utils::Vector{
|
||||||
|
Stage(ast::PipelineStage::kFragment),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
auto* builtin_expr = Sem().Get(builtin);
|
||||||
|
ASSERT_NE(builtin_expr, nullptr);
|
||||||
|
EXPECT_EQ(builtin_expr->Value(), builtin::BuiltinValue::kFrontFacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(crbug.com/tint/1846): This isn't a validation test, but this sits next to other @builtin
|
||||||
|
// tests. Clean this up.
|
||||||
|
TEST_F(ResolverBuiltinsValidationTest, ParamAttributeMapsToSemBuiltinEnum) {
|
||||||
|
// @fragment
|
||||||
|
// fn f(@builtin(front_facing) b : bool) {}
|
||||||
|
|
||||||
|
auto* builtin = Builtin(builtin::BuiltinValue::kFrontFacing);
|
||||||
|
Func("f", utils::Vector{Param("b", ty.bool_(), utils::Vector{builtin})}, ty.void_(),
|
||||||
|
utils::Empty,
|
||||||
|
utils::Vector{
|
||||||
|
Stage(ast::PipelineStage::kFragment),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
auto* builtin_expr = Sem().Get(builtin);
|
||||||
|
ASSERT_NE(builtin_expr, nullptr);
|
||||||
|
EXPECT_EQ(builtin_expr->Value(), builtin::BuiltinValue::kFrontFacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {
|
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
#include "src/tint/ast/while_statement.h"
|
#include "src/tint/ast/while_statement.h"
|
||||||
#include "src/tint/ast/workgroup_attribute.h"
|
#include "src/tint/ast/workgroup_attribute.h"
|
||||||
#include "src/tint/builtin/builtin.h"
|
#include "src/tint/builtin/builtin.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/scope_stack.h"
|
#include "src/tint/scope_stack.h"
|
||||||
#include "src/tint/sem/builtin.h"
|
#include "src/tint/sem/builtin.h"
|
||||||
#include "src/tint/symbol_table.h"
|
#include "src/tint/symbol_table.h"
|
||||||
|
@ -424,6 +425,10 @@ class DependencyScanner {
|
||||||
TraverseValueExpression(binding->expr);
|
TraverseValueExpression(binding->expr);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
[&](const ast::BuiltinAttribute* builtin) {
|
||||||
|
TraverseExpression(builtin->builtin, "builtin", "references");
|
||||||
|
return true;
|
||||||
|
},
|
||||||
[&](const ast::GroupAttribute* group) {
|
[&](const ast::GroupAttribute* group) {
|
||||||
TraverseValueExpression(group->expr);
|
TraverseValueExpression(group->expr);
|
||||||
return true;
|
return true;
|
||||||
|
@ -480,6 +485,11 @@ class DependencyScanner {
|
||||||
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_ty));
|
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_ty));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (auto builtin_val = builtin::ParseBuiltinValue(s);
|
||||||
|
builtin_val != builtin::BuiltinValue::kUndefined) {
|
||||||
|
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_val));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (auto addr = builtin::ParseAddressSpace(s);
|
if (auto addr = builtin::ParseAddressSpace(s);
|
||||||
addr != builtin::AddressSpace::kUndefined) {
|
addr != builtin::AddressSpace::kUndefined) {
|
||||||
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(addr));
|
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(addr));
|
||||||
|
@ -863,6 +873,9 @@ std::string ResolvedIdentifier::String(const SymbolTable& symbols, diag::List& d
|
||||||
if (auto builtin_ty = BuiltinType(); builtin_ty != builtin::Builtin::kUndefined) {
|
if (auto builtin_ty = BuiltinType(); builtin_ty != builtin::Builtin::kUndefined) {
|
||||||
return "builtin type '" + utils::ToString(builtin_ty) + "'";
|
return "builtin type '" + utils::ToString(builtin_ty) + "'";
|
||||||
}
|
}
|
||||||
|
if (auto builtin_val = BuiltinValue(); builtin_val != builtin::BuiltinValue::kUndefined) {
|
||||||
|
return "builtin value '" + utils::ToString(builtin_val) + "'";
|
||||||
|
}
|
||||||
if (auto access = Access(); access != builtin::Access::kUndefined) {
|
if (auto access = Access(); access != builtin::Access::kUndefined) {
|
||||||
return "access '" + utils::ToString(access) + "'";
|
return "access '" + utils::ToString(access) + "'";
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "src/tint/ast/module.h"
|
#include "src/tint/ast/module.h"
|
||||||
#include "src/tint/builtin/access.h"
|
#include "src/tint/builtin/access.h"
|
||||||
#include "src/tint/builtin/builtin.h"
|
#include "src/tint/builtin/builtin.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/builtin/texel_format.h"
|
#include "src/tint/builtin/texel_format.h"
|
||||||
#include "src/tint/diagnostic/diagnostic.h"
|
#include "src/tint/diagnostic/diagnostic.h"
|
||||||
#include "src/tint/sem/builtin_type.h"
|
#include "src/tint/sem/builtin_type.h"
|
||||||
|
@ -38,6 +39,7 @@ namespace tint::resolver {
|
||||||
/// - builtin::Access
|
/// - builtin::Access
|
||||||
/// - builtin::AddressSpace
|
/// - builtin::AddressSpace
|
||||||
/// - builtin::Builtin
|
/// - builtin::Builtin
|
||||||
|
/// - builtin::BuiltinValue
|
||||||
/// - builtin::TexelFormat
|
/// - builtin::TexelFormat
|
||||||
class ResolvedIdentifier {
|
class ResolvedIdentifier {
|
||||||
public:
|
public:
|
||||||
|
@ -95,8 +97,17 @@ class ResolvedIdentifier {
|
||||||
return builtin::Builtin::kUndefined;
|
return builtin::Builtin::kUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return the texel format if the ResolvedIdentifier holds builtin::TexelFormat, otherwise
|
/// @return the builtin value if the ResolvedIdentifier holds builtin::BuiltinValue, otherwise
|
||||||
/// builtin::TexelFormat::kUndefined
|
/// builtin::BuiltinValue::kUndefined
|
||||||
|
builtin::BuiltinValue BuiltinValue() const {
|
||||||
|
if (auto n = std::get_if<builtin::BuiltinValue>(&value_)) {
|
||||||
|
return *n;
|
||||||
|
}
|
||||||
|
return builtin::BuiltinValue::kUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the texel format if the ResolvedIdentifier holds type::TexelFormat, otherwise
|
||||||
|
/// type::TexelFormat::kUndefined
|
||||||
builtin::TexelFormat TexelFormat() const {
|
builtin::TexelFormat TexelFormat() const {
|
||||||
if (auto n = std::get_if<builtin::TexelFormat>(&value_)) {
|
if (auto n = std::get_if<builtin::TexelFormat>(&value_)) {
|
||||||
return *n;
|
return *n;
|
||||||
|
@ -133,6 +144,7 @@ class ResolvedIdentifier {
|
||||||
builtin::Access,
|
builtin::Access,
|
||||||
builtin::AddressSpace,
|
builtin::AddressSpace,
|
||||||
builtin::Builtin,
|
builtin::Builtin,
|
||||||
|
builtin::BuiltinValue,
|
||||||
builtin::TexelFormat>
|
builtin::TexelFormat>
|
||||||
value_;
|
value_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1500,6 +1500,91 @@ INSTANTIATE_TEST_SUITE_P(Functions,
|
||||||
|
|
||||||
} // namespace resolve_to_address_space
|
} // namespace resolve_to_address_space
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Resolve to builtin::BuiltinValue tests
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace resolve_to_builtin_value {
|
||||||
|
|
||||||
|
using ResolverDependencyGraphResolveToBuiltinValue =
|
||||||
|
ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
|
||||||
|
|
||||||
|
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, Resolve) {
|
||||||
|
const auto use = std::get<0>(GetParam());
|
||||||
|
const auto name = std::get<1>(GetParam());
|
||||||
|
const auto symbol = Symbols().New(name);
|
||||||
|
|
||||||
|
SymbolTestHelper helper(this);
|
||||||
|
auto* ident = helper.Add(use, symbol);
|
||||||
|
helper.Build();
|
||||||
|
|
||||||
|
auto resolved = Build().resolved_identifiers.Get(ident);
|
||||||
|
ASSERT_TRUE(resolved);
|
||||||
|
EXPECT_EQ(resolved->BuiltinValue(), builtin::ParseBuiltinValue(name))
|
||||||
|
<< resolved->String(Symbols(), Diagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, ShadowedByGlobalConst) {
|
||||||
|
const auto use = std::get<0>(GetParam());
|
||||||
|
const auto builtin = std::get<1>(GetParam());
|
||||||
|
const auto symbol = Symbols().New(utils::ToString(builtin));
|
||||||
|
|
||||||
|
SymbolTestHelper helper(this);
|
||||||
|
auto* decl = helper.Add(SymbolDeclKind::GlobalConst, symbol);
|
||||||
|
auto* ident = helper.Add(use, symbol);
|
||||||
|
helper.Build();
|
||||||
|
|
||||||
|
auto resolved = Build().resolved_identifiers.Get(ident);
|
||||||
|
ASSERT_TRUE(resolved);
|
||||||
|
EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, ShadowedByStruct) {
|
||||||
|
const auto use = std::get<0>(GetParam());
|
||||||
|
const auto builtin = std::get<1>(GetParam());
|
||||||
|
const auto symbol = Symbols().New(utils::ToString(builtin));
|
||||||
|
|
||||||
|
SymbolTestHelper helper(this);
|
||||||
|
auto* decl = helper.Add(SymbolDeclKind::Struct, symbol);
|
||||||
|
auto* ident = helper.Add(use, symbol);
|
||||||
|
helper.Build();
|
||||||
|
|
||||||
|
auto resolved = Build().resolved_identifiers.Get(ident);
|
||||||
|
ASSERT_TRUE(resolved);
|
||||||
|
EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, ShadowedByFunc) {
|
||||||
|
const auto use = std::get<0>(GetParam());
|
||||||
|
const auto builtin = std::get<1>(GetParam());
|
||||||
|
const auto symbol = Symbols().New(utils::ToString(builtin));
|
||||||
|
|
||||||
|
SymbolTestHelper helper(this);
|
||||||
|
auto* decl = helper.Add(SymbolDeclKind::Function, symbol);
|
||||||
|
auto* ident = helper.Add(use, symbol);
|
||||||
|
helper.Build();
|
||||||
|
|
||||||
|
auto resolved = Build().resolved_identifiers.Get(ident);
|
||||||
|
ASSERT_TRUE(resolved);
|
||||||
|
EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(Types,
|
||||||
|
ResolverDependencyGraphResolveToBuiltinValue,
|
||||||
|
testing::Combine(testing::ValuesIn(kTypeUseKinds),
|
||||||
|
testing::ValuesIn(builtin::kBuiltinValueStrings)));
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(Values,
|
||||||
|
ResolverDependencyGraphResolveToBuiltinValue,
|
||||||
|
testing::Combine(testing::ValuesIn(kValueUseKinds),
|
||||||
|
testing::ValuesIn(builtin::kBuiltinValueStrings)));
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(Functions,
|
||||||
|
ResolverDependencyGraphResolveToBuiltinValue,
|
||||||
|
testing::Combine(testing::ValuesIn(kFuncUseKinds),
|
||||||
|
testing::ValuesIn(builtin::kBuiltinValueStrings)));
|
||||||
|
|
||||||
|
} // namespace resolve_to_builtin_value
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Resolve to builtin::TexelFormat tests
|
// Resolve to builtin::TexelFormat tests
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1686,6 +1771,7 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
|
||||||
Param(Sym(), T,
|
Param(Sym(), T,
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Location(V), // Parameter attributes
|
Location(V), // Parameter attributes
|
||||||
|
Builtin(V),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
T, // Return type
|
T, // Return type
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "src/tint/ast/location_attribute.h"
|
#include "src/tint/ast/location_attribute.h"
|
||||||
#include "src/tint/ast/return_statement.h"
|
#include "src/tint/ast/return_statement.h"
|
||||||
#include "src/tint/ast/stage_attribute.h"
|
#include "src/tint/ast/stage_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver.h"
|
#include "src/tint/resolver/resolver.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Missing) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: missing entry point IO attribute on return type");
|
EXPECT_EQ(r()->error(), R"(12:34 error: missing entry point IO attribute on return type)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
|
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
|
||||||
|
@ -116,7 +117,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
||||||
13:43 note: previously consumed location(0))");
|
13:43 note: previously consumed @location)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_Valid) {
|
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_Valid) {
|
||||||
|
@ -170,7 +171,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_MemberMultipleAttribu
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
||||||
13:43 note: previously consumed location(0)
|
13:43 note: previously consumed @location
|
||||||
12:34 note: while analyzing entry point 'main')");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,9 +227,8 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateBuiltins) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(r()->error(),
|
||||||
r()->error(),
|
R"(12:34 error: @builtin(frag_depth) appears multiple times as pipeline output
|
||||||
R"(12:34 error: builtin(frag_depth) attribute appears multiple times as pipeline output
|
|
||||||
12:34 note: while analyzing entry point 'main')");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Missing) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "13:43 error: missing entry point IO attribute on parameter");
|
EXPECT_EQ(r()->error(), R"(13:43 error: missing entry point IO attribute on parameter)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
|
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
|
||||||
|
@ -287,7 +287,7 @@ TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
||||||
13:43 note: previously consumed location(0))");
|
13:43 note: previously consumed @location)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_Valid) {
|
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_Valid) {
|
||||||
|
@ -341,7 +341,7 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_MemberMultipleAttribut
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
|
||||||
13:43 note: previously consumed location(0)
|
13:43 note: previously consumed @location
|
||||||
12:34 note: while analyzing entry point 'main')");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,8 +396,7 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_DuplicateBuiltins) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: builtin(sample_index) attribute appears multiple times as "
|
"12:34 error: @builtin(sample_index) appears multiple times as pipeline input");
|
||||||
"pipeline input");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
|
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
|
||||||
|
@ -432,9 +431,8 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(r()->error(),
|
||||||
r()->error(),
|
R"(12:34 error: @builtin(sample_index) appears multiple times as pipeline input
|
||||||
R"(12:34 error: builtin(sample_index) attribute appears multiple times as pipeline input
|
|
||||||
12:34 note: while analyzing entry point 'main')");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,10 +783,8 @@ TEST_F(LocationAttributeTests, BadType_Input_bool) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: cannot apply 'location' attribute to declaration of "
|
R"(12:34 error: cannot apply @location to declaration of type 'bool'
|
||||||
"type 'bool'\n"
|
34:56 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"34:56 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadType_Output_Array) {
|
TEST_F(LocationAttributeTests, BadType_Output_Array) {
|
||||||
|
@ -808,10 +804,8 @@ TEST_F(LocationAttributeTests, BadType_Output_Array) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: cannot apply 'location' attribute to declaration of "
|
R"(12:34 error: cannot apply @location to declaration of type 'array<f32, 2>'
|
||||||
"type 'array<f32, 2>'\n"
|
34:56 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"34:56 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadType_Input_Struct) {
|
TEST_F(LocationAttributeTests, BadType_Input_Struct) {
|
||||||
|
@ -838,10 +832,8 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: cannot apply 'location' attribute to declaration of "
|
R"(12:34 error: cannot apply @location to declaration of type 'Input'
|
||||||
"type 'Input'\n"
|
13:43 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"13:43 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadType_Input_Struct_NestedStruct) {
|
TEST_F(LocationAttributeTests, BadType_Input_Struct_NestedStruct) {
|
||||||
|
@ -872,8 +864,8 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct_NestedStruct) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"14:52 error: nested structures cannot be used for entry point IO\n"
|
R"(14:52 error: nested structures cannot be used for entry point IO
|
||||||
"12:34 note: while analyzing entry point 'main'");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
|
TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
|
||||||
|
@ -898,10 +890,8 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"13:43 error: cannot apply 'location' attribute to declaration of "
|
R"(13:43 error: cannot apply @location to declaration of type 'array<f32>'
|
||||||
"type 'array<f32>'\n"
|
note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"note: 'location' attribute must only be applied to declarations "
|
|
||||||
"of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadMemberType_Input) {
|
TEST_F(LocationAttributeTests, BadMemberType_Input) {
|
||||||
|
@ -927,10 +917,8 @@ TEST_F(LocationAttributeTests, BadMemberType_Input) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"34:56 error: cannot apply 'location' attribute to declaration of "
|
R"(34:56 error: cannot apply @location to declaration of type 'array<i32>'
|
||||||
"type 'array<i32>'\n"
|
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"12:34 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadMemberType_Output) {
|
TEST_F(LocationAttributeTests, BadMemberType_Output) {
|
||||||
|
@ -954,10 +942,8 @@ TEST_F(LocationAttributeTests, BadMemberType_Output) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"34:56 error: cannot apply 'location' attribute to declaration of "
|
R"(34:56 error: cannot apply @location to declaration of type 'atomic<i32>'
|
||||||
"type 'atomic<i32>'\n"
|
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"12:34 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, BadMemberType_Unused) {
|
TEST_F(LocationAttributeTests, BadMemberType_Unused) {
|
||||||
|
@ -971,10 +957,8 @@ TEST_F(LocationAttributeTests, BadMemberType_Unused) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"34:56 error: cannot apply 'location' attribute to declaration of "
|
R"(34:56 error: cannot apply @location to declaration of type 'mat3x2<f32>'
|
||||||
"type 'mat3x2<f32>'\n"
|
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"12:34 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, ReturnType_Struct_Valid) {
|
TEST_F(LocationAttributeTests, ReturnType_Struct_Valid) {
|
||||||
|
@ -1026,11 +1010,8 @@ TEST_F(LocationAttributeTests, ReturnType_Struct) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(), R"(12:34 error: cannot apply @location to declaration of type 'Output'
|
||||||
"12:34 error: cannot apply 'location' attribute to declaration of "
|
13:43 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"type 'Output'\n"
|
|
||||||
"13:43 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, ReturnType_Struct_NestedStruct) {
|
TEST_F(LocationAttributeTests, ReturnType_Struct_NestedStruct) {
|
||||||
|
@ -1059,8 +1040,8 @@ TEST_F(LocationAttributeTests, ReturnType_Struct_NestedStruct) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"14:52 error: nested structures cannot be used for entry point IO\n"
|
R"(14:52 error: nested structures cannot be used for entry point IO
|
||||||
"12:34 note: while analyzing entry point 'main'");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
|
TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
|
||||||
|
@ -1085,10 +1066,8 @@ TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"13:43 error: cannot apply 'location' attribute to declaration of "
|
R"(13:43 error: cannot apply @location to declaration of type 'array<f32>'
|
||||||
"type 'array<f32>'\n"
|
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
|
||||||
"12:34 note: 'location' attribute must only be applied to "
|
|
||||||
"declarations of numeric scalar or numeric vector type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
|
TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
|
||||||
|
@ -1105,7 +1084,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader output");
|
EXPECT_EQ(r()->error(), R"(12:34 error: attribute is not valid for compute shader output)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
|
TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
|
||||||
|
@ -1120,7 +1099,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader inputs");
|
EXPECT_EQ(r()->error(), R"(12:34 error: attribute is not valid for compute shader inputs)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
|
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
|
||||||
|
@ -1186,7 +1165,7 @@ TEST_F(LocationAttributeTests, Duplicate_input) {
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: location(1) attribute appears multiple times");
|
EXPECT_EQ(r()->error(), R"(12:34 error: @location(1) appears multiple times)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LocationAttributeTests, Duplicate_struct) {
|
TEST_F(LocationAttributeTests, Duplicate_struct) {
|
||||||
|
@ -1219,8 +1198,8 @@ TEST_F(LocationAttributeTests, Duplicate_struct) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"34:56 error: location(1) attribute appears multiple times\n"
|
R"(34:56 error: @location(1) appears multiple times
|
||||||
"12:34 note: while analyzing entry point 'main'");
|
12:34 note: while analyzing entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum class Def {
|
||||||
kAddressSpace,
|
kAddressSpace,
|
||||||
kBuiltinFunction,
|
kBuiltinFunction,
|
||||||
kBuiltinType,
|
kBuiltinType,
|
||||||
|
kBuiltinValue,
|
||||||
kFunction,
|
kFunction,
|
||||||
kStruct,
|
kStruct,
|
||||||
kTexelFormat,
|
kTexelFormat,
|
||||||
|
@ -44,6 +45,8 @@ std::ostream& operator<<(std::ostream& out, Def def) {
|
||||||
return out << "Def::kBuiltinFunction";
|
return out << "Def::kBuiltinFunction";
|
||||||
case Def::kBuiltinType:
|
case Def::kBuiltinType:
|
||||||
return out << "Def::kBuiltinType";
|
return out << "Def::kBuiltinType";
|
||||||
|
case Def::kBuiltinValue:
|
||||||
|
return out << "Def::kBuiltinValue";
|
||||||
case Def::kFunction:
|
case Def::kFunction:
|
||||||
return out << "Def::kFunction";
|
return out << "Def::kFunction";
|
||||||
case Def::kStruct:
|
case Def::kStruct:
|
||||||
|
@ -62,6 +65,7 @@ enum class Use {
|
||||||
kAccess,
|
kAccess,
|
||||||
kAddressSpace,
|
kAddressSpace,
|
||||||
kBinaryOp,
|
kBinaryOp,
|
||||||
|
kBuiltinValue,
|
||||||
kCallExpr,
|
kCallExpr,
|
||||||
kCallStmt,
|
kCallStmt,
|
||||||
kFunctionReturnType,
|
kFunctionReturnType,
|
||||||
|
@ -80,6 +84,8 @@ std::ostream& operator<<(std::ostream& out, Use use) {
|
||||||
return out << "Use::kAddressSpace";
|
return out << "Use::kAddressSpace";
|
||||||
case Use::kBinaryOp:
|
case Use::kBinaryOp:
|
||||||
return out << "Use::kBinaryOp";
|
return out << "Use::kBinaryOp";
|
||||||
|
case Use::kBuiltinValue:
|
||||||
|
return out << "Use::kBuiltinValue";
|
||||||
case Use::kCallExpr:
|
case Use::kCallExpr:
|
||||||
return out << "Use::kCallExpr";
|
return out << "Use::kCallExpr";
|
||||||
case Use::kCallStmt:
|
case Use::kCallStmt:
|
||||||
|
@ -156,6 +162,16 @@ TEST_P(ResolverExpressionKindTest, Test) {
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Def::kBuiltinValue: {
|
||||||
|
sym = Sym("position");
|
||||||
|
check_expr = [](const sem::Expression* expr) {
|
||||||
|
ASSERT_NE(expr, nullptr);
|
||||||
|
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::BuiltinValue>>();
|
||||||
|
ASSERT_NE(enum_expr, nullptr);
|
||||||
|
EXPECT_EQ(enum_expr->Value(), builtin::BuiltinValue::kPosition);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Def::kFunction: {
|
case Def::kFunction: {
|
||||||
auto* fn = Func(kDefSource, "FUNCTION", utils::Empty, ty.i32(), Return(1_i));
|
auto* fn = Func(kDefSource, "FUNCTION", utils::Empty, ty.i32(), Return(1_i));
|
||||||
sym = Sym("FUNCTION");
|
sym = Sym("FUNCTION");
|
||||||
|
@ -233,6 +249,10 @@ TEST_P(ResolverExpressionKindTest, Test) {
|
||||||
case Use::kBinaryOp:
|
case Use::kBinaryOp:
|
||||||
GlobalVar("v", builtin::AddressSpace::kPrivate, Mul(1_a, expr));
|
GlobalVar("v", builtin::AddressSpace::kPrivate, Mul(1_a, expr));
|
||||||
break;
|
break;
|
||||||
|
case Use::kBuiltinValue:
|
||||||
|
Func("f", utils::Vector{Param("p", ty.vec4<f32>(), utils::Vector{Builtin(expr)})},
|
||||||
|
ty.void_(), utils::Empty, utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
break;
|
||||||
case Use::kFunctionReturnType:
|
case Use::kFunctionReturnType:
|
||||||
Func("f", utils::Empty, ty(expr), Return(Call(sym)));
|
Func("f", utils::Empty, ty(expr), Return(Call(sym)));
|
||||||
break;
|
break;
|
||||||
|
@ -271,6 +291,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
{Def::kAccess, Use::kAddressSpace,
|
{Def::kAccess, Use::kAddressSpace,
|
||||||
R"(5:6 error: cannot use access 'write' as address space)"},
|
R"(5:6 error: cannot use access 'write' as address space)"},
|
||||||
{Def::kAccess, Use::kBinaryOp, R"(5:6 error: cannot use access 'write' as value)"},
|
{Def::kAccess, Use::kBinaryOp, R"(5:6 error: cannot use access 'write' as value)"},
|
||||||
|
{Def::kAccess, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use access 'write' as builtin value)"},
|
||||||
{Def::kAccess, Use::kCallExpr, R"(5:6 error: cannot use access 'write' as call target)"},
|
{Def::kAccess, Use::kCallExpr, R"(5:6 error: cannot use access 'write' as call target)"},
|
||||||
{Def::kAccess, Use::kCallStmt, R"(5:6 error: cannot use access 'write' as call target)"},
|
{Def::kAccess, Use::kCallStmt, R"(5:6 error: cannot use access 'write' as call target)"},
|
||||||
{Def::kAccess, Use::kFunctionReturnType, R"(5:6 error: cannot use access 'write' as type)"},
|
{Def::kAccess, Use::kFunctionReturnType, R"(5:6 error: cannot use access 'write' as type)"},
|
||||||
|
@ -286,6 +308,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
{Def::kAddressSpace, Use::kAddressSpace, kPass},
|
{Def::kAddressSpace, Use::kAddressSpace, kPass},
|
||||||
{Def::kAddressSpace, Use::kBinaryOp,
|
{Def::kAddressSpace, Use::kBinaryOp,
|
||||||
R"(5:6 error: cannot use address space 'workgroup' as value)"},
|
R"(5:6 error: cannot use address space 'workgroup' as value)"},
|
||||||
|
{Def::kAddressSpace, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use address space 'workgroup' as builtin value)"},
|
||||||
{Def::kAddressSpace, Use::kCallExpr,
|
{Def::kAddressSpace, Use::kCallExpr,
|
||||||
R"(5:6 error: cannot use address space 'workgroup' as call target)"},
|
R"(5:6 error: cannot use address space 'workgroup' as call target)"},
|
||||||
{Def::kAddressSpace, Use::kCallStmt,
|
{Def::kAddressSpace, Use::kCallStmt,
|
||||||
|
@ -309,6 +333,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
R"(7:8 error: missing '(' for builtin function call)"},
|
R"(7:8 error: missing '(' for builtin function call)"},
|
||||||
{Def::kBuiltinFunction, Use::kBinaryOp,
|
{Def::kBuiltinFunction, Use::kBinaryOp,
|
||||||
R"(7:8 error: missing '(' for builtin function call)"},
|
R"(7:8 error: missing '(' for builtin function call)"},
|
||||||
|
{Def::kBuiltinFunction, Use::kBuiltinValue,
|
||||||
|
R"(7:8 error: missing '(' for builtin function call)"},
|
||||||
{Def::kBuiltinFunction, Use::kCallStmt, kPass},
|
{Def::kBuiltinFunction, Use::kCallStmt, kPass},
|
||||||
{Def::kBuiltinFunction, Use::kFunctionReturnType,
|
{Def::kBuiltinFunction, Use::kFunctionReturnType,
|
||||||
R"(7:8 error: missing '(' for builtin function call)"},
|
R"(7:8 error: missing '(' for builtin function call)"},
|
||||||
|
@ -329,6 +355,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
{Def::kBuiltinType, Use::kBinaryOp,
|
{Def::kBuiltinType, Use::kBinaryOp,
|
||||||
R"(5:6 error: cannot use type 'vec4<f32>' as value
|
R"(5:6 error: cannot use type 'vec4<f32>' as value
|
||||||
7:8 note: are you missing '()' for type initializer?)"},
|
7:8 note: are you missing '()' for type initializer?)"},
|
||||||
|
{Def::kBuiltinType, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use type 'vec4<f32>' as builtin value)"},
|
||||||
{Def::kBuiltinType, Use::kCallExpr, kPass},
|
{Def::kBuiltinType, Use::kCallExpr, kPass},
|
||||||
{Def::kBuiltinType, Use::kFunctionReturnType, kPass},
|
{Def::kBuiltinType, Use::kFunctionReturnType, kPass},
|
||||||
{Def::kBuiltinType, Use::kMemberType, kPass},
|
{Def::kBuiltinType, Use::kMemberType, kPass},
|
||||||
|
@ -342,12 +370,39 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
R"(5:6 error: cannot use type 'vec4<f32>' as value
|
R"(5:6 error: cannot use type 'vec4<f32>' as value
|
||||||
7:8 note: are you missing '()' for type initializer?)"},
|
7:8 note: are you missing '()' for type initializer?)"},
|
||||||
|
|
||||||
|
{Def::kBuiltinValue, Use::kAccess,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as access)"},
|
||||||
|
{Def::kBuiltinValue, Use::kAddressSpace,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as address space)"},
|
||||||
|
{Def::kBuiltinValue, Use::kBinaryOp,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as value)"},
|
||||||
|
{Def::kBuiltinValue, Use::kBuiltinValue, kPass},
|
||||||
|
{Def::kBuiltinValue, Use::kCallStmt,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as call target)"},
|
||||||
|
{Def::kBuiltinValue, Use::kCallExpr,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as call target)"},
|
||||||
|
{Def::kBuiltinValue, Use::kFunctionReturnType,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as type)"},
|
||||||
|
{Def::kBuiltinValue, Use::kMemberType,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as type)"},
|
||||||
|
{Def::kBuiltinValue, Use::kTexelFormat,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as texel format)"},
|
||||||
|
{Def::kBuiltinValue, Use::kValueExpression,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as value)"},
|
||||||
|
{Def::kBuiltinValue, Use::kVariableType,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as type)"},
|
||||||
|
{Def::kBuiltinValue, Use::kUnaryOp,
|
||||||
|
R"(5:6 error: cannot use builtin value 'position' as value)"},
|
||||||
|
|
||||||
{Def::kFunction, Use::kAccess, R"(5:6 error: cannot use function 'FUNCTION' as access
|
{Def::kFunction, Use::kAccess, R"(5:6 error: cannot use function 'FUNCTION' as access
|
||||||
1:2 note: function 'FUNCTION' declared here)"},
|
1:2 note: function 'FUNCTION' declared here)"},
|
||||||
{Def::kFunction, Use::kAddressSpace,
|
{Def::kFunction, Use::kAddressSpace,
|
||||||
R"(5:6 error: cannot use function 'FUNCTION' as address space
|
R"(5:6 error: cannot use function 'FUNCTION' as address space
|
||||||
1:2 note: function 'FUNCTION' declared here)"},
|
1:2 note: function 'FUNCTION' declared here)"},
|
||||||
{Def::kFunction, Use::kBinaryOp, R"(5:6 error: cannot use function 'FUNCTION' as value
|
{Def::kFunction, Use::kBinaryOp, R"(5:6 error: cannot use function 'FUNCTION' as value
|
||||||
|
1:2 note: function 'FUNCTION' declared here)"},
|
||||||
|
{Def::kFunction, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use function 'FUNCTION' as builtin value
|
||||||
1:2 note: function 'FUNCTION' declared here)"},
|
1:2 note: function 'FUNCTION' declared here)"},
|
||||||
{Def::kFunction, Use::kCallExpr, kPass},
|
{Def::kFunction, Use::kCallExpr, kPass},
|
||||||
{Def::kFunction, Use::kCallStmt, kPass},
|
{Def::kFunction, Use::kCallStmt, kPass},
|
||||||
|
@ -375,6 +430,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
{Def::kStruct, Use::kBinaryOp, R"(5:6 error: cannot use type 'STRUCT' as value
|
{Def::kStruct, Use::kBinaryOp, R"(5:6 error: cannot use type 'STRUCT' as value
|
||||||
7:8 note: are you missing '()' for type initializer?
|
7:8 note: are you missing '()' for type initializer?
|
||||||
1:2 note: struct 'STRUCT' declared here)"},
|
1:2 note: struct 'STRUCT' declared here)"},
|
||||||
|
{Def::kStruct, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use type 'STRUCT' as builtin value)"},
|
||||||
{Def::kStruct, Use::kFunctionReturnType, kPass},
|
{Def::kStruct, Use::kFunctionReturnType, kPass},
|
||||||
{Def::kStruct, Use::kMemberType, kPass},
|
{Def::kStruct, Use::kMemberType, kPass},
|
||||||
{Def::kStruct, Use::kTexelFormat, R"(5:6 error: cannot use type 'STRUCT' as texel format)"},
|
{Def::kStruct, Use::kTexelFormat, R"(5:6 error: cannot use type 'STRUCT' as texel format)"},
|
||||||
|
@ -394,6 +451,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
R"(5:6 error: cannot use texel format 'rgba8unorm' as address space)"},
|
R"(5:6 error: cannot use texel format 'rgba8unorm' as address space)"},
|
||||||
{Def::kTexelFormat, Use::kBinaryOp,
|
{Def::kTexelFormat, Use::kBinaryOp,
|
||||||
R"(5:6 error: cannot use texel format 'rgba8unorm' as value)"},
|
R"(5:6 error: cannot use texel format 'rgba8unorm' as value)"},
|
||||||
|
{Def::kTexelFormat, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use texel format 'rgba8unorm' as builtin value)"},
|
||||||
{Def::kTexelFormat, Use::kCallExpr,
|
{Def::kTexelFormat, Use::kCallExpr,
|
||||||
R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"},
|
R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"},
|
||||||
{Def::kTexelFormat, Use::kCallStmt,
|
{Def::kTexelFormat, Use::kCallStmt,
|
||||||
|
@ -416,6 +475,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
{Def::kTypeAlias, Use::kBinaryOp,
|
{Def::kTypeAlias, Use::kBinaryOp,
|
||||||
R"(5:6 error: cannot use type 'i32' as value
|
R"(5:6 error: cannot use type 'i32' as value
|
||||||
7:8 note: are you missing '()' for type initializer?)"},
|
7:8 note: are you missing '()' for type initializer?)"},
|
||||||
|
{Def::kTypeAlias, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use type 'i32' as builtin value)"},
|
||||||
{Def::kTypeAlias, Use::kCallExpr, kPass},
|
{Def::kTypeAlias, Use::kCallExpr, kPass},
|
||||||
{Def::kTypeAlias, Use::kFunctionReturnType, kPass},
|
{Def::kTypeAlias, Use::kFunctionReturnType, kPass},
|
||||||
{Def::kTypeAlias, Use::kMemberType, kPass},
|
{Def::kTypeAlias, Use::kMemberType, kPass},
|
||||||
|
@ -434,6 +495,9 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
R"(5:6 error: cannot use const 'VARIABLE' as address space
|
R"(5:6 error: cannot use const 'VARIABLE' as address space
|
||||||
1:2 note: const 'VARIABLE' declared here)"},
|
1:2 note: const 'VARIABLE' declared here)"},
|
||||||
{Def::kVariable, Use::kBinaryOp, kPass},
|
{Def::kVariable, Use::kBinaryOp, kPass},
|
||||||
|
{Def::kVariable, Use::kBuiltinValue,
|
||||||
|
R"(5:6 error: cannot use const 'VARIABLE' as builtin value
|
||||||
|
1:2 note: const 'VARIABLE' declared here)"},
|
||||||
{Def::kVariable, Use::kCallStmt,
|
{Def::kVariable, Use::kCallStmt,
|
||||||
R"(5:6 error: cannot use const 'VARIABLE' as call target
|
R"(5:6 error: cannot use const 'VARIABLE' as call target
|
||||||
1:2 note: const 'VARIABLE' declared here)"},
|
1:2 note: const 'VARIABLE' declared here)"},
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "src/tint/ast/discard_statement.h"
|
#include "src/tint/ast/discard_statement.h"
|
||||||
#include "src/tint/ast/return_statement.h"
|
#include "src/tint/ast/return_statement.h"
|
||||||
#include "src/tint/ast/stage_attribute.h"
|
#include "src/tint/ast/stage_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver.h"
|
#include "src/tint/resolver/resolver.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@
|
||||||
|
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::Access>);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::Access>);
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::AddressSpace>);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::AddressSpace>);
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::BuiltinValue>);
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::TexelFormat>);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::TexelFormat>);
|
||||||
|
|
||||||
namespace tint::resolver {
|
namespace tint::resolver {
|
||||||
|
@ -611,7 +612,9 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto* attr : param->attributes) {
|
for (auto* attr : param->attributes) {
|
||||||
Mark(attr);
|
if (!Attribute(attr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!validator_.NoDuplicateAttributes(param->attributes)) {
|
if (!validator_.NoDuplicateAttributes(param->attributes)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -792,7 +795,9 @@ sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* attr : v->attributes) {
|
for (auto* attr : v->attributes) {
|
||||||
Mark(attr);
|
if (!Attribute(attr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validator_.NoDuplicateAttributes(v->attributes)) {
|
if (!validator_.NoDuplicateAttributes(v->attributes)) {
|
||||||
|
@ -854,11 +859,8 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||||
validator_.DiagnosticFilters().Push();
|
validator_.DiagnosticFilters().Push();
|
||||||
TINT_DEFER(validator_.DiagnosticFilters().Pop());
|
TINT_DEFER(validator_.DiagnosticFilters().Pop());
|
||||||
for (auto* attr : decl->attributes) {
|
for (auto* attr : decl->attributes) {
|
||||||
Mark(attr);
|
if (!Attribute(attr)) {
|
||||||
if (auto* dc = attr->As<ast::DiagnosticAttribute>()) {
|
return nullptr;
|
||||||
if (!DiagnosticControl(dc->control)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!validator_.NoDuplicateAttributes(decl->attributes)) {
|
if (!validator_.NoDuplicateAttributes(decl->attributes)) {
|
||||||
|
@ -921,7 +923,9 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||||
// Determine if the return type has a location
|
// Determine if the return type has a location
|
||||||
std::optional<uint32_t> return_location;
|
std::optional<uint32_t> return_location;
|
||||||
for (auto* attr : decl->return_type_attributes) {
|
for (auto* attr : decl->return_type_attributes) {
|
||||||
Mark(attr);
|
if (!Attribute(attr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
|
if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
|
||||||
auto value = LocationAttribute(loc_attr);
|
auto value = LocationAttribute(loc_attr);
|
||||||
|
@ -1502,6 +1506,11 @@ sem::BuiltinEnumExpression<builtin::AddressSpace>* Resolver::AddressSpaceExpress
|
||||||
return sem_.AsAddressSpace(Expression(expr));
|
return sem_.AsAddressSpace(Expression(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sem::BuiltinEnumExpression<builtin::BuiltinValue>* Resolver::BuiltinValueExpression(
|
||||||
|
const ast::Expression* expr) {
|
||||||
|
return sem_.AsBuiltinValue(Expression(expr));
|
||||||
|
}
|
||||||
|
|
||||||
sem::BuiltinEnumExpression<builtin::TexelFormat>* Resolver::TexelFormatExpression(
|
sem::BuiltinEnumExpression<builtin::TexelFormat>* Resolver::TexelFormatExpression(
|
||||||
const ast::Expression* expr) {
|
const ast::Expression* expr) {
|
||||||
return sem_.AsTexelFormat(Expression(expr));
|
return sem_.AsTexelFormat(Expression(expr));
|
||||||
|
@ -2987,6 +2996,11 @@ sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
|
||||||
expr, current_statement_, addr);
|
expr, current_statement_, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto builtin = resolved->BuiltinValue(); builtin != builtin::BuiltinValue::kUndefined) {
|
||||||
|
return builder_->create<sem::BuiltinEnumExpression<builtin::BuiltinValue>>(
|
||||||
|
expr, current_statement_, builtin);
|
||||||
|
}
|
||||||
|
|
||||||
if (auto fmt = resolved->TexelFormat(); fmt != builtin::TexelFormat::kUndefined) {
|
if (auto fmt = resolved->TexelFormat(); fmt != builtin::TexelFormat::kUndefined) {
|
||||||
return builder_->create<sem::BuiltinEnumExpression<builtin::TexelFormat>>(
|
return builder_->create<sem::BuiltinEnumExpression<builtin::TexelFormat>>(
|
||||||
expr, current_statement_, fmt);
|
expr, current_statement_, fmt);
|
||||||
|
@ -3307,6 +3321,26 @@ sem::ValueExpression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
|
||||||
return sem;
|
return sem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Resolver::Attribute(const ast::Attribute* attr) {
|
||||||
|
Mark(attr);
|
||||||
|
return Switch(
|
||||||
|
attr, //
|
||||||
|
[&](const ast::BuiltinAttribute* b) { return BuiltinAttribute(b); },
|
||||||
|
[&](const ast::DiagnosticAttribute* dc) { return DiagnosticControl(dc->control); },
|
||||||
|
[&](Default) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Resolver::BuiltinAttribute(const ast::BuiltinAttribute* attr) {
|
||||||
|
auto* builtin_expr = BuiltinValueExpression(attr->builtin);
|
||||||
|
if (!builtin_expr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Apply the resolved tint::sem::BuiltinEnumExpression<tint::builtin::BuiltinValue> to the
|
||||||
|
// attribute.
|
||||||
|
builder_->Sem().Add(attr, builtin_expr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) {
|
bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) {
|
||||||
Mark(control.rule_name);
|
Mark(control.rule_name);
|
||||||
|
|
||||||
|
@ -3522,7 +3556,9 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||||
bool has_size_attr = false;
|
bool has_size_attr = false;
|
||||||
std::optional<uint32_t> location;
|
std::optional<uint32_t> location;
|
||||||
for (auto* attr : member->attributes) {
|
for (auto* attr : member->attributes) {
|
||||||
Mark(attr);
|
if (!Attribute(attr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
bool ok = Switch(
|
bool ok = Switch(
|
||||||
attr, //
|
attr, //
|
||||||
[&](const ast::StructMemberOffsetAttribute* o) {
|
[&](const ast::StructMemberOffsetAttribute* o) {
|
||||||
|
|
|
@ -146,9 +146,15 @@ class Resolver {
|
||||||
const ast::Expression* expr);
|
const ast::Expression* expr);
|
||||||
|
|
||||||
/// @returns the call of Expression() cast to a
|
/// @returns the call of Expression() cast to a
|
||||||
/// sem::BuiltinEnumExpression<builtin::TexelFormat>. If the sem::Expression is not a
|
/// sem::BuiltinEnumExpression<builtin::BuiltinValue>. If the sem::Expression is not a
|
||||||
/// sem::BuiltinEnumExpression<builtin::TexelFormat>, then an error diagnostic is raised and
|
/// sem::BuiltinEnumExpression<builtin::BuiltinValue>, then an error diagnostic is raised and
|
||||||
/// nullptr is returned.
|
/// nullptr is returned.
|
||||||
|
sem::BuiltinEnumExpression<builtin::BuiltinValue>* BuiltinValueExpression(
|
||||||
|
const ast::Expression* expr);
|
||||||
|
|
||||||
|
/// @returns the call of Expression() cast to a sem::BuiltinEnumExpression<type::TexelFormat>.
|
||||||
|
/// If the sem::Expression is not a sem::BuiltinEnumExpression<type::TexelFormat>, then an error
|
||||||
|
/// diagnostic is raised and nullptr is returned.
|
||||||
sem::BuiltinEnumExpression<builtin::TexelFormat>* TexelFormatExpression(
|
sem::BuiltinEnumExpression<builtin::TexelFormat>* TexelFormatExpression(
|
||||||
const ast::Expression* expr);
|
const ast::Expression* expr);
|
||||||
|
|
||||||
|
@ -290,6 +296,14 @@ class Resolver {
|
||||||
/// current_function_
|
/// current_function_
|
||||||
bool WorkgroupSize(const ast::Function*);
|
bool WorkgroupSize(const ast::Function*);
|
||||||
|
|
||||||
|
/// Resolves the attribute @p attr
|
||||||
|
/// @returns true on success, false on failure
|
||||||
|
bool Attribute(const ast::Attribute* attr);
|
||||||
|
|
||||||
|
/// Resolves the `@builtin` attribute @p attr
|
||||||
|
/// @returns true on success, false on failure
|
||||||
|
bool BuiltinAttribute(const ast::BuiltinAttribute* attr);
|
||||||
|
|
||||||
/// @param control the diagnostic control
|
/// @param control the diagnostic control
|
||||||
/// @returns true on success, false on failure
|
/// @returns true on success, false on failure
|
||||||
bool DiagnosticControl(const ast::DiagnosticControl& control);
|
bool DiagnosticControl(const ast::DiagnosticControl& control);
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "src/tint/ast/unary_op_expression.h"
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
#include "src/tint/ast/variable_decl_statement.h"
|
#include "src/tint/ast/variable_decl_statement.h"
|
||||||
#include "src/tint/ast/workgroup_attribute.h"
|
#include "src/tint/ast/workgroup_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
#include "src/tint/sem/call.h"
|
#include "src/tint/sem/call.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
|
|
|
@ -84,6 +84,11 @@ void SemHelper::ErrorUnexpectedExprKind(const sem::Expression* expr,
|
||||||
std::string(wanted),
|
std::string(wanted),
|
||||||
addr->Declaration()->source);
|
addr->Declaration()->source);
|
||||||
},
|
},
|
||||||
|
[&](const sem::BuiltinEnumExpression<builtin::BuiltinValue>* builtin) {
|
||||||
|
AddError("cannot use builtin value '" + utils::ToString(builtin->Value()) + "' as " +
|
||||||
|
std::string(wanted),
|
||||||
|
builtin->Declaration()->source);
|
||||||
|
},
|
||||||
[&](const sem::BuiltinEnumExpression<builtin::TexelFormat>* fmt) {
|
[&](const sem::BuiltinEnumExpression<builtin::TexelFormat>* fmt) {
|
||||||
AddError("cannot use texel format '" + utils::ToString(fmt->Value()) + "' as " +
|
AddError("cannot use texel format '" + utils::ToString(fmt->Value()) + "' as " +
|
||||||
std::string(wanted),
|
std::string(wanted),
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/diagnostic/diagnostic.h"
|
#include "src/tint/diagnostic/diagnostic.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/resolver/dependency_graph.h"
|
#include "src/tint/resolver/dependency_graph.h"
|
||||||
|
@ -120,8 +121,23 @@ class SemHelper {
|
||||||
|
|
||||||
/// @param expr the semantic node
|
/// @param expr the semantic node
|
||||||
/// @returns nullptr if @p expr is nullptr, or @p expr cast to
|
/// @returns nullptr if @p expr is nullptr, or @p expr cast to
|
||||||
/// sem::BuiltinEnumExpression<builtin::TexelFormat> if the cast is successful, otherwise an
|
/// sem::BuiltinEnumExpression<builtin::BuiltinValue> if the cast is successful, otherwise an
|
||||||
/// error diagnostic is raised.
|
/// error diagnostic is raised.
|
||||||
|
sem::BuiltinEnumExpression<builtin::BuiltinValue>* AsBuiltinValue(sem::Expression* expr) const {
|
||||||
|
if (TINT_LIKELY(expr)) {
|
||||||
|
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::BuiltinValue>>();
|
||||||
|
if (TINT_LIKELY(enum_expr)) {
|
||||||
|
return enum_expr;
|
||||||
|
}
|
||||||
|
ErrorUnexpectedExprKind(expr, "builtin value");
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param expr the semantic node
|
||||||
|
/// @returns nullptr if @p expr is nullptr, or @p expr cast to
|
||||||
|
/// sem::BuiltinEnumExpression<type::TexelFormat> if the cast is successful, otherwise an error
|
||||||
|
/// diagnostic is raised.
|
||||||
sem::BuiltinEnumExpression<builtin::TexelFormat>* AsTexelFormat(sem::Expression* expr) const {
|
sem::BuiltinEnumExpression<builtin::TexelFormat>* AsTexelFormat(sem::Expression* expr) const {
|
||||||
if (TINT_LIKELY(expr)) {
|
if (TINT_LIKELY(expr)) {
|
||||||
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::TexelFormat>>();
|
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::TexelFormat>>();
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "src/tint/ast/stage_attribute.h"
|
#include "src/tint/ast/stage_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
#include "src/tint/sem/struct.h"
|
#include "src/tint/sem/struct.h"
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/resolver/dependency_graph.h"
|
#include "src/tint/resolver/dependency_graph.h"
|
||||||
#include "src/tint/scope_stack.h"
|
#include "src/tint/scope_stack.h"
|
||||||
|
@ -1145,11 +1146,12 @@ class UniformityGraph {
|
||||||
const ast::IdentifierExpression* ident,
|
const ast::IdentifierExpression* ident,
|
||||||
bool load_rule = false) {
|
bool load_rule = false) {
|
||||||
// Helper to check if the entry point attribute of `obj` indicates non-uniformity.
|
// Helper to check if the entry point attribute of `obj` indicates non-uniformity.
|
||||||
auto has_nonuniform_entry_point_attribute = [](auto* obj) {
|
auto has_nonuniform_entry_point_attribute = [&](auto* obj) {
|
||||||
// Only the num_workgroups and workgroup_id builtins are uniform.
|
// Only the num_workgroups and workgroup_id builtins are uniform.
|
||||||
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(obj->attributes)) {
|
if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(obj->attributes)) {
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kNumWorkgroups ||
|
auto builtin = builder_->Sem().Get(builtin_attr)->Value();
|
||||||
builtin->builtin == builtin::BuiltinValue::kWorkgroupId) {
|
if (builtin == builtin::BuiltinValue::kNumWorkgroups ||
|
||||||
|
builtin == builtin::BuiltinValue::kWorkgroupId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "src/tint/ast/switch_statement.h"
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ast/unary_op_expression.h"
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
#include "src/tint/ast/variable_decl_statement.h"
|
#include "src/tint/ast/variable_decl_statement.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
#include "src/tint/sem/call.h"
|
#include "src/tint/sem/call.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
|
|
|
@ -114,15 +114,11 @@ bool IsValidStorageTextureTexelFormat(builtin::TexelFormat format) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to stringify a pipeline IO attribute.
|
// Helper to stringify a pipeline IO attribute.
|
||||||
std::string attr_to_str(const ast::Attribute* attr,
|
std::string AttrToStr(const ast::Attribute* attr) {
|
||||||
std::optional<uint32_t> location = std::nullopt) {
|
return Switch(
|
||||||
std::stringstream str;
|
attr, //
|
||||||
if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
|
[&](const ast::BuiltinAttribute*) { return "@builtin"; },
|
||||||
str << "builtin(" << builtin->builtin << ")";
|
[&](const ast::LocationAttribute*) { return "@location"; });
|
||||||
} else if (attr->Is<ast::LocationAttribute>()) {
|
|
||||||
str << "location(" << location.value() << ")";
|
|
||||||
}
|
|
||||||
return str.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CALLBACK>
|
template <typename CALLBACK>
|
||||||
|
@ -868,7 +864,8 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
stage_name << stage;
|
stage_name << stage;
|
||||||
bool is_stage_mismatch = false;
|
bool is_stage_mismatch = false;
|
||||||
bool is_output = !is_input;
|
bool is_output = !is_input;
|
||||||
switch (attr->builtin) {
|
auto builtin = sem_.Get(attr)->Value();
|
||||||
|
switch (builtin) {
|
||||||
case builtin::BuiltinValue::kPosition:
|
case builtin::BuiltinValue::kPosition:
|
||||||
if (stage != ast::PipelineStage::kNone &&
|
if (stage != ast::PipelineStage::kNone &&
|
||||||
!((is_input && stage == ast::PipelineStage::kFragment) ||
|
!((is_input && stage == ast::PipelineStage::kFragment) ||
|
||||||
|
@ -876,8 +873,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!(type->is_float_vector() && type->As<type::Vector>()->Width() == 4)) {
|
if (!(type->is_float_vector() && type->As<type::Vector>()->Width() == 4)) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'vec4<f32>'",
|
std::stringstream err;
|
||||||
attr->source);
|
err << "store type of @builtin(" << builtin << ") must be 'vec4<f32>'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -890,8 +888,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!(type->is_unsigned_integer_vector() && type->As<type::Vector>()->Width() == 3)) {
|
if (!(type->is_unsigned_integer_vector() && type->As<type::Vector>()->Width() == 3)) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'vec3<u32>'",
|
std::stringstream err;
|
||||||
attr->source);
|
err << "store type of @builtin(" << builtin << ") must be 'vec3<u32>'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -901,7 +900,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!type->Is<type::F32>()) {
|
if (!type->Is<type::F32>()) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'f32'", attr->source);
|
std::stringstream err;
|
||||||
|
err << "store type of @builtin(" << builtin << ") must be 'f32'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -911,7 +912,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!type->Is<type::Bool>()) {
|
if (!type->Is<type::Bool>()) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'bool'", attr->source);
|
std::stringstream err;
|
||||||
|
err << "store type of @builtin(" << builtin << ") must be 'bool'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -921,7 +924,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!type->Is<type::U32>()) {
|
if (!type->Is<type::U32>()) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
|
std::stringstream err;
|
||||||
|
err << "store type of @builtin(" << builtin << ") must be 'u32'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -932,7 +937,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!type->Is<type::U32>()) {
|
if (!type->Is<type::U32>()) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
|
std::stringstream err;
|
||||||
|
err << "store type of @builtin(" << builtin << ") must be 'u32'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -941,7 +948,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!type->Is<type::U32>()) {
|
if (!type->Is<type::U32>()) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
|
std::stringstream err;
|
||||||
|
err << "store type of @builtin(" << builtin << ") must be 'u32'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -951,7 +960,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
is_stage_mismatch = true;
|
is_stage_mismatch = true;
|
||||||
}
|
}
|
||||||
if (!type->Is<type::U32>()) {
|
if (!type->Is<type::U32>()) {
|
||||||
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
|
std::stringstream err;
|
||||||
|
err << "store type of @builtin(" << builtin << ") must be 'u32'";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -960,9 +971,10 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_stage_mismatch) {
|
if (is_stage_mismatch) {
|
||||||
AddError(attr_to_str(attr) + " cannot be used in " +
|
std::stringstream err;
|
||||||
(is_input ? "input of " : "output of ") + stage_name.str() + " pipeline stage",
|
err << "@builtin(" << builtin << ") cannot be used in "
|
||||||
attr->source);
|
<< (is_input ? "input of " : "output of ") << stage_name.str() << " pipeline stage";
|
||||||
|
AddError(err.str(), attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,32 +1110,34 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
||||||
for (auto* attr : attrs) {
|
for (auto* attr : attrs) {
|
||||||
auto is_invalid_compute_shader_attribute = false;
|
auto is_invalid_compute_shader_attribute = false;
|
||||||
|
|
||||||
if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
|
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
||||||
|
auto builtin = sem_.Get(builtin_attr)->Value();
|
||||||
|
|
||||||
if (pipeline_io_attribute) {
|
if (pipeline_io_attribute) {
|
||||||
AddError("multiple entry point IO attributes", attr->source);
|
AddError("multiple entry point IO attributes", attr->source);
|
||||||
AddNote("previously consumed " + attr_to_str(pipeline_io_attribute, location),
|
AddNote("previously consumed " + AttrToStr(pipeline_io_attribute),
|
||||||
pipeline_io_attribute->source);
|
pipeline_io_attribute->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pipeline_io_attribute = attr;
|
pipeline_io_attribute = attr;
|
||||||
|
|
||||||
if (builtins.Contains(builtin->builtin)) {
|
if (builtins.Contains(builtin)) {
|
||||||
AddError(attr_to_str(builtin) +
|
std::stringstream err;
|
||||||
" attribute appears multiple times as pipeline " +
|
err << "@builtin(" << builtin << ") appears multiple times as pipeline "
|
||||||
(param_or_ret == ParamOrRetType::kParameter ? "input" : "output"),
|
<< (param_or_ret == ParamOrRetType::kParameter ? "input" : "output");
|
||||||
decl->source);
|
AddError(err.str(), decl->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!BuiltinAttribute(builtin, ty, stage,
|
if (!BuiltinAttribute(builtin_attr, ty, stage,
|
||||||
/* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
|
/* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
builtins.Add(builtin->builtin);
|
builtins.Add(builtin);
|
||||||
} else if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
|
} else if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
|
||||||
if (pipeline_io_attribute) {
|
if (pipeline_io_attribute) {
|
||||||
AddError("multiple entry point IO attributes", attr->source);
|
AddError("multiple entry point IO attributes", attr->source);
|
||||||
AddNote("previously consumed " + attr_to_str(pipeline_io_attribute),
|
AddNote("previously consumed " + AttrToStr(pipeline_io_attribute),
|
||||||
pipeline_io_attribute->source);
|
pipeline_io_attribute->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1183,16 +1197,16 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
||||||
if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
|
if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
|
||||||
param_or_ret == ParamOrRetType::kReturnType) {
|
param_or_ret == ParamOrRetType::kReturnType) {
|
||||||
AddError(
|
AddError(
|
||||||
"integral user-defined vertex outputs must have a flat "
|
"integral user-defined vertex outputs must have a flat interpolation "
|
||||||
"interpolation attribute",
|
"attribute",
|
||||||
source);
|
source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (decl->PipelineStage() == ast::PipelineStage::kFragment &&
|
if (decl->PipelineStage() == ast::PipelineStage::kFragment &&
|
||||||
param_or_ret == ParamOrRetType::kParameter) {
|
param_or_ret == ParamOrRetType::kParameter) {
|
||||||
AddError(
|
AddError(
|
||||||
"integral user-defined fragment inputs must have a flat "
|
"integral user-defined fragment inputs must have a flat interpolation "
|
||||||
"interpolation attribute",
|
"attribute",
|
||||||
source);
|
source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1211,15 +1225,14 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
||||||
if (invariant_attribute) {
|
if (invariant_attribute) {
|
||||||
bool has_position = false;
|
bool has_position = false;
|
||||||
if (pipeline_io_attribute) {
|
if (pipeline_io_attribute) {
|
||||||
if (auto* builtin = pipeline_io_attribute->As<ast::BuiltinAttribute>()) {
|
if (auto* builtin_attr = pipeline_io_attribute->As<ast::BuiltinAttribute>()) {
|
||||||
has_position = (builtin->builtin == builtin::BuiltinValue::kPosition);
|
auto builtin = sem_.Get(builtin_attr)->Value();
|
||||||
|
has_position = (builtin == builtin::BuiltinValue::kPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_position) {
|
if (!has_position) {
|
||||||
AddError(
|
AddError("invariant attribute must only be applied to a position builtin",
|
||||||
"invariant attribute must only be applied to a position "
|
invariant_attribute->source);
|
||||||
"builtin",
|
|
||||||
invariant_attribute->source);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1280,9 +1293,10 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
||||||
// Check module-scope variables, as the SPIR-V sanitizer generates these.
|
// Check module-scope variables, as the SPIR-V sanitizer generates these.
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto* global : func->TransitivelyReferencedGlobals()) {
|
for (auto* global : func->TransitivelyReferencedGlobals()) {
|
||||||
if (auto* builtin =
|
if (auto* builtin_attr =
|
||||||
ast::GetAttribute<ast::BuiltinAttribute>(global->Declaration()->attributes)) {
|
ast::GetAttribute<ast::BuiltinAttribute>(global->Declaration()->attributes)) {
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kPosition) {
|
auto builtin = sem_.Get(builtin_attr)->Value();
|
||||||
|
if (builtin == builtin::BuiltinValue::kPosition) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2120,12 +2134,13 @@ bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) cons
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[&](const ast::BuiltinAttribute* builtin) {
|
[&](const ast::BuiltinAttribute* builtin_attr) {
|
||||||
if (!BuiltinAttribute(builtin, member->Type(), stage,
|
if (!BuiltinAttribute(builtin_attr, member->Type(), stage,
|
||||||
/* is_input */ false)) {
|
/* is_input */ false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kPosition) {
|
auto builtin = sem_.Get(builtin_attr)->Value();
|
||||||
|
if (builtin == builtin::BuiltinValue::kPosition) {
|
||||||
has_position = true;
|
has_position = true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -2208,18 +2223,18 @@ bool Validator::LocationAttribute(const ast::LocationAttribute* loc_attr,
|
||||||
|
|
||||||
if (!type->is_numeric_scalar_or_vector()) {
|
if (!type->is_numeric_scalar_or_vector()) {
|
||||||
std::string invalid_type = sem_.TypeNameOf(type);
|
std::string invalid_type = sem_.TypeNameOf(type);
|
||||||
AddError("cannot apply 'location' attribute to declaration of type '" + invalid_type + "'",
|
AddError("cannot apply @location to declaration of type '" + invalid_type + "'", source);
|
||||||
source);
|
|
||||||
AddNote(
|
AddNote(
|
||||||
"'location' attribute must only be applied to declarations of numeric scalar or "
|
"@location must only be applied to declarations of numeric scalar or numeric vector "
|
||||||
"numeric vector type",
|
"type",
|
||||||
loc_attr->source);
|
loc_attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!locations.Add(location)) {
|
if (!locations.Add(location)) {
|
||||||
AddError(attr_to_str(loc_attr, location) + " attribute appears multiple times",
|
std::stringstream err;
|
||||||
loc_attr->source);
|
err << "@location(" << location << ") appears multiple times";
|
||||||
|
AddError(err.str(), loc_attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "src/tint/sem/builtin_enum_expression.h"
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace tint {
|
namespace tint {
|
||||||
class CastableBase;
|
class CastableBase;
|
||||||
|
@ -25,6 +27,7 @@ namespace tint::ast {
|
||||||
class AccessorExpression;
|
class AccessorExpression;
|
||||||
class BinaryExpression;
|
class BinaryExpression;
|
||||||
class BitcastExpression;
|
class BitcastExpression;
|
||||||
|
class BuiltinAttribute;
|
||||||
class CallExpression;
|
class CallExpression;
|
||||||
class Expression;
|
class Expression;
|
||||||
class ForLoopStatement;
|
class ForLoopStatement;
|
||||||
|
@ -43,6 +46,9 @@ class Variable;
|
||||||
class WhileStatement;
|
class WhileStatement;
|
||||||
class UnaryOpExpression;
|
class UnaryOpExpression;
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
namespace tint::builtin {
|
||||||
|
enum class BuiltinValue;
|
||||||
|
}
|
||||||
namespace tint::sem {
|
namespace tint::sem {
|
||||||
class Expression;
|
class Expression;
|
||||||
class ForLoopStatement;
|
class ForLoopStatement;
|
||||||
|
@ -71,21 +77,22 @@ namespace tint::sem {
|
||||||
/// rules will be used to infer the return type based on the argument type.
|
/// rules will be used to infer the return type based on the argument type.
|
||||||
struct TypeMappings {
|
struct TypeMappings {
|
||||||
//! @cond Doxygen_Suppress
|
//! @cond Doxygen_Suppress
|
||||||
|
BuiltinEnumExpression<builtin::BuiltinValue>* operator()(ast::BuiltinAttribute*);
|
||||||
|
CastableBase* operator()(ast::Node*);
|
||||||
|
Expression* operator()(ast::Expression*);
|
||||||
ForLoopStatement* operator()(ast::ForLoopStatement*);
|
ForLoopStatement* operator()(ast::ForLoopStatement*);
|
||||||
Function* operator()(ast::Function*);
|
Function* operator()(ast::Function*);
|
||||||
IfStatement* operator()(ast::IfStatement*);
|
|
||||||
CastableBase* operator()(ast::Node*);
|
|
||||||
GlobalVariable* operator()(ast::Override*);
|
GlobalVariable* operator()(ast::Override*);
|
||||||
|
IfStatement* operator()(ast::IfStatement*);
|
||||||
Statement* operator()(ast::Statement*);
|
Statement* operator()(ast::Statement*);
|
||||||
Struct* operator()(ast::Struct*);
|
Struct* operator()(ast::Struct*);
|
||||||
StructMember* operator()(ast::StructMember*);
|
StructMember* operator()(ast::StructMember*);
|
||||||
SwitchStatement* operator()(ast::SwitchStatement*);
|
SwitchStatement* operator()(ast::SwitchStatement*);
|
||||||
type::Type* operator()(ast::TypeDecl*);
|
type::Type* operator()(ast::TypeDecl*);
|
||||||
Expression* operator()(ast::Expression*);
|
|
||||||
ValueExpression* operator()(ast::AccessorExpression*);
|
ValueExpression* operator()(ast::AccessorExpression*);
|
||||||
ValueExpression* operator()(ast::CallExpression*);
|
|
||||||
ValueExpression* operator()(ast::BinaryExpression*);
|
ValueExpression* operator()(ast::BinaryExpression*);
|
||||||
ValueExpression* operator()(ast::BitcastExpression*);
|
ValueExpression* operator()(ast::BitcastExpression*);
|
||||||
|
ValueExpression* operator()(ast::CallExpression*);
|
||||||
ValueExpression* operator()(ast::LiteralExpression*);
|
ValueExpression* operator()(ast::LiteralExpression*);
|
||||||
ValueExpression* operator()(ast::PhonyExpression*);
|
ValueExpression* operator()(ast::PhonyExpression*);
|
||||||
ValueExpression* operator()(ast::UnaryOpExpression*);
|
ValueExpression* operator()(ast::UnaryOpExpression*);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/tint/ast/disable_validation_attribute.h"
|
#include "src/tint/ast/disable_validation_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
#include "src/tint/transform/unshadow.h"
|
#include "src/tint/transform/unshadow.h"
|
||||||
|
@ -81,46 +82,12 @@ uint32_t BuiltinOrder(builtin::BuiltinValue builtin) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Comparison function used to reorder struct members such that all members with
|
|
||||||
/// location attributes appear first (ordered by location slot), followed by
|
|
||||||
/// those with builtin attributes.
|
|
||||||
/// @param a a struct member
|
|
||||||
/// @param b another struct member
|
|
||||||
/// @returns true if a comes before b
|
|
||||||
bool StructMemberComparator(const MemberInfo& a, const MemberInfo& b) {
|
|
||||||
auto* a_loc = ast::GetAttribute<ast::LocationAttribute>(a.member->attributes);
|
|
||||||
auto* b_loc = ast::GetAttribute<ast::LocationAttribute>(b.member->attributes);
|
|
||||||
auto* a_blt = ast::GetAttribute<ast::BuiltinAttribute>(a.member->attributes);
|
|
||||||
auto* b_blt = ast::GetAttribute<ast::BuiltinAttribute>(b.member->attributes);
|
|
||||||
if (a_loc) {
|
|
||||||
if (!b_loc) {
|
|
||||||
// `a` has location attribute and `b` does not: `a` goes first.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Both have location attributes: smallest goes first.
|
|
||||||
return a.location < b.location;
|
|
||||||
} else {
|
|
||||||
if (b_loc) {
|
|
||||||
// `b` has location attribute and `a` does not: `b` goes first.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Both are builtins: order matters for FXC.
|
|
||||||
return BuiltinOrder(a_blt->builtin) < BuiltinOrder(b_blt->builtin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if `attr` is a shader IO attribute.
|
// Returns true if `attr` is a shader IO attribute.
|
||||||
bool IsShaderIOAttribute(const ast::Attribute* attr) {
|
bool IsShaderIOAttribute(const ast::Attribute* attr) {
|
||||||
return attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute, ast::InvariantAttribute,
|
return attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute, ast::InvariantAttribute,
|
||||||
ast::LocationAttribute>();
|
ast::LocationAttribute>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if `attrs` contains a `sample_mask` builtin.
|
|
||||||
bool HasSampleMask(utils::VectorRef<const ast::Attribute*> attrs) {
|
|
||||||
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attrs);
|
|
||||||
return builtin && builtin->builtin == builtin::BuiltinValue::kSampleMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
/// PIMPL state for the transform
|
/// PIMPL state for the transform
|
||||||
|
@ -132,7 +99,7 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
/// The type of the output value.
|
/// The type of the output value.
|
||||||
ast::Type type;
|
ast::Type type;
|
||||||
/// The shader IO attributes.
|
/// The shader IO attributes.
|
||||||
utils::Vector<const ast::Attribute*, 2> attributes;
|
utils::Vector<const ast::Attribute*, 8> attributes;
|
||||||
/// The value itself.
|
/// The value itself.
|
||||||
const ast::Expression* value;
|
const ast::Expression* value;
|
||||||
/// The output location.
|
/// The output location.
|
||||||
|
@ -165,6 +132,8 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
utils::Vector<const ast::Statement*, 8> wrapper_body;
|
utils::Vector<const ast::Statement*, 8> wrapper_body;
|
||||||
/// Input names used by the entrypoint
|
/// Input names used by the entrypoint
|
||||||
std::unordered_set<std::string> input_names;
|
std::unordered_set<std::string> input_names;
|
||||||
|
/// A map of cloned attribute to builtin value
|
||||||
|
utils::Hashmap<const ast::BuiltinAttribute*, builtin::BuiltinValue, 16> builtin_attrs;
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param context the clone context
|
/// @param context the clone context
|
||||||
|
@ -175,20 +144,64 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
const ast::Function* function)
|
const ast::Function* function)
|
||||||
: ctx(context), cfg(config), func_ast(function), func_sem(ctx.src->Sem().Get(function)) {}
|
: ctx(context), cfg(config), func_ast(function), func_sem(ctx.src->Sem().Get(function)) {}
|
||||||
|
|
||||||
/// Clones the shader IO attributes from `src`.
|
/// Clones the attributes from @p in and adds it to @p out. If @p in is a builtin attribute,
|
||||||
/// @param src the attributes to clone
|
/// then builtin_attrs is updated with the builtin information.
|
||||||
|
/// @param in the attribute to clone
|
||||||
|
/// @param out the output Attributes
|
||||||
|
template <size_t N>
|
||||||
|
void CloneAttribute(const ast::Attribute* in, utils::Vector<const ast::Attribute*, N>& out) {
|
||||||
|
auto* cloned = ctx.Clone(in);
|
||||||
|
out.Push(cloned);
|
||||||
|
if (auto* builtin = in->As<ast::BuiltinAttribute>()) {
|
||||||
|
builtin_attrs.Add(cloned->As<ast::BuiltinAttribute>(),
|
||||||
|
ctx.src->Sem().Get(builtin)->Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clones the shader IO attributes from @p in.
|
||||||
|
/// @param in the attributes to clone
|
||||||
/// @param do_interpolate whether to clone InterpolateAttribute
|
/// @param do_interpolate whether to clone InterpolateAttribute
|
||||||
/// @return the cloned attributes
|
/// @return the cloned attributes
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
auto CloneShaderIOAttributes(utils::Vector<const ast::Attribute*, N> src, bool do_interpolate) {
|
auto CloneShaderIOAttributes(const utils::Vector<const ast::Attribute*, N> in,
|
||||||
utils::Vector<const ast::Attribute*, N> new_attributes;
|
bool do_interpolate) {
|
||||||
for (auto* attr : src) {
|
utils::Vector<const ast::Attribute*, N> out;
|
||||||
|
for (auto* attr : in) {
|
||||||
if (IsShaderIOAttribute(attr) &&
|
if (IsShaderIOAttribute(attr) &&
|
||||||
(do_interpolate || !attr->template Is<ast::InterpolateAttribute>())) {
|
(do_interpolate || !attr->template Is<ast::InterpolateAttribute>())) {
|
||||||
new_attributes.Push(ctx.Clone(attr));
|
CloneAttribute(attr, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new_attributes;
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param attr the input attribute
|
||||||
|
/// @returns the builtin value of the attribute
|
||||||
|
builtin::BuiltinValue BuiltinOf(const ast::BuiltinAttribute* attr) {
|
||||||
|
if (attr->program_id == ctx.dst->ID()) {
|
||||||
|
// attr belongs to the target program.
|
||||||
|
// Obtain the builtin value from #builtin_attrs.
|
||||||
|
if (auto b = builtin_attrs.Get(attr)) {
|
||||||
|
return *b;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// attr belongs to the source program.
|
||||||
|
// Obtain the builtin value from the semantic info.
|
||||||
|
return ctx.src->Sem().Get(attr)->Value();
|
||||||
|
}
|
||||||
|
TINT_ICE(Resolver, ctx.dst->Diagnostics())
|
||||||
|
<< "could not obtain builtin value from attribute";
|
||||||
|
return builtin::BuiltinValue::kUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param attrs the input attribute list
|
||||||
|
/// @returns the builtin value if any of the attributes in @p attrs is a builtin attribute,
|
||||||
|
/// otherwise builtin::BuiltinValue::kUndefined
|
||||||
|
builtin::BuiltinValue BuiltinOf(utils::VectorRef<const ast::Attribute*> attrs) {
|
||||||
|
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attrs)) {
|
||||||
|
return BuiltinOf(builtin);
|
||||||
|
}
|
||||||
|
return builtin::BuiltinValue::kUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create or return a symbol for the wrapper function's struct parameter.
|
/// Create or return a symbol for the wrapper function's struct parameter.
|
||||||
|
@ -204,13 +217,16 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
/// @param name the name of the shader input
|
/// @param name the name of the shader input
|
||||||
/// @param type the type of the shader input
|
/// @param type the type of the shader input
|
||||||
/// @param location the location if provided
|
/// @param location the location if provided
|
||||||
/// @param attributes the attributes to apply to the shader input
|
/// @param attrs the attributes to apply to the shader input
|
||||||
/// @returns an expression which evaluates to the value of the shader input
|
/// @returns an expression which evaluates to the value of the shader input
|
||||||
const ast::Expression* AddInput(std::string name,
|
const ast::Expression* AddInput(std::string name,
|
||||||
const type::Type* type,
|
const type::Type* type,
|
||||||
std::optional<uint32_t> location,
|
std::optional<uint32_t> location,
|
||||||
utils::Vector<const ast::Attribute*, 8> attributes) {
|
utils::Vector<const ast::Attribute*, 8> attrs) {
|
||||||
auto ast_type = CreateASTTypeFor(ctx, type);
|
auto ast_type = CreateASTTypeFor(ctx, type);
|
||||||
|
|
||||||
|
auto builtin_attr = BuiltinOf(attrs);
|
||||||
|
|
||||||
if (cfg.shader_style == ShaderStyle::kSpirv || cfg.shader_style == ShaderStyle::kGlsl) {
|
if (cfg.shader_style == ShaderStyle::kSpirv || cfg.shader_style == ShaderStyle::kGlsl) {
|
||||||
// Vulkan requires that integer user-defined fragment inputs are always decorated with
|
// Vulkan requires that integer user-defined fragment inputs are always decorated with
|
||||||
// `Flat`. See:
|
// `Flat`. See:
|
||||||
|
@ -219,21 +235,21 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
// required for integers.
|
// required for integers.
|
||||||
if (func_ast->PipelineStage() == ast::PipelineStage::kFragment &&
|
if (func_ast->PipelineStage() == ast::PipelineStage::kFragment &&
|
||||||
type->is_integer_scalar_or_vector() &&
|
type->is_integer_scalar_or_vector() &&
|
||||||
!ast::HasAttribute<ast::InterpolateAttribute>(attributes) &&
|
!ast::HasAttribute<ast::InterpolateAttribute>(attrs) &&
|
||||||
(ast::HasAttribute<ast::LocationAttribute>(attributes) ||
|
(ast::HasAttribute<ast::LocationAttribute>(attrs) ||
|
||||||
cfg.shader_style == ShaderStyle::kSpirv)) {
|
cfg.shader_style == ShaderStyle::kSpirv)) {
|
||||||
attributes.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
|
attrs.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
|
||||||
builtin::InterpolationSampling::kUndefined));
|
builtin::InterpolationSampling::kUndefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable validation for use of the `input` address space.
|
// Disable validation for use of the `input` address space.
|
||||||
attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
|
attrs.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
|
||||||
|
|
||||||
// In GLSL, if it's a builtin, override the name with the
|
// In GLSL, if it's a builtin, override the name with the
|
||||||
// corresponding gl_ builtin name
|
// corresponding gl_ builtin name
|
||||||
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
|
if (cfg.shader_style == ShaderStyle::kGlsl &&
|
||||||
if (cfg.shader_style == ShaderStyle::kGlsl && builtin) {
|
builtin_attr != builtin::BuiltinValue::kUndefined) {
|
||||||
name = GLSLBuiltinToString(builtin->builtin, func_ast->PipelineStage(),
|
name = GLSLBuiltinToString(builtin_attr, func_ast->PipelineStage(),
|
||||||
builtin::AddressSpace::kIn);
|
builtin::AddressSpace::kIn);
|
||||||
}
|
}
|
||||||
auto symbol = ctx.dst->Symbols().New(name);
|
auto symbol = ctx.dst->Symbols().New(name);
|
||||||
|
@ -241,32 +257,32 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
// Create the global variable and use its value for the shader input.
|
// Create the global variable and use its value for the shader input.
|
||||||
const ast::Expression* value = ctx.dst->Expr(symbol);
|
const ast::Expression* value = ctx.dst->Expr(symbol);
|
||||||
|
|
||||||
if (builtin) {
|
if (builtin_attr != builtin::BuiltinValue::kUndefined) {
|
||||||
if (cfg.shader_style == ShaderStyle::kGlsl) {
|
if (cfg.shader_style == ShaderStyle::kGlsl) {
|
||||||
value = FromGLSLBuiltin(builtin->builtin, value, ast_type);
|
value = FromGLSLBuiltin(builtin_attr, value, ast_type);
|
||||||
} else if (builtin->builtin == builtin::BuiltinValue::kSampleMask) {
|
} else if (builtin_attr == builtin::BuiltinValue::kSampleMask) {
|
||||||
// Vulkan requires the type of a SampleMask builtin to be an array.
|
// Vulkan requires the type of a SampleMask builtin to be an array.
|
||||||
// Declare it as array<u32, 1> and then load the first element.
|
// Declare it as array<u32, 1> and then load the first element.
|
||||||
ast_type = ctx.dst->ty.array(ast_type, 1_u);
|
ast_type = ctx.dst->ty.array(ast_type, 1_u);
|
||||||
value = ctx.dst->IndexAccessor(value, 0_i);
|
value = ctx.dst->IndexAccessor(value, 0_i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.dst->GlobalVar(symbol, ast_type, builtin::AddressSpace::kIn, std::move(attributes));
|
ctx.dst->GlobalVar(symbol, ast_type, builtin::AddressSpace::kIn, std::move(attrs));
|
||||||
return value;
|
return value;
|
||||||
} else if (cfg.shader_style == ShaderStyle::kMsl &&
|
} else if (cfg.shader_style == ShaderStyle::kMsl &&
|
||||||
ast::HasAttribute<ast::BuiltinAttribute>(attributes)) {
|
builtin_attr != builtin::BuiltinValue::kUndefined) {
|
||||||
// If this input is a builtin and we are targeting MSL, then add it to the
|
// If this input is a builtin and we are targeting MSL, then add it to the
|
||||||
// parameter list and pass it directly to the inner function.
|
// parameter list and pass it directly to the inner function.
|
||||||
Symbol symbol = input_names.emplace(name).second ? ctx.dst->Symbols().Register(name)
|
Symbol symbol = input_names.emplace(name).second ? ctx.dst->Symbols().Register(name)
|
||||||
: ctx.dst->Symbols().New(name);
|
: ctx.dst->Symbols().New(name);
|
||||||
wrapper_ep_parameters.Push(ctx.dst->Param(symbol, ast_type, std::move(attributes)));
|
wrapper_ep_parameters.Push(ctx.dst->Param(symbol, ast_type, std::move(attrs)));
|
||||||
return ctx.dst->Expr(symbol);
|
return ctx.dst->Expr(symbol);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, move it to the new structure member list.
|
// Otherwise, move it to the new structure member list.
|
||||||
Symbol symbol = input_names.emplace(name).second ? ctx.dst->Symbols().Register(name)
|
Symbol symbol = input_names.emplace(name).second ? ctx.dst->Symbols().Register(name)
|
||||||
: ctx.dst->Symbols().New(name);
|
: ctx.dst->Symbols().New(name);
|
||||||
wrapper_struct_param_members.Push(
|
wrapper_struct_param_members.Push(
|
||||||
{ctx.dst->Member(symbol, ast_type, std::move(attributes)), location});
|
{ctx.dst->Member(symbol, ast_type, std::move(attrs)), location});
|
||||||
return ctx.dst->MemberAccessor(InputStructSymbol(), symbol);
|
return ctx.dst->MemberAccessor(InputStructSymbol(), symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,13 +291,14 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
/// @param name the name of the shader output
|
/// @param name the name of the shader output
|
||||||
/// @param type the type of the shader output
|
/// @param type the type of the shader output
|
||||||
/// @param location the location if provided
|
/// @param location the location if provided
|
||||||
/// @param attributes the attributes to apply to the shader output
|
/// @param attrs the attributes to apply to the shader output
|
||||||
/// @param value the value of the shader output
|
/// @param value the value of the shader output
|
||||||
void AddOutput(std::string name,
|
void AddOutput(std::string name,
|
||||||
const type::Type* type,
|
const type::Type* type,
|
||||||
std::optional<uint32_t> location,
|
std::optional<uint32_t> location,
|
||||||
utils::Vector<const ast::Attribute*, 8> attributes,
|
utils::Vector<const ast::Attribute*, 8> attrs,
|
||||||
const ast::Expression* value) {
|
const ast::Expression* value) {
|
||||||
|
auto builtin_attr = BuiltinOf(attrs);
|
||||||
// Vulkan requires that integer user-defined vertex outputs are always decorated with
|
// Vulkan requires that integer user-defined vertex outputs are always decorated with
|
||||||
// `Flat`.
|
// `Flat`.
|
||||||
// TODO(crbug.com/tint/1224): Remove this once a flat interpolation attribute is required
|
// TODO(crbug.com/tint/1224): Remove this once a flat interpolation attribute is required
|
||||||
|
@ -289,26 +306,26 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
if (cfg.shader_style == ShaderStyle::kSpirv &&
|
if (cfg.shader_style == ShaderStyle::kSpirv &&
|
||||||
func_ast->PipelineStage() == ast::PipelineStage::kVertex &&
|
func_ast->PipelineStage() == ast::PipelineStage::kVertex &&
|
||||||
type->is_integer_scalar_or_vector() &&
|
type->is_integer_scalar_or_vector() &&
|
||||||
ast::HasAttribute<ast::LocationAttribute>(attributes) &&
|
ast::HasAttribute<ast::LocationAttribute>(attrs) &&
|
||||||
!ast::HasAttribute<ast::InterpolateAttribute>(attributes)) {
|
!ast::HasAttribute<ast::InterpolateAttribute>(attrs)) {
|
||||||
attributes.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
|
attrs.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
|
||||||
builtin::InterpolationSampling::kUndefined));
|
builtin::InterpolationSampling::kUndefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
// In GLSL, if it's a builtin, override the name with the
|
// In GLSL, if it's a builtin, override the name with the
|
||||||
// corresponding gl_ builtin name
|
// corresponding gl_ builtin name
|
||||||
if (cfg.shader_style == ShaderStyle::kGlsl) {
|
if (cfg.shader_style == ShaderStyle::kGlsl) {
|
||||||
if (auto* b = ast::GetAttribute<ast::BuiltinAttribute>(attributes)) {
|
if (builtin_attr != builtin::BuiltinValue::kUndefined) {
|
||||||
name = GLSLBuiltinToString(b->builtin, func_ast->PipelineStage(),
|
name = GLSLBuiltinToString(builtin_attr, func_ast->PipelineStage(),
|
||||||
builtin::AddressSpace::kOut);
|
builtin::AddressSpace::kOut);
|
||||||
value = ToGLSLBuiltin(b->builtin, value, type);
|
value = ToGLSLBuiltin(builtin_attr, value, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputValue output;
|
OutputValue output;
|
||||||
output.name = name;
|
output.name = name;
|
||||||
output.type = CreateASTTypeFor(ctx, type);
|
output.type = CreateASTTypeFor(ctx, type);
|
||||||
output.attributes = std::move(attributes);
|
output.attributes = std::move(attrs);
|
||||||
output.value = value;
|
output.value = value;
|
||||||
output.location = location;
|
output.location = location;
|
||||||
wrapper_output_values.Push(output);
|
wrapper_output_values.Push(output);
|
||||||
|
@ -322,14 +339,14 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
void ProcessNonStructParameter(const sem::Parameter* param) {
|
void ProcessNonStructParameter(const sem::Parameter* param) {
|
||||||
// Do not add interpolation attributes on vertex input
|
// Do not add interpolation attributes on vertex input
|
||||||
bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kVertex;
|
bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kVertex;
|
||||||
// Remove the shader IO attributes from the inner function parameter, and
|
// Remove the shader IO attributes from the inner function parameter, and attach them to the
|
||||||
// attach them to the new object instead.
|
// new object instead.
|
||||||
utils::Vector<const ast::Attribute*, 8> attributes;
|
utils::Vector<const ast::Attribute*, 8> attributes;
|
||||||
for (auto* attr : param->Declaration()->attributes) {
|
for (auto* attr : param->Declaration()->attributes) {
|
||||||
if (IsShaderIOAttribute(attr)) {
|
if (IsShaderIOAttribute(attr)) {
|
||||||
ctx.Remove(param->Declaration()->attributes, attr);
|
ctx.Remove(param->Declaration()->attributes, attr);
|
||||||
if ((do_interpolate || !attr->Is<ast::InterpolateAttribute>())) {
|
if ((do_interpolate || !attr->Is<ast::InterpolateAttribute>())) {
|
||||||
attributes.Push(ctx.Clone(attr));
|
CloneAttribute(attr, attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,25 +429,28 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
void AddFixedSampleMask() {
|
void AddFixedSampleMask() {
|
||||||
// Check the existing output values for a sample mask builtin.
|
// Check the existing output values for a sample mask builtin.
|
||||||
for (auto& outval : wrapper_output_values) {
|
for (auto& outval : wrapper_output_values) {
|
||||||
if (HasSampleMask(outval.attributes)) {
|
if (BuiltinOf(outval.attributes) == builtin::BuiltinValue::kSampleMask) {
|
||||||
// Combine the authored sample mask with the fixed mask.
|
// Combine the authored sample mask with the fixed mask.
|
||||||
outval.value = ctx.dst->And(outval.value, u32(cfg.fixed_sample_mask));
|
outval.value = ctx.dst->And(outval.value, u32(cfg.fixed_sample_mask));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No existing sample mask builtin was found, so create a new output value
|
// No existing sample mask builtin was found, so create a new output value using the fixed
|
||||||
// using the fixed sample mask.
|
// sample mask.
|
||||||
AddOutput("fixed_sample_mask", ctx.dst->create<type::U32>(), std::nullopt,
|
auto* builtin = ctx.dst->Builtin(builtin::BuiltinValue::kSampleMask);
|
||||||
{ctx.dst->Builtin(builtin::BuiltinValue::kSampleMask)},
|
builtin_attrs.Add(builtin, builtin::BuiltinValue::kSampleMask);
|
||||||
|
AddOutput("fixed_sample_mask", ctx.dst->create<type::U32>(), std::nullopt, {builtin},
|
||||||
ctx.dst->Expr(u32(cfg.fixed_sample_mask)));
|
ctx.dst->Expr(u32(cfg.fixed_sample_mask)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a point size builtin to the wrapper function output.
|
/// Add a point size builtin to the wrapper function output.
|
||||||
void AddVertexPointSize() {
|
void AddVertexPointSize() {
|
||||||
// Create a new output value and assign it a literal 1.0 value.
|
// Create a new output value and assign it a literal 1.0 value.
|
||||||
AddOutput("vertex_point_size", ctx.dst->create<type::F32>(), std::nullopt,
|
auto* builtin = ctx.dst->Builtin(builtin::BuiltinValue::kPointSize);
|
||||||
{ctx.dst->Builtin(builtin::BuiltinValue::kPointSize)}, ctx.dst->Expr(1_f));
|
builtin_attrs.Add(builtin, builtin::BuiltinValue::kPointSize);
|
||||||
|
AddOutput("vertex_point_size", ctx.dst->create<type::F32>(), std::nullopt, {builtin},
|
||||||
|
ctx.dst->Expr(1_f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an expression for gl_Position.[component]
|
/// Create an expression for gl_Position.[component]
|
||||||
|
@ -442,11 +462,40 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
return ctx.dst->MemberAccessor(ctx.dst->Expr(pos), c);
|
return ctx.dst->MemberAccessor(ctx.dst->Expr(pos), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Comparison function used to reorder struct members such that all members with
|
||||||
|
/// location attributes appear first (ordered by location slot), followed by
|
||||||
|
/// those with builtin attributes.
|
||||||
|
/// @param a a struct member
|
||||||
|
/// @param b another struct member
|
||||||
|
/// @returns true if a comes before b
|
||||||
|
bool StructMemberComparator(const MemberInfo& a, const MemberInfo& b) {
|
||||||
|
auto* a_loc = ast::GetAttribute<ast::LocationAttribute>(a.member->attributes);
|
||||||
|
auto* b_loc = ast::GetAttribute<ast::LocationAttribute>(b.member->attributes);
|
||||||
|
auto* a_blt = ast::GetAttribute<ast::BuiltinAttribute>(a.member->attributes);
|
||||||
|
auto* b_blt = ast::GetAttribute<ast::BuiltinAttribute>(b.member->attributes);
|
||||||
|
if (a_loc) {
|
||||||
|
if (!b_loc) {
|
||||||
|
// `a` has location attribute and `b` does not: `a` goes first.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Both have location attributes: smallest goes first.
|
||||||
|
return a.location < b.location;
|
||||||
|
} else {
|
||||||
|
if (b_loc) {
|
||||||
|
// `b` has location attribute and `a` does not: `b` goes first.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Both are builtins: order matters for FXC.
|
||||||
|
auto builtin_a = BuiltinOf(a_blt);
|
||||||
|
auto builtin_b = BuiltinOf(b_blt);
|
||||||
|
return BuiltinOrder(builtin_a) < BuiltinOrder(builtin_b);
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Create the wrapper function's struct parameter and type objects.
|
/// Create the wrapper function's struct parameter and type objects.
|
||||||
void CreateInputStruct() {
|
void CreateInputStruct() {
|
||||||
// Sort the struct members to satisfy HLSL interfacing matching rules.
|
// Sort the struct members to satisfy HLSL interfacing matching rules.
|
||||||
std::sort(wrapper_struct_param_members.begin(), wrapper_struct_param_members.end(),
|
std::sort(wrapper_struct_param_members.begin(), wrapper_struct_param_members.end(),
|
||||||
StructMemberComparator);
|
[&](auto& a, auto& b) { return StructMemberComparator(a, b); });
|
||||||
|
|
||||||
utils::Vector<const ast::StructMember*, 8> members;
|
utils::Vector<const ast::StructMember*, 8> members;
|
||||||
for (auto& mem : wrapper_struct_param_members) {
|
for (auto& mem : wrapper_struct_param_members) {
|
||||||
|
@ -483,16 +532,17 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
}
|
}
|
||||||
member_names.insert(ctx.dst->Symbols().NameFor(name));
|
member_names.insert(ctx.dst->Symbols().NameFor(name));
|
||||||
|
|
||||||
wrapper_struct_output_members.Push(
|
wrapper_struct_output_members.Push({
|
||||||
{ctx.dst->Member(name, outval.type, std::move(outval.attributes)),
|
ctx.dst->Member(name, outval.type, std::move(outval.attributes)),
|
||||||
outval.location});
|
outval.location,
|
||||||
|
});
|
||||||
assignments.Push(
|
assignments.Push(
|
||||||
ctx.dst->Assign(ctx.dst->MemberAccessor(wrapper_result, name), outval.value));
|
ctx.dst->Assign(ctx.dst->MemberAccessor(wrapper_result, name), outval.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the struct members to satisfy HLSL interfacing matching rules.
|
// Sort the struct members to satisfy HLSL interfacing matching rules.
|
||||||
std::sort(wrapper_struct_output_members.begin(), wrapper_struct_output_members.end(),
|
std::sort(wrapper_struct_output_members.begin(), wrapper_struct_output_members.end(),
|
||||||
StructMemberComparator);
|
[&](auto& a, auto& b) { return StructMemberComparator(a, b); });
|
||||||
|
|
||||||
utils::Vector<const ast::StructMember*, 8> members;
|
utils::Vector<const ast::StructMember*, 8> members;
|
||||||
for (auto& mem : wrapper_struct_output_members) {
|
for (auto& mem : wrapper_struct_output_members) {
|
||||||
|
@ -519,14 +569,14 @@ struct CanonicalizeEntryPointIO::State {
|
||||||
void CreateGlobalOutputVariables() {
|
void CreateGlobalOutputVariables() {
|
||||||
for (auto& outval : wrapper_output_values) {
|
for (auto& outval : wrapper_output_values) {
|
||||||
// Disable validation for use of the `output` address space.
|
// Disable validation for use of the `output` address space.
|
||||||
utils::Vector<const ast::Attribute*, 8> attributes = std::move(outval.attributes);
|
auto attributes = std::move(outval.attributes);
|
||||||
attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
|
attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
|
||||||
|
|
||||||
// Create the global variable and assign it the output value.
|
// Create the global variable and assign it the output value.
|
||||||
auto name = ctx.dst->Symbols().New(outval.name);
|
auto name = ctx.dst->Symbols().New(outval.name);
|
||||||
ast::Type type = outval.type;
|
ast::Type type = outval.type;
|
||||||
const ast::Expression* lhs = ctx.dst->Expr(name);
|
const ast::Expression* lhs = ctx.dst->Expr(name);
|
||||||
if (HasSampleMask(attributes)) {
|
if (BuiltinOf(attributes) == builtin::BuiltinValue::kSampleMask) {
|
||||||
// Vulkan requires the type of a SampleMask builtin to be an array.
|
// Vulkan requires the type of a SampleMask builtin to be an array.
|
||||||
// Declare it as array<u32, 1> and then store to the first element.
|
// Declare it as array<u32, 1> and then store to the first element.
|
||||||
type = ctx.dst->ty.array(type, 1_u);
|
type = ctx.dst->ty.array(type, 1_u);
|
||||||
|
|
|
@ -3163,7 +3163,7 @@ fn vert_main() -> @builtin(position) vec4<f32> {
|
||||||
auto* expect = R"(
|
auto* expect = R"(
|
||||||
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> value : vec4<f32>;
|
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> value : vec4<f32>;
|
||||||
|
|
||||||
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
|
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
|
||||||
|
|
||||||
fn vert_main_inner() -> vec4<f32> {
|
fn vert_main_inner() -> vec4<f32> {
|
||||||
return vec4<f32>();
|
return vec4<f32>();
|
||||||
|
@ -3197,7 +3197,7 @@ fn vert_main() -> @builtin(position) vec4<f32> {
|
||||||
struct tint_symbol {
|
struct tint_symbol {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
value : vec4<f32>,
|
value : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size : f32,
|
vertex_point_size : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3238,7 +3238,7 @@ fn vert_main() -> VertOut {
|
||||||
auto* expect = R"(
|
auto* expect = R"(
|
||||||
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> pos_1 : vec4<f32>;
|
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> pos_1 : vec4<f32>;
|
||||||
|
|
||||||
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
|
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
|
||||||
|
|
||||||
struct VertOut {
|
struct VertOut {
|
||||||
pos : vec4<f32>,
|
pos : vec4<f32>,
|
||||||
|
@ -3279,7 +3279,7 @@ struct VertOut {
|
||||||
auto* expect = R"(
|
auto* expect = R"(
|
||||||
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> pos_1 : vec4<f32>;
|
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> pos_1 : vec4<f32>;
|
||||||
|
|
||||||
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
|
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
|
||||||
|
|
||||||
fn vert_main_inner() -> VertOut {
|
fn vert_main_inner() -> VertOut {
|
||||||
return VertOut();
|
return VertOut();
|
||||||
|
@ -3325,7 +3325,7 @@ struct VertOut {
|
||||||
struct tint_symbol {
|
struct tint_symbol {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
pos : vec4<f32>,
|
pos : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size : f32,
|
vertex_point_size : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3367,7 +3367,7 @@ struct VertOut {
|
||||||
struct tint_symbol {
|
struct tint_symbol {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
pos : vec4<f32>,
|
pos : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size : f32,
|
vertex_point_size : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3432,7 +3432,7 @@ fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
|
||||||
|
|
||||||
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_1_1 : vec4<f32>;
|
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_1_1 : vec4<f32>;
|
||||||
|
|
||||||
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
|
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
|
||||||
|
|
||||||
var<private> vertex_point_size : f32;
|
var<private> vertex_point_size : f32;
|
||||||
|
|
||||||
|
@ -3510,7 +3510,7 @@ struct VertOut {
|
||||||
|
|
||||||
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_1_1 : vec4<f32>;
|
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_1_1 : vec4<f32>;
|
||||||
|
|
||||||
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
|
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
|
||||||
|
|
||||||
fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
|
fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
|
||||||
let x = (collide.collide + collide_1.collide);
|
let x = (collide.collide + collide_1.collide);
|
||||||
|
@ -3601,7 +3601,7 @@ struct tint_symbol_2 {
|
||||||
vertex_point_size : vec4<f32>,
|
vertex_point_size : vec4<f32>,
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
vertex_point_size_1 : vec4<f32>,
|
vertex_point_size_1 : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size_2 : f32,
|
vertex_point_size_2 : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3664,7 +3664,7 @@ struct tint_symbol_2 {
|
||||||
vertex_point_size : vec4<f32>,
|
vertex_point_size : vec4<f32>,
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
vertex_point_size_1 : vec4<f32>,
|
vertex_point_size_1 : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size_2 : f32,
|
vertex_point_size_2 : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3753,7 +3753,7 @@ struct tint_symbol_2 {
|
||||||
vertex_point_size : vec4<f32>,
|
vertex_point_size : vec4<f32>,
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
vertex_point_size_1 : vec4<f32>,
|
vertex_point_size_1 : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size_2 : f32,
|
vertex_point_size_2 : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3816,7 +3816,7 @@ struct tint_symbol_2 {
|
||||||
vertex_point_size : vec4<f32>,
|
vertex_point_size : vec4<f32>,
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
vertex_point_size_1 : vec4<f32>,
|
vertex_point_size_1 : vec4<f32>,
|
||||||
@builtin(point_size)
|
@builtin(__point_size)
|
||||||
vertex_point_size_2 : f32,
|
vertex_point_size_2 : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,178 +33,196 @@ TINT_INSTANTIATE_TYPEINFO(tint::transform::ClampFragDepth);
|
||||||
|
|
||||||
namespace tint::transform {
|
namespace tint::transform {
|
||||||
|
|
||||||
namespace {
|
/// PIMPL state for the transform
|
||||||
|
struct ClampFragDepth::State {
|
||||||
|
/// The source program
|
||||||
|
const Program* const src;
|
||||||
|
/// The target program builder
|
||||||
|
ProgramBuilder b{};
|
||||||
|
/// The clone context
|
||||||
|
CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
|
||||||
|
/// The sem::Info of the program
|
||||||
|
const sem::Info& sem = src->Sem();
|
||||||
|
/// The symbols of the program
|
||||||
|
const SymbolTable& sym = src->Symbols();
|
||||||
|
|
||||||
bool ContainsFragDepth(utils::VectorRef<const ast::Attribute*> attributes) {
|
/// Runs the transform
|
||||||
for (auto* attribute : attributes) {
|
/// @returns the new program or SkipTransform if the transform is not required
|
||||||
if (auto* builtin_attribute = attribute->As<ast::BuiltinAttribute>()) {
|
Transform::ApplyResult Run() {
|
||||||
if (builtin_attribute->builtin == builtin::BuiltinValue::kFragDepth) {
|
// Abort on any use of push constants in the module.
|
||||||
|
for (auto* global : src->AST().GlobalVariables()) {
|
||||||
|
if (auto* var = global->As<ast::Var>()) {
|
||||||
|
auto* v = src->Sem().Get(var);
|
||||||
|
if (TINT_UNLIKELY(v->AddressSpace() == builtin::AddressSpace::kPushConstant)) {
|
||||||
|
TINT_ICE(Transform, b.Diagnostics())
|
||||||
|
<< "ClampFragDepth doesn't know how to handle module that already use push "
|
||||||
|
"constants";
|
||||||
|
return Program(std::move(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShouldRun()) {
|
||||||
|
return SkipTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At least one entry-point needs clamping. Add the following to the module:
|
||||||
|
//
|
||||||
|
// enable chromium_experimental_push_constant;
|
||||||
|
//
|
||||||
|
// struct FragDepthClampArgs {
|
||||||
|
// min : f32,
|
||||||
|
// max : f32,
|
||||||
|
// }
|
||||||
|
// var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
|
||||||
|
//
|
||||||
|
// fn clamp_frag_depth(v : f32) -> f32 {
|
||||||
|
// return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
|
||||||
|
// }
|
||||||
|
b.Enable(builtin::Extension::kChromiumExperimentalPushConstant);
|
||||||
|
|
||||||
|
b.Structure(b.Symbols().New("FragDepthClampArgs"),
|
||||||
|
utils::Vector{b.Member("min", b.ty.f32()), b.Member("max", b.ty.f32())});
|
||||||
|
|
||||||
|
auto args_sym = b.Symbols().New("frag_depth_clamp_args");
|
||||||
|
b.GlobalVar(args_sym, b.ty("FragDepthClampArgs"), builtin::AddressSpace::kPushConstant);
|
||||||
|
|
||||||
|
auto base_fn_sym = b.Symbols().New("clamp_frag_depth");
|
||||||
|
b.Func(base_fn_sym, utils::Vector{b.Param("v", b.ty.f32())}, b.ty.f32(),
|
||||||
|
utils::Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(args_sym, "min"),
|
||||||
|
b.MemberAccessor(args_sym, "max")))});
|
||||||
|
|
||||||
|
// If true, the currently cloned function returns frag depth directly as a scalar
|
||||||
|
bool returns_frag_depth_as_value = false;
|
||||||
|
|
||||||
|
// If valid, the currently cloned function returns frag depth in a struct
|
||||||
|
// The symbol is the name of the helper function to apply the depth clamping.
|
||||||
|
Symbol returns_frag_depth_as_struct_helper;
|
||||||
|
|
||||||
|
// Map of io struct to helper function to return the structure with the depth clamping
|
||||||
|
// applied.
|
||||||
|
utils::Hashmap<const ast::Struct*, Symbol, 4u> io_structs_clamp_helpers;
|
||||||
|
|
||||||
|
// Register a callback that will be called for each visted AST function.
|
||||||
|
// This call wraps the cloning of the function's statements, and will assign to
|
||||||
|
// `returns_frag_depth_as_value` or `returns_frag_depth_as_struct_helper` if the function's
|
||||||
|
// return value requires depth clamping.
|
||||||
|
ctx.ReplaceAll([&](const ast::Function* fn) {
|
||||||
|
if (fn->PipelineStage() != ast::PipelineStage::kFragment) {
|
||||||
|
return ctx.CloneWithoutTransform(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReturnsFragDepthAsValue(fn)) {
|
||||||
|
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_value, true);
|
||||||
|
return ctx.CloneWithoutTransform(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReturnsFragDepthInStruct(fn)) {
|
||||||
|
// At most once per I/O struct, add the conversion function:
|
||||||
|
//
|
||||||
|
// fn clamp_frag_depth_S(s : S) -> S {
|
||||||
|
// return S(s.first, s.second, clamp_frag_depth(s.frag_depth), s.last);
|
||||||
|
// }
|
||||||
|
auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()->Declaration();
|
||||||
|
auto helper = io_structs_clamp_helpers.GetOrCreate(struct_ty, [&] {
|
||||||
|
auto return_ty = fn->return_type;
|
||||||
|
auto fn_sym =
|
||||||
|
b.Symbols().New("clamp_frag_depth_" + sym.NameFor(struct_ty->name->symbol));
|
||||||
|
|
||||||
|
utils::Vector<const ast::Expression*, 8u> initializer_args;
|
||||||
|
for (auto* member : struct_ty->members) {
|
||||||
|
const ast::Expression* arg =
|
||||||
|
b.MemberAccessor("s", ctx.Clone(member->name->symbol));
|
||||||
|
if (ContainsFragDepth(member->attributes)) {
|
||||||
|
arg = b.Call(base_fn_sym, arg);
|
||||||
|
}
|
||||||
|
initializer_args.Push(arg);
|
||||||
|
}
|
||||||
|
utils::Vector params{b.Param("s", ctx.Clone(return_ty))};
|
||||||
|
utils::Vector body{
|
||||||
|
b.Return(b.Call(ctx.Clone(return_ty), std::move(initializer_args))),
|
||||||
|
};
|
||||||
|
b.Func(fn_sym, params, ctx.Clone(return_ty), body);
|
||||||
|
return fn_sym;
|
||||||
|
});
|
||||||
|
|
||||||
|
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_struct_helper, helper);
|
||||||
|
return ctx.CloneWithoutTransform(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.CloneWithoutTransform(fn);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Replace the return statements `return expr` with `return clamp_frag_depth(expr)`.
|
||||||
|
ctx.ReplaceAll([&](const ast::ReturnStatement* stmt) -> const ast::ReturnStatement* {
|
||||||
|
if (returns_frag_depth_as_value) {
|
||||||
|
return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value)));
|
||||||
|
}
|
||||||
|
if (returns_frag_depth_as_struct_helper.IsValid()) {
|
||||||
|
return b.Return(stmt->source, b.Call(returns_frag_depth_as_struct_helper,
|
||||||
|
ctx.Clone(stmt->value)));
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.Clone();
|
||||||
|
return Program(std::move(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @returns true if the transform should run
|
||||||
|
bool ShouldRun() {
|
||||||
|
for (auto* fn : src->AST().Functions()) {
|
||||||
|
if (fn->PipelineStage() == ast::PipelineStage::kFragment &&
|
||||||
|
(ReturnsFragDepthAsValue(fn) || ReturnsFragDepthInStruct(fn))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
/// @param attrs the attributes to examine
|
||||||
return false;
|
/// @returns true if @p attrs contains a `@builtin(frag_depth)` attribute
|
||||||
}
|
bool ContainsFragDepth(utils::VectorRef<const ast::Attribute*> attrs) {
|
||||||
|
for (auto* attribute : attrs) {
|
||||||
bool ReturnsFragDepthAsValue(const ast::Function* fn) {
|
if (auto* builtin_attr = attribute->As<ast::BuiltinAttribute>()) {
|
||||||
return ContainsFragDepth(fn->return_type_attributes);
|
auto builtin = sem.Get(builtin_attr)->Value();
|
||||||
}
|
if (builtin == builtin::BuiltinValue::kFragDepth) {
|
||||||
|
return true;
|
||||||
bool ReturnsFragDepthInStruct(const sem::Info& sem, const ast::Function* fn) {
|
}
|
||||||
if (auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()) {
|
|
||||||
for (auto* member : struct_ty->Members()) {
|
|
||||||
if (ContainsFragDepth(member->Declaration()->attributes)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
/// @param fn the function to examine
|
||||||
}
|
/// @returns true if @p fn has a return type with a `@builtin(frag_depth)` attribute
|
||||||
|
bool ReturnsFragDepthAsValue(const ast::Function* fn) {
|
||||||
|
return ContainsFragDepth(fn->return_type_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
bool ShouldRun(const Program* program) {
|
/// @param fn the function to examine
|
||||||
auto& sem = program->Sem();
|
/// @returns true if @p fn has a return structure with a `@builtin(frag_depth)` attribute on one
|
||||||
|
/// of the members
|
||||||
for (auto* fn : program->AST().Functions()) {
|
bool ReturnsFragDepthInStruct(const ast::Function* fn) {
|
||||||
if (fn->PipelineStage() == ast::PipelineStage::kFragment &&
|
if (auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()) {
|
||||||
(ReturnsFragDepthAsValue(fn) || ReturnsFragDepthInStruct(sem, fn))) {
|
for (auto* member : struct_ty->Members()) {
|
||||||
return true;
|
if (ContainsFragDepth(member->Declaration()->attributes)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
ClampFragDepth::ClampFragDepth() = default;
|
ClampFragDepth::ClampFragDepth() = default;
|
||||||
ClampFragDepth::~ClampFragDepth() = default;
|
ClampFragDepth::~ClampFragDepth() = default;
|
||||||
|
|
||||||
Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&, DataMap&) const {
|
Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&, DataMap&) const {
|
||||||
ProgramBuilder b;
|
return State{src}.Run();
|
||||||
CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
|
|
||||||
|
|
||||||
// Abort on any use of push constants in the module.
|
|
||||||
for (auto* global : src->AST().GlobalVariables()) {
|
|
||||||
if (auto* var = global->As<ast::Var>()) {
|
|
||||||
auto* v = src->Sem().Get(var);
|
|
||||||
if (TINT_UNLIKELY(v->AddressSpace() == builtin::AddressSpace::kPushConstant)) {
|
|
||||||
TINT_ICE(Transform, b.Diagnostics())
|
|
||||||
<< "ClampFragDepth doesn't know how to handle module that already use push "
|
|
||||||
"constants";
|
|
||||||
return Program(std::move(b));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ShouldRun(src)) {
|
|
||||||
return SkipTransform;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& sem = src->Sem();
|
|
||||||
auto& sym = src->Symbols();
|
|
||||||
|
|
||||||
// At least one entry-point needs clamping. Add the following to the module:
|
|
||||||
//
|
|
||||||
// enable chromium_experimental_push_constant;
|
|
||||||
//
|
|
||||||
// struct FragDepthClampArgs {
|
|
||||||
// min : f32,
|
|
||||||
// max : f32,
|
|
||||||
// }
|
|
||||||
// var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
|
|
||||||
//
|
|
||||||
// fn clamp_frag_depth(v : f32) -> f32 {
|
|
||||||
// return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
|
|
||||||
// }
|
|
||||||
b.Enable(builtin::Extension::kChromiumExperimentalPushConstant);
|
|
||||||
|
|
||||||
b.Structure(b.Symbols().New("FragDepthClampArgs"),
|
|
||||||
utils::Vector{b.Member("min", b.ty.f32()), b.Member("max", b.ty.f32())});
|
|
||||||
|
|
||||||
auto args_sym = b.Symbols().New("frag_depth_clamp_args");
|
|
||||||
b.GlobalVar(args_sym, b.ty("FragDepthClampArgs"), builtin::AddressSpace::kPushConstant);
|
|
||||||
|
|
||||||
auto base_fn_sym = b.Symbols().New("clamp_frag_depth");
|
|
||||||
b.Func(base_fn_sym, utils::Vector{b.Param("v", b.ty.f32())}, b.ty.f32(),
|
|
||||||
utils::Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(args_sym, "min"),
|
|
||||||
b.MemberAccessor(args_sym, "max")))});
|
|
||||||
|
|
||||||
// If true, the currently cloned function returns frag depth directly as a scalar
|
|
||||||
bool returns_frag_depth_as_value = false;
|
|
||||||
|
|
||||||
// If valid, the currently cloned function returns frag depth in a struct
|
|
||||||
// The symbol is the name of the helper function to apply the depth clamping.
|
|
||||||
Symbol returns_frag_depth_as_struct_helper;
|
|
||||||
|
|
||||||
// Map of io struct to helper function to return the structure with the depth clamping applied.
|
|
||||||
utils::Hashmap<const ast::Struct*, Symbol, 4u> io_structs_clamp_helpers;
|
|
||||||
|
|
||||||
// Register a callback that will be called for each visted AST function.
|
|
||||||
// This call wraps the cloning of the function's statements, and will assign to
|
|
||||||
// `returns_frag_depth_as_value` or `returns_frag_depth_as_struct_helper` if the function's
|
|
||||||
// return value requires depth clamping.
|
|
||||||
ctx.ReplaceAll([&](const ast::Function* fn) {
|
|
||||||
if (fn->PipelineStage() != ast::PipelineStage::kFragment) {
|
|
||||||
return ctx.CloneWithoutTransform(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReturnsFragDepthAsValue(fn)) {
|
|
||||||
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_value, true);
|
|
||||||
return ctx.CloneWithoutTransform(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReturnsFragDepthInStruct(sem, fn)) {
|
|
||||||
// At most once per I/O struct, add the conversion function:
|
|
||||||
//
|
|
||||||
// fn clamp_frag_depth_S(s : S) -> S {
|
|
||||||
// return S(s.first, s.second, clamp_frag_depth(s.frag_depth), s.last);
|
|
||||||
// }
|
|
||||||
auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()->Declaration();
|
|
||||||
auto helper = io_structs_clamp_helpers.GetOrCreate(struct_ty, [&] {
|
|
||||||
auto return_ty = fn->return_type;
|
|
||||||
auto fn_sym =
|
|
||||||
b.Symbols().New("clamp_frag_depth_" + sym.NameFor(struct_ty->name->symbol));
|
|
||||||
|
|
||||||
utils::Vector<const ast::Expression*, 8u> initializer_args;
|
|
||||||
for (auto* member : struct_ty->members) {
|
|
||||||
const ast::Expression* arg =
|
|
||||||
b.MemberAccessor("s", ctx.Clone(member->name->symbol));
|
|
||||||
if (ContainsFragDepth(member->attributes)) {
|
|
||||||
arg = b.Call(base_fn_sym, arg);
|
|
||||||
}
|
|
||||||
initializer_args.Push(arg);
|
|
||||||
}
|
|
||||||
utils::Vector params{b.Param("s", ctx.Clone(return_ty))};
|
|
||||||
utils::Vector body{
|
|
||||||
b.Return(b.Call(ctx.Clone(return_ty), std::move(initializer_args))),
|
|
||||||
};
|
|
||||||
b.Func(fn_sym, params, ctx.Clone(return_ty), body);
|
|
||||||
return fn_sym;
|
|
||||||
});
|
|
||||||
|
|
||||||
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_struct_helper, helper);
|
|
||||||
return ctx.CloneWithoutTransform(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.CloneWithoutTransform(fn);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Replace the return statements `return expr` with `return clamp_frag_depth(expr)`.
|
|
||||||
ctx.ReplaceAll([&](const ast::ReturnStatement* stmt) -> const ast::ReturnStatement* {
|
|
||||||
if (returns_frag_depth_as_value) {
|
|
||||||
return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value)));
|
|
||||||
}
|
|
||||||
if (returns_frag_depth_as_struct_helper.IsValid()) {
|
|
||||||
return b.Return(stmt->source,
|
|
||||||
b.Call(returns_frag_depth_as_struct_helper, ctx.Clone(stmt->value)));
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.Clone();
|
|
||||||
return Program(std::move(b));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::transform
|
} // namespace tint::transform
|
||||||
|
|
|
@ -65,6 +65,9 @@ class ClampFragDepth final : public Castable<ClampFragDepth, Transform> {
|
||||||
ApplyResult Apply(const Program* program,
|
ApplyResult Apply(const Program* program,
|
||||||
const DataMap& inputs,
|
const DataMap& inputs,
|
||||||
DataMap& outputs) const override;
|
DataMap& outputs) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct State;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::transform
|
} // namespace tint::transform
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
#include "src/tint/sem/member_accessor_expression.h"
|
#include "src/tint/sem/member_accessor_expression.h"
|
||||||
|
@ -88,7 +89,7 @@ Transform::ApplyResult FirstIndexOffset::Apply(const Program* src,
|
||||||
if (auto* var = node->As<ast::Variable>()) {
|
if (auto* var = node->As<ast::Variable>()) {
|
||||||
for (auto* attr : var->attributes) {
|
for (auto* attr : var->attributes) {
|
||||||
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
||||||
builtin::BuiltinValue builtin = builtin_attr->builtin;
|
builtin::BuiltinValue builtin = src->Sem().Get(builtin_attr)->Value();
|
||||||
if (builtin == builtin::BuiltinValue::kVertexIndex) {
|
if (builtin == builtin::BuiltinValue::kVertexIndex) {
|
||||||
auto* sem_var = ctx.src->Sem().Get(var);
|
auto* sem_var = ctx.src->Sem().Get(var);
|
||||||
builtin_vars.emplace(sem_var, kFirstVertexName);
|
builtin_vars.emplace(sem_var, kFirstVertexName);
|
||||||
|
@ -105,7 +106,7 @@ Transform::ApplyResult FirstIndexOffset::Apply(const Program* src,
|
||||||
if (auto* member = node->As<ast::StructMember>()) {
|
if (auto* member = node->As<ast::StructMember>()) {
|
||||||
for (auto* attr : member->attributes) {
|
for (auto* attr : member->attributes) {
|
||||||
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
||||||
builtin::BuiltinValue builtin = builtin_attr->builtin;
|
builtin::BuiltinValue builtin = src->Sem().Get(builtin_attr)->Value();
|
||||||
if (builtin == builtin::BuiltinValue::kVertexIndex) {
|
if (builtin == builtin::BuiltinValue::kVertexIndex) {
|
||||||
auto* sem_mem = ctx.src->Sem().Get(member);
|
auto* sem_mem = ctx.src->Sem().Get(member);
|
||||||
builtin_members.emplace(sem_mem, kFirstVertexName);
|
builtin_members.emplace(sem_mem, kFirstVertexName);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
#include "src/tint/transform/canonicalize_entry_point_io.h"
|
#include "src/tint/transform/canonicalize_entry_point_io.h"
|
||||||
|
@ -33,7 +34,7 @@ namespace {
|
||||||
bool ShouldRun(const Program* program) {
|
bool ShouldRun(const Program* program) {
|
||||||
for (auto* node : program->ASTNodes().Objects()) {
|
for (auto* node : program->ASTNodes().Objects()) {
|
||||||
if (auto* attr = node->As<ast::BuiltinAttribute>()) {
|
if (auto* attr = node->As<ast::BuiltinAttribute>()) {
|
||||||
if (attr->builtin == builtin::BuiltinValue::kNumWorkgroups) {
|
if (program->Sem().Get(attr)->Value() == builtin::BuiltinValue::kNumWorkgroups) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +101,8 @@ Transform::ApplyResult NumWorkgroupsFromUniform::Apply(const Program* src,
|
||||||
for (auto* member : str->Members()) {
|
for (auto* member : str->Members()) {
|
||||||
auto* builtin =
|
auto* builtin =
|
||||||
ast::GetAttribute<ast::BuiltinAttribute>(member->Declaration()->attributes);
|
ast::GetAttribute<ast::BuiltinAttribute>(member->Declaration()->attributes);
|
||||||
if (!builtin || builtin->builtin != builtin::BuiltinValue::kNumWorkgroups) {
|
if (!builtin ||
|
||||||
|
src->Sem().Get(builtin)->Value() != builtin::BuiltinValue::kNumWorkgroups) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "src/tint/ast/assignment_statement.h"
|
#include "src/tint/ast/assignment_statement.h"
|
||||||
#include "src/tint/ast/bitcast_expression.h"
|
#include "src/tint/ast/bitcast_expression.h"
|
||||||
#include "src/tint/ast/variable_decl_statement.h"
|
#include "src/tint/ast/variable_decl_statement.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/sem/variable.h"
|
#include "src/tint/sem/variable.h"
|
||||||
#include "src/tint/utils/compiler_macros.h"
|
#include "src/tint/utils/compiler_macros.h"
|
||||||
|
@ -773,17 +774,18 @@ struct VertexPulling::State {
|
||||||
}
|
}
|
||||||
location_info[sem->Location().value()] = info;
|
location_info[sem->Location().value()] = info;
|
||||||
} else {
|
} else {
|
||||||
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes);
|
auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes);
|
||||||
if (TINT_UNLIKELY(!builtin)) {
|
if (TINT_UNLIKELY(!builtin_attr)) {
|
||||||
TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
|
TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto builtin = src->Sem().Get(builtin_attr)->Value();
|
||||||
// Check for existing vertex_index and instance_index builtins.
|
// Check for existing vertex_index and instance_index builtins.
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kVertexIndex) {
|
if (builtin == builtin::BuiltinValue::kVertexIndex) {
|
||||||
vertex_index_expr = [this, param]() {
|
vertex_index_expr = [this, param]() {
|
||||||
return b.Expr(ctx.Clone(param->name->symbol));
|
return b.Expr(ctx.Clone(param->name->symbol));
|
||||||
};
|
};
|
||||||
} else if (builtin->builtin == builtin::BuiltinValue::kInstanceIndex) {
|
} else if (builtin == builtin::BuiltinValue::kInstanceIndex) {
|
||||||
instance_index_expr = [this, param]() {
|
instance_index_expr = [this, param]() {
|
||||||
return b.Expr(ctx.Clone(param->name->symbol));
|
return b.Expr(ctx.Clone(param->name->symbol));
|
||||||
};
|
};
|
||||||
|
@ -826,15 +828,16 @@ struct VertexPulling::State {
|
||||||
location_info[sem->Location().value()] = info;
|
location_info[sem->Location().value()] = info;
|
||||||
has_locations = true;
|
has_locations = true;
|
||||||
} else {
|
} else {
|
||||||
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);
|
auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);
|
||||||
if (TINT_UNLIKELY(!builtin)) {
|
if (TINT_UNLIKELY(!builtin_attr)) {
|
||||||
TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
|
TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto builtin = src->Sem().Get(builtin_attr)->Value();
|
||||||
// Check for existing vertex_index and instance_index builtins.
|
// Check for existing vertex_index and instance_index builtins.
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kVertexIndex) {
|
if (builtin == builtin::BuiltinValue::kVertexIndex) {
|
||||||
vertex_index_expr = member_expr;
|
vertex_index_expr = member_expr;
|
||||||
} else if (builtin->builtin == builtin::BuiltinValue::kInstanceIndex) {
|
} else if (builtin == builtin::BuiltinValue::kInstanceIndex) {
|
||||||
instance_index_expr = member_expr;
|
instance_index_expr = member_expr;
|
||||||
}
|
}
|
||||||
members_to_clone.Push(member);
|
members_to_clone.Push(member);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/tint/ast/workgroup_attribute.h"
|
#include "src/tint/ast/workgroup_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/sem/function.h"
|
#include "src/tint/sem/function.h"
|
||||||
#include "src/tint/sem/variable.h"
|
#include "src/tint/sem/variable.h"
|
||||||
|
@ -159,8 +160,9 @@ struct ZeroInitWorkgroupMemory::State {
|
||||||
// parameter
|
// parameter
|
||||||
std::function<const ast::Expression*()> local_index;
|
std::function<const ast::Expression*()> local_index;
|
||||||
for (auto* param : fn->params) {
|
for (auto* param : fn->params) {
|
||||||
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
|
if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
|
auto builtin = sem.Get(builtin_attr)->Value();
|
||||||
|
if (builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
|
||||||
local_index = [=] { return b.Expr(ctx.Clone(param->name->symbol)); };
|
local_index = [=] { return b.Expr(ctx.Clone(param->name->symbol)); };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -168,9 +170,10 @@ struct ZeroInitWorkgroupMemory::State {
|
||||||
|
|
||||||
if (auto* str = sem.Get(param)->Type()->As<sem::Struct>()) {
|
if (auto* str = sem.Get(param)->Type()->As<sem::Struct>()) {
|
||||||
for (auto* member : str->Members()) {
|
for (auto* member : str->Members()) {
|
||||||
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
|
if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(
|
||||||
member->Declaration()->attributes)) {
|
member->Declaration()->attributes)) {
|
||||||
if (builtin->builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
|
auto builtin = sem.Get(builtin_attr)->Value();
|
||||||
|
if (builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
|
||||||
local_index = [=] {
|
local_index = [=] {
|
||||||
auto* param_expr = b.Expr(ctx.Clone(param->name->symbol));
|
auto* param_expr = b.Expr(ctx.Clone(param->name->symbol));
|
||||||
auto* member_name = ctx.Clone(member->Declaration()->name);
|
auto* member_name = ctx.Clone(member->Declaration()->name);
|
||||||
|
@ -184,10 +187,9 @@ struct ZeroInitWorkgroupMemory::State {
|
||||||
}
|
}
|
||||||
if (!local_index) {
|
if (!local_index) {
|
||||||
// No existing local index parameter. Append one to the entry point.
|
// No existing local index parameter. Append one to the entry point.
|
||||||
auto* param = b.Param(b.Symbols().New("local_invocation_index"), b.ty.u32(),
|
auto param_name = b.Symbols().New("local_invocation_index");
|
||||||
utils::Vector{
|
auto* local_invocation_index = b.Builtin(builtin::BuiltinValue::kLocalInvocationIndex);
|
||||||
b.Builtin(builtin::BuiltinValue::kLocalInvocationIndex),
|
auto* param = b.Param(param_name, b.ty.u32(), utils::Vector{local_invocation_index});
|
||||||
});
|
|
||||||
ctx.InsertBack(fn->params, param);
|
ctx.InsertBack(fn->params, param);
|
||||||
local_index = [=] { return b.Expr(param->name->symbol); };
|
local_index = [=] { return b.Expr(param->name->symbol); };
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,14 +192,14 @@ struct EnumSet {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @returns an read-only iterator to the beginning of the set
|
/// @returns an read-only iterator to the beginning of the set
|
||||||
Iterator begin() {
|
Iterator begin() const {
|
||||||
auto it = Iterator{set, -1};
|
auto it = Iterator{set, -1};
|
||||||
++it; // Move to first set bit
|
++it; // Move to first set bit
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns an iterator to the beginning of the set
|
/// @returns an iterator to the beginning of the set
|
||||||
Iterator end() { return Iterator{set, Iterator::kEnd}; }
|
Iterator end() const { return Iterator{set, Iterator::kEnd}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint64_t Bit(Enum value) {
|
static constexpr uint64_t Bit(Enum value) {
|
||||||
|
|
|
@ -2155,9 +2155,10 @@ bool GeneratorImpl::EmitWorkgroupVariable(const sem::Variable* var) {
|
||||||
bool GeneratorImpl::EmitIOVariable(const sem::GlobalVariable* var) {
|
bool GeneratorImpl::EmitIOVariable(const sem::GlobalVariable* var) {
|
||||||
auto* decl = var->Declaration();
|
auto* decl = var->Declaration();
|
||||||
|
|
||||||
if (auto* b = ast::GetAttribute<ast::BuiltinAttribute>(decl->attributes)) {
|
if (auto* attr = ast::GetAttribute<ast::BuiltinAttribute>(decl->attributes)) {
|
||||||
|
auto builtin = program_->Sem().Get(attr)->Value();
|
||||||
// Use of gl_SampleID requires the GL_OES_sample_variables extension
|
// Use of gl_SampleID requires the GL_OES_sample_variables extension
|
||||||
if (RequiresOESSampleVariables(b->builtin)) {
|
if (RequiresOESSampleVariables(builtin)) {
|
||||||
requires_oes_sample_variables_ = true;
|
requires_oes_sample_variables_ = true;
|
||||||
}
|
}
|
||||||
// Do not emit builtin (gl_) variables.
|
// Do not emit builtin (gl_) variables.
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "src/tint/ast/return_statement.h"
|
#include "src/tint/ast/return_statement.h"
|
||||||
#include "src/tint/ast/switch_statement.h"
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ast/unary_op_expression.h"
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/scope_stack.h"
|
#include "src/tint/scope_stack.h"
|
||||||
#include "src/tint/transform/decompose_memory_access.h"
|
#include "src/tint/transform/decompose_memory_access.h"
|
||||||
|
|
|
@ -4210,8 +4210,9 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
|
||||||
} else {
|
} else {
|
||||||
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
|
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
|
||||||
}
|
}
|
||||||
} else if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
|
} else if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
|
||||||
auto name = builtin_to_attribute(builtin->builtin);
|
auto builtin = program_->Sem().Get(builtin_attr)->Value();
|
||||||
|
auto name = builtin_to_attribute(builtin);
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
|
diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "src/tint/ast/return_statement.h"
|
#include "src/tint/ast/return_statement.h"
|
||||||
#include "src/tint/ast/switch_statement.h"
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ast/unary_op_expression.h"
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/scope_stack.h"
|
#include "src/tint/scope_stack.h"
|
||||||
#include "src/tint/sem/binding_point.h"
|
#include "src/tint/sem/binding_point.h"
|
||||||
|
|
|
@ -2077,14 +2077,15 @@ bool GeneratorImpl::EmitEntryPointFunction(const ast::Function* func) {
|
||||||
auto& attrs = param->attributes;
|
auto& attrs = param->attributes;
|
||||||
bool builtin_found = false;
|
bool builtin_found = false;
|
||||||
for (auto* attr : attrs) {
|
for (auto* attr : attrs) {
|
||||||
auto* builtin = attr->As<ast::BuiltinAttribute>();
|
auto* builtin_attr = attr->As<ast::BuiltinAttribute>();
|
||||||
if (!builtin) {
|
if (!builtin_attr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto builtin = program_->Sem().Get(builtin_attr)->Value();
|
||||||
|
|
||||||
builtin_found = true;
|
builtin_found = true;
|
||||||
|
|
||||||
auto name = builtin_to_attribute(builtin->builtin);
|
auto name = builtin_to_attribute(builtin);
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
|
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
|
||||||
return false;
|
return false;
|
||||||
|
@ -2854,8 +2855,9 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
|
||||||
for (auto* attr : decl->attributes) {
|
for (auto* attr : decl->attributes) {
|
||||||
bool ok = Switch(
|
bool ok = Switch(
|
||||||
attr,
|
attr,
|
||||||
[&](const ast::BuiltinAttribute* builtin) {
|
[&](const ast::BuiltinAttribute* builtin_attr) {
|
||||||
auto name = builtin_to_attribute(builtin->builtin);
|
auto builtin = program_->Sem().Get(builtin_attr)->Value();
|
||||||
|
auto name = builtin_to_attribute(builtin);
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
|
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "src/tint/ast/return_statement.h"
|
#include "src/tint/ast/return_statement.h"
|
||||||
#include "src/tint/ast/switch_statement.h"
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ast/unary_op_expression.h"
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program.h"
|
#include "src/tint/program.h"
|
||||||
#include "src/tint/scope_stack.h"
|
#include "src/tint/scope_stack.h"
|
||||||
#include "src/tint/sem/struct.h"
|
#include "src/tint/sem/struct.h"
|
||||||
|
|
|
@ -548,8 +548,9 @@ bool Builder::GenerateExecutionModes(const ast::Function* func, uint32_t id) {
|
||||||
Operand(wgsize[0].value()), Operand(wgsize[1].value()), Operand(wgsize[2].value())});
|
Operand(wgsize[0].value()), Operand(wgsize[1].value()), Operand(wgsize[2].value())});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto builtin : func_sem->TransitivelyReferencedBuiltinVariables()) {
|
for (auto it : func_sem->TransitivelyReferencedBuiltinVariables()) {
|
||||||
if (builtin.second->builtin == builtin::BuiltinValue::kFragDepth) {
|
auto builtin = builder_.Sem().Get(it.second)->Value();
|
||||||
|
if (builtin == builtin::BuiltinValue::kFragDepth) {
|
||||||
push_execution_mode(spv::Op::OpExecutionMode,
|
push_execution_mode(spv::Op::OpExecutionMode,
|
||||||
{Operand(id), U32Operand(SpvExecutionModeDepthReplacing)});
|
{Operand(id), U32Operand(SpvExecutionModeDepthReplacing)});
|
||||||
}
|
}
|
||||||
|
@ -837,10 +838,11 @@ bool Builder::GenerateGlobalVariable(const ast::Variable* v) {
|
||||||
for (auto* attr : v->attributes) {
|
for (auto* attr : v->attributes) {
|
||||||
bool ok = Switch(
|
bool ok = Switch(
|
||||||
attr,
|
attr,
|
||||||
[&](const ast::BuiltinAttribute* builtin) {
|
[&](const ast::BuiltinAttribute* builtin_attr) {
|
||||||
|
auto builtin = builder_.Sem().Get(builtin_attr)->Value();
|
||||||
push_annot(spv::Op::OpDecorate,
|
push_annot(spv::Op::OpDecorate,
|
||||||
{Operand(var_id), U32Operand(SpvDecorationBuiltIn),
|
{Operand(var_id), U32Operand(SpvDecorationBuiltIn),
|
||||||
U32Operand(ConvertBuiltin(builtin->builtin, sem->AddressSpace()))});
|
U32Operand(ConvertBuiltin(builtin, sem->AddressSpace()))});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[&](const ast::LocationAttribute*) {
|
[&](const ast::LocationAttribute*) {
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "src/tint/ast/switch_statement.h"
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ast/unary_op_expression.h"
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
#include "src/tint/ast/variable_decl_statement.h"
|
#include "src/tint/ast/variable_decl_statement.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/scope_stack.h"
|
#include "src/tint/scope_stack.h"
|
||||||
#include "src/tint/sem/builtin.h"
|
#include "src/tint/sem/builtin.h"
|
||||||
|
|
|
@ -564,7 +564,11 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[&](const ast::BuiltinAttribute* builtin) {
|
[&](const ast::BuiltinAttribute* builtin) {
|
||||||
out << "builtin(" << builtin->builtin << ")";
|
out << "builtin(";
|
||||||
|
if (!EmitExpression(out, builtin->builtin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << ")";
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[&](const ast::DiagnosticAttribute* diagnostic) {
|
[&](const ast::DiagnosticAttribute* diagnostic) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "src/tint/ast/stage_attribute.h"
|
#include "src/tint/ast/stage_attribute.h"
|
||||||
#include "src/tint/ast/variable_decl_statement.h"
|
#include "src/tint/ast/variable_decl_statement.h"
|
||||||
#include "src/tint/ast/workgroup_attribute.h"
|
#include "src/tint/ast/workgroup_attribute.h"
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/writer/wgsl/test_helper.h"
|
#include "src/tint/writer/wgsl/test_helper.h"
|
||||||
|
|
||||||
using namespace tint::number_suffixes; // NOLINT
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/tint/builtin/builtin_value.h"
|
||||||
#include "src/tint/type/depth_texture.h"
|
#include "src/tint/type/depth_texture.h"
|
||||||
#include "src/tint/type/multisampled_texture.h"
|
#include "src/tint/type/multisampled_texture.h"
|
||||||
#include "src/tint/type/sampled_texture.h"
|
#include "src/tint/type/sampled_texture.h"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
static float3 position = float3(0.0f, 0.0f, 0.0f);
|
static float3 position_1 = float3(0.0f, 0.0f, 0.0f);
|
||||||
cbuffer cbuffer_x_14 : register(b2, space2) {
|
cbuffer cbuffer_x_14 : register(b2, space2) {
|
||||||
uint4 x_14[17];
|
uint4 x_14[17];
|
||||||
};
|
};
|
||||||
|
@ -18,13 +18,13 @@ float4x4 tint_symbol_4(uint4 buffer[17], uint offset) {
|
||||||
void main_1() {
|
void main_1() {
|
||||||
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
float3 p = float3(0.0f, 0.0f, 0.0f);
|
float3 p = float3(0.0f, 0.0f, 0.0f);
|
||||||
const float3 x_13 = position;
|
const float3 x_13 = position_1;
|
||||||
q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
|
q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
|
||||||
const float4 x_21 = q;
|
const float4 x_21 = q;
|
||||||
p = float3(x_21.x, x_21.y, x_21.z);
|
p = float3(x_21.x, x_21.y, x_21.z);
|
||||||
const float x_27 = p.x;
|
const float x_27 = p.x;
|
||||||
const float x_41 = asfloat(x_14[13].x);
|
const float x_41 = asfloat(x_14[13].x);
|
||||||
const float x_45 = position.y;
|
const float x_45 = position_1.y;
|
||||||
const float x_49 = asfloat(x_14[4].x);
|
const float x_49 = asfloat(x_14[4].x);
|
||||||
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
||||||
const float x_55 = p.y;
|
const float x_55 = p.y;
|
||||||
|
@ -45,7 +45,7 @@ struct main_out {
|
||||||
float2 vUV_1;
|
float2 vUV_1;
|
||||||
};
|
};
|
||||||
struct tint_symbol_1 {
|
struct tint_symbol_1 {
|
||||||
float3 position_param : TEXCOORD0;
|
float3 position_1_param : TEXCOORD0;
|
||||||
float3 normal_param : TEXCOORD1;
|
float3 normal_param : TEXCOORD1;
|
||||||
float2 uv_param : TEXCOORD2;
|
float2 uv_param : TEXCOORD2;
|
||||||
};
|
};
|
||||||
|
@ -54,8 +54,8 @@ struct tint_symbol_2 {
|
||||||
float4 gl_Position : SV_Position;
|
float4 gl_Position : SV_Position;
|
||||||
};
|
};
|
||||||
|
|
||||||
main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param) {
|
main_out main_inner(float3 position_1_param, float2 uv_param, float3 normal_param) {
|
||||||
position = position_param;
|
position_1 = position_1_param;
|
||||||
uv = uv_param;
|
uv = uv_param;
|
||||||
normal = normal_param;
|
normal = normal_param;
|
||||||
main_1();
|
main_1();
|
||||||
|
@ -64,7 +64,7 @@ main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param)
|
||||||
}
|
}
|
||||||
|
|
||||||
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
|
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
|
||||||
const main_out inner_result = main_inner(tint_symbol.position_param, tint_symbol.uv_param, tint_symbol.normal_param);
|
const main_out inner_result = main_inner(tint_symbol.position_1_param, tint_symbol.uv_param, tint_symbol.normal_param);
|
||||||
tint_symbol_2 wrapper_result = (tint_symbol_2)0;
|
tint_symbol_2 wrapper_result = (tint_symbol_2)0;
|
||||||
wrapper_result.gl_Position = inner_result.gl_Position;
|
wrapper_result.gl_Position = inner_result.gl_Position;
|
||||||
wrapper_result.vUV_1 = inner_result.vUV_1;
|
wrapper_result.vUV_1 = inner_result.vUV_1;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
static float3 position = float3(0.0f, 0.0f, 0.0f);
|
static float3 position_1 = float3(0.0f, 0.0f, 0.0f);
|
||||||
cbuffer cbuffer_x_14 : register(b2, space2) {
|
cbuffer cbuffer_x_14 : register(b2, space2) {
|
||||||
uint4 x_14[17];
|
uint4 x_14[17];
|
||||||
};
|
};
|
||||||
|
@ -18,13 +18,13 @@ float4x4 tint_symbol_4(uint4 buffer[17], uint offset) {
|
||||||
void main_1() {
|
void main_1() {
|
||||||
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
float3 p = float3(0.0f, 0.0f, 0.0f);
|
float3 p = float3(0.0f, 0.0f, 0.0f);
|
||||||
const float3 x_13 = position;
|
const float3 x_13 = position_1;
|
||||||
q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
|
q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
|
||||||
const float4 x_21 = q;
|
const float4 x_21 = q;
|
||||||
p = float3(x_21.x, x_21.y, x_21.z);
|
p = float3(x_21.x, x_21.y, x_21.z);
|
||||||
const float x_27 = p.x;
|
const float x_27 = p.x;
|
||||||
const float x_41 = asfloat(x_14[13].x);
|
const float x_41 = asfloat(x_14[13].x);
|
||||||
const float x_45 = position.y;
|
const float x_45 = position_1.y;
|
||||||
const float x_49 = asfloat(x_14[4].x);
|
const float x_49 = asfloat(x_14[4].x);
|
||||||
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
||||||
const float x_55 = p.y;
|
const float x_55 = p.y;
|
||||||
|
@ -45,7 +45,7 @@ struct main_out {
|
||||||
float2 vUV_1;
|
float2 vUV_1;
|
||||||
};
|
};
|
||||||
struct tint_symbol_1 {
|
struct tint_symbol_1 {
|
||||||
float3 position_param : TEXCOORD0;
|
float3 position_1_param : TEXCOORD0;
|
||||||
float3 normal_param : TEXCOORD1;
|
float3 normal_param : TEXCOORD1;
|
||||||
float2 uv_param : TEXCOORD2;
|
float2 uv_param : TEXCOORD2;
|
||||||
};
|
};
|
||||||
|
@ -54,8 +54,8 @@ struct tint_symbol_2 {
|
||||||
float4 gl_Position : SV_Position;
|
float4 gl_Position : SV_Position;
|
||||||
};
|
};
|
||||||
|
|
||||||
main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param) {
|
main_out main_inner(float3 position_1_param, float2 uv_param, float3 normal_param) {
|
||||||
position = position_param;
|
position_1 = position_1_param;
|
||||||
uv = uv_param;
|
uv = uv_param;
|
||||||
normal = normal_param;
|
normal = normal_param;
|
||||||
main_1();
|
main_1();
|
||||||
|
@ -64,7 +64,7 @@ main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param)
|
||||||
}
|
}
|
||||||
|
|
||||||
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
|
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
|
||||||
const main_out inner_result = main_inner(tint_symbol.position_param, tint_symbol.uv_param, tint_symbol.normal_param);
|
const main_out inner_result = main_inner(tint_symbol.position_1_param, tint_symbol.uv_param, tint_symbol.normal_param);
|
||||||
tint_symbol_2 wrapper_result = (tint_symbol_2)0;
|
tint_symbol_2 wrapper_result = (tint_symbol_2)0;
|
||||||
wrapper_result.gl_Position = inner_result.gl_Position;
|
wrapper_result.gl_Position = inner_result.gl_Position;
|
||||||
wrapper_result.vUV_1 = inner_result.vUV_1;
|
wrapper_result.vUV_1 = inner_result.vUV_1;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#version 310 es
|
#version 310 es
|
||||||
|
|
||||||
layout(location = 0) in vec3 position_param_1;
|
layout(location = 0) in vec3 position_1_param_1;
|
||||||
layout(location = 2) in vec2 uv_param_1;
|
layout(location = 2) in vec2 uv_param_1;
|
||||||
layout(location = 1) in vec3 normal_param_1;
|
layout(location = 1) in vec3 normal_param_1;
|
||||||
layout(location = 0) out vec2 vUV_1_1;
|
layout(location = 0) out vec2 vUV_1_1;
|
||||||
|
@ -21,7 +21,7 @@ struct LeftOver {
|
||||||
strided_arr test[4];
|
strided_arr test[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
vec3 position = vec3(0.0f, 0.0f, 0.0f);
|
vec3 position_1 = vec3(0.0f, 0.0f, 0.0f);
|
||||||
layout(binding = 2, std140) uniform x_14_block_ubo {
|
layout(binding = 2, std140) uniform x_14_block_ubo {
|
||||||
LeftOver inner;
|
LeftOver inner;
|
||||||
} x_14;
|
} x_14;
|
||||||
|
@ -33,13 +33,13 @@ vec4 tint_symbol = vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
void main_1() {
|
void main_1() {
|
||||||
vec4 q = vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
vec4 q = vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
vec3 p = vec3(0.0f, 0.0f, 0.0f);
|
vec3 p = vec3(0.0f, 0.0f, 0.0f);
|
||||||
vec3 x_13 = position;
|
vec3 x_13 = position_1;
|
||||||
q = vec4(x_13.x, x_13.y, x_13.z, 1.0f);
|
q = vec4(x_13.x, x_13.y, x_13.z, 1.0f);
|
||||||
vec4 x_21 = q;
|
vec4 x_21 = q;
|
||||||
p = vec3(x_21.x, x_21.y, x_21.z);
|
p = vec3(x_21.x, x_21.y, x_21.z);
|
||||||
float x_27 = p.x;
|
float x_27 = p.x;
|
||||||
float x_41 = x_14.inner.test[0].el;
|
float x_41 = x_14.inner.test[0].el;
|
||||||
float x_45 = position.y;
|
float x_45 = position_1.y;
|
||||||
float x_49 = x_14.inner.time;
|
float x_49 = x_14.inner.time;
|
||||||
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
||||||
float x_55 = p.y;
|
float x_55 = p.y;
|
||||||
|
@ -60,8 +60,8 @@ struct main_out {
|
||||||
vec2 vUV_1;
|
vec2 vUV_1;
|
||||||
};
|
};
|
||||||
|
|
||||||
main_out tint_symbol_1(vec3 position_param, vec2 uv_param, vec3 normal_param) {
|
main_out tint_symbol_1(vec3 position_1_param, vec2 uv_param, vec3 normal_param) {
|
||||||
position = position_param;
|
position_1 = position_1_param;
|
||||||
uv = uv_param;
|
uv = uv_param;
|
||||||
normal = normal_param;
|
normal = normal_param;
|
||||||
main_1();
|
main_1();
|
||||||
|
@ -71,7 +71,7 @@ main_out tint_symbol_1(vec3 position_param, vec2 uv_param, vec3 normal_param) {
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_PointSize = 1.0;
|
gl_PointSize = 1.0;
|
||||||
main_out inner_result = tint_symbol_1(position_param_1, uv_param_1, normal_param_1);
|
main_out inner_result = tint_symbol_1(position_1_param_1, uv_param_1, normal_param_1);
|
||||||
gl_Position = inner_result.tint_symbol;
|
gl_Position = inner_result.tint_symbol;
|
||||||
vUV_1_1 = inner_result.vUV_1;
|
vUV_1_1 = inner_result.vUV_1;
|
||||||
gl_Position.y = -(gl_Position.y);
|
gl_Position.y = -(gl_Position.y);
|
||||||
|
|
|
@ -58,7 +58,7 @@ struct main_out {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tint_symbol_2 {
|
struct tint_symbol_2 {
|
||||||
float3 position_param [[attribute(0)]];
|
float3 position_1_param [[attribute(0)]];
|
||||||
float3 normal_param [[attribute(1)]];
|
float3 normal_param [[attribute(1)]];
|
||||||
float2 uv_param [[attribute(2)]];
|
float2 uv_param [[attribute(2)]];
|
||||||
};
|
};
|
||||||
|
@ -68,9 +68,9 @@ struct tint_symbol_3 {
|
||||||
float4 gl_Position [[position]];
|
float4 gl_Position [[position]];
|
||||||
};
|
};
|
||||||
|
|
||||||
main_out tint_symbol_inner(float3 position_param, float2 uv_param, float3 normal_param, thread float3* const tint_symbol_10, thread float2* const tint_symbol_11, const constant LeftOver* const tint_symbol_13, thread float4* const tint_symbol_14, thread float2* const tint_symbol_15) {
|
main_out tint_symbol_inner(float3 position_1_param, float2 uv_param, float3 normal_param, thread float3* const tint_symbol_10, thread float2* const tint_symbol_11, const constant LeftOver* const tint_symbol_13, thread float4* const tint_symbol_14, thread float2* const tint_symbol_15) {
|
||||||
thread float3 tint_symbol_12 = 0.0f;
|
thread float3 tint_symbol_12 = 0.0f;
|
||||||
*(tint_symbol_10) = position_param;
|
*(tint_symbol_10) = position_1_param;
|
||||||
*(tint_symbol_11) = uv_param;
|
*(tint_symbol_11) = uv_param;
|
||||||
tint_symbol_12 = normal_param;
|
tint_symbol_12 = normal_param;
|
||||||
main_1(tint_symbol_10, tint_symbol_13, tint_symbol_14, tint_symbol_11, tint_symbol_15);
|
main_1(tint_symbol_10, tint_symbol_13, tint_symbol_14, tint_symbol_11, tint_symbol_15);
|
||||||
|
@ -83,7 +83,7 @@ vertex tint_symbol_3 tint_symbol(const constant LeftOver* tint_symbol_18 [[buffe
|
||||||
thread float2 tint_symbol_17 = 0.0f;
|
thread float2 tint_symbol_17 = 0.0f;
|
||||||
thread float4 tint_symbol_19 = 0.0f;
|
thread float4 tint_symbol_19 = 0.0f;
|
||||||
thread float2 tint_symbol_20 = 0.0f;
|
thread float2 tint_symbol_20 = 0.0f;
|
||||||
main_out const inner_result = tint_symbol_inner(tint_symbol_1.position_param, tint_symbol_1.uv_param, tint_symbol_1.normal_param, &(tint_symbol_16), &(tint_symbol_17), tint_symbol_18, &(tint_symbol_19), &(tint_symbol_20));
|
main_out const inner_result = tint_symbol_inner(tint_symbol_1.position_1_param, tint_symbol_1.uv_param, tint_symbol_1.normal_param, &(tint_symbol_16), &(tint_symbol_17), tint_symbol_18, &(tint_symbol_19), &(tint_symbol_20));
|
||||||
tint_symbol_3 wrapper_result = {};
|
tint_symbol_3 wrapper_result = {};
|
||||||
wrapper_result.gl_Position = inner_result.gl_Position;
|
wrapper_result.gl_Position = inner_result.gl_Position;
|
||||||
wrapper_result.vUV_1 = inner_result.vUV_1;
|
wrapper_result.vUV_1 = inner_result.vUV_1;
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
%76 = OpExtInstImport "GLSL.std.450"
|
%76 = OpExtInstImport "GLSL.std.450"
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
OpEntryPoint Vertex %main "main" %position_param_1 %uv_param_1 %normal_param_1 %gl_Position_1 %vUV_1_1 %vertex_point_size
|
OpEntryPoint Vertex %main "main" %position_1_param_1 %uv_param_1 %normal_param_1 %gl_Position_1 %vUV_1_1 %vertex_point_size
|
||||||
OpName %position_param_1 "position_param_1"
|
OpName %position_1_param_1 "position_1_param_1"
|
||||||
OpName %uv_param_1 "uv_param_1"
|
OpName %uv_param_1 "uv_param_1"
|
||||||
OpName %normal_param_1 "normal_param_1"
|
OpName %normal_param_1 "normal_param_1"
|
||||||
OpName %gl_Position_1 "gl_Position_1"
|
OpName %gl_Position_1 "gl_Position_1"
|
||||||
OpName %vUV_1_1 "vUV_1_1"
|
OpName %vUV_1_1 "vUV_1_1"
|
||||||
OpName %vertex_point_size "vertex_point_size"
|
OpName %vertex_point_size "vertex_point_size"
|
||||||
OpName %position "position"
|
OpName %position_1 "position_1"
|
||||||
OpName %x_14_block "x_14_block"
|
OpName %x_14_block "x_14_block"
|
||||||
OpMemberName %x_14_block 0 "inner"
|
OpMemberName %x_14_block 0 "inner"
|
||||||
OpName %LeftOver "LeftOver"
|
OpName %LeftOver "LeftOver"
|
||||||
|
@ -35,11 +35,11 @@
|
||||||
OpMemberName %main_out 0 "gl_Position"
|
OpMemberName %main_out 0 "gl_Position"
|
||||||
OpMemberName %main_out 1 "vUV_1"
|
OpMemberName %main_out 1 "vUV_1"
|
||||||
OpName %main_inner "main_inner"
|
OpName %main_inner "main_inner"
|
||||||
OpName %position_param "position_param"
|
OpName %position_1_param "position_1_param"
|
||||||
OpName %uv_param "uv_param"
|
OpName %uv_param "uv_param"
|
||||||
OpName %normal_param "normal_param"
|
OpName %normal_param "normal_param"
|
||||||
OpName %main "main"
|
OpName %main "main"
|
||||||
OpDecorate %position_param_1 Location 0
|
OpDecorate %position_1_param_1 Location 0
|
||||||
OpDecorate %uv_param_1 Location 2
|
OpDecorate %uv_param_1 Location 2
|
||||||
OpDecorate %normal_param_1 Location 1
|
OpDecorate %normal_param_1 Location 1
|
||||||
OpDecorate %gl_Position_1 BuiltIn Position
|
OpDecorate %gl_Position_1 BuiltIn Position
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
%float = OpTypeFloat 32
|
%float = OpTypeFloat 32
|
||||||
%v3float = OpTypeVector %float 3
|
%v3float = OpTypeVector %float 3
|
||||||
%_ptr_Input_v3float = OpTypePointer Input %v3float
|
%_ptr_Input_v3float = OpTypePointer Input %v3float
|
||||||
%position_param_1 = OpVariable %_ptr_Input_v3float Input
|
%position_1_param_1 = OpVariable %_ptr_Input_v3float Input
|
||||||
%v2float = OpTypeVector %float 2
|
%v2float = OpTypeVector %float 2
|
||||||
%_ptr_Input_v2float = OpTypePointer Input %v2float
|
%_ptr_Input_v2float = OpTypePointer Input %v2float
|
||||||
%uv_param_1 = OpVariable %_ptr_Input_v2float Input
|
%uv_param_1 = OpVariable %_ptr_Input_v2float Input
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
%vertex_point_size = OpVariable %_ptr_Output_float Output %18
|
%vertex_point_size = OpVariable %_ptr_Output_float Output %18
|
||||||
%_ptr_Private_v3float = OpTypePointer Private %v3float
|
%_ptr_Private_v3float = OpTypePointer Private %v3float
|
||||||
%21 = OpConstantNull %v3float
|
%21 = OpConstantNull %v3float
|
||||||
%position = OpVariable %_ptr_Private_v3float Private %21
|
%position_1 = OpVariable %_ptr_Private_v3float Private %21
|
||||||
%mat4v4float = OpTypeMatrix %v4float 4
|
%mat4v4float = OpTypeMatrix %v4float 4
|
||||||
%uint = OpTypeInt 32 0
|
%uint = OpTypeInt 32 0
|
||||||
%uint_2 = OpConstant %uint 2
|
%uint_2 = OpConstant %uint 2
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
%42 = OpLabel
|
%42 = OpLabel
|
||||||
%q = OpVariable %_ptr_Function_v4float Function %12
|
%q = OpVariable %_ptr_Function_v4float Function %12
|
||||||
%p = OpVariable %_ptr_Function_v3float Function %21
|
%p = OpVariable %_ptr_Function_v3float Function %21
|
||||||
%47 = OpLoad %v3float %position
|
%47 = OpLoad %v3float %position_1
|
||||||
%48 = OpCompositeExtract %float %47 0
|
%48 = OpCompositeExtract %float %47 0
|
||||||
%49 = OpCompositeExtract %float %47 1
|
%49 = OpCompositeExtract %float %47 1
|
||||||
%50 = OpCompositeExtract %float %47 2
|
%50 = OpCompositeExtract %float %47 2
|
||||||
|
@ -139,7 +139,7 @@
|
||||||
%61 = OpLoad %float %60
|
%61 = OpLoad %float %60
|
||||||
%66 = OpAccessChain %_ptr_Uniform_float %x_14 %uint_0 %uint_3 %64 %uint_0
|
%66 = OpAccessChain %_ptr_Uniform_float %x_14 %uint_0 %uint_3 %64 %uint_0
|
||||||
%67 = OpLoad %float %66
|
%67 = OpLoad %float %66
|
||||||
%70 = OpAccessChain %_ptr_Private_float %position %uint_1
|
%70 = OpAccessChain %_ptr_Private_float %position_1 %uint_1
|
||||||
%71 = OpLoad %float %70
|
%71 = OpLoad %float %70
|
||||||
%72 = OpAccessChain %_ptr_Uniform_float %x_14 %uint_0 %uint_1
|
%72 = OpAccessChain %_ptr_Uniform_float %x_14 %uint_0 %uint_1
|
||||||
%73 = OpLoad %float %72
|
%73 = OpLoad %float %72
|
||||||
|
@ -177,11 +177,11 @@
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
%main_inner = OpFunction %main_out None %104
|
%main_inner = OpFunction %main_out None %104
|
||||||
%position_param = OpFunctionParameter %v3float
|
%position_1_param = OpFunctionParameter %v3float
|
||||||
%uv_param = OpFunctionParameter %v2float
|
%uv_param = OpFunctionParameter %v2float
|
||||||
%normal_param = OpFunctionParameter %v3float
|
%normal_param = OpFunctionParameter %v3float
|
||||||
%110 = OpLabel
|
%110 = OpLabel
|
||||||
OpStore %position %position_param
|
OpStore %position_1 %position_1_param
|
||||||
OpStore %uv %uv_param
|
OpStore %uv %uv_param
|
||||||
OpStore %normal %normal_param
|
OpStore %normal %normal_param
|
||||||
%111 = OpFunctionCall %void %main_1
|
%111 = OpFunctionCall %void %main_1
|
||||||
|
@ -192,7 +192,7 @@
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
%main = OpFunction %void None %39
|
%main = OpFunction %void None %39
|
||||||
%116 = OpLabel
|
%116 = OpLabel
|
||||||
%118 = OpLoad %v3float %position_param_1
|
%118 = OpLoad %v3float %position_1_param_1
|
||||||
%119 = OpLoad %v2float %uv_param_1
|
%119 = OpLoad %v2float %uv_param_1
|
||||||
%120 = OpLoad %v3float %normal_param_1
|
%120 = OpLoad %v3float %normal_param_1
|
||||||
%117 = OpFunctionCall %main_out %main_inner %118 %119 %120
|
%117 = OpFunctionCall %main_out %main_inner %118 %119 %120
|
||||||
|
|
|
@ -18,7 +18,7 @@ struct LeftOver {
|
||||||
test : Arr_1,
|
test : Arr_1,
|
||||||
}
|
}
|
||||||
|
|
||||||
var<private> position : vec3<f32>;
|
var<private> position_1 : vec3<f32>;
|
||||||
|
|
||||||
@group(2) @binding(2) var<uniform> x_14 : LeftOver;
|
@group(2) @binding(2) var<uniform> x_14 : LeftOver;
|
||||||
|
|
||||||
|
@ -33,13 +33,13 @@ var<private> gl_Position : vec4<f32>;
|
||||||
fn main_1() {
|
fn main_1() {
|
||||||
var q : vec4<f32>;
|
var q : vec4<f32>;
|
||||||
var p : vec3<f32>;
|
var p : vec3<f32>;
|
||||||
let x_13 : vec3<f32> = position;
|
let x_13 : vec3<f32> = position_1;
|
||||||
q = vec4<f32>(x_13.x, x_13.y, x_13.z, 1.0f);
|
q = vec4<f32>(x_13.x, x_13.y, x_13.z, 1.0f);
|
||||||
let x_21 : vec4<f32> = q;
|
let x_21 : vec4<f32> = q;
|
||||||
p = vec3<f32>(x_21.x, x_21.y, x_21.z);
|
p = vec3<f32>(x_21.x, x_21.y, x_21.z);
|
||||||
let x_27 : f32 = p.x;
|
let x_27 : f32 = p.x;
|
||||||
let x_41 : f32 = x_14.test[0i].el;
|
let x_41 : f32 = x_14.test[0i].el;
|
||||||
let x_45 : f32 = position.y;
|
let x_45 : f32 = position_1.y;
|
||||||
let x_49 : f32 = x_14.time;
|
let x_49 : f32 = x_14.time;
|
||||||
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
|
||||||
let x_55 : f32 = p.y;
|
let x_55 : f32 = p.y;
|
||||||
|
@ -63,8 +63,8 @@ struct main_out {
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn main(@location(0) position_param : vec3<f32>, @location(2) uv_param : vec2<f32>, @location(1) normal_param : vec3<f32>) -> main_out {
|
fn main(@location(0) position_1_param : vec3<f32>, @location(2) uv_param : vec2<f32>, @location(1) normal_param : vec3<f32>) -> main_out {
|
||||||
position = position_param;
|
position_1 = position_1_param;
|
||||||
uv = uv_param;
|
uv = uv_param;
|
||||||
normal = normal_param;
|
normal = normal_param;
|
||||||
main_1();
|
main_1();
|
||||||
|
|
|
@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_25 = idx;
|
const uint x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_25 = idx;
|
const uint x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -10,9 +10,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint x_25 = idx;
|
uint x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -22,9 +22,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % select(rhs, 1u, (rhs == 0u)));
|
return (lhs % select(rhs, 1u, (rhs == 0u)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
|
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint const x_25 = idx;
|
uint const x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
OpName %lhs_0 "lhs"
|
OpName %lhs_0 "lhs"
|
||||||
OpName %rhs_0 "rhs"
|
OpName %rhs_0 "rhs"
|
||||||
OpName %compute_main_inner "compute_main_inner"
|
OpName %compute_main_inner "compute_main_inner"
|
||||||
OpName %local_invocation_index "local_invocation_index"
|
OpName %local_invocation_index_2 "local_invocation_index_2"
|
||||||
OpName %idx "idx"
|
OpName %idx "idx"
|
||||||
OpName %compute_main_1 "compute_main_1"
|
OpName %compute_main_1 "compute_main_1"
|
||||||
OpName %compute_main_inner_1 "compute_main_inner_1"
|
OpName %compute_main_inner_1 "compute_main_inner_1"
|
||||||
|
@ -75,11 +75,11 @@
|
||||||
OpReturnValue %30
|
OpReturnValue %30
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
%compute_main_inner = OpFunction %void None %31
|
%compute_main_inner = OpFunction %void None %31
|
||||||
%local_invocation_index = OpFunctionParameter %uint
|
%local_invocation_index_2 = OpFunctionParameter %uint
|
||||||
%35 = OpLabel
|
%35 = OpLabel
|
||||||
%idx = OpVariable %_ptr_Function_uint Function %6
|
%idx = OpVariable %_ptr_Function_uint Function %6
|
||||||
OpStore %idx %6
|
OpStore %idx %6
|
||||||
OpStore %idx %local_invocation_index
|
OpStore %idx %local_invocation_index_2
|
||||||
OpBranch %38
|
OpBranch %38
|
||||||
%38 = OpLabel
|
%38 = OpLabel
|
||||||
OpLoopMerge %39 %40 None
|
OpLoopMerge %39 %40 None
|
||||||
|
|
|
@ -8,9 +8,9 @@ var<private> local_invocation_index_1 : u32;
|
||||||
|
|
||||||
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
|
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
|
||||||
|
|
||||||
fn compute_main_inner(local_invocation_index : u32) {
|
fn compute_main_inner(local_invocation_index_2 : u32) {
|
||||||
var idx : u32 = 0u;
|
var idx : u32 = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
loop {
|
loop {
|
||||||
let x_25 : u32 = idx;
|
let x_25 : u32 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
static uint local_invocation_index_1 = 0u;
|
static uint local_invocation_index_1 = 0u;
|
||||||
groupshared uint wg[4];
|
groupshared uint wg[4];
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_21 = idx;
|
const uint x_21 = idx;
|
||||||
if (!((x_21 < 4u))) {
|
if (!((x_21 < 4u))) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
static uint local_invocation_index_1 = 0u;
|
static uint local_invocation_index_1 = 0u;
|
||||||
groupshared uint wg[4];
|
groupshared uint wg[4];
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_21 = idx;
|
const uint x_21 = idx;
|
||||||
if (!((x_21 < 4u))) {
|
if (!((x_21 < 4u))) {
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
uint local_invocation_index_1 = 0u;
|
uint local_invocation_index_1 = 0u;
|
||||||
shared uint wg[4];
|
shared uint wg[4];
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint x_21 = idx;
|
uint x_21 = idx;
|
||||||
if (!((x_21 < 4u))) {
|
if (!((x_21 < 4u))) {
|
||||||
|
|
|
@ -14,9 +14,9 @@ struct tint_array {
|
||||||
T elements[N];
|
T elements[N];
|
||||||
};
|
};
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<atomic_uint, 4>* const tint_symbol) {
|
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<atomic_uint, 4>* const tint_symbol) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint const x_21 = idx;
|
uint const x_21 = idx;
|
||||||
if (!((x_21 < 4u))) {
|
if (!((x_21 < 4u))) {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
OpName %local_invocation_index_1 "local_invocation_index_1"
|
OpName %local_invocation_index_1 "local_invocation_index_1"
|
||||||
OpName %wg "wg"
|
OpName %wg "wg"
|
||||||
OpName %compute_main_inner "compute_main_inner"
|
OpName %compute_main_inner "compute_main_inner"
|
||||||
OpName %local_invocation_index "local_invocation_index"
|
OpName %local_invocation_index_2 "local_invocation_index_2"
|
||||||
OpName %idx "idx"
|
OpName %idx "idx"
|
||||||
OpName %compute_main_1 "compute_main_1"
|
OpName %compute_main_1 "compute_main_1"
|
||||||
OpName %compute_main_inner_1 "compute_main_inner_1"
|
OpName %compute_main_inner_1 "compute_main_inner_1"
|
||||||
|
@ -43,11 +43,11 @@
|
||||||
%int_1 = OpConstant %int 1
|
%int_1 = OpConstant %int 1
|
||||||
%45 = OpTypeFunction %void
|
%45 = OpTypeFunction %void
|
||||||
%compute_main_inner = OpFunction %void None %11
|
%compute_main_inner = OpFunction %void None %11
|
||||||
%local_invocation_index = OpFunctionParameter %uint
|
%local_invocation_index_2 = OpFunctionParameter %uint
|
||||||
%15 = OpLabel
|
%15 = OpLabel
|
||||||
%idx = OpVariable %_ptr_Function_uint Function %6
|
%idx = OpVariable %_ptr_Function_uint Function %6
|
||||||
OpStore %idx %6
|
OpStore %idx %6
|
||||||
OpStore %idx %local_invocation_index
|
OpStore %idx %local_invocation_index_2
|
||||||
OpBranch %18
|
OpBranch %18
|
||||||
%18 = OpLabel
|
%18 = OpLabel
|
||||||
OpLoopMerge %19 %20 None
|
OpLoopMerge %19 %20 None
|
||||||
|
|
|
@ -4,9 +4,9 @@ var<private> local_invocation_index_1 : u32;
|
||||||
|
|
||||||
var<workgroup> wg : array<atomic<u32>, 4u>;
|
var<workgroup> wg : array<atomic<u32>, 4u>;
|
||||||
|
|
||||||
fn compute_main_inner(local_invocation_index : u32) {
|
fn compute_main_inner(local_invocation_index_2 : u32) {
|
||||||
var idx : u32 = 0u;
|
var idx : u32 = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
loop {
|
loop {
|
||||||
let x_21 : u32 = idx;
|
let x_21 : u32 = idx;
|
||||||
if (!((x_21 < 4u))) {
|
if (!((x_21 < 4u))) {
|
||||||
|
|
|
@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_25 = idx;
|
const uint x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_25 = idx;
|
const uint x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -10,9 +10,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
return (lhs % ((rhs == 0u) ? 1u : rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint x_25 = idx;
|
uint x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -22,9 +22,9 @@ uint tint_mod(uint lhs, uint rhs) {
|
||||||
return (lhs % select(rhs, 1u, (rhs == 0u)));
|
return (lhs % select(rhs, 1u, (rhs == 0u)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
|
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint const x_25 = idx;
|
uint const x_25 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
OpName %lhs_0 "lhs"
|
OpName %lhs_0 "lhs"
|
||||||
OpName %rhs_0 "rhs"
|
OpName %rhs_0 "rhs"
|
||||||
OpName %compute_main_inner "compute_main_inner"
|
OpName %compute_main_inner "compute_main_inner"
|
||||||
OpName %local_invocation_index "local_invocation_index"
|
OpName %local_invocation_index_2 "local_invocation_index_2"
|
||||||
OpName %idx "idx"
|
OpName %idx "idx"
|
||||||
OpName %compute_main_1 "compute_main_1"
|
OpName %compute_main_1 "compute_main_1"
|
||||||
OpName %compute_main_inner_1 "compute_main_inner_1"
|
OpName %compute_main_inner_1 "compute_main_inner_1"
|
||||||
|
@ -75,11 +75,11 @@
|
||||||
OpReturnValue %30
|
OpReturnValue %30
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
%compute_main_inner = OpFunction %void None %31
|
%compute_main_inner = OpFunction %void None %31
|
||||||
%local_invocation_index = OpFunctionParameter %uint
|
%local_invocation_index_2 = OpFunctionParameter %uint
|
||||||
%35 = OpLabel
|
%35 = OpLabel
|
||||||
%idx = OpVariable %_ptr_Function_uint Function %6
|
%idx = OpVariable %_ptr_Function_uint Function %6
|
||||||
OpStore %idx %6
|
OpStore %idx %6
|
||||||
OpStore %idx %local_invocation_index
|
OpStore %idx %local_invocation_index_2
|
||||||
OpBranch %38
|
OpBranch %38
|
||||||
%38 = OpLabel
|
%38 = OpLabel
|
||||||
OpLoopMerge %39 %40 None
|
OpLoopMerge %39 %40 None
|
||||||
|
|
|
@ -8,9 +8,9 @@ var<private> local_invocation_index_1 : u32;
|
||||||
|
|
||||||
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
|
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
|
||||||
|
|
||||||
fn compute_main_inner(local_invocation_index : u32) {
|
fn compute_main_inner(local_invocation_index_2 : u32) {
|
||||||
var idx : u32 = 0u;
|
var idx : u32 = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
loop {
|
loop {
|
||||||
let x_25 : u32 = idx;
|
let x_25 : u32 = idx;
|
||||||
if (!((x_25 < 6u))) {
|
if (!((x_25 < 6u))) {
|
||||||
|
|
|
@ -7,9 +7,9 @@ struct S_atomic {
|
||||||
static uint local_invocation_index_1 = 0u;
|
static uint local_invocation_index_1 = 0u;
|
||||||
groupshared S_atomic wg[10];
|
groupshared S_atomic wg[10];
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_23 = idx;
|
const uint x_23 = idx;
|
||||||
if (!((x_23 < 10u))) {
|
if (!((x_23 < 10u))) {
|
||||||
|
|
|
@ -7,9 +7,9 @@ struct S_atomic {
|
||||||
static uint local_invocation_index_1 = 0u;
|
static uint local_invocation_index_1 = 0u;
|
||||||
groupshared S_atomic wg[10];
|
groupshared S_atomic wg[10];
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint x_23 = idx;
|
const uint x_23 = idx;
|
||||||
if (!((x_23 < 10u))) {
|
if (!((x_23 < 10u))) {
|
||||||
|
|
|
@ -14,9 +14,9 @@ struct S {
|
||||||
|
|
||||||
uint local_invocation_index_1 = 0u;
|
uint local_invocation_index_1 = 0u;
|
||||||
shared S_atomic wg[10];
|
shared S_atomic wg[10];
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint x_23 = idx;
|
uint x_23 = idx;
|
||||||
if (!((x_23 < 10u))) {
|
if (!((x_23 < 10u))) {
|
||||||
|
|
|
@ -26,9 +26,9 @@ struct S {
|
||||||
uint y;
|
uint y;
|
||||||
};
|
};
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<S_atomic, 10>* const tint_symbol) {
|
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<S_atomic, 10>* const tint_symbol) {
|
||||||
uint idx = 0u;
|
uint idx = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint const x_23 = idx;
|
uint const x_23 = idx;
|
||||||
if (!((x_23 < 10u))) {
|
if (!((x_23 < 10u))) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
OpMemberName %S_atomic 2 "y"
|
OpMemberName %S_atomic 2 "y"
|
||||||
OpName %wg "wg"
|
OpName %wg "wg"
|
||||||
OpName %compute_main_inner "compute_main_inner"
|
OpName %compute_main_inner "compute_main_inner"
|
||||||
OpName %local_invocation_index "local_invocation_index"
|
OpName %local_invocation_index_2 "local_invocation_index_2"
|
||||||
OpName %idx "idx"
|
OpName %idx "idx"
|
||||||
OpName %compute_main_1 "compute_main_1"
|
OpName %compute_main_1 "compute_main_1"
|
||||||
OpName %compute_main_inner_1 "compute_main_inner_1"
|
OpName %compute_main_inner_1 "compute_main_inner_1"
|
||||||
|
@ -54,11 +54,11 @@
|
||||||
%int_4 = OpConstant %int 4
|
%int_4 = OpConstant %int 4
|
||||||
%51 = OpTypeFunction %void
|
%51 = OpTypeFunction %void
|
||||||
%compute_main_inner = OpFunction %void None %13
|
%compute_main_inner = OpFunction %void None %13
|
||||||
%local_invocation_index = OpFunctionParameter %uint
|
%local_invocation_index_2 = OpFunctionParameter %uint
|
||||||
%17 = OpLabel
|
%17 = OpLabel
|
||||||
%idx = OpVariable %_ptr_Function_uint Function %6
|
%idx = OpVariable %_ptr_Function_uint Function %6
|
||||||
OpStore %idx %6
|
OpStore %idx %6
|
||||||
OpStore %idx %local_invocation_index
|
OpStore %idx %local_invocation_index_2
|
||||||
OpBranch %20
|
OpBranch %20
|
||||||
%20 = OpLabel
|
%20 = OpLabel
|
||||||
OpLoopMerge %21 %22 None
|
OpLoopMerge %21 %22 None
|
||||||
|
|
|
@ -22,9 +22,9 @@ var<private> local_invocation_index_1 : u32;
|
||||||
|
|
||||||
var<workgroup> wg : array<S_atomic, 10u>;
|
var<workgroup> wg : array<S_atomic, 10u>;
|
||||||
|
|
||||||
fn compute_main_inner(local_invocation_index : u32) {
|
fn compute_main_inner(local_invocation_index_2 : u32) {
|
||||||
var idx : u32 = 0u;
|
var idx : u32 = 0u;
|
||||||
idx = local_invocation_index;
|
idx = local_invocation_index_2;
|
||||||
loop {
|
loop {
|
||||||
let x_23 : u32 = idx;
|
let x_23 : u32 = idx;
|
||||||
if (!((x_23 < 10u))) {
|
if (!((x_23 < 10u))) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct S_atomic {
|
||||||
static uint local_invocation_index_1 = 0u;
|
static uint local_invocation_index_1 = 0u;
|
||||||
groupshared S_atomic wg;
|
groupshared S_atomic wg;
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
wg.x = 0;
|
wg.x = 0;
|
||||||
uint atomic_result = 0u;
|
uint atomic_result = 0u;
|
||||||
InterlockedExchange(wg.a, 0u, atomic_result);
|
InterlockedExchange(wg.a, 0u, atomic_result);
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct S_atomic {
|
||||||
static uint local_invocation_index_1 = 0u;
|
static uint local_invocation_index_1 = 0u;
|
||||||
groupshared S_atomic wg;
|
groupshared S_atomic wg;
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
wg.x = 0;
|
wg.x = 0;
|
||||||
uint atomic_result = 0u;
|
uint atomic_result = 0u;
|
||||||
InterlockedExchange(wg.a, 0u, atomic_result);
|
InterlockedExchange(wg.a, 0u, atomic_result);
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct S {
|
||||||
|
|
||||||
uint local_invocation_index_1 = 0u;
|
uint local_invocation_index_1 = 0u;
|
||||||
shared S_atomic wg;
|
shared S_atomic wg;
|
||||||
void compute_main_inner(uint local_invocation_index) {
|
void compute_main_inner(uint local_invocation_index_2) {
|
||||||
wg.x = 0;
|
wg.x = 0;
|
||||||
atomicExchange(wg.a, 0u);
|
atomicExchange(wg.a, 0u);
|
||||||
atomicExchange(wg.b, 0u);
|
atomicExchange(wg.b, 0u);
|
||||||
|
|
|
@ -13,7 +13,7 @@ struct S {
|
||||||
uint b;
|
uint b;
|
||||||
};
|
};
|
||||||
|
|
||||||
void compute_main_inner(uint local_invocation_index, threadgroup S_atomic* const tint_symbol) {
|
void compute_main_inner(uint local_invocation_index_2, threadgroup S_atomic* const tint_symbol) {
|
||||||
(*(tint_symbol)).x = 0;
|
(*(tint_symbol)).x = 0;
|
||||||
atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
|
atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
|
||||||
atomic_store_explicit(&((*(tint_symbol)).b), 0u, memory_order_relaxed);
|
atomic_store_explicit(&((*(tint_symbol)).b), 0u, memory_order_relaxed);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue