mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-05 04:36:02 +00:00
tint: Add TINT_REFLECT() & ForeachField()
This uses template and macro magic to reflect the fields of a class. Dawn: * Reflect the fields of the types that are used by Dawn's stream::Stream<T> specializations, and use tint::ForeachField() to call StreamIn(). Fuzzers: * Replace tint::fuzzers::DataBuilder::BuildImpl<T> specializations with the new reflection system. * static_assert that the type is either POD or reflected. Add a specialization for std::optional which was missing. Move tint::transform::BindingPoints into MultiplanarExternalTexture, as this is only used by MultiplanarExternalTexture. All this reduces fragility of the struct declarations slipping out of sync with the uses. Bug: tint:1640 Change-Id: I08729c1c356f1b427e85983efe3c2678fc2ce717 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101001 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
parent
bd6bcf1e0c
commit
648bd7b4be
@ -19,6 +19,15 @@
|
|||||||
|
|
||||||
namespace dawn::native {
|
namespace dawn::native {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename OBJECT>
|
||||||
|
void StreamInTintObject(const OBJECT& object, stream::Sink* sink) {
|
||||||
|
tint::ForeachField(object, [&](auto& field) { StreamIn(sink, field); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::Program>::Write(stream::Sink* sink, const tint::Program& p) {
|
void stream::Stream<tint::Program>::Write(stream::Sink* sink, const tint::Program& p) {
|
||||||
@ -36,82 +45,48 @@ void stream::Stream<tint::Program>::Write(stream::Sink* sink, const tint::Progra
|
|||||||
// static
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::sem::BindingPoint>::Write(stream::Sink* sink,
|
void stream::Stream<tint::sem::BindingPoint>::Write(stream::Sink* sink,
|
||||||
const tint::sem::BindingPoint& p) {
|
const tint::sem::BindingPoint& point) {
|
||||||
static_assert(offsetof(tint::sem::BindingPoint, group) == 0,
|
StreamInTintObject(point, sink);
|
||||||
"Please update serialization for tint::sem::BindingPoint");
|
|
||||||
static_assert(offsetof(tint::sem::BindingPoint, binding) == 4,
|
|
||||||
"Please update serialization for tint::sem::BindingPoint");
|
|
||||||
static_assert(sizeof(tint::sem::BindingPoint) == 8,
|
|
||||||
"Please update serialization for tint::sem::BindingPoint");
|
|
||||||
StreamIn(sink, p.group, p.binding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::transform::BindingPoints>::Write(
|
void stream::Stream<tint::transform::MultiplanarExternalTexture::BindingPoints>::Write(
|
||||||
stream::Sink* sink,
|
stream::Sink* sink,
|
||||||
const tint::transform::BindingPoints& points) {
|
const tint::transform::MultiplanarExternalTexture::BindingPoints& points) {
|
||||||
static_assert(offsetof(tint::transform::BindingPoints, plane_1) == 0,
|
StreamInTintObject(points, sink);
|
||||||
"Please update serialization for tint::transform::BindingPoints");
|
|
||||||
static_assert(offsetof(tint::transform::BindingPoints, params) == 8,
|
|
||||||
"Please update serialization for tint::transform::BindingPoints");
|
|
||||||
static_assert(sizeof(tint::transform::BindingPoints) == 16,
|
|
||||||
"Please update serialization for tint::transform::BindingPoints");
|
|
||||||
StreamIn(sink, points.plane_1, points.params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::transform::VertexPulling::Config>::Write(
|
void stream::Stream<tint::transform::VertexPulling::Config>::Write(
|
||||||
stream::Sink* sink,
|
stream::Sink* sink,
|
||||||
const tint::transform::VertexPulling::Config& cfg) {
|
const tint::transform::VertexPulling::Config& cfg) {
|
||||||
StreamIn(sink, cfg.entry_point_name, cfg.vertex_state, cfg.pulling_group);
|
StreamInTintObject(cfg, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::transform::VertexBufferLayoutDescriptor>::Write(
|
void stream::Stream<tint::transform::VertexBufferLayoutDescriptor>::Write(
|
||||||
stream::Sink* sink,
|
stream::Sink* sink,
|
||||||
const tint::transform::VertexBufferLayoutDescriptor& layout) {
|
const tint::transform::VertexBufferLayoutDescriptor& layout) {
|
||||||
using Layout = tint::transform::VertexBufferLayoutDescriptor;
|
StreamInTintObject(layout, sink);
|
||||||
static_assert(offsetof(Layout, array_stride) == 0,
|
|
||||||
"Please update serialization for tint::transform::VertexBufferLayoutDescriptor");
|
|
||||||
static_assert(offsetof(Layout, step_mode) == 4,
|
|
||||||
"Please update serialization for tint::transform::VertexBufferLayoutDescriptor");
|
|
||||||
static_assert(offsetof(Layout, attributes) == 8,
|
|
||||||
"Please update serialization for tint::transform::VertexBufferLayoutDescriptor");
|
|
||||||
StreamIn(sink, layout.array_stride, layout.step_mode, layout.attributes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::transform::VertexAttributeDescriptor>::Write(
|
void stream::Stream<tint::transform::VertexAttributeDescriptor>::Write(
|
||||||
stream::Sink* sink,
|
stream::Sink* sink,
|
||||||
const tint::transform::VertexAttributeDescriptor& attrib) {
|
const tint::transform::VertexAttributeDescriptor& attrib) {
|
||||||
using Attrib = tint::transform::VertexAttributeDescriptor;
|
StreamInTintObject(attrib, sink);
|
||||||
static_assert(offsetof(Attrib, format) == 0,
|
|
||||||
"Please update serialization for tint::transform::VertexAttributeDescriptor");
|
|
||||||
static_assert(offsetof(Attrib, offset) == 4,
|
|
||||||
"Please update serialization for tint::transform::VertexAttributeDescriptor");
|
|
||||||
static_assert(offsetof(Attrib, shader_location) == 8,
|
|
||||||
"Please update serialization for tint::transform::VertexAttributeDescriptor");
|
|
||||||
static_assert(sizeof(Attrib) == 12,
|
|
||||||
"Please update serialization for tint::transform::VertexAttributeDescriptor");
|
|
||||||
StreamIn(sink, attrib.format, attrib.offset, attrib.shader_location);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
template <>
|
template <>
|
||||||
void stream::Stream<tint::writer::ArrayLengthFromUniformOptions>::Write(
|
void stream::Stream<tint::writer::ArrayLengthFromUniformOptions>::Write(
|
||||||
stream::Sink* sink,
|
stream::Sink* sink,
|
||||||
const tint::writer::ArrayLengthFromUniformOptions& o) {
|
const tint::writer::ArrayLengthFromUniformOptions& options) {
|
||||||
static_assert(offsetof(tint::writer::ArrayLengthFromUniformOptions, ubo_binding) == 0,
|
StreamInTintObject(options, sink);
|
||||||
"Please update serialization for tint::writer::ArrayLengthFromUniformOptions");
|
|
||||||
static_assert(
|
|
||||||
offsetof(tint::writer::ArrayLengthFromUniformOptions, bindpoint_to_size_index) == 8,
|
|
||||||
"Please update serialization for tint::writer::ArrayLengthFromUniformOptions");
|
|
||||||
static_assert(
|
|
||||||
sizeof(tint::writer::ArrayLengthFromUniformOptions) ==
|
|
||||||
8 + sizeof(tint::writer::ArrayLengthFromUniformOptions::bindpoint_to_size_index),
|
|
||||||
"Please update serialization for tint::writer::ArrayLengthFromUniformOptions");
|
|
||||||
StreamIn(sink, o.ubo_binding, o.bindpoint_to_size_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dawn::native
|
} // namespace dawn::native
|
||||||
|
@ -265,7 +265,7 @@ TEST(SerializeTests, TintSemBindingPoint) {
|
|||||||
|
|
||||||
// Test that ByteVectorSink serializes tint::transform::BindingPoints as expected.
|
// Test that ByteVectorSink serializes tint::transform::BindingPoints as expected.
|
||||||
TEST(SerializeTests, TintTransformBindingPoints) {
|
TEST(SerializeTests, TintTransformBindingPoints) {
|
||||||
tint::transform::BindingPoints points{
|
tint::transform::MultiplanarExternalTexture::BindingPoints points{
|
||||||
tint::sem::BindingPoint{1, 4},
|
tint::sem::BindingPoint{1, 4},
|
||||||
tint::sem::BindingPoint{3, 7},
|
tint::sem::BindingPoint{3, 7},
|
||||||
};
|
};
|
||||||
|
@ -390,6 +390,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||||||
"program_builder.h",
|
"program_builder.h",
|
||||||
"program_id.cc",
|
"program_id.cc",
|
||||||
"program_id.h",
|
"program_id.h",
|
||||||
|
"reflection.h",
|
||||||
"reader/reader.cc",
|
"reader/reader.cc",
|
||||||
"reader/reader.h",
|
"reader/reader.h",
|
||||||
"resolver/const_eval.cc",
|
"resolver/const_eval.cc",
|
||||||
@ -568,6 +569,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||||||
"utils/debugger.cc",
|
"utils/debugger.cc",
|
||||||
"utils/debugger.h",
|
"utils/debugger.h",
|
||||||
"utils/enum_set.h",
|
"utils/enum_set.h",
|
||||||
|
"utils/foreach_macro.h",
|
||||||
"utils/hash.h",
|
"utils/hash.h",
|
||||||
"utils/hashmap.h",
|
"utils/hashmap.h",
|
||||||
"utils/hashset.h",
|
"utils/hashset.h",
|
||||||
@ -1609,6 +1611,7 @@ if (tint_build_unittests) {
|
|||||||
"number_test.cc",
|
"number_test.cc",
|
||||||
"program_builder_test.cc",
|
"program_builder_test.cc",
|
||||||
"program_test.cc",
|
"program_test.cc",
|
||||||
|
"reflection_test.cc",
|
||||||
"scope_stack_test.cc",
|
"scope_stack_test.cc",
|
||||||
"source_test.cc",
|
"source_test.cc",
|
||||||
"symbol_table_test.cc",
|
"symbol_table_test.cc",
|
||||||
|
@ -252,6 +252,7 @@ set(TINT_LIB_SRCS
|
|||||||
program_id.h
|
program_id.h
|
||||||
program.cc
|
program.cc
|
||||||
program.h
|
program.h
|
||||||
|
reflection.h
|
||||||
reader/reader.cc
|
reader/reader.cc
|
||||||
reader/reader.h
|
reader/reader.h
|
||||||
resolver/const_eval.cc
|
resolver/const_eval.cc
|
||||||
@ -478,6 +479,7 @@ set(TINT_LIB_SRCS
|
|||||||
utils/concat.h
|
utils/concat.h
|
||||||
utils/crc32.h
|
utils/crc32.h
|
||||||
utils/enum_set.h
|
utils/enum_set.h
|
||||||
|
utils/foreach_macro.h
|
||||||
utils/hash.h
|
utils/hash.h
|
||||||
utils/hashmap.h
|
utils/hashmap.h
|
||||||
utils/hashset.h
|
utils/hashset.h
|
||||||
@ -777,6 +779,7 @@ if(TINT_BUILD_TESTS)
|
|||||||
number_test.cc
|
number_test.cc
|
||||||
program_builder_test.cc
|
program_builder_test.cc
|
||||||
program_test.cc
|
program_test.cc
|
||||||
|
reflection_test.cc
|
||||||
resolver/array_accessor_test.cc
|
resolver/array_accessor_test.cc
|
||||||
resolver/assignment_validation_test.cc
|
resolver/assignment_validation_test.cc
|
||||||
resolver/atomics_test.cc
|
resolver/atomics_test.cc
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -128,11 +129,26 @@ class DataBuilder {
|
|||||||
/// @returns a variable of type T filled with pseudo-random data
|
/// @returns a variable of type T filled with pseudo-random data
|
||||||
static T impl(DataBuilder* b) {
|
static T impl(DataBuilder* b) {
|
||||||
T out{};
|
T out{};
|
||||||
|
if constexpr (tint::HasReflection<T>) {
|
||||||
|
ForeachField(out, [&](auto& field) { b->build(field); });
|
||||||
|
} else if constexpr (std::is_pod_v<T>) {
|
||||||
b->build(&out, sizeof(T));
|
b->build(&out, sizeof(T));
|
||||||
|
} else {
|
||||||
|
static_assert(sizeof(T) == 0, "cannot build type");
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Specialization for bool
|
||||||
|
template <>
|
||||||
|
struct BuildImpl<bool> {
|
||||||
|
/// Generate a pseudo-random bool
|
||||||
|
/// @param b - data builder to use
|
||||||
|
/// @returns a boolean with even odds of being true or false
|
||||||
|
static bool impl(DataBuilder* b) { return b->generator_.GetBool(); }
|
||||||
|
};
|
||||||
|
|
||||||
/// Specialization for std::string
|
/// Specialization for std::string
|
||||||
template <>
|
template <>
|
||||||
struct BuildImpl<std::string> {
|
struct BuildImpl<std::string> {
|
||||||
@ -150,74 +166,17 @@ class DataBuilder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Specialization for bool
|
/// Specialization for std::optional
|
||||||
template <>
|
template <typename T>
|
||||||
struct BuildImpl<bool> {
|
struct BuildImpl<std::optional<T>> {
|
||||||
/// Generate a pseudo-random bool
|
/// Generate a pseudo-random optional<T>
|
||||||
/// @param b - data builder to use
|
/// @param b - data builder to use
|
||||||
/// @returns a boolean with even odds of being true or false
|
/// @returns a either a nullopt, or a randomly filled T
|
||||||
static bool impl(DataBuilder* b) { return b->generator_.GetBool(); }
|
static std::optional<T> impl(DataBuilder* b) {
|
||||||
};
|
if (b->build<bool>()) {
|
||||||
|
return b->build<T>();
|
||||||
/// Specialization for writer::msl::Options
|
|
||||||
template <>
|
|
||||||
struct BuildImpl<writer::msl::Options> {
|
|
||||||
/// Generate a pseudo-random writer::msl::Options struct
|
|
||||||
/// @param b - data builder to use
|
|
||||||
/// @returns writer::msl::Options filled with pseudo-random data
|
|
||||||
static writer::msl::Options impl(DataBuilder* b) {
|
|
||||||
writer::msl::Options out{};
|
|
||||||
b->build(out.buffer_size_ubo_index);
|
|
||||||
b->build(out.fixed_sample_mask);
|
|
||||||
b->build(out.emit_vertex_point_size);
|
|
||||||
b->build(out.disable_workgroup_init);
|
|
||||||
b->build(out.generate_external_texture_bindings);
|
|
||||||
b->build(out.array_length_from_uniform);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
};
|
return std::nullopt;
|
||||||
|
|
||||||
/// Specialization for writer::hlsl::Options
|
|
||||||
template <>
|
|
||||||
struct BuildImpl<writer::hlsl::Options> {
|
|
||||||
/// Generate a pseudo-random writer::hlsl::Options struct
|
|
||||||
/// @param b - data builder to use
|
|
||||||
/// @returns writer::hlsl::Options filled with pseudo-random data
|
|
||||||
static writer::hlsl::Options impl(DataBuilder* b) {
|
|
||||||
writer::hlsl::Options out{};
|
|
||||||
b->build(out.root_constant_binding_point);
|
|
||||||
b->build(out.disable_workgroup_init);
|
|
||||||
b->build(out.array_length_from_uniform);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Specialization for writer::spirv::Options
|
|
||||||
template <>
|
|
||||||
struct BuildImpl<writer::spirv::Options> {
|
|
||||||
/// Generate a pseudo-random writer::spirv::Options struct
|
|
||||||
/// @param b - data builder to use
|
|
||||||
/// @returns writer::spirv::Options filled with pseudo-random data
|
|
||||||
static writer::spirv::Options impl(DataBuilder* b) {
|
|
||||||
writer::spirv::Options out{};
|
|
||||||
b->build(out.emit_vertex_point_size);
|
|
||||||
b->build(out.disable_workgroup_init);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Specialization for writer::ArrayLengthFromUniformOptions
|
|
||||||
template <>
|
|
||||||
struct BuildImpl<writer::ArrayLengthFromUniformOptions> {
|
|
||||||
/// Generate a pseudo-random writer::ArrayLengthFromUniformOptions struct
|
|
||||||
/// @param b - data builder to use
|
|
||||||
/// @returns writer::ArrayLengthFromUniformOptions filled with pseudo-random
|
|
||||||
/// data
|
|
||||||
static writer::ArrayLengthFromUniformOptions impl(DataBuilder* b) {
|
|
||||||
writer::ArrayLengthFromUniformOptions out{};
|
|
||||||
b->build(out.ubo_binding);
|
|
||||||
b->build(out.bindpoint_to_size_index);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
67
src/tint/reflection.h
Normal file
67
src/tint/reflection.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2022 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_REFLECTION_H_
|
||||||
|
#define SRC_TINT_REFLECTION_H_
|
||||||
|
|
||||||
|
#include "src/tint/traits.h"
|
||||||
|
#include "src/tint/utils/concat.h"
|
||||||
|
#include "src/tint/utils/foreach_macro.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/// Helper for detecting whether the type T contains a nested Reflection class.
|
||||||
|
template <typename T, typename ENABLE = void>
|
||||||
|
struct HasReflection : std::false_type {};
|
||||||
|
|
||||||
|
/// Specialization for types that have a nested Reflection class.
|
||||||
|
template <typename T>
|
||||||
|
struct HasReflection<T, std::void_t<typename T::Reflection>> : std::true_type {};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Is true if the class T has reflected its fields with TINT_REFLECT()
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool HasReflection = detail::HasReflection<T>::value;
|
||||||
|
|
||||||
|
/// Calls @p callback with each field of @p object
|
||||||
|
/// @param object the object
|
||||||
|
/// @param callback a function that is called for each field of @p object.
|
||||||
|
/// @tparam CB a function with the signature `void(FIELD)`
|
||||||
|
template <typename OBJECT, typename CB>
|
||||||
|
void ForeachField(OBJECT&& object, CB&& callback) {
|
||||||
|
using T = std::decay_t<OBJECT>;
|
||||||
|
static_assert(HasReflection<T>, "object type requires a tint::Reflect<> specialization");
|
||||||
|
T::Reflection::Fields(object, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro used by TINT_FOREACH() in TINT_REFLECT() to call the callback function with each field in
|
||||||
|
/// the variadic.
|
||||||
|
#define TINT_REFLECT_CALLBACK_FIELD(field) callback(object.field);
|
||||||
|
|
||||||
|
// TINT_REFLECT(...) reflects each of the fields arguments so that the types can be used with
|
||||||
|
// tint::ForeachField().
|
||||||
|
#define TINT_REFLECT(...) \
|
||||||
|
struct Reflection { \
|
||||||
|
template <typename OBJECT, typename CB> \
|
||||||
|
static void Fields(OBJECT&& object, CB&& callback) { \
|
||||||
|
TINT_FOREACH(TINT_REFLECT_CALLBACK_FIELD, __VA_ARGS__) \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
#endif // SRC_TINT_REFLECTION_H_
|
91
src/tint/reflection_test.cc
Normal file
91
src/tint/reflection_test.cc
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2022 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/tint/reflection.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
int i;
|
||||||
|
unsigned u;
|
||||||
|
bool b;
|
||||||
|
TINT_REFLECT(i, u, b);
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(!HasReflection<int>);
|
||||||
|
static_assert(HasReflection<S>);
|
||||||
|
|
||||||
|
TEST(ReflectionTest, ForeachFieldConst) {
|
||||||
|
const S s{1, 2, true};
|
||||||
|
size_t field_idx = 0;
|
||||||
|
ForeachField(s, [&](auto& field) {
|
||||||
|
using T = std::decay_t<decltype(field)>;
|
||||||
|
switch (field_idx) {
|
||||||
|
case 0:
|
||||||
|
EXPECT_TRUE((std::is_same_v<T, int>));
|
||||||
|
EXPECT_EQ(field, static_cast<T>(1));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EXPECT_TRUE((std::is_same_v<T, unsigned>));
|
||||||
|
EXPECT_EQ(field, static_cast<T>(2));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
EXPECT_TRUE((std::is_same_v<T, bool>));
|
||||||
|
EXPECT_EQ(field, static_cast<T>(true));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL() << "unexpected field";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
field_idx++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReflectionTest, ForeachFieldNonConst) {
|
||||||
|
S s{1, 2, true};
|
||||||
|
size_t field_idx = 0;
|
||||||
|
ForeachField(s, [&](auto& field) {
|
||||||
|
using T = std::decay_t<decltype(field)>;
|
||||||
|
switch (field_idx) {
|
||||||
|
case 0:
|
||||||
|
EXPECT_TRUE((std::is_same_v<T, int>));
|
||||||
|
EXPECT_EQ(field, static_cast<T>(1));
|
||||||
|
field = static_cast<T>(10);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EXPECT_TRUE((std::is_same_v<T, unsigned>));
|
||||||
|
EXPECT_EQ(field, static_cast<T>(2));
|
||||||
|
field = static_cast<T>(20);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
EXPECT_TRUE((std::is_same_v<T, bool>));
|
||||||
|
EXPECT_EQ(field, static_cast<T>(true));
|
||||||
|
field = static_cast<T>(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL() << "unexpected field";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
field_idx++;
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EQ(s.i, 10);
|
||||||
|
EXPECT_EQ(s.u, 20u);
|
||||||
|
EXPECT_EQ(s.b, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#include "src/tint/reflection.h"
|
||||||
#include "src/tint/utils/hash.h"
|
#include "src/tint/utils/hash.h"
|
||||||
|
|
||||||
namespace tint::sem {
|
namespace tint::sem {
|
||||||
@ -30,6 +31,9 @@ struct BindingPoint {
|
|||||||
/// The `@binding` part of the binding point
|
/// The `@binding` part of the binding point
|
||||||
uint32_t binding = 0;
|
uint32_t binding = 0;
|
||||||
|
|
||||||
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
|
TINT_REFLECT(group, binding);
|
||||||
|
|
||||||
/// Equality operator
|
/// Equality operator
|
||||||
/// @param rhs the BindingPoint to compare against
|
/// @param rhs the BindingPoint to compare against
|
||||||
/// @returns true if this BindingPoint is equal to `rhs`
|
/// @returns true if this BindingPoint is equal to `rhs`
|
||||||
|
@ -28,6 +28,19 @@ namespace tint::transform {
|
|||||||
/// BindingPoint is an alias to sem::BindingPoint
|
/// BindingPoint is an alias to sem::BindingPoint
|
||||||
using BindingPoint = sem::BindingPoint;
|
using BindingPoint = sem::BindingPoint;
|
||||||
|
|
||||||
|
/// Within the MultiplanarExternalTexture transform, each instance of a
|
||||||
|
/// texture_external binding is unpacked into two texture_2d<f32> bindings
|
||||||
|
/// representing two possible planes of a texture and a uniform buffer binding
|
||||||
|
/// representing a struct of parameters. Calls to textureLoad or
|
||||||
|
/// textureSampleLevel that contain a texture_external parameter will be
|
||||||
|
/// transformed into a newly generated version of the function, which can
|
||||||
|
/// perform the desired operation on a single RGBA plane or on separate Y and UV
|
||||||
|
/// planes, and do colorspace conversions including yuv->rgb conversion, gamma
|
||||||
|
/// decoding, gamut conversion, and gamma encoding steps. Specifically
|
||||||
|
// for BT.709 to SRGB conversion, it takes the fast path only doing the yuv->rgb
|
||||||
|
// step and skipping all other steps.
|
||||||
|
class MultiplanarExternalTexture final : public Castable<MultiplanarExternalTexture, Transform> {
|
||||||
|
public:
|
||||||
/// This struct identifies the binding groups and locations for new bindings to
|
/// This struct identifies the binding groups and locations for new bindings to
|
||||||
/// use when transforming a texture_external instance.
|
/// use when transforming a texture_external instance.
|
||||||
struct BindingPoints {
|
struct BindingPoints {
|
||||||
@ -37,21 +50,11 @@ struct BindingPoints {
|
|||||||
/// The desired binding location of the ExternalTextureParams uniform when a
|
/// The desired binding location of the ExternalTextureParams uniform when a
|
||||||
/// texture_external binding is expanded.
|
/// texture_external binding is expanded.
|
||||||
BindingPoint params;
|
BindingPoint params;
|
||||||
|
|
||||||
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
|
TINT_REFLECT(plane_1, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Within the MultiplanarExternalTexture transform, each instance of a
|
|
||||||
/// texture_external binding is unpacked into two texture_2d<f32> bindings
|
|
||||||
/// representing two possible planes of a texture and a uniform buffer binding
|
|
||||||
/// representing a struct of parameters. Calls to textureLoad or
|
|
||||||
/// textureSampleLevel that contain a texture_external parameter will be
|
|
||||||
/// transformed into a newly generated version of the function, which can
|
|
||||||
/// perform the desired operation on a single RGBA plane or on seperate Y and UV
|
|
||||||
/// planes, and do colorspace conversions including yuv->rgb conversion, gamma
|
|
||||||
/// decoding, gamut conversion, and gamma encoding steps. Specifically
|
|
||||||
// for BT.709 to SRGB conversion, it takes the fast path only doing the yuv->rgb
|
|
||||||
// step and skipping all other steps.
|
|
||||||
class MultiplanarExternalTexture final : public Castable<MultiplanarExternalTexture, Transform> {
|
|
||||||
public:
|
|
||||||
/// BindingsMap is a map where the key is the binding location of a
|
/// BindingsMap is a map where the key is the binding location of a
|
||||||
/// texture_external and the value is a struct containing the desired
|
/// texture_external and the value is a struct containing the desired
|
||||||
/// locations for new bindings expanded from the texture_external instance.
|
/// locations for new bindings expanded from the texture_external instance.
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/tint/reflection.h"
|
||||||
#include "src/tint/transform/transform.h"
|
#include "src/tint/transform/transform.h"
|
||||||
|
|
||||||
namespace tint::transform {
|
namespace tint::transform {
|
||||||
@ -72,6 +73,9 @@ struct VertexAttributeDescriptor {
|
|||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
/// The shader location used for the attribute
|
/// The shader location used for the attribute
|
||||||
uint32_t shader_location;
|
uint32_t shader_location;
|
||||||
|
|
||||||
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
|
TINT_REFLECT(format, offset, shader_location);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes a buffer containing multiple vertex attributes
|
/// Describes a buffer containing multiple vertex attributes
|
||||||
@ -102,6 +106,9 @@ struct VertexBufferLayoutDescriptor {
|
|||||||
VertexStepMode step_mode = VertexStepMode::kVertex;
|
VertexStepMode step_mode = VertexStepMode::kVertex;
|
||||||
/// The vertex attributes
|
/// The vertex attributes
|
||||||
std::vector<VertexAttributeDescriptor> attributes;
|
std::vector<VertexAttributeDescriptor> attributes;
|
||||||
|
|
||||||
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
|
TINT_REFLECT(array_stride, step_mode, attributes);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes vertex state, which consists of many buffers containing vertex
|
/// Describes vertex state, which consists of many buffers containing vertex
|
||||||
@ -154,6 +161,9 @@ class VertexPulling final : public Castable<VertexPulling, Transform> {
|
|||||||
/// The "group" we will put all our vertex buffers into (as storage buffers)
|
/// The "group" we will put all our vertex buffers into (as storage buffers)
|
||||||
/// Default to 4 as it is past the limits of user-accessible groups
|
/// Default to 4 as it is past the limits of user-accessible groups
|
||||||
uint32_t pulling_group = 4u;
|
uint32_t pulling_group = 4u;
|
||||||
|
|
||||||
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
|
TINT_REFLECT(entry_point_name, vertex_state, pulling_group);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
|
91
src/tint/utils/foreach_macro.h
Normal file
91
src/tint/utils/foreach_macro.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2022 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_UTILS_FOREACH_MACRO_H_
|
||||||
|
#define SRC_TINT_UTILS_FOREACH_MACRO_H_
|
||||||
|
|
||||||
|
// Macro magic to perform macro variadic dispatch.
|
||||||
|
// See:
|
||||||
|
// https://renenyffenegger.ch/notes/development/languages/C-C-plus-plus/preprocessor/macros/__VA_ARGS__/count-arguments
|
||||||
|
// Note, this doesn't attempt to use the ##__VA_ARGS__ trick to handle 0
|
||||||
|
|
||||||
|
// Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler.
|
||||||
|
#define TINT_MSVC_EXPAND_BUG(X) X
|
||||||
|
|
||||||
|
/// TINT_COUNT_ARGUMENTS_NTH_ARG is used by TINT_COUNT_ARGUMENTS to get the number of arguments in a
|
||||||
|
/// variadic macro call.
|
||||||
|
#define TINT_COUNT_ARGUMENTS_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
|
||||||
|
_15, _16, N, ...) \
|
||||||
|
N
|
||||||
|
|
||||||
|
/// TINT_COUNT_ARGUMENTS evaluates to the number of arguments passed to the macro
|
||||||
|
#define TINT_COUNT_ARGUMENTS(...) \
|
||||||
|
TINT_MSVC_EXPAND_BUG(TINT_COUNT_ARGUMENTS_NTH_ARG(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, \
|
||||||
|
8, 7, 6, 5, 4, 3, 2, 1, 0))
|
||||||
|
|
||||||
|
// Correctness checks.
|
||||||
|
static_assert(1 == TINT_COUNT_ARGUMENTS(a), "TINT_COUNT_ARGUMENTS broken");
|
||||||
|
static_assert(2 == TINT_COUNT_ARGUMENTS(a, b), "TINT_COUNT_ARGUMENTS broken");
|
||||||
|
static_assert(3 == TINT_COUNT_ARGUMENTS(a, b, c), "TINT_COUNT_ARGUMENTS broken");
|
||||||
|
|
||||||
|
/// TINT_FOREACH calls CB with each of the variadic arguments.
|
||||||
|
#define TINT_FOREACH(CB, ...) \
|
||||||
|
TINT_MSVC_EXPAND_BUG( \
|
||||||
|
TINT_CONCAT(TINT_FOREACH_, TINT_COUNT_ARGUMENTS(__VA_ARGS__))(CB, __VA_ARGS__))
|
||||||
|
|
||||||
|
#define TINT_FOREACH_1(CB, _1) CB(_1)
|
||||||
|
#define TINT_FOREACH_2(CB, _1, _2) \
|
||||||
|
TINT_FOREACH_1(CB, _1) \
|
||||||
|
CB(_2)
|
||||||
|
#define TINT_FOREACH_3(CB, _1, _2, _3) \
|
||||||
|
TINT_FOREACH_2(CB, _1, _2) \
|
||||||
|
CB(_3)
|
||||||
|
#define TINT_FOREACH_4(CB, _1, _2, _3, _4) \
|
||||||
|
TINT_FOREACH_3(CB, _1, _2, _3) \
|
||||||
|
CB(_4)
|
||||||
|
#define TINT_FOREACH_5(CB, _1, _2, _3, _4, _5) \
|
||||||
|
TINT_FOREACH_4(CB, _1, _2, _3, _4) \
|
||||||
|
CB(_5)
|
||||||
|
#define TINT_FOREACH_6(CB, _1, _2, _3, _4, _5, _6) \
|
||||||
|
TINT_FOREACH_5(CB, _1, _2, _3, _4, _5) \
|
||||||
|
CB(_6)
|
||||||
|
#define TINT_FOREACH_7(CB, _1, _2, _3, _4, _5, _6, _7) \
|
||||||
|
TINT_FOREACH_6(CB, _1, _2, _3, _4, _5, _6) \
|
||||||
|
CB(_7)
|
||||||
|
#define TINT_FOREACH_8(CB, _1, _2, _3, _4, _5, _6, _7, _8) \
|
||||||
|
TINT_FOREACH_7(CB, _1, _2, _3, _4, _5, _6, _7) \
|
||||||
|
CB(_8)
|
||||||
|
#define TINT_FOREACH_9(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
|
||||||
|
TINT_FOREACH_8(CB, _1, _2, _3, _4, _5, _6, _7, _8) \
|
||||||
|
CB(_9)
|
||||||
|
#define TINT_FOREACH_10(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||||
|
TINT_FOREACH_9(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
|
||||||
|
CB(_10)
|
||||||
|
#define TINT_FOREACH_11(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||||
|
TINT_FOREACH_10(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||||
|
CB(_11)
|
||||||
|
#define TINT_FOREACH_12(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||||
|
TINT_FOREACH_11(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||||
|
CB(_12)
|
||||||
|
#define TINT_FOREACH_13(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
|
||||||
|
TINT_FOREACH_11(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||||
|
CB(_13)
|
||||||
|
#define TINT_FOREACH_14(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
|
||||||
|
TINT_FOREACH_11(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
|
||||||
|
CB(_14)
|
||||||
|
#define TINT_FOREACH_15(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
|
||||||
|
TINT_FOREACH_11(CB, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
|
||||||
|
CB(_15)
|
||||||
|
|
||||||
|
#endif // SRC_TINT_UTILS_FOREACH_MACRO_H_
|
@ -43,8 +43,8 @@ struct ArrayLengthFromUniformOptions {
|
|||||||
/// uniform buffer where the length of the buffer is stored.
|
/// uniform buffer where the length of the buffer is stored.
|
||||||
std::unordered_map<sem::BindingPoint, uint32_t> bindpoint_to_size_index;
|
std::unordered_map<sem::BindingPoint, uint32_t> bindpoint_to_size_index;
|
||||||
|
|
||||||
// NOTE: Update src/tint/fuzzers/data_builder.h when adding or changing any
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
// struct members.
|
TINT_REFLECT(ubo_binding, bindpoint_to_size_index);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::writer
|
} // namespace tint::writer
|
||||||
|
@ -50,7 +50,8 @@ transform::MultiplanarExternalTexture::BindingsMap GenerateExternalTextureBindin
|
|||||||
for (auto bp : ext_tex_bps) {
|
for (auto bp : ext_tex_bps) {
|
||||||
uint32_t g = bp.group;
|
uint32_t g = bp.group;
|
||||||
uint32_t& next_num = group_to_next_binding_number[g];
|
uint32_t& next_num = group_to_next_binding_number[g];
|
||||||
auto new_bps = transform::BindingPoints{{g, next_num++}, {g, next_num++}};
|
auto new_bps =
|
||||||
|
transform::MultiplanarExternalTexture::BindingPoints{{g, next_num++}, {g, next_num++}};
|
||||||
new_bindings_map[bp] = new_bps;
|
new_bindings_map[bp] = new_bps;
|
||||||
}
|
}
|
||||||
return new_bindings_map;
|
return new_bindings_map;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/tint/ast/pipeline_stage.h"
|
#include "src/tint/ast/pipeline_stage.h"
|
||||||
|
#include "src/tint/reflection.h"
|
||||||
#include "src/tint/sem/binding_point.h"
|
#include "src/tint/sem/binding_point.h"
|
||||||
#include "src/tint/writer/array_length_from_uniform_options.h"
|
#include "src/tint/writer/array_length_from_uniform_options.h"
|
||||||
#include "src/tint/writer/text.h"
|
#include "src/tint/writer/text.h"
|
||||||
@ -56,8 +57,11 @@ struct Options {
|
|||||||
/// from which to load buffer sizes.
|
/// from which to load buffer sizes.
|
||||||
ArrayLengthFromUniformOptions array_length_from_uniform = {};
|
ArrayLengthFromUniformOptions array_length_from_uniform = {};
|
||||||
|
|
||||||
// NOTE: Update src/tint/fuzzers/data_builder.h when adding or changing any
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
// struct members.
|
TINT_REFLECT(root_constant_binding_point,
|
||||||
|
disable_workgroup_init,
|
||||||
|
generate_external_texture_bindings,
|
||||||
|
array_length_from_uniform);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The result produced when generating HLSL.
|
/// The result produced when generating HLSL.
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/tint/reflection.h"
|
||||||
#include "src/tint/writer/array_length_from_uniform_options.h"
|
#include "src/tint/writer/array_length_from_uniform_options.h"
|
||||||
#include "src/tint/writer/text.h"
|
#include "src/tint/writer/text.h"
|
||||||
|
|
||||||
@ -65,8 +66,13 @@ struct Options {
|
|||||||
/// from which to load buffer sizes.
|
/// from which to load buffer sizes.
|
||||||
ArrayLengthFromUniformOptions array_length_from_uniform = {};
|
ArrayLengthFromUniformOptions array_length_from_uniform = {};
|
||||||
|
|
||||||
// NOTE: Update src/tint/fuzzers/data_builder.h when adding or changing any
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
// struct members.
|
TINT_REFLECT(buffer_size_ubo_index,
|
||||||
|
fixed_sample_mask,
|
||||||
|
emit_vertex_point_size,
|
||||||
|
disable_workgroup_init,
|
||||||
|
generate_external_texture_bindings,
|
||||||
|
array_length_from_uniform);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The result produced when generating MSL.
|
/// The result produced when generating MSL.
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/tint/reflection.h"
|
||||||
#include "src/tint/writer/writer.h"
|
#include "src/tint/writer/writer.h"
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
@ -47,6 +48,12 @@ struct Options {
|
|||||||
/// Set to `true` to initialize workgroup memory with OpConstantNull when
|
/// Set to `true` to initialize workgroup memory with OpConstantNull when
|
||||||
/// VK_KHR_zero_initialize_workgroup_memory is enabled.
|
/// VK_KHR_zero_initialize_workgroup_memory is enabled.
|
||||||
bool use_zero_initialize_workgroup_memory_extension = false;
|
bool use_zero_initialize_workgroup_memory_extension = false;
|
||||||
|
|
||||||
|
/// Reflect the fields of this class so that it can be used by tint::ForeachField()
|
||||||
|
TINT_REFLECT(emit_vertex_point_size,
|
||||||
|
disable_workgroup_init,
|
||||||
|
generate_external_texture_bindings,
|
||||||
|
use_zero_initialize_workgroup_memory_extension);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The result produced when generating SPIR-V.
|
/// The result produced when generating SPIR-V.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user