ChainUtil autogenerated validation for unpacking.
- The new generated functions only unpack the chains while verifying
only expected extensions are on the chain.
- Further validations can be implemented on top of the tuples with
templating if desired.
Bug: dawn:1955
Change-Id: Iec47072299dd4bb6d6cb093fdd7c211f8dbdb494
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/146361
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 91c8ee7..1ebf88b 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -247,12 +247,14 @@
if self.chained:
assert self.chained == 'in' or self.chained == 'out'
assert 'chain roots' in json_data
+ self.chain_roots = []
if self.extensible:
assert self.extensible == 'in' or self.extensible == 'out'
# Chained structs inherit from wgpu::ChainedStruct, which has
# nextInChain, so setting both extensible and chained would result in
# two nextInChain members.
assert not (self.extensible and self.chained)
+ self.extensions = []
def update_metadata(self):
Record.update_metadata(self)
@@ -384,6 +386,9 @@
def link_structure(struct, types):
struct.members = linked_record_members(struct.json_data['members'], types)
+ for root in struct.json_data.get('chain roots', []):
+ struct.chain_roots.append(types[root])
+ types[root].extensions.append(struct)
struct.chain_roots = [types[root] for root in struct.json_data.get('chain roots', [])]
assert all((root.category == 'structure' for root in struct.chain_roots))
diff --git a/generator/templates/dawn/native/ChainUtils.cpp b/generator/templates/dawn/native/ChainUtils.cpp
index 1600c41..1f2e6bf 100644
--- a/generator/templates/dawn/native/ChainUtils.cpp
+++ b/generator/templates/dawn/native/ChainUtils.cpp
@@ -18,7 +18,9 @@
{% set native_dir = impl_dir + namespace_name.Dirs() %}
#include "{{native_dir}}/ChainUtils_autogen.h"
+#include <tuple>
#include <unordered_set>
+#include <utility>
namespace {{native_namespace}} {
@@ -75,4 +77,56 @@
return {};
}
+//
+// Unpacked chain helpers.
+//
+{% for type in by_category["structure"] %}
+ {% if type.extensible == "in" %}
+ {% set unpackedChain = "Unpacked" + as_cppType(type.name) + "Chain" %}
+ ResultOrError<{{unpackedChain}}> ValidateAndUnpackChain(const {{as_cppType(type.name)}}* chain) {
+ const ChainedStruct* next = chain->nextInChain;
+ {{unpackedChain}} result;
+
+ //* Branching generation block to avoid warnings when the struct is not currently
+ //* extendable:
+ //* -Wunreachable-code-loop-increment:
+ //* error: loop will run at most once (loop increment never executed)
+ {% if len(type.extensions) == 0 %}
+ if (next != nullptr) {
+ return DAWN_VALIDATION_ERROR(
+ "Unexpected chained struct of type %s found on %s chain.",
+ next->sType, "{{as_cppType(type.name)}}"
+ );
+ }
+ {% else %}
+ for (; next != nullptr; next = next->nextInChain) {
+ switch (next->sType) {
+ {% for extension in type.extensions %}
+ case STypeFor<{{as_cppType(extension.name)}}>: {
+ auto& member = std::get<const {{as_cppType(extension.name)}}*>(result);
+ if (member != nullptr) {
+ return DAWN_VALIDATION_ERROR(
+ "Duplicate chained struct of type %s found on %s chain.",
+ next->sType, "{{as_cppType(type.name)}}"
+ );
+ } else {
+ member = static_cast<const {{as_cppType(extension.name)}}*>(next);
+ }
+ break;
+ }
+ {% endfor %}
+ default:
+ return DAWN_VALIDATION_ERROR(
+ "Unexpected chained struct of type %s found on %s chain.",
+ next->sType, "{{as_cppType(type.name)}}"
+ );
+ }
+ }
+ {% endif %}
+ return result;
+ }
+
+ {% endif %}
+{% endfor %}
+
} // namespace {{native_namespace}}
diff --git a/generator/templates/dawn/native/ChainUtils.h b/generator/templates/dawn/native/ChainUtils.h
index 112ed0c..d4a479e 100644
--- a/generator/templates/dawn/native/ChainUtils.h
+++ b/generator/templates/dawn/native/ChainUtils.h
@@ -23,8 +23,11 @@
{% set native_namespace = namespace_name.namespace_case() %}
{% set native_dir = impl_dir + namespace_name.Dirs() %}
{% set prefix = metadata.proc_table_prefix.lower() %}
+#include <tuple>
+
#include "{{native_dir}}/{{prefix}}_platform.h"
#include "{{native_dir}}/Error.h"
+#include "{{native_dir}}/{{namespace}}_structs_autogen.h"
namespace {{native_namespace}} {
@@ -35,7 +38,7 @@
{% for value in types["s type"].values %}
{% if value.valid and value.name.get() in types %}
template <>
- inline {{namespace}}::SType STypeFor<{{as_cppEnum(value.name)}}> = {{namespace}}::SType::{{as_cppEnum(value.name)}};
+ constexpr inline {{namespace}}::SType STypeFor<{{as_cppEnum(value.name)}}> = {{namespace}}::SType::{{as_cppEnum(value.name)}};
{% endif %}
{% endfor %}
@@ -139,6 +142,25 @@
return ValidateSingleSTypeInner(chain, sType, sTypes...);
}
+ //
+ // Unpacked chain types structs and helpers.
+ // Note that unpacked types are tuples to enable further templating extensions based on
+ // typing via something like std::get<const Extension*> in templated functions.
+ //
+ {% for type in by_category["structure"] %}
+ {% if type.extensible == "in" %}
+ {% set unpackedChain = "Unpacked" + as_cppType(type.name) + "Chain" %}
+ using {{unpackedChain}} = std::tuple<
+ {% for extension in type.extensions %}
+ const {{as_cppType(extension.name)}}*{{ "," if not loop.last else "" }}
+ {% endfor %}
+ >;
+ ResultOrError<{{unpackedChain}}> ValidateAndUnpackChain(const {{as_cppType(type.name)}}* chain);
+
+ {% endif %}
+ {% endfor %}
+
+
} // namespace {{native_namespace}}
#endif // {{DIR}}_CHAIN_UTILS_H_