blob: 61d79a6a197d757f76de77b913a83797178bfcb6 [file] [log] [blame] [edit]
// 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"
#include "dawn/utils/WireHelper.h"
namespace dawn {
namespace {
class WGSLFeatureValidationTest : public ValidationTest {
protected:
void SetUp() override { ValidationTest::SetUp(); }
struct InstanceSpec {
bool useTestingFeatures = true;
bool allowUnsafeAPIs = false;
bool exposeExperimental = false;
std::vector<const char*> blocklist = {};
};
wgpu::Instance CreateInstance(InstanceSpec spec) {
// The blocklist that will be shared between both the native and wire descriptors.
wgpu::DawnWGSLBlocklist blocklist;
blocklist.blocklistedFeatureCount = spec.blocklist.size();
blocklist.blocklistedFeatures = spec.blocklist.data();
// Build the native instance descriptor.
std::vector<const char*> enabledToggles;
if (spec.useTestingFeatures) {
enabledToggles.push_back("expose_wgsl_testing_features");
}
if (spec.allowUnsafeAPIs) {
enabledToggles.push_back("allow_unsafe_apis");
}
if (spec.exposeExperimental) {
enabledToggles.push_back("expose_wgsl_experimental_features");
}
wgpu::DawnTogglesDescriptor togglesDesc;
togglesDesc.nextInChain = &blocklist;
togglesDesc.enabledToggleCount = enabledToggles.size();
togglesDesc.enabledToggles = enabledToggles.data();
wgpu::InstanceDescriptor nativeDesc;
nativeDesc.nextInChain = &togglesDesc;
// Build the wire instance descriptor.
wgpu::DawnWireWGSLControl wgslControl;
wgslControl.nextInChain = &blocklist;
wgslControl.enableExperimental = spec.allowUnsafeAPIs || spec.exposeExperimental;
wgslControl.enableUnsafe = spec.allowUnsafeAPIs;
wgslControl.enableTesting = spec.useTestingFeatures;
wgpu::InstanceDescriptor wireDesc;
wireDesc.nextInChain = &wgslControl;
return GetWireHelper()->CreateInstances(&nativeDesc, &wireDesc).first;
}
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, HasFeatureExposeExperimental) {
wgpu::Instance instance = CreateInstance({.exposeExperimental = 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));
// Unsafe and unimplemented features are not present.
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));
// 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;
)"));
}
// Test using DawnWGSLBlocklist to block features with a killswitch by name.
TEST_F(WGSLFeatureValidationTest, BlockListOfKillswitchedFeatures) {
wgpu::Instance instance = CreateInstance(
{.allowUnsafeAPIs = true, .blocklist = {"chromium_testing_shipped_with_killswitch"}});
// The blocklisted feature is not present.
ASSERT_FALSE(instance.HasWGSLLanguageFeature(
wgpu::WGSLFeatureName::ChromiumTestingShippedWithKillswitch));
// The others are.
ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
ASSERT_TRUE(
instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
ASSERT_TRUE(
instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
// Using the blocklisted extension fails.
wgpu::Device device = CreateDeviceOnInstance(instance);
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
requires chromium_testing_shipped_with_killswitch;
)"));
}
// Test that DawnWGSLBlocklist can block any feature name (even without a killswitch).
TEST_F(WGSLFeatureValidationTest, BlockListOfAnyFeature) {
wgpu::Instance instance =
CreateInstance({.allowUnsafeAPIs = true,
.blocklist = {"chromium_testing_shipped", "chromium_testing_experimental",
"chromium_testing_unsafe_experimental"}});
// All blocklisted features aren't present.
ASSERT_FALSE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
ASSERT_FALSE(
instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingExperimental));
ASSERT_FALSE(
instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingUnsafeExperimental));
}
// Test that DawnWGSLBlocklist can contain garbage names without causing problems.
TEST_F(WGSLFeatureValidationTest, BlockListGarbageName) {
wgpu::Instance instance = CreateInstance({.blocklist = {"LE_GARBAGE"}});
ASSERT_NE(instance, nullptr);
}
} // anonymous namespace
} // namespace dawn