Support WGPUStringView in non-struct input arguments
- Add WGPUStringView struct. In C++ there is both wgpu::StringView and
wgpu::NullableStringView to differentiate different types of strings.
These structs appear identically in C.
- Add support for serializing / deserializing WGPUStringView in the wire,
with tests.
- Update methods with input args to take WGPUStringView in addition to
the legacy const char* path.
Methods with WGPUStringView are suffixed with "2", but overloaded
without the suffix in the C++ API.
- wgpu::StringView may be implicitly constructed from std::string_view,
const char* to ease use in C++. Add tests for
these constructors. wgpu::NullableStringView may be constructed from
nullptr and std::nullopt
- Add tests for passing wgpu::StringView or passing it via implicit
conversion into the API.
- WGPUStringView is passed into Dawn as std::string_view or as a
std::optional<std::string_view> depending on whether the arg is
nullable or not. This simplifies usage in Dawn and makes it more
type safe.
Bug: 42241188
Change-Id: Ia3dc6ef5d55f382dc8d132d3f997bb84c08cda5b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/198795
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index fd621aa..48e52a3 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -801,8 +801,8 @@
return ''.join([name.concatcase().lower() for name in names])
-def unreachable_code():
- assert False
+def unreachable_code(msg="unreachable_code"):
+ assert False, msg
############################################################
@@ -872,12 +872,13 @@
return False
return True
- # TODO(42240932): Remove this filtering once the deprecated "callback info" structures are
- # removed.
def include_structure(structure):
# TODO(352710628) support converting callback info.
if structure.name.canonical_case().endswith(" callback info"):
return False
+ if (structure.name.canonical_case() == "string view"
+ or structure.name.canonical_case() == "nullable string view"):
+ return False
return True
def jni_name(type):
@@ -924,6 +925,9 @@
# Special case for 'bool' because it has a typedef for compatibility.
if name.native and name.get() != 'bool':
return name.concatcase()
+ elif name.get() == 'nullable string view':
+ # nullable string view type doesn't exist in C.
+ return c_prefix + 'StringView'
else:
return c_prefix + name.CamelCase()
@@ -1270,6 +1274,10 @@
[RENDER_PARAMS_BASE, params_dawn]))
if 'cpp_headers' in targets:
+ imported_templates += [
+ "dawn/cpp_macros.tmpl",
+ ]
+
renders.append(
FileRender('api_cpp.h', 'include/dawn/' + api + '_cpp.h', [
RENDER_PARAMS_BASE, params_all, {
@@ -1324,6 +1332,10 @@
[RENDER_PARAMS_BASE, params_upstream]))
if 'emdawnwebgpu_headers' in targets:
+ imported_templates += [
+ "dawn/cpp_macros.tmpl",
+ ]
+
assert api == 'webgpu'
params_emscripten = parse_json(
loaded_json, enabled_tags=['compat', 'emscripten'])
@@ -1390,6 +1402,10 @@
}
]
+ imported_templates += [
+ "dawn/cpp_macros.tmpl",
+ ]
+
impl_dir = metadata.impl_dir + '/' if metadata.impl_dir else ''
native_dir = impl_dir + Name(metadata.native_namespace).Dirs()
namespace = metadata.namespace
@@ -1596,13 +1612,15 @@
by_category = params_kotlin['by_category']
for structure in by_category['structure']:
- renders.append(
- FileRender('art/api_kotlin_structure.kt',
- 'java/' + jni_name(structure) + '.kt', [
- RENDER_PARAMS_BASE, params_kotlin, {
- 'structure': structure
- }
- ]))
+ if (structure.name.get() != "string view"
+ and structure.name.get() != "nullable string view"):
+ renders.append(
+ FileRender('art/api_kotlin_structure.kt',
+ 'java/' + jni_name(structure) + '.kt', [
+ RENDER_PARAMS_BASE, params_kotlin, {
+ 'structure': structure
+ }
+ ]))
for obj in by_category['object']:
renders.append(
FileRender(
diff --git a/generator/templates/api.h b/generator/templates/api.h
index 16f5166..a5a5d59 100644
--- a/generator/templates/api.h
+++ b/generator/templates/api.h
@@ -97,7 +97,7 @@
{% endfor %}
// Structure forward declarations
-{% for type in by_category["structure"] %}
+{% for type in by_category["structure"] if type.name.get() != "nullable string view" %}
struct {{as_cType(type.name)}};
{% endfor %}
@@ -196,7 +196,8 @@
})
{% endfor %}
-{% for type in by_category["structure"] %}
+
+{% for type in by_category["structure"] if type.name.get() != "nullable string view" %}
{% for root in type.chain_roots %}
// Can be chained in {{as_cType(root.name)}}
{% endfor %}
diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h
index 410ab84..cec17a7 100644
--- a/generator/templates/api_cpp.h
+++ b/generator/templates/api_cpp.h
@@ -24,6 +24,8 @@
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+{% from 'dawn/cpp_macros.tmpl' import wgpu_string_constructors with context %}
+
{% set API = metadata.api.upper() %}
{% set api = API.lower() %}
{% set CAPI = metadata.c_prefix %}
@@ -41,7 +43,11 @@
#include <cstddef>
#include <cstdint>
#include <memory>
+#include <optional>
#include <functional>
+#include <string_view>
+#include <type_traits>
+#include <utility>
#include "{{c_header}}"
#include "{{api}}/{{api}}_cpp_chained_struct.h"
@@ -328,7 +334,7 @@
{% macro render_cpp_method_declaration(type, method, dfn=False) %}
{% set CppType = as_cppType(type.name) %}
{% set OriginalMethodName = method.name.CamelCase() %}
- {% set MethodName = OriginalMethodName[:-1] if method.name.chunks[-1] == "f" else OriginalMethodName %}
+ {% set MethodName = OriginalMethodName[:-1] if method.name.chunks[-1] == "f" or method.name.chunks[-1] == "2" else OriginalMethodName %}
{% set MethodName = CppType + "::" + MethodName if dfn else MethodName %}
{{"ConvertibleStatus" if method.return_type.name.get() == "status" else as_cppType(method.return_type.name)}} {{MethodName}}(
{%- for arg in method.arguments -%}
@@ -556,6 +562,14 @@
{{member_declaration}};
{% endif %}
{% endfor %}
+
+ //* Custom string constructors
+ {% if type.name.get() == "string view" %}
+ {{wgpu_string_constructors(as_cppType(type.name), false) | indent(4)}}
+ {% elif type.name.get() == "nullable string view" %}
+ {{wgpu_string_constructors(as_cppType(type.name), true) | indent(4)}}
+ {% endif %}
+
{% if type.has_free_members_function %}
private:
diff --git a/generator/templates/art/api_jni_types.kt b/generator/templates/art/api_jni_types.kt
index 60718c4..f27cd22 100644
--- a/generator/templates/art/api_jni_types.kt
+++ b/generator/templates/art/api_jni_types.kt
@@ -81,6 +81,6 @@
{%- elif member.type.name.get() == 'bool' -%}
Z
{%- else -%}
- {{ unreachable_code() }}
+ {{ unreachable_code('Unsupported type: ' + member.type.name.get()) }}
{%- endif -%}
{% endmacro %}
diff --git a/generator/templates/art/api_kotlin_types.kt b/generator/templates/art/api_kotlin_types.kt
index 65aef1b..2e41d4e 100644
--- a/generator/templates/art/api_kotlin_types.kt
+++ b/generator/templates/art/api_kotlin_types.kt
@@ -114,7 +114,7 @@
Long
{% endif %}
{%- else -%}
- {{ unreachable_code() }}
+ {{ unreachable_code('Unsupported type: ' + type.name.get()) }}
{%- endif %}
{% endmacro %}
diff --git a/generator/templates/dawn/cpp_macros.tmpl b/generator/templates/dawn/cpp_macros.tmpl
new file mode 100644
index 0000000..2539505
--- /dev/null
+++ b/generator/templates/dawn/cpp_macros.tmpl
@@ -0,0 +1,57 @@
+//* Copyright 2024 The Dawn & Tint Authors
+//*
+//* Redistribution and use in source and binary forms, with or without
+//* modification, are permitted provided that the following conditions are met:
+//*
+//* 1. Redistributions of source code must retain the above copyright notice, this
+//* list of conditions and the following disclaimer.
+//*
+//* 2. Redistributions in binary form must reproduce the above copyright notice,
+//* this list of conditions and the following disclaimer in the documentation
+//* and/or other materials provided with the distribution.
+//*
+//* 3. Neither the name of the copyright holder nor the names of its
+//* contributors may be used to endorse or promote products derived from
+//* this software without specific prior written permission.
+//*
+//* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{% macro wgpu_string_constructors(CppType, is_nullable) %}
+ // NOLINTNEXTLINE(runtime/explicit) allow implicit construction
+ inline constexpr {{CppType}}(const std::string_view& sv) noexcept {
+ this->data = sv.data();
+ this->length = sv.length();
+ }
+ // NOLINTNEXTLINE(runtime/explicit) allow implicit construction
+ inline constexpr {{CppType}}(const char* s) {
+ this->data = s;
+ this->length = SIZE_MAX; // use strlen
+ }
+ inline constexpr {{CppType}}(const char* data, size_t length) {
+ this->data = data;
+ this->length = length;
+ }
+ {% if is_nullable %}
+ inline constexpr {{CppType}}() noexcept = default;
+
+ // NOLINTNEXTLINE(runtime/explicit) allow implicit construction
+ inline constexpr {{CppType}}(std::nullptr_t) {
+ this->data = nullptr;
+ this->length = SIZE_MAX;
+ }
+ // NOLINTNEXTLINE(runtime/explicit) allow implicit construction
+ inline constexpr {{CppType}}(std::nullopt_t) {
+ this->data = nullptr;
+ this->length = SIZE_MAX;
+ }
+ {% endif %}
+{% endmacro %}
diff --git a/generator/templates/dawn/native/api_structs.cpp b/generator/templates/dawn/native/api_structs.cpp
index 59e835d..cf5c6c8 100644
--- a/generator/templates/dawn/native/api_structs.cpp
+++ b/generator/templates/dawn/native/api_structs.cpp
@@ -34,6 +34,8 @@
#include <tuple>
+#include "dawn/common/Assert.h"
+
#if defined(__GNUC__) || defined(__clang__)
// error: 'offsetof' within non-standard-layout type '{{namespace}}::XXX' is conditionally-supported
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
@@ -104,7 +106,7 @@
return copy;
}
{% endif %}
- bool {{CppType}}::operator==(const {{as_cppType(type.name)}}& rhs) const {
+ bool {{CppType}}::operator==(const {{CppType}}& rhs) const {
return {% if type.extensible or type.chained -%}
(nextInChain == rhs.nextInChain) &&
{%- endif %} std::tie(
@@ -166,4 +168,24 @@
{% endfor %}
+ StringView::operator std::string_view() const {
+ const bool isNull = this->data == nullptr;
+ const bool useStrlen = this->length == SIZE_MAX;
+ DAWN_ASSERT(!(isNull && useStrlen));
+ return std::string_view(this->data, isNull ? 0
+ : useStrlen ? strlen(this->data)
+ : this->length);
+ }
+
+ NullableStringView::operator std::optional<std::string_view>() const {
+ const bool isNull = this->data == nullptr;
+ const bool useStrlen = this->length == SIZE_MAX;
+ if (isNull && useStrlen) {
+ return std::nullopt;
+ }
+ return std::string_view(this->data, isNull ? 0
+ : useStrlen ? strlen(this->data)
+ : this->length);
+ }
+
} // namespace {{native_namespace}}
diff --git a/generator/templates/dawn/native/api_structs.h b/generator/templates/dawn/native/api_structs.h
index 33352e6..f2cecd0 100644
--- a/generator/templates/dawn/native/api_structs.h
+++ b/generator/templates/dawn/native/api_structs.h
@@ -24,6 +24,7 @@
//* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
//* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+{% from 'dawn/cpp_macros.tmpl' import wgpu_string_constructors with context %}
{% set namespace_name = Name(metadata.native_namespace) %}
{% set DIR = namespace_name.concatcase().upper() %}
@@ -38,7 +39,10 @@
{% set native_namespace = namespace_name.namespace_case() %}
{% set native_dir = impl_dir + namespace_name.Dirs() %}
#include "{{native_dir}}/Forward.h"
+
#include <cmath>
+#include <optional>
+#include <string_view>
namespace {{native_namespace}} {
@@ -64,24 +68,25 @@
using {{namespace}}::ChainedStructOut;
{% for type in by_category["structure"] %}
+ {% set CppType = as_cppType(type.name) %}
{% if type.chained %}
{% set chainedStructType = "ChainedStructOut" if type.chained == "out" else "ChainedStruct" %}
- struct {{as_cppType(type.name)}} : {{chainedStructType}} {
- {{as_cppType(type.name)}}() {
+ struct {{CppType}} : {{chainedStructType}} {
+ {{CppType}}() {
sType = {{namespace}}::SType::{{type.name.CamelCase()}};
}
{% else %}
- struct {{as_cppType(type.name)}} {
+ struct {{CppType}} {
{% if type.has_free_members_function %}
- {{as_cppType(type.name)}}() = default;
+ {{CppType}}() = default;
{% endif %}
{% endif %}
{% if type.has_free_members_function %}
- ~{{as_cppType(type.name)}}();
- {{as_cppType(type.name)}}(const {{as_cppType(type.name)}}&) = delete;
- {{as_cppType(type.name)}}& operator=(const {{as_cppType(type.name)}}&) = delete;
- {{as_cppType(type.name)}}({{as_cppType(type.name)}}&&);
- {{as_cppType(type.name)}}& operator=({{as_cppType(type.name)}}&&);
+ ~{{CppType}}();
+ {{CppType}}(const {{CppType}}&) = delete;
+ {{CppType}}& operator=(const {{CppType}}&) = delete;
+ {{CppType}}({{CppType}}&&);
+ {{CppType}}& operator=({{CppType}}&&);
{% endif %}
{% if type.extensible %}
@@ -93,23 +98,36 @@
{% if type.chained and loop.first %}
//* Align the first member after ChainedStruct to match the C struct layout.
//* It has to be aligned both to its natural and ChainedStruct's alignment.
- alignas({{namespace}}::{{as_cppType(type.name)}}::kFirstMemberAlignment) {{member_declaration}};
+ alignas({{namespace}}::{{CppType}}::kFirstMemberAlignment) {{member_declaration}};
{% else %}
{{member_declaration}};
{% endif %}
{% endfor %}
+ //* Custom string constructors / conversion
+ {% if type.name.get() == "string view" %}
+ {{wgpu_string_constructors(CppType, false) | indent(8)}}
+
+ // NOLINTNEXTLINE(runtime/explicit) allow implicit conversion
+ operator std::string_view() const;
+ {% elif type.name.get() == "nullable string view" %}
+ {{wgpu_string_constructors(CppType, true) | indent(8)}}
+
+ // NOLINTNEXTLINE(runtime/explicit) allow implicit conversion
+ operator std::optional<std::string_view>() const;
+ {% endif %}
+
{% if type.any_member_requires_struct_defaulting %}
// This method makes a copy of the struct, then, for any enum members with trivial
// defaulting (where something like "Undefined" is replaced with a default), applies
// all of the defaults for the struct, and recursively its by-value substructs (but
// NOT by-pointer substructs since they are const*). It must be called in an
// appropriate place in Dawn.
- [[nodiscard]] {{as_cppType(type.name)}} WithTrivialFrontendDefaults() const;
+ [[nodiscard]] {{CppType}} WithTrivialFrontendDefaults() const;
{% endif %}
// Equality operators, mostly for testing. Note that this tests
// strict pointer-pointer equality if the struct contains member pointers.
- bool operator==(const {{as_cppType(type.name)}}& rhs) const;
+ bool operator==(const {{CppType}}& rhs) const;
{% if type.has_free_members_function %}
private:
diff --git a/generator/templates/dawn/native/dawn_platform.h b/generator/templates/dawn/native/dawn_platform.h
index 6855f96..a1909c1 100644
--- a/generator/templates/dawn/native/dawn_platform.h
+++ b/generator/templates/dawn/native/dawn_platform.h
@@ -75,7 +75,7 @@
{% set ChainedStructName = Name("chained struct") %}
{{render_structure_conversions(ChainedStructName)|indent}}
- {% for type in by_category["structure"] %}
+ {% for type in by_category["structure"] if type.name.get() != "string view" and type.name.get() != "nullable string view" %}
{{render_structure_conversions(type.name)|indent}}
{% endfor %}
diff --git a/generator/templates/dawn/wire/WireCmd.cpp b/generator/templates/dawn/wire/WireCmd.cpp
index 39c34cf..f3338b5 100644
--- a/generator/templates/dawn/wire/WireCmd.cpp
+++ b/generator/templates/dawn/wire/WireCmd.cpp
@@ -202,29 +202,26 @@
{% continue %}
{% endif %}
//* Normal handling for pointer members and structs.
- {% if member.annotation != "value" or member.type.category == "structure" %}
+ {% if member.annotation != "value" %}
{% if member.type.category != "object" and member.optional %}
- if (record.{{as_varName(member.name)}} != nullptr) {
- {% else %}
- {
+ if (record.{{as_varName(member.name)}} != nullptr)
{% endif %}
- {% if member.annotation != "value" %}
- {% do assert(member.annotation != "const*const*", "const*const* not valid here") %}
- auto memberLength = {{member_length(member, "record.")}};
- auto size = WireAlignSizeofN<{{member_transfer_type(member)}}>(memberLength);
- DAWN_ASSERT(size);
- result += *size;
- //* Structures might contain more pointers so we need to add their extra size as well.
- {% if member.type.category == "structure" %}
- for (decltype(memberLength) i = 0; i < memberLength; ++i) {
- {% do assert(member.annotation == "const*" or member.annotation == "*", "unhandled annotation: " + member.annotation)%}
- result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}[i]);
- }
- {% endif %}
- {% elif member.type.category == "structure" %}
- result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}});
+ {
+ {% do assert(member.annotation != "const*const*", "const*const* not valid here") %}
+ auto memberLength = {{member_length(member, "record.")}};
+ auto size = WireAlignSizeofN<{{member_transfer_type(member)}}>(memberLength);
+ DAWN_ASSERT(size);
+ result += *size;
+ //* Structures might contain more pointers so we need to add their extra size as well.
+ {% if member.type.category == "structure" %}
+ for (decltype(memberLength) i = 0; i < memberLength; ++i) {
+ {% do assert(member.annotation == "const*" or member.annotation == "*", "unhandled annotation: " + member.annotation)%}
+ result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}[i]);
+ }
{% endif %}
}
+ {% elif member.type.category == "structure" %}
+ result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}});
{% endif %}
{% endfor %}
return result;
@@ -737,10 +734,95 @@
DeserializeAllocator* allocator,
const ObjectIdResolver& resolver);
+// Manually define serialization and deserialization for WGPUStringView because
+// it has a special encoding where:
+// { .data = nullptr, .length = SIZE_MAX } --> nil
+// { .data = non-null, .length = SIZE_MAX } --> null-terminated, use strlen
+// { .data = ..., .length = 0 } --> ""
+// { .data = ..., .length > 0 } --> string of size `length`
+struct WGPUStringViewTransfer {
+ bool has_data;
+ uint64_t length;
+};
+
+size_t WGPUStringViewGetExtraRequiredSize(const WGPUStringView& record) {
+ size_t size = record.length;
+ if (size == SIZE_MAX) {
+ // This is a null-terminated string, or it's nil.
+ size = record.data ? std::strlen(record.data) : 0;
+ }
+ return Align(size, kWireBufferAlignment);
+}
+
+WireResult WGPUStringViewSerialize(
+ const WGPUStringView& record,
+ WGPUStringViewTransfer* transfer,
+ SerializeBuffer* buffer) {
+
+ bool has_data = record.data != nullptr;
+ uint64_t length = record.length;
+ transfer->has_data = has_data;
+
+ if (!has_data) {
+ transfer->length = length;
+ return WireResult::Success;
+ }
+ if (length == SIZE_MAX) {
+ length = std::strlen(record.data);
+ }
+ if (length > 0) {
+ char* memberBuffer;
+ WIRE_TRY(buffer->NextN(length, &memberBuffer));
+ memcpy(memberBuffer, record.data, length);
+ }
+ transfer->length = length;
+ return WireResult::Success;
+}
+
+WireResult WGPUStringViewDeserialize(
+ WGPUStringView* record,
+ const volatile WGPUStringViewTransfer* transfer,
+ DeserializeBuffer* deserializeBuffer,
+ DeserializeAllocator* allocator) {
+
+ bool has_data = transfer->has_data;
+ uint64_t length = transfer->length;
+
+ if (length > SIZE_MAX) {
+ return WireResult::FatalError;
+ }
+ if (!has_data) {
+ record->data = nullptr;
+ if (length != 0 && length != SIZE_MAX) {
+ // Invalid string.
+ return WireResult::FatalError;
+ }
+ record->length = static_cast<size_t>(length);
+ return WireResult::Success;
+ }
+ if (length == 0) {
+ record->data = "";
+ record->length = 0;
+ return WireResult::Success;
+ }
+
+ size_t stringLength = static_cast<size_t>(length);
+ const volatile char* stringInBuffer;
+ WIRE_TRY(deserializeBuffer->ReadN(stringLength, &stringInBuffer));
+
+ char* copiedString;
+ WIRE_TRY(GetSpace(allocator, stringLength, &copiedString));
+ memcpy(copiedString, const_cast<const char*>(stringInBuffer), stringLength);
+
+ record->data = copiedString;
+ record->length = stringLength;
+ return WireResult::Success;
+}
+
//* Output structure [de]serialization first because it is used by commands.
{% for type in by_category["structure"] %}
{%- set name = as_cType(type.name) -%}
- {% if type.name.CamelCase() not in client_side_structures -%}
+ {% if type.name.CamelCase() not in client_side_structures and name != "WGPUStringView" -%}
{{write_record_serialization_helpers(type, name, type.members, is_cmd=False)}}
{% endif %}
{% endfor %}
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 706ea80..43adfa0 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -360,6 +360,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -394,6 +401,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -602,6 +616,20 @@
"category": "native",
"wasm type": "i"
},
+ "string view": {
+ "category": "structure",
+ "members": [
+ {"name": "data", "type": "char", "annotation": "const*", "optional": true},
+ {"name": "length", "type": "size_t", "default": "SIZE_MAX"}
+ ]
+ },
+ "nullable string view": {
+ "category": "structure",
+ "members": [
+ {"name": "data", "type": "char", "annotation": "const*", "optional": true},
+ {"name": "length", "type": "size_t", "default": "SIZE_MAX"}
+ ]
+ },
"buffer": {
"category": "object",
"methods": [
@@ -663,6 +691,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "get usage",
"no autolock": true,
"returns": "buffer usage"
@@ -834,6 +869,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -919,12 +961,25 @@
]
},
{
+ "name": "inject validation error 2",
+ "tags": ["dawn"],
+ "args": [
+ {"name": "message", "type": "string view"}
+ ]
+ },
+ {
"name": "insert debug marker",
"args": [
{"name": "marker label", "type": "char", "annotation": "const*", "length": "strlen"}
]
},
{
+ "name": "insert debug marker 2",
+ "args": [
+ {"name": "marker label", "type": "string view"}
+ ]
+ },
+ {
"name": "pop debug group",
"args": []
},
@@ -935,6 +990,12 @@
]
},
{
+ "name": "push debug group 2",
+ "args": [
+ {"name": "group label", "type": "string view"}
+ ]
+ },
+ {
"name": "resolve query set",
"args": [
{"name": "query set", "type": "query set"},
@@ -967,6 +1028,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -1084,6 +1152,12 @@
]
},
{
+ "name": "insert debug marker 2",
+ "args": [
+ {"name": "marker label", "type": "string view"}
+ ]
+ },
+ {
"name": "pop debug group",
"args": []
},
@@ -1094,6 +1168,12 @@
]
},
{
+ "name": "push debug group 2",
+ "args": [
+ {"name": "group label", "type": "string view"}
+ ]
+ },
+ {
"name": "set pipeline",
"args": [
{"name": "pipeline", "type": "compute pipeline"}
@@ -1140,6 +1220,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -1177,6 +1264,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -1493,6 +1587,15 @@
]
},
{
+ "name": "create error shader module 2",
+ "returns": "shader module",
+ "tags": ["dawn"],
+ "args": [
+ {"name": "descriptor", "type": "shader module descriptor", "annotation": "const*"},
+ {"name": "error message", "type": "string view"}
+ ]
+ },
+ {
"name": "create swap chain",
"tags": ["art", "dawn", "emscripten"],
"returns": "swap chain",
@@ -1591,6 +1694,14 @@
"tags": ["dawn"]
},
{
+ "name": "inject error 2",
+ "args": [
+ {"name": "type", "type": "error type"},
+ {"name": "message", "type": "string view"}
+ ],
+ "tags": ["dawn"]
+ },
+ {
"name": "force loss",
"args": [
{"name": "type", "type": "device lost reason"},
@@ -1599,6 +1710,14 @@
"tags": ["dawn"]
},
{
+ "name": "force loss 2",
+ "args": [
+ {"name": "type", "type": "device lost reason"},
+ {"name": "message", "type": "string view"}
+ ],
+ "tags": ["dawn"]
+ },
+ {
"name": "tick",
"no autolock": true,
"tags": ["dawn"]
@@ -1669,6 +1788,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "validate texture descriptor",
"tags": ["dawn"],
"args": [
@@ -1941,6 +2067,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "destroy",
"returns": "void"
},
@@ -2008,6 +2141,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "get properties",
"returns": "status",
"args": [
@@ -2073,6 +2213,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "get properties",
"returns": "status",
"args": [
@@ -2813,6 +2960,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -2895,6 +3049,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "get type",
"returns": "query type"
},
@@ -3006,6 +3167,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -3066,6 +3234,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -3129,6 +3304,12 @@
]
},
{
+ "name": "insert debug marker 2",
+ "args": [
+ {"name": "marker label", "type": "string view"}
+ ]
+ },
+ {
"name": "pop debug group",
"args": []
},
@@ -3139,6 +3320,12 @@
]
},
{
+ "name": "push debug group 2",
+ "args": [
+ {"name": "group label", "type": "string view"}
+ ]
+ },
+ {
"name": "set vertex buffer",
"args": [
{"name": "slot", "type": "uint32_t"},
@@ -3169,6 +3356,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -3340,6 +3534,12 @@
]
},
{
+ "name": "insert debug marker 2",
+ "args": [
+ {"name": "marker label", "type": "string view"}
+ ]
+ },
+ {
"name": "pop debug group",
"args": []
},
@@ -3350,6 +3550,12 @@
]
},
{
+ "name": "push debug group 2",
+ "args": [
+ {"name": "group label", "type": "string view"}
+ ]
+ },
+ {
"name": "set stencil reference",
"args": [
{"name": "reference", "type": "uint32_t"}
@@ -3429,6 +3635,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -3456,8 +3669,14 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
-
]
},
@@ -3640,6 +3859,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -3694,6 +3920,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -3828,11 +4061,17 @@
},
{
"name": "set label",
- "tags": [],
"returns": "void",
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
@@ -4066,6 +4305,13 @@
]
},
{
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
+ },
+ {
"name": "get width",
"returns": "uint32_t"
},
@@ -4329,6 +4575,13 @@
"args": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen"}
]
+ },
+ {
+ "name": "set label 2",
+ "returns": "void",
+ "args": [
+ {"name": "label", "type": "nullable string view"}
+ ]
}
]
},
diff --git a/src/dawn/native/CommandAllocator.cpp b/src/dawn/native/CommandAllocator.cpp
index 1fe51ee..ab73e78 100644
--- a/src/dawn/native/CommandAllocator.cpp
+++ b/src/dawn/native/CommandAllocator.cpp
@@ -228,4 +228,15 @@
mEndPtr = reinterpret_cast<char*>(&mPlaceholderSpace[1]);
}
+const char* CommandAllocator::CopyAsNullTerminatedString(std::string_view in) {
+ // Include extra null-terminator character. The string_view may not be
+ // null-terminated. It also may already have a null-terminator inside of
+ // it, in which case adding the null-terminator is unnecessary. However,
+ // this is unlikely, so always include the extra character.
+ char* out = AllocateData<char>(in.length() + 1);
+ memcpy(out, in.data(), in.length());
+ out[in.length()] = '\0';
+ return out;
+}
+
} // namespace dawn::native
diff --git a/src/dawn/native/CommandAllocator.h b/src/dawn/native/CommandAllocator.h
index 9ad0db7..ed5b4ae 100644
--- a/src/dawn/native/CommandAllocator.h
+++ b/src/dawn/native/CommandAllocator.h
@@ -32,6 +32,7 @@
#include <cstdint>
#include <limits>
#include <memory>
+#include <string_view>
#include <vector>
#include "dawn/common/Assert.h"
@@ -206,6 +207,8 @@
return result;
}
+ const char* CopyAsNullTerminatedString(std::string_view in);
+
private:
// This is used for some internal computations and can be any power of two as long as code
// using the CommandAllocator passes the static_asserts.
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index 3314159..aac38df 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -58,6 +58,7 @@
#include "dawn/native/RenderPassWorkaroundsHelper.h"
#include "dawn/native/RenderPipeline.h"
#include "dawn/native/ValidationUtils_autogen.h"
+#include "dawn/native/utils/WGPUHelpers.h"
#include "dawn/platform/DawnPlatform.h"
#include "dawn/platform/tracing/TraceEvent.h"
@@ -1875,23 +1876,25 @@
"encoding %s.ClearBuffer(%s, %u, %u).", this, buffer, offset, size);
}
-void CommandEncoder::APIInjectValidationError(const char* message) {
- if (!mEncodingContext.ConsumedError(mEncodingContext.CheckCurrentEncoder(this),
- "injecting validation error: %s.", message)) {
- mEncodingContext.HandleError(DAWN_MAKE_ERROR(InternalErrorType::Validation, message));
- }
+void CommandEncoder::APIInjectValidationError2(std::string_view message) {
+ message = utils::NormalizeLabel(message);
+ mEncodingContext.TryEncode(
+ this,
+ [&](CommandAllocator*) -> MaybeError {
+ return DAWN_MAKE_ERROR(InternalErrorType::Validation, std::string(message));
+ },
+ "injecting validation error: %s.", message);
}
-void CommandEncoder::APIInsertDebugMarker(const char* groupLabel) {
+void CommandEncoder::APIInsertDebugMarker2(std::string_view groupLabel) {
+ groupLabel = utils::NormalizeLabel(groupLabel);
mEncodingContext.TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
InsertDebugMarkerCmd* cmd =
allocator->Allocate<InsertDebugMarkerCmd>(Command::InsertDebugMarker);
- cmd->length = strlen(groupLabel);
-
- char* label = allocator->AllocateData<char>(cmd->length + 1);
- memcpy(label, groupLabel, cmd->length + 1);
+ cmd->length = groupLabel.length();
+ allocator->CopyAsNullTerminatedString(groupLabel);
return {};
},
@@ -1915,19 +1918,18 @@
"encoding %s.PopDebugGroup().", this);
}
-void CommandEncoder::APIPushDebugGroup(const char* groupLabel) {
+void CommandEncoder::APIPushDebugGroup2(std::string_view groupLabel) {
+ groupLabel = utils::NormalizeLabel(groupLabel);
mEncodingContext.TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
PushDebugGroupCmd* cmd =
allocator->Allocate<PushDebugGroupCmd>(Command::PushDebugGroup);
- cmd->length = strlen(groupLabel);
-
- char* label = allocator->AllocateData<char>(cmd->length + 1);
- memcpy(label, groupLabel, cmd->length + 1);
+ cmd->length = groupLabel.length();
+ const char* label = allocator->CopyAsNullTerminatedString(groupLabel);
mDebugGroupStackSize++;
- mEncodingContext.PushDebugGroupLabel(groupLabel);
+ mEncodingContext.PushDebugGroupLabel(std::string_view(label, cmd->length));
return {};
},
@@ -2036,7 +2038,7 @@
auto deviceLock(GetDevice()->GetScopedLock());
Ref<CommandBufferBase> commandBuffer;
- if (GetDevice()->ConsumedError(Finish(descriptor), &commandBuffer)) {
+ if (GetDevice()->ConsumedError(Finish(descriptor), &commandBuffer, "finishing %s.", this)) {
Ref<CommandBufferBase> errorCommandBuffer =
CommandBufferBase::MakeError(GetDevice(), descriptor ? descriptor->label : nullptr);
errorCommandBuffer->SetEncoderLabel(this->GetLabel());
diff --git a/src/dawn/native/CommandEncoder.h b/src/dawn/native/CommandEncoder.h
index 552e65a..e742ca1 100644
--- a/src/dawn/native/CommandEncoder.h
+++ b/src/dawn/native/CommandEncoder.h
@@ -89,10 +89,14 @@
const Extent3D* copySize);
void APIClearBuffer(BufferBase* destination, uint64_t destinationOffset, uint64_t size);
- void APIInjectValidationError(const char* message);
- void APIInsertDebugMarker(const char* groupLabel);
+ // TODO(crbug.com/42241188): Remove const char* version of the methods.
+ void APIInjectValidationError(const char* message) { APIInjectValidationError2(message); }
+ void APIInjectValidationError2(std::string_view message);
+ void APIInsertDebugMarker(const char* groupLabel) { APIInsertDebugMarker2(groupLabel); }
+ void APIInsertDebugMarker2(std::string_view groupLabel);
void APIPopDebugGroup();
- void APIPushDebugGroup(const char* groupLabel);
+ void APIPushDebugGroup(const char* groupLabel) { APIPushDebugGroup2(groupLabel); }
+ void APIPushDebugGroup2(std::string_view groupLabel);
void APIResolveQuerySet(QuerySetBase* querySet,
uint32_t firstQuery,
diff --git a/src/dawn/native/CompilationMessages.cpp b/src/dawn/native/CompilationMessages.cpp
index 11a7bd0..3fed812 100644
--- a/src/dawn/native/CompilationMessages.cpp
+++ b/src/dawn/native/CompilationMessages.cpp
@@ -90,13 +90,13 @@
OwnedCompilationMessages::~OwnedCompilationMessages() = default;
-void OwnedCompilationMessages::AddUnanchoredMessage(std::string message,
+void OwnedCompilationMessages::AddUnanchoredMessage(std::string_view message,
wgpu::CompilationMessageType type) {
AddMessage(message, {nullptr, nullptr, static_cast<WGPUCompilationMessageType>(type), 0, 0, 0,
0, 0, 0, 0});
}
-void OwnedCompilationMessages::AddMessageForTesting(std::string message,
+void OwnedCompilationMessages::AddMessageForTesting(std::string_view message,
wgpu::CompilationMessageType type,
uint64_t lineNum,
uint64_t linePos,
@@ -168,7 +168,7 @@
return {};
}
-void OwnedCompilationMessages::AddMessage(std::string messageString,
+void OwnedCompilationMessages::AddMessage(std::string_view messageString,
const WGPUCompilationMessage& message) {
// Cannot add messages after GetCompilationInfo has been called.
DAWN_ASSERT(mCompilationInfo.messages == nullptr);
@@ -177,7 +177,7 @@
// The message string won't be populated until GetCompilationInfo.
DAWN_ASSERT(message.message == nullptr);
- mMessageStrings.push_back(messageString);
+ mMessageStrings.push_back(std::string(messageString));
mMessages.push_back(message);
}
diff --git a/src/dawn/native/CompilationMessages.h b/src/dawn/native/CompilationMessages.h
index d77b085..55c7c8b 100644
--- a/src/dawn/native/CompilationMessages.h
+++ b/src/dawn/native/CompilationMessages.h
@@ -52,12 +52,12 @@
// Adds a message on line 0 (before the first line).
void AddUnanchoredMessage(
- std::string message,
+ std::string_view message,
wgpu::CompilationMessageType type = wgpu::CompilationMessageType::Info);
// For testing only. Uses the linePos/offset/length for both utf8 and utf16
// (which is incorrect for non-ASCII strings).
void AddMessageForTesting(
- std::string message,
+ std::string_view message,
wgpu::CompilationMessageType type = wgpu::CompilationMessageType::Info,
uint64_t lineNum = 0,
uint64_t linePos = 0,
@@ -72,7 +72,7 @@
private:
MaybeError AddMessage(const tint::diag::Diagnostic& diagnostic);
- void AddMessage(std::string messageString, const WGPUCompilationMessage& message);
+ void AddMessage(std::string_view messageString, const WGPUCompilationMessage& message);
void AddFormattedTintMessages(const tint::diag::List& diagnostics);
WGPUCompilationInfo mCompilationInfo;
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 4e88ab2..fb2ac7a 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -983,14 +983,15 @@
return {};
}
-void DeviceBase::APIForceLoss(wgpu::DeviceLostReason reason, const char* message) {
+void DeviceBase::APIForceLoss2(wgpu::DeviceLostReason reason, std::string_view message) {
+ message = utils::NormalizeLabel(message);
if (mState != State::Alive) {
return;
}
// Note that since we are passing None as the allowedErrors, an additional message will be
// appended noting that the error was unexpected. Since this call is for testing only it is not
// too important, but useful for users to understand where the extra message is coming from.
- HandleError(DAWN_INTERNAL_ERROR(message), InternalErrorType::None, ToAPI(reason));
+ HandleError(DAWN_INTERNAL_ERROR(std::string(message)), InternalErrorType::None, ToAPI(reason));
}
DeviceBase::State DeviceBase::GetState() const {
@@ -1604,8 +1605,8 @@
return ReturnToAPI(std::move(result));
}
-ShaderModuleBase* DeviceBase::APICreateErrorShaderModule(const ShaderModuleDescriptor* descriptor,
- const char* errorMessage) {
+ShaderModuleBase* DeviceBase::APICreateErrorShaderModule2(const ShaderModuleDescriptor* descriptor,
+ std::string_view errorMessage) {
Ref<ShaderModuleBase> result =
ShaderModuleBase::MakeError(this, descriptor ? descriptor->label : nullptr);
std::unique_ptr<OwnedCompilationMessages> compilationMessages(
@@ -2014,7 +2015,7 @@
return mEnabledFeatures.EnumerateFeatures(features);
}
-void DeviceBase::APIInjectError(wgpu::ErrorType type, const char* message) {
+void DeviceBase::APIInjectError2(wgpu::ErrorType type, std::string_view message) {
if (ConsumedError(ValidateErrorType(type))) {
return;
}
@@ -2027,7 +2028,9 @@
return;
}
- HandleError(DAWN_MAKE_ERROR(FromWGPUErrorType(type), message), InternalErrorType::OutOfMemory);
+ message = utils::NormalizeLabel(message);
+ HandleError(DAWN_MAKE_ERROR(FromWGPUErrorType(type), std::string(message)),
+ InternalErrorType::OutOfMemory);
}
void DeviceBase::APIValidateTextureDescriptor(const TextureDescriptor* descriptorOrig) {
@@ -2504,7 +2507,12 @@
}
void DeviceBase::APISetLabel(const char* label) {
- mLabel = label;
+ mLabel = label ? label : "";
+ SetLabelImpl();
+}
+
+void DeviceBase::APISetLabel2(std::optional<std::string_view> label) {
+ mLabel = utils::NormalizeLabel(label);
SetLabelImpl();
}
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 267829a..183ce56 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -279,7 +279,11 @@
SamplerBase* APICreateSampler(const SamplerDescriptor* descriptor);
ShaderModuleBase* APICreateShaderModule(const ShaderModuleDescriptor* descriptor);
ShaderModuleBase* APICreateErrorShaderModule(const ShaderModuleDescriptor* descriptor,
- const char* errorMessage);
+ const char* errorMessage) {
+ return APICreateErrorShaderModule2(descriptor, errorMessage);
+ }
+ ShaderModuleBase* APICreateErrorShaderModule2(const ShaderModuleDescriptor* descriptor,
+ std::string_view errorMessage);
// TODO(crbug.com/dawn/2320): Remove after deprecation.
SwapChainBase* APICreateSwapChain(Surface* surface, const SwapChainDescriptor* descriptor);
TextureBase* APICreateTexture(const TextureDescriptor* descriptor);
@@ -301,7 +305,11 @@
wgpu::Status APIGetLimits(SupportedLimits* limits) const;
bool APIHasFeature(wgpu::FeatureName feature) const;
size_t APIEnumerateFeatures(wgpu::FeatureName* features) const;
- void APIInjectError(wgpu::ErrorType type, const char* message);
+ void APIInjectError(wgpu::ErrorType type, const char* message) {
+ // TODO(crbug.com/42241188): Remove const char* version of the method.
+ APIInjectError2(type, message);
+ }
+ void APIInjectError2(wgpu::ErrorType type, std::string_view message);
bool APITick();
void APIValidateTextureDescriptor(const TextureDescriptor* desc);
@@ -371,7 +379,11 @@
void EmitLog(const char* message);
void EmitLog(WGPULoggingType loggingType, const char* message);
void EmitCompilationLog(const ShaderModuleBase* module);
- void APIForceLoss(wgpu::DeviceLostReason reason, const char* message);
+ void APIForceLoss(wgpu::DeviceLostReason reason, const char* message) {
+ // TODO(crbug.com/42241188): Remove const char* version of the method.
+ return APIForceLoss2(reason, message);
+ }
+ void APIForceLoss2(wgpu::DeviceLostReason reason, std::string_view message);
QueueBase* GetQueue() const;
friend class IgnoreLazyClearCountScope;
@@ -419,7 +431,9 @@
const CacheKey& GetCacheKey() const;
const std::string& GetLabel() const;
+ // TODO(crbug.com/42241188): Remove const char* version of the method.
void APISetLabel(const char* label);
+ void APISetLabel2(std::optional<std::string_view> label);
void APIDestroy();
virtual void AppendDebugLayerMessages(ErrorData* error) {}
diff --git a/src/dawn/native/EncodingContext.cpp b/src/dawn/native/EncodingContext.cpp
index e70bd2f..1000783 100644
--- a/src/dawn/native/EncodingContext.cpp
+++ b/src/dawn/native/EncodingContext.cpp
@@ -208,7 +208,7 @@
return std::move(mComputePassUsages);
}
-void EncodingContext::PushDebugGroupLabel(const char* groupLabel) {
+void EncodingContext::PushDebugGroupLabel(std::string_view groupLabel) {
mDebugGroupLabels.emplace_back(groupLabel);
}
diff --git a/src/dawn/native/EncodingContext.h b/src/dawn/native/EncodingContext.h
index 601c5fe..909ced9 100644
--- a/src/dawn/native/EncodingContext.h
+++ b/src/dawn/native/EncodingContext.h
@@ -160,7 +160,7 @@
RenderPassUsages AcquireRenderPassUsages();
ComputePassUsages AcquireComputePassUsages();
- void PushDebugGroupLabel(const char* groupLabel);
+ void PushDebugGroupLabel(std::string_view groupLabel);
void PopDebugGroupLabel();
private:
@@ -195,7 +195,7 @@
bool mDestroyed = false;
std::unique_ptr<ErrorData> mError;
- std::vector<std::string> mDebugGroupLabels;
+ std::vector<std::string_view> mDebugGroupLabels;
};
} // namespace dawn::native
diff --git a/src/dawn/native/ErrorData.cpp b/src/dawn/native/ErrorData.cpp
index 9d111c8..fd01df5 100644
--- a/src/dawn/native/ErrorData.cpp
+++ b/src/dawn/native/ErrorData.cpp
@@ -43,7 +43,7 @@
const char* file,
const char* function,
int line) {
- std::unique_ptr<ErrorData> error = std::make_unique<ErrorData>(type, message);
+ std::unique_ptr<ErrorData> error = std::make_unique<ErrorData>(type, std::move(message));
error->AppendBacktrace(file, function, line);
auto [var, present] = GetEnvironmentVar("DAWN_DEBUG_BREAK_ON_ERROR");
@@ -72,8 +72,8 @@
mContexts.push_back(std::move(context));
}
-void ErrorData::AppendDebugGroup(std::string label) {
- mDebugGroups.push_back(std::move(label));
+void ErrorData::AppendDebugGroup(std::string_view label) {
+ mDebugGroups.push_back(std::string(label));
}
void ErrorData::AppendBackendMessage(std::string message) {
diff --git a/src/dawn/native/ErrorData.h b/src/dawn/native/ErrorData.h
index 38956d8..c1d72d2 100644
--- a/src/dawn/native/ErrorData.h
+++ b/src/dawn/native/ErrorData.h
@@ -75,7 +75,7 @@
AppendContext(absl::StrFormat("[Failed to format error: \"%s\"]", formatStr));
}
}
- void AppendDebugGroup(std::string label);
+ void AppendDebugGroup(std::string_view label);
void AppendBackendMessage(std::string message);
template <typename... Args>
void AppendBackendMessage(const char* formatStr, const Args&... args) {
diff --git a/src/dawn/native/ObjectBase.cpp b/src/dawn/native/ObjectBase.cpp
index c3e9fbe..2242825 100644
--- a/src/dawn/native/ObjectBase.cpp
+++ b/src/dawn/native/ObjectBase.cpp
@@ -34,6 +34,7 @@
#include "dawn/native/Device.h"
#include "dawn/native/ObjectBase.h"
#include "dawn/native/ObjectType_autogen.h"
+#include "dawn/native/utils/WGPUHelpers.h"
namespace dawn::native {
@@ -105,7 +106,11 @@
}
void ApiObjectBase::APISetLabel(const char* label) {
- SetLabel(label);
+ SetLabel(std::string(label ? label : ""));
+}
+
+void ApiObjectBase::APISetLabel2(std::optional<std::string_view> label) {
+ SetLabel(std::string(utils::NormalizeLabel(label)));
}
void ApiObjectBase::SetLabel(std::string label) {
diff --git a/src/dawn/native/ObjectBase.h b/src/dawn/native/ObjectBase.h
index 67c9465..ed80cb7 100644
--- a/src/dawn/native/ObjectBase.h
+++ b/src/dawn/native/ObjectBase.h
@@ -29,6 +29,7 @@
#define SRC_DAWN_NATIVE_OBJECTBASE_H_
#include <mutex>
+#include <optional>
#include <string>
#include "absl/strings/str_format.h"
@@ -139,7 +140,9 @@
void Destroy();
// Dawn API
+ // TODO(crbug.com/42241188): Remove const char* version of the method.
void APISetLabel(const char* label);
+ void APISetLabel2(std::optional<std::string_view> label);
protected:
// Overriding of the RefCounted's DeleteThis function ensures that instances of objects
diff --git a/src/dawn/native/ProgrammableEncoder.cpp b/src/dawn/native/ProgrammableEncoder.cpp
index 3372931..47f7af0 100644
--- a/src/dawn/native/ProgrammableEncoder.cpp
+++ b/src/dawn/native/ProgrammableEncoder.cpp
@@ -38,6 +38,7 @@
#include "dawn/native/Device.h"
#include "dawn/native/ObjectType_autogen.h"
#include "dawn/native/ValidationUtils_autogen.h"
+#include "dawn/native/utils/WGPUHelpers.h"
namespace dawn::native {
@@ -67,16 +68,15 @@
return {};
}
-void ProgrammableEncoder::APIInsertDebugMarker(const char* groupLabel) {
+void ProgrammableEncoder::APIInsertDebugMarker2(std::string_view groupLabel) {
+ groupLabel = utils::NormalizeLabel(groupLabel);
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
InsertDebugMarkerCmd* cmd =
allocator->Allocate<InsertDebugMarkerCmd>(Command::InsertDebugMarker);
- cmd->length = strlen(groupLabel);
-
- char* label = allocator->AllocateData<char>(cmd->length + 1);
- memcpy(label, groupLabel, cmd->length + 1);
+ cmd->length = groupLabel.length();
+ allocator->CopyAsNullTerminatedString(groupLabel);
return {};
},
@@ -100,19 +100,18 @@
"encoding %s.PopDebugGroup().", this);
}
-void ProgrammableEncoder::APIPushDebugGroup(const char* groupLabel) {
+void ProgrammableEncoder::APIPushDebugGroup2(std::string_view groupLabel) {
+ groupLabel = utils::NormalizeLabel(groupLabel);
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
PushDebugGroupCmd* cmd =
allocator->Allocate<PushDebugGroupCmd>(Command::PushDebugGroup);
- cmd->length = strlen(groupLabel);
-
- char* label = allocator->AllocateData<char>(cmd->length + 1);
- memcpy(label, groupLabel, cmd->length + 1);
+ cmd->length = groupLabel.length();
+ const char* label = allocator->CopyAsNullTerminatedString(groupLabel);
mDebugGroupStackSize++;
- mEncodingContext->PushDebugGroupLabel(groupLabel);
+ mEncodingContext->PushDebugGroupLabel(std::string_view(label, cmd->length));
return {};
},
diff --git a/src/dawn/native/ProgrammableEncoder.h b/src/dawn/native/ProgrammableEncoder.h
index 74583e4..cb89145 100644
--- a/src/dawn/native/ProgrammableEncoder.h
+++ b/src/dawn/native/ProgrammableEncoder.h
@@ -48,9 +48,12 @@
public:
ProgrammableEncoder(DeviceBase* device, const char* label, EncodingContext* encodingContext);
- void APIInsertDebugMarker(const char* groupLabel);
+ // TODO(crbug.com/42241188): Remove const char* version of the methods.
+ void APIInsertDebugMarker(const char* groupLabel) { APIInsertDebugMarker2(groupLabel); }
+ void APIInsertDebugMarker2(std::string_view groupLabel);
void APIPopDebugGroup();
- void APIPushDebugGroup(const char* groupLabel);
+ void APIPushDebugGroup(const char* groupLabel) { APIPushDebugGroup2(groupLabel); }
+ void APIPushDebugGroup2(std::string_view groupLabel);
protected:
bool IsValidationEnabled() const;
diff --git a/src/dawn/native/Surface.cpp b/src/dawn/native/Surface.cpp
index c0b322b..fbad6ef 100644
--- a/src/dawn/native/Surface.cpp
+++ b/src/dawn/native/Surface.cpp
@@ -661,7 +661,11 @@
}
void Surface::APISetLabel(const char* label) {
- mLabel = label;
+ mLabel = label ? label : "";
+}
+
+void Surface::APISetLabel2(std::optional<std::string_view> label) {
+ mLabel = utils::NormalizeLabel(label);
}
} // namespace dawn::native
diff --git a/src/dawn/native/Surface.h b/src/dawn/native/Surface.h
index 9ac666a..3907328 100644
--- a/src/dawn/native/Surface.h
+++ b/src/dawn/native/Surface.h
@@ -128,7 +128,9 @@
wgpu::TextureFormat APIGetPreferredFormat(AdapterBase* adapter) const;
void APIPresent();
void APIUnconfigure();
+ // TODO(crbug.com/42241188): Remove const char* version of the method.
void APISetLabel(const char* label);
+ void APISetLabel2(std::optional<std::string_view> label);
private:
Surface(InstanceBase* instance, ErrorMonad::ErrorTag tag);
diff --git a/src/dawn/native/utils/WGPUHelpers.cpp b/src/dawn/native/utils/WGPUHelpers.cpp
index b1606ac..74d61a1 100644
--- a/src/dawn/native/utils/WGPUHelpers.cpp
+++ b/src/dawn/native/utils/WGPUHelpers.cpp
@@ -211,4 +211,16 @@
return (label == nullptr || strlen(label) == 0) ? "None" : label;
}
+std::string_view NormalizeLabel(std::string_view in) {
+ return std::string_view(in.data(), strnlen(in.data(), in.length()));
+}
+
+std::string_view NormalizeLabel(std::optional<std::string_view> in) {
+ if (in) {
+ return NormalizeLabel(*in);
+ } else {
+ return {};
+ }
+}
+
} // namespace dawn::native::utils
diff --git a/src/dawn/native/utils/WGPUHelpers.h b/src/dawn/native/utils/WGPUHelpers.h
index c501fd7..745a16d 100644
--- a/src/dawn/native/utils/WGPUHelpers.h
+++ b/src/dawn/native/utils/WGPUHelpers.h
@@ -167,6 +167,10 @@
*apiSize = 0;
}
+// Normalize the label, truncating it at the first null-terminator, if any.
+std::string_view NormalizeLabel(std::string_view in);
+std::string_view NormalizeLabel(std::optional<std::string_view> in);
+
} // namespace dawn::native::utils
#endif // SRC_DAWN_NATIVE_UTILS_WGPUHELPERS_H_
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index bf0d055..6b6321f 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -327,6 +327,7 @@
"unittests/CommandAllocatorTests.cpp",
"unittests/CommandLineParserTests.cpp",
"unittests/ContentLessObjectCacheTests.cpp",
+ "unittests/CppAPITests.cpp",
"unittests/DefaultTests.cpp",
"unittests/EnumClassBitmasksTests.cpp",
"unittests/EnumMaskIteratorTests.cpp",
diff --git a/src/dawn/tests/unittests/CppAPITests.cpp b/src/dawn/tests/unittests/CppAPITests.cpp
new file mode 100644
index 0000000..dcb72a0
--- /dev/null
+++ b/src/dawn/tests/unittests/CppAPITests.cpp
@@ -0,0 +1,225 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "dawn/native/dawn_platform.h"
+
+namespace dawn::native {
+namespace {
+
+// Test that default construction or assignment to wgpu::NullableStringView produces the nil string.
+TEST(CppAPITests, WGPUStringDefault) {
+ {
+ wgpu::NullableStringView s;
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s{};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = {};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = wgpu::NullableStringView();
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+
+ // Test that resetting the string, clears both data and length.
+ std::string_view sv("hello world!");
+ {
+ wgpu::NullableStringView s(sv);
+ s = {};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s(sv);
+ s = wgpu::NullableStringView();
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+}
+
+// Test that construction or assignment to wgpu::NullableStringView from const char*.
+TEST(CppAPITests, WGPUStringFromCstr) {
+ {
+ wgpu::NullableStringView s("hello world!");
+ EXPECT_STREQ(s.data, "hello world!");
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s{"hello world!"};
+ EXPECT_STREQ(s.data, "hello world!");
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = {"hello world!"};
+ EXPECT_STREQ(s.data, "hello world!");
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = wgpu::NullableStringView("hello world!");
+ EXPECT_STREQ(s.data, "hello world!");
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+
+ // Test that setting to a cstr clears the length.
+ std::string_view sv("hello world!");
+ {
+ wgpu::NullableStringView s(sv);
+ s = "other str";
+ EXPECT_STREQ(s.data, "other str");
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+}
+
+// Test that construction or assignment to wgpu::NullableStringView from std::string_view
+TEST(CppAPITests, WGPUStringFromStdStringView) {
+ std::string_view sv("hello\x00world!");
+ {
+ wgpu::NullableStringView s(sv);
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+ {
+ wgpu::NullableStringView s{sv};
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+ {
+ wgpu::NullableStringView s = {sv};
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+ {
+ wgpu::NullableStringView s = wgpu::NullableStringView(sv);
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+}
+
+// Test that construction or assignment to wgpu::NullableStringView from pointer and length
+TEST(CppAPITests, WGPUStringFromPtrAndLength) {
+ std::string_view sv("hello\x00world!");
+ {
+ wgpu::NullableStringView s(sv.data(), sv.length());
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+ {
+ wgpu::NullableStringView s{sv.data(), sv.length()};
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+ {
+ wgpu::NullableStringView s = {sv.data(), sv.length()};
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+ {
+ wgpu::NullableStringView s = wgpu::NullableStringView(sv.data(), sv.length());
+ EXPECT_EQ(s.data, sv.data());
+ EXPECT_EQ(s.length, sv.length());
+ }
+}
+
+// Test that construction or assignment to wgpu::NullableStringView from nullptr
+TEST(CppAPITests, WGPUStringFromNullptr) {
+ {
+ wgpu::NullableStringView s(nullptr);
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s{nullptr};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = {nullptr};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = wgpu::NullableStringView(nullptr);
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+
+ // Test that setting to nullptr, clears both data and length.
+ std::string_view sv("hello world!");
+ {
+ wgpu::NullableStringView s(sv);
+ s = nullptr;
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+}
+
+// Test that construction or assignment to wgpu::NullableStringView from std::nullopt
+TEST(CppAPITests, WGPUStringFromNullopt) {
+ {
+ wgpu::NullableStringView s(std::nullopt);
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s{std::nullopt};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = {std::nullopt};
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+ {
+ wgpu::NullableStringView s = wgpu::NullableStringView(std::nullopt);
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+
+ // Test that setting to std::nullopt, clears both data and length.
+ std::string_view sv("hello world!");
+ {
+ wgpu::NullableStringView s(sv);
+ s = std::nullopt;
+ EXPECT_EQ(s.data, nullptr);
+ EXPECT_EQ(s.length, SIZE_MAX);
+ }
+}
+
+} // anonymous namespace
+} // namespace dawn::native
diff --git a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
index 093876f..0e7ab0b 100644
--- a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
@@ -37,6 +37,7 @@
namespace {
using ::testing::HasSubstr;
+using ::testing::StartsWith;
class CommandBufferValidationTest : public ValidationTest {};
@@ -342,6 +343,80 @@
ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("my error"));
}
+// Test that calling inject validation error with a std::string_view produces an error which
+// preserves the string.
+TEST_F(CommandBufferValidationTest, InjectedValidateErrorStringView) {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ std::string_view sv = "my error";
+ encoder.InjectValidationError(sv);
+ ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr(sv));
+}
+
+// Test that calling inject validation error with various wgpu::NullableStringView produces
+// an error which preserves the string.
+TEST_F(CommandBufferValidationTest, InjectedValidateErrorVariousStringTypes) {
+ // Use strlen
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ const char* s = "my error";
+ encoder.InjectValidationError(s);
+ ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr(s));
+ }
+
+ // Use explicit length which truncates a null-terminated string.
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.InjectValidationError(std::string_view("my error bad", 8));
+ ASSERT_DEVICE_ERROR(encoder.Finish(),
+ testing::AllOf(HasSubstr("my error"), Not(HasSubstr("bad"))));
+ }
+
+ // Empty, nullptr string
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.InjectValidationError(std::string_view(nullptr, 0));
+ // empty error string, followed by a newline and error context
+ ASSERT_DEVICE_ERROR(encoder.Finish(), StartsWith("\n"));
+ }
+
+ // Empty, non-null string
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.InjectValidationError(std::string_view("foobar", 0));
+ // empty error string, followed by a newline and error context
+ ASSERT_DEVICE_ERROR(encoder.Finish(), StartsWith("\n"));
+ }
+
+ // Set label on encoder and inject validation error
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.SetLabel("my encoder");
+ encoder.InjectValidationError("err");
+ ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("my encoder"));
+ }
+
+ // Set label on encoder, then clear it, and inject validation error
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.SetLabel("my encoder");
+ encoder.SetLabel(std::nullopt);
+ encoder.InjectValidationError("err");
+ ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("CommandEncoder (unlabeled)"));
+ }
+
+ // Encoder label has a null terminator and the injected error has a null terminator.
+ // Both get truncated at the null terminator, but they don't truncate each other.
+ // The error should have both the first part of the encoder label and the first
+ // part of the injected error.
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.SetLabel(std::string_view("my\0encoder", 10));
+ encoder.InjectValidationError(std::string_view("err\0or", 6));
+ ASSERT_DEVICE_ERROR(encoder.Finish(), AllOf(HasSubstr("validation error: err."),
+ HasSubstr("[CommandEncoder \"my\"]")));
+ }
+}
+
TEST_F(CommandBufferValidationTest, DestroyEncoder) {
// Skip these tests if we are using wire because the destroy functionality is not exposed
// and needs to use a cast to call manually. We cannot test this in the wire case since the
diff --git a/src/dawn/tests/unittests/wire/WireArgumentTests.cpp b/src/dawn/tests/unittests/wire/WireArgumentTests.cpp
index 517ad89..ee11864 100644
--- a/src/dawn/tests/unittests/wire/WireArgumentTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireArgumentTests.cpp
@@ -35,9 +35,18 @@
namespace {
using testing::_;
+using testing::AllOf;
+using testing::Eq;
+using testing::Field;
using testing::Return;
using testing::Sequence;
+MATCHER_P2(EqBytes, bytes, size, "") {
+ const char* dataToCheck = arg;
+ bool isMatch = (memcmp(dataToCheck, bytes, size) == 0);
+ return isMatch;
+}
+
class WireArgumentTests : public WireTest {
public:
WireArgumentTests() {}
@@ -164,6 +173,57 @@
FlushClient();
}
+// Test that the wire is able to send WGPUStringViews
+TEST_F(WireArgumentTests, WGPUStringView) {
+ // Create shader module
+ wgpu::ShaderModuleDescriptor vertexDescriptor = {};
+ wgpu::ShaderModule vsModule = device.CreateShaderModule(&vertexDescriptor);
+ WGPUShaderModule apiVsModule = api.GetNewShaderModule();
+ EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
+
+ const char* label = "null-terminated label\0more string";
+ vsModule.SetLabel(std::string_view(label));
+ EXPECT_CALL(api, ShaderModuleSetLabel2(apiVsModule,
+ AllOf(Field(&WGPUStringView::data, EqBytes(label, 21u)),
+ Field(&WGPUStringView::length, Eq(21u)))));
+ FlushClient();
+
+ // Give it a longer, explicit length that contains the null-terminator.
+ vsModule.SetLabel(std::string_view(label, 34));
+ EXPECT_CALL(api, ShaderModuleSetLabel2(apiVsModule,
+ AllOf(Field(&WGPUStringView::data, EqBytes(label, 34u)),
+ Field(&WGPUStringView::length, Eq(34u)))));
+ FlushClient();
+
+ // Give it a shorder, explicit length.
+ vsModule.SetLabel(std::string_view(label, 2));
+ EXPECT_CALL(api, ShaderModuleSetLabel2(apiVsModule,
+ AllOf(Field(&WGPUStringView::data, EqBytes(label, 2u)),
+ Field(&WGPUStringView::length, Eq(2u)))));
+ FlushClient();
+
+ // Give it a zero length.
+ vsModule.SetLabel(std::string_view(label, 0));
+ EXPECT_CALL(
+ api, ShaderModuleSetLabel2(apiVsModule, AllOf(Field(&WGPUStringView::data, EqBytes("", 1u)),
+ Field(&WGPUStringView::length, Eq(0u)))));
+ FlushClient();
+
+ // Give it zero length and data.
+ vsModule.SetLabel(std::string_view(nullptr, 0));
+ EXPECT_CALL(api,
+ ShaderModuleSetLabel2(apiVsModule, AllOf(Field(&WGPUStringView::data, nullptr),
+ Field(&WGPUStringView::length, Eq(0u)))));
+ FlushClient();
+
+ // Give it the nil string with nullopt.
+ vsModule.SetLabel(std::nullopt);
+ EXPECT_CALL(api, ShaderModuleSetLabel2(apiVsModule,
+ AllOf(Field(&WGPUStringView::data, nullptr),
+ Field(&WGPUStringView::length, Eq(SIZE_MAX)))));
+ FlushClient();
+}
+
// Test that the wire is able to send objects as value arguments
TEST_F(WireArgumentTests, ObjectAsValueArgument) {
wgpu::CommandEncoder cmdBufEncoder = device.CreateCommandEncoder();