dawn::native: Implement wgslLanguageFeatures.
Adds support for Instance::HasWGSLLanguageFeature and
Instance::EnumerateWGSLLanguageFeatures based on the data provided by
Tint in src/tint/lang/wgsl/features/status.h. A toggle is added to (by
default) hide the "chromium_testing_*" language features.
Use these testing features in a unittests.
Make the device use the instance's list of enabled language features to
pass the AllowedWGSLFeatures to Tint's WGSL reader.
Bug: dawn:2260
Change-Id: Ia6de5f9daf7d5a4f04c017624c32cf149382c6be
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/162500
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index 8657d95..b182288 100644
--- a/dawn.json
+++ b/dawn.json
@@ -2052,6 +2052,20 @@
{"name": "options", "type": "request adapter options", "annotation": "const*", "optional": true, "no_default": true},
{"name": "callback info", "type": "request adapter callback info"}
]
+ },
+ {
+ "name": "has WGSL language feature",
+ "returns": "bool",
+ "args": [
+ {"name": "feature", "type": "WGSL feature name"}
+ ]
+ },
+ {
+ "name": "enumerate WGSL language features",
+ "returns": "size_t",
+ "args": [
+ {"name": "features", "type": "WGSL feature name", "annotation": "*"}
+ ]
}
]
},
@@ -3664,6 +3678,21 @@
{"value": 31, "name": "unorm 10_10_10_2", "jsrepr": "'unorm10-10-10-2'"}
]
},
+ "WGSL feature name": {
+ "category": "enum",
+ "values": [
+ {"value": 0, "name": "undefined", "valid": false, "jsrepr": "undefined"},
+ {"value": 1, "name": "readonly and readwrite storage textures"},
+ {"value": 2, "name": "packed 4x8 integer dot product"},
+ {"value": 3, "name": "unrestricted pointer parameters"},
+ {"value": 4, "name": "pointer composite access"},
+ {"value": 1000, "name": "chromium testing unimplemented", "tags": ["dawn"]},
+ {"value": 1001, "name": "chromium testing unsafe experimental", "tags": ["dawn"]},
+ {"value": 1002, "name": "chromium testing experimental", "tags": ["dawn"]},
+ {"value": 1003, "name": "chromium testing shipped with killswitch", "tags": ["dawn"]},
+ {"value": 1004, "name": "chromium testing shipped", "tags": ["dawn"]}
+ ]
+ },
"whole size" : {
"category": "constant",
"type": "uint64_t",
diff --git a/dawn_wire.json b/dawn_wire.json
index cc541ed..831f8be 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -216,6 +216,8 @@
"DeviceSetDeviceLostCallback",
"DeviceSetUncapturedErrorCallback",
"DeviceSetLoggingCallback",
+ "InstanceEnumerateWGSLLanguageFeatures",
+ "InstanceHasWGSLLanguageFeature",
"InstanceRequestAdapter",
"InstanceRequestAdapterF",
"ShaderModuleGetCompilationInfo",
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 8f9ad3c..f9c900c 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -63,7 +63,7 @@
return chunk[0].upper() + chunk[1:]
def canonical_case(self):
- return (' '.join(self.chunks)).lower()
+ return ' '.join(self.chunks)
def concatcase(self):
return ''.join(self.chunks)
diff --git a/src/dawn/common/WGSLFeatureMapping.h b/src/dawn/common/WGSLFeatureMapping.h
new file mode 100644
index 0000000..d570b82
--- /dev/null
+++ b/src/dawn/common/WGSLFeatureMapping.h
@@ -0,0 +1,40 @@
+// Copyright 2023 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.
+
+#ifndef SRC_DAWN_COMMON_WGSLFEATUREMAPPING_H_
+#define SRC_DAWN_COMMON_WGSLFEATUREMAPPING_H_
+
+#define DAWN_FOREACH_WGSL_FEATURE(X) \
+ X(kUndefined, Undefined) \
+ X(kReadonlyAndReadwriteStorageTextures, ReadonlyAndReadwriteStorageTextures) \
+ X(kChromiumTestingUnimplemented, ChromiumTestingUnimplemented) \
+ X(kChromiumTestingUnsafeExperimental, ChromiumTestingUnsafeExperimental) \
+ X(kChromiumTestingExperimental, ChromiumTestingExperimental) \
+ X(kChromiumTestingShippedWithKillswitch, ChromiumTestingShippedWithKillswitch) \
+ X(kChromiumTestingShipped, ChromiumTestingShipped)
+
+#endif // SRC_DAWN_COMMON_WGSLFEATUREMAPPING_H_
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 497dc0d..487bcc0 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -159,6 +159,7 @@
":utils_gen",
"${dawn_root}/src/dawn/common",
"${dawn_root}/src/tint/api",
+ "${dawn_root}/src/tint/lang/wgsl/features",
# TODO(dawn:286): These should only be necessary if SPIR-V validation is
# enabled with dawn_enable_spirv_validation
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 0687856..6825209 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -1465,10 +1465,6 @@
if (IsToggleEnabled(Toggle::AllowUnsafeAPIs)) {
mWGSLAllowedFeatures.extensions.insert(
tint::wgsl::Extension::kChromiumDisableUniformityAnalysis);
-
- // Allow language features that are still under development.
- mWGSLAllowedFeatures.features.insert(
- tint::wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures);
}
if (mEnabledFeatures.IsEnabled(Feature::DualSourceBlending)) {
mWGSLAllowedFeatures.extensions.insert(
@@ -1483,6 +1479,13 @@
mWGSLAllowedFeatures.extensions.insert(
tint::wgsl::Extension::kChromiumExperimentalFramebufferFetch);
}
+
+ // Language features are enabled instance-wide.
+ // mAdapter is not set for mock test devices.
+ // TODO(crbug.com/dawn/1702): using a mock adapter and instance could avoid the null checking.
+ if (mAdapter != nullptr) {
+ mWGSLAllowedFeatures.features = GetInstance()->GetAllowedWGSLLanguageFeatures();
+ }
}
const tint::wgsl::AllowedFeatures& DeviceBase::GetWGSLAllowedFeatures() const {
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index e9ead1b..73890af 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -74,8 +74,6 @@
struct ShaderModuleParseResult;
struct TrackedFutureWaitInfo;
-using WGSLExtensionSet = std::unordered_set<std::string>;
-
class DeviceBase : public RefCountedWithExternalCount {
public:
DeviceBase(AdapterBase* adapter,
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index 2a9069b..dac0627 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -34,6 +34,7 @@
#include "dawn/common/GPUInfo.h"
#include "dawn/common/Log.h"
#include "dawn/common/SystemUtils.h"
+#include "dawn/common/WGSLFeatureMapping.h"
#include "dawn/native/CallbackTaskManager.h"
#include "dawn/native/ChainUtils.h"
#include "dawn/native/Device.h"
@@ -42,6 +43,7 @@
#include "dawn/native/Toggles.h"
#include "dawn/native/ValidationUtils_autogen.h"
#include "dawn/platform/DawnPlatform.h"
+#include "tint/lang/wgsl/features/status.h"
// For SwiftShader fallback
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
@@ -106,6 +108,31 @@
return nullptr;
}
+wgpu::WGSLFeatureName ToWGPUFeature(tint::wgsl::LanguageFeature f) {
+ switch (f) {
+#define CASE(WgslName, WgpuName) \
+ case tint::wgsl::LanguageFeature::WgslName: \
+ return wgpu::WGSLFeatureName::WgpuName;
+ DAWN_FOREACH_WGSL_FEATURE(CASE)
+#undef CASE
+ }
+}
+
+tint::wgsl::LanguageFeature ToWGSLFeature(wgpu::WGSLFeatureName f) {
+ switch (f) {
+#define CASE(WgslName, WgpuName) \
+ case wgpu::WGSLFeatureName::WgpuName: \
+ return tint::wgsl::LanguageFeature::WgslName;
+ DAWN_FOREACH_WGSL_FEATURE(CASE)
+#undef CASE
+ case wgpu::WGSLFeatureName::Packed4x8IntegerDotProduct:
+ case wgpu::WGSLFeatureName::UnrestrictedPointerParameters:
+ case wgpu::WGSLFeatureName::PointerCompositeAccess:
+ return tint::wgsl::LanguageFeature::kUndefined;
+ }
+}
+DAWN_UNUSED_FUNC(ToWGSLFeature);
+
} // anonymous namespace
wgpu::Bool APIGetInstanceFeatures(InstanceFeatures* features) {
@@ -222,6 +249,8 @@
DAWN_TRY(mEventManager.Initialize(descriptor));
+ GatherWGSLFeatures();
+
return {};
}
@@ -548,4 +577,65 @@
return new Surface(this, descriptor);
}
+const std::unordered_set<tint::wgsl::LanguageFeature>&
+InstanceBase::GetAllowedWGSLLanguageFeatures() const {
+ return mTintLanguageFeatures;
+}
+
+void InstanceBase::GatherWGSLFeatures() {
+ for (auto wgslFeature : tint::wgsl::kAllLanguageFeatures) {
+ // Skip over testing features if we don't have the toggle to expose them.
+ if (!mToggles.IsEnabled(Toggle::ExposeWGSLTestingFeatures)) {
+ switch (wgslFeature) {
+ case tint::wgsl::LanguageFeature::kChromiumTestingUnimplemented:
+ case tint::wgsl::LanguageFeature::kChromiumTestingUnsafeExperimental:
+ case tint::wgsl::LanguageFeature::kChromiumTestingExperimental:
+ case tint::wgsl::LanguageFeature::kChromiumTestingShippedWithKillswitch:
+ case tint::wgsl::LanguageFeature::kChromiumTestingShipped:
+ continue;
+ default:
+ break;
+ }
+ }
+
+ // Expose the feature depending on its status and allow_unsafe_apis.
+ bool enable = false;
+ switch (tint::wgsl::GetLanguageFeatureStatus(wgslFeature)) {
+ case tint::wgsl::FeatureStatus::kUnknown:
+ case tint::wgsl::FeatureStatus::kUnimplemented:
+ enable = false;
+ break;
+
+ case tint::wgsl::FeatureStatus::kUnsafeExperimental:
+ case tint::wgsl::FeatureStatus::kExperimental:
+ enable = mToggles.IsEnabled(Toggle::AllowUnsafeAPIs);
+ break;
+
+ case tint::wgsl::FeatureStatus::kShippedWithKillswitch:
+ case tint::wgsl::FeatureStatus::kShipped:
+ enable = true;
+ break;
+ }
+
+ if (enable) {
+ mWGSLFeatures.emplace(ToWGPUFeature(wgslFeature));
+ mTintLanguageFeatures.emplace(wgslFeature);
+ }
+ }
+}
+
+bool InstanceBase::APIHasWGSLLanguageFeature(wgpu::WGSLFeatureName feature) const {
+ return mWGSLFeatures.count(feature) != 0;
+}
+
+size_t InstanceBase::APIEnumerateWGSLLanguageFeatures(wgpu::WGSLFeatureName* features) const {
+ if (features != nullptr) {
+ for (wgpu::WGSLFeatureName f : mWGSLFeatures) {
+ *features = f;
+ ++features;
+ }
+ }
+ return mWGSLFeatures.size();
+}
+
} // namespace dawn::native
diff --git a/src/dawn/native/Instance.h b/src/dawn/native/Instance.h
index 8a74906..2ec2d77 100644
--- a/src/dawn/native/Instance.h
+++ b/src/dawn/native/Instance.h
@@ -48,6 +48,7 @@
#include "dawn/native/RefCountedWithExternalCount.h"
#include "dawn/native/Toggles.h"
#include "dawn/native/dawn_platform.h"
+#include "tint/lang/wgsl/features/language_feature.h"
namespace dawn::platform {
class Platform;
@@ -114,6 +115,7 @@
}
const TogglesState& GetTogglesState() const;
+ const std::unordered_set<tint::wgsl::LanguageFeature>& GetAllowedWGSLLanguageFeatures() const;
// Used to query the details of a toggle. Return nullptr if toggleName is not a valid name
// of a toggle supported in Dawn.
@@ -162,6 +164,10 @@
[[nodiscard]] wgpu::WaitStatus APIWaitAny(size_t count,
FutureWaitInfo* futures,
uint64_t timeoutNS);
+ bool APIHasWGSLLanguageFeature(wgpu::WGSLFeatureName feature) const;
+ // Always writes the full list when features is not nullptr.
+ // TODO(https://github.com/webgpu-native/webgpu-headers/issues/252): Add a count argument.
+ size_t APIEnumerateWGSLLanguageFeatures(wgpu::WGSLFeatureName* features) const;
private:
explicit InstanceBase(const TogglesState& instanceToggles);
@@ -191,6 +197,7 @@
const DawnTogglesDescriptor* requiredAdapterToggles,
wgpu::PowerPreference powerPreference) const;
+ void GatherWGSLFeatures();
void ConsumeError(std::unique_ptr<ErrorData> error);
std::unordered_set<std::string> warningMessages;
@@ -212,6 +219,9 @@
TogglesState mToggles;
TogglesInfo mTogglesInfo;
+ std::unordered_set<wgpu::WGSLFeatureName> mWGSLFeatures;
+ std::unordered_set<tint::wgsl::LanguageFeature> mTintLanguageFeatures;
+
#if defined(DAWN_USE_X11)
std::unique_ptr<X11Functions> mX11Functions;
#endif // defined(DAWN_USE_X11)
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index b2b88e1..982b146 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -490,6 +490,11 @@
{"polyfill_packed_4x8_dot_product",
"Always use the polyfill version of dot4I8Packed() and dot4U8Packed().",
"https://crbug.com/tint/1497", ToggleStage::Device}},
+ {Toggle::ExposeWGSLTestingFeatures,
+ {"expose_wgsl_testing_features",
+ "Make the Instance expose the ChromiumExperimental* features for testing of "
+ "wgslLanguageFeatures functionality.",
+ "https://crbug.com/dawn/2260", ToggleStage::Instance}},
{Toggle::NoWorkaroundSampleMaskBecomesZeroForAllButLastColorTarget,
{"no_workaround_sample_mask_becomes_zero_for_all_but_last_color_target",
"MacOS 12.0+ Intel has a bug where the sample mask is only applied for the last color "
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index b1a125f..a262fed 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -123,6 +123,7 @@
UseTintIR,
D3DDisableIEEEStrictness,
PolyFillPacked4x8DotProduct,
+ ExposeWGSLTestingFeatures,
// Unresolved issues.
NoWorkaroundSampleMaskBecomesZeroForAllButLastColorTarget,
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index e0c4040..6021591 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -409,6 +409,7 @@
"unittests/validation/ValidationTest.h",
"unittests/validation/VertexBufferValidationTests.cpp",
"unittests/validation/VertexStateValidationTests.cpp",
+ "unittests/validation/WGSLFeatureValidationTests.cpp",
"unittests/validation/WritableBufferBindingAliasingValidationTests.cpp",
"unittests/validation/WritableTextureBindingAliasingValidationTests.cpp",
"unittests/validation/WriteBufferTests.cpp",
diff --git a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
index ae154bf..c98996e 100644
--- a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -1181,18 +1181,40 @@
}
// Check that both read-only and read-write storage textures are validated as unsafe in render and
-// compute pipelines.
-TEST_F(ReadWriteStorageTextureDisallowUnsafeAPITests, ReadWriteStorageTextureInPipeline) {
+// compute pipelines when the layout is defaulted.
+TEST_F(ReadWriteStorageTextureDisallowUnsafeAPITests, ReadWriteStorageTextureInDefaultedLayout) {
constexpr std::array<wgpu::StorageTextureAccess, 2> kStorageTextureAccesses = {
{wgpu::StorageTextureAccess::ReadOnly, wgpu::StorageTextureAccess::ReadWrite}};
- constexpr std::array<wgpu::ShaderStage, 3> kShaderStages = {
- {wgpu::ShaderStage::Vertex, wgpu::ShaderStage::Fragment, wgpu::ShaderStage::Compute}};
for (wgpu::StorageTextureAccess access : kStorageTextureAccesses) {
- for (wgpu::ShaderStage shaderStage : kShaderStages) {
- std::string shader = CreateShaderWithStorageTexture(
- access, wgpu::TextureFormat::R32Float, "texture_storage_2d", shaderStage);
- ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, shader.c_str()));
+ // Compute stage
+ {
+ wgpu::ComputePipelineDescriptor desc;
+ desc.compute.module = utils::CreateShaderModule(
+ device,
+ CreateShaderWithStorageTexture(access, wgpu::TextureFormat::R32Float,
+ "texture_storage_2d", wgpu::ShaderStage::Compute));
+ ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&desc));
+ }
+ // Vertex stage
+ {
+ utils::ComboRenderPipelineDescriptor desc;
+ desc.vertex.module = utils::CreateShaderModule(
+ device,
+ CreateShaderWithStorageTexture(access, wgpu::TextureFormat::R32Float,
+ "texture_storage_2d", wgpu::ShaderStage::Vertex));
+ desc.cFragment.module = mDefaultFSModule;
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&desc));
+ }
+ // Fragment stage
+ {
+ utils::ComboRenderPipelineDescriptor desc;
+ desc.cFragment.module = utils::CreateShaderModule(
+ device,
+ CreateShaderWithStorageTexture(access, wgpu::TextureFormat::R32Float,
+ "texture_storage_2d", wgpu::ShaderStage::Fragment));
+ desc.vertex.module = mDefaultVSModule;
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&desc));
}
}
}
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp
index f05cb50..4a5c9ff 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.cpp
+++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -123,8 +123,10 @@
procs.adapterRequestDevice = [](WGPUAdapter self, const WGPUDeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback, void* userdata) {
DAWN_ASSERT(gCurrentTest);
- wgpu::DeviceDescriptor deviceDesc =
- *(reinterpret_cast<const wgpu::DeviceDescriptor*>(descriptor));
+ wgpu::DeviceDescriptor deviceDesc = {};
+ if (descriptor != nullptr) {
+ deviceDesc = *(reinterpret_cast<const wgpu::DeviceDescriptor*>(descriptor));
+ }
WGPUDevice cDevice = gCurrentTest->CreateTestDevice(
dawn::native::Adapter(reinterpret_cast<dawn::native::AdapterBase*>(self)), deviceDesc);
DAWN_ASSERT(cDevice != nullptr);
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.h b/src/dawn/tests/unittests/validation/ValidationTest.h
index 0a8c203..7ce1364 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.h
+++ b/src/dawn/tests/unittests/validation/ValidationTest.h
@@ -170,6 +170,8 @@
wgpu::DeviceDescriptor descriptor);
wgpu::Device RequestDeviceSync(const wgpu::DeviceDescriptor& deviceDesc);
+ static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
+ static void OnDeviceLost(WGPUDeviceLostReason reason, const char* message, void* userdata);
virtual bool UseCompatibilityMode() const;
@@ -186,8 +188,6 @@
std::unique_ptr<dawn::utils::WireHelper> mWireHelper;
WGPUDevice mLastCreatedBackendDevice;
- static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
- static void OnDeviceLost(WGPUDeviceLostReason reason, const char* message, void* userdata);
std::string mDeviceErrorMessage;
bool mExpectError = false;
bool mError = false;
diff --git a/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp b/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
new file mode 100644
index 0000000..f7e9707
--- /dev/null
+++ b/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
@@ -0,0 +1,228 @@
+// Copyright 2023 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 <algorithm>
+#include <vector>
+
+#include "dawn/tests/unittests/validation/ValidationTest.h"
+#include "dawn/utils/WGPUHelpers.h"
+
+namespace dawn {
+namespace {
+
+class WGSLFeatureValidationTest : public ValidationTest {
+ protected:
+ void SetUp() override {
+ ValidationTest::SetUp();
+ DAWN_SKIP_TEST_IF(UsesWire());
+ }
+
+ struct InstanceSpec {
+ bool useTestingFeatures = true;
+ bool allowUnsafeAPIs = false;
+ };
+
+ wgpu::Instance CreateInstance(InstanceSpec spec) {
+ wgpu::InstanceDescriptor desc;
+
+ std::vector<const char*> enabledToggles;
+ if (spec.useTestingFeatures) {
+ enabledToggles.push_back("expose_wgsl_testing_features");
+ }
+ if (spec.allowUnsafeAPIs) {
+ enabledToggles.push_back("allow_unsafe_apis");
+ }
+
+ wgpu::DawnTogglesDescriptor togglesDesc;
+ togglesDesc.nextInChain = desc.nextInChain;
+ desc.nextInChain = &togglesDesc;
+ togglesDesc.enabledToggleCount = enabledToggles.size();
+ togglesDesc.enabledToggles = enabledToggles.data();
+
+ return wgpu::CreateInstance(&desc);
+ }
+
+ wgpu::Device CreateDeviceOnInstance(wgpu::Instance instance) {
+ // Get the adapter
+ wgpu::Adapter adapter;
+ instance.RequestAdapter(
+ nullptr,
+ [](WGPURequestAdapterStatus status, WGPUAdapter a, const char* message,
+ void* userdata) {
+ ASSERT_EQ(status, WGPURequestAdapterStatus_Success);
+ ASSERT_NE(a, nullptr);
+ *reinterpret_cast<wgpu::Adapter*>(userdata) = wgpu::Adapter::Acquire(a);
+ },
+ &adapter);
+
+ while (!adapter) {
+ FlushWire();
+ }
+ EXPECT_NE(nullptr, adapter.Get());
+
+ // Get the device
+ wgpu::Device device;
+ adapter.RequestDevice(
+ nullptr,
+ [](WGPURequestDeviceStatus status, WGPUDevice d, const char* message, void* userdata) {
+ ASSERT_EQ(status, WGPURequestDeviceStatus_Success);
+ ASSERT_NE(d, nullptr);
+ *reinterpret_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(d);
+ },
+ &device);
+
+ while (!device) {
+ FlushWire();
+ }
+ EXPECT_NE(nullptr, device.Get());
+
+ device.SetUncapturedErrorCallback(ValidationTest::OnDeviceError, this);
+ return device;
+ }
+};
+
+wgpu::WGSLFeatureName kNonExistentFeature = static_cast<wgpu::WGSLFeatureName>(0xFFFF'FFFF);
+
+// Check HasFeature for an Instance that doesn't have unsafe APIs.
+TEST_F(WGSLFeatureValidationTest, HasFeatureDefaultInstance) {
+ wgpu::Instance instance = CreateInstance({});
+
+ // Shipped features are present.
+ ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
+ ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+ wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
+
+ // Experimental and unimplemented features are not present.
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+
+ // Non-existent features are not present.
+ ASSERT_FALSE(instance.HasWGSLLanguageFeature(kNonExistentFeature));
+}
+
+// Check HasFeature for an Instance that has unsafe APIs.
+TEST_F(WGSLFeatureValidationTest, HasFeatureAllowUnsafeInstance) {
+ wgpu::Instance instance = CreateInstance({.allowUnsafeAPIs = true});
+
+ // Shipped and experimental features are present.
+ ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
+ ASSERT_TRUE(instance.HasWGSLLanguageFeature(
+ wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
+ ASSERT_TRUE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
+ ASSERT_TRUE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
+
+ // Experimental and unimplemented features are not present.
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+
+ // Non-existent features are not present.
+ ASSERT_FALSE(instance.HasWGSLLanguageFeature(kNonExistentFeature));
+}
+
+// Check HasFeature for an Instance that doesn't have the expose_wgsl_testing_features toggle.
+TEST_F(WGSLFeatureValidationTest, HasFeatureWithoutExposeWGSLTestingFeatures) {
+ wgpu::Instance instance = CreateInstance({.useTestingFeatures = false});
+
+ // None of the testing features are present.
+ ASSERT_FALSE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
+ ASSERT_FALSE(instance.HasWGSLLanguageFeature(
+ wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
+ ASSERT_FALSE(
+ instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnimplemented));
+}
+
+// Tests for the behavior of WGSL feature enumeration.
+TEST_F(WGSLFeatureValidationTest, EnumerateFeatures) {
+ wgpu::Instance instance = CreateInstance({});
+
+ size_t featureCount = instance.EnumerateWGSLLanguageFeatures(nullptr);
+
+ std::vector<wgpu::WGSLFeatureName> features(featureCount + 1, kNonExistentFeature);
+ size_t secondFeatureCount = instance.EnumerateWGSLLanguageFeatures(features.data());
+
+ // Exactly featureCount features should be written, and all return true in HasWGSLFeature.
+ ASSERT_EQ(secondFeatureCount, featureCount);
+ for (size_t i = 0; i < featureCount; i++) {
+ ASSERT_TRUE(instance.HasWGSLLanguageFeature(features[i]));
+ ASSERT_NE(kNonExistentFeature, features[i]);
+ }
+ ASSERT_EQ(kNonExistentFeature, features[featureCount]);
+
+ // Test the presence / absence of some known testing features.
+ ASSERT_NE(
+ std::find(features.begin(), features.end(), wgpu::WGSLFeatureName::ChromiumTestingShipped),
+ features.end());
+ ASSERT_NE(std::find(features.begin(), features.end(),
+ wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch),
+ features.end());
+
+ ASSERT_EQ(std::find(features.begin(), features.end(),
+ wgpu::WGSLFeatureName::ChromiumTestingUnimplemented),
+ features.end());
+ ASSERT_EQ(std::find(features.begin(), features.end(),
+ wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental),
+ features.end());
+ ASSERT_EQ(std::find(features.begin(), features.end(),
+ wgpu::WGSLFeatureName::ChromiumTestingExperimental),
+ features.end());
+}
+
+// Check that the enabled / disabled features are used to validate the WGSL shaders.
+TEST_F(WGSLFeatureValidationTest, UsingFeatureInShaderModule) {
+ wgpu::Instance instance = CreateInstance({});
+ wgpu::Device device = CreateDeviceOnInstance(instance);
+
+ utils::CreateShaderModule(device, R"(
+ requires chromium_testing_shipped;
+ )");
+ utils::CreateShaderModule(device, R"(
+ requires chromium_testing_shipped_with_killswitch;
+ )");
+
+ ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
+ requires chromium_testing_unimplemented;
+ )"));
+ ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
+ requires chromium_testing_unsafe_experimental;
+ )"));
+ ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
+ requires chromium_testing_experimental;
+ )"));
+}
+
+} // anonymous namespace
+} // namespace dawn
diff --git a/src/dawn/wire/client/Instance.cpp b/src/dawn/wire/client/Instance.cpp
index f88b467..5f0c090 100644
--- a/src/dawn/wire/client/Instance.cpp
+++ b/src/dawn/wire/client/Instance.cpp
@@ -210,4 +210,14 @@
return GetClient()->GetEventManager()->WaitAny(count, infos, timeoutNS);
}
+bool Instance::HasWGSLLanguageFeature(WGPUWGSLFeatureName feature) const {
+ // TODO(dawn:2260): Implemented wgslLanguageFeatures on the wire.
+ return false;
+}
+
+size_t Instance::EnumerateWGSLLanguageFeatures(WGPUWGSLFeatureName* features) const {
+ // TODO(dawn:2260): Implemented wgslLanguageFeatures on the wire.
+ return 0;
+}
+
} // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/Instance.h b/src/dawn/wire/client/Instance.h
index 9c9f92d..c320682 100644
--- a/src/dawn/wire/client/Instance.h
+++ b/src/dawn/wire/client/Instance.h
@@ -58,6 +58,11 @@
void ProcessEvents();
WGPUWaitStatus WaitAny(size_t count, WGPUFutureWaitInfo* infos, uint64_t timeoutNS);
+
+ bool HasWGSLLanguageFeature(WGPUWGSLFeatureName feature) const;
+ // Always writes the full list when features is not nullptr.
+ // TODO(https://github.com/webgpu-native/webgpu-headers/issues/252): Add a count argument.
+ size_t EnumerateWGSLLanguageFeatures(WGPUWGSLFeatureName* features) const;
};
} // namespace dawn::wire::client