blob: 0f9e8b8674d51e4b5a428d77b1c9369db13e9422 [file] [log] [blame]
// Copyright 2021 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/ChainUtils.h"
#include "dawn/native/dawn_platform.h"
namespace dawn::native {
namespace {
using ::testing::HasSubstr;
// Empty chain on roots that have and don't have valid extensions should not fail validation and all
// values should be nullptr.
TEST(ChainUtilsTests, ValidateAndUnpackEmpty) {
{
// TextureViewDescriptor has at least 1 valid chain extension..
TextureViewDescriptor desc;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_TRUE(unpacked.Empty());
}
{
// InstanceDescriptor has at least 1 valid chain extension.
InstanceDescriptor desc;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_TRUE(unpacked.Empty());
}
{
// SharedTextureMemoryProperties has at least 1 valid chain extension.
SharedTextureMemoryProperties properties;
auto unpacked = ValidateAndUnpack(&properties).AcquireSuccess();
EXPECT_TRUE(unpacked.Empty());
}
{
// SharedFenceExportInfo has at least 1 valid chain extension.
SharedFenceExportInfo properties;
auto unpacked = ValidateAndUnpack(&properties).AcquireSuccess();
EXPECT_TRUE(unpacked.Empty());
}
}
// Invalid chain extensions cause an error.
TEST(ChainUtilsTests, ValidateAndUnpackUnexpected) {
{
// TextureViewDescriptor (as of when this test was written) does not have any valid chains
// in the JSON nor via additional extensions.
TextureViewDescriptor desc;
ChainedStruct chain;
desc.nextInChain = &chain;
EXPECT_THAT(ValidateAndUnpack(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Unexpected"));
}
{
// InstanceDescriptor has at least 1 valid chain extension.
InstanceDescriptor desc;
ChainedStruct chain;
desc.nextInChain = &chain;
EXPECT_THAT(ValidateAndUnpack(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Unexpected"));
}
}
// Nominal unpacking valid descriptors should return the expected descriptors in the unpacked type.
TEST(ChainUtilsTests, ValidateAndUnpack) {
// DawnTogglesDescriptor is a valid extension for InstanceDescriptor.
InstanceDescriptor desc;
DawnTogglesDescriptor chain;
desc.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
auto ext = unpacked.Get<DawnTogglesDescriptor>();
EXPECT_EQ(ext, &chain);
// For ChainedStructs, the resulting pointer from Get should be a const type.
static_assert(std::is_const_v<std::remove_reference_t<decltype(*ext)>>);
}
// Nominal unpacking valid descriptors should return the expected descriptors in the unpacked type.
TEST(ChainUtilsTests, ValidateAndUnpackOut) {
// DawnAdapterPropertiesPowerPreference is a valid extension for AdapterProperties.
AdapterProperties properties;
DawnAdapterPropertiesPowerPreference chain;
properties.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&properties).AcquireSuccess();
auto ext = unpacked.Get<DawnAdapterPropertiesPowerPreference>();
EXPECT_EQ(ext, &chain);
// For ChainedStructOuts, the resulting pointer from Get should not be a const type.
static_assert(!std::is_const_v<std::remove_reference_t<decltype(*ext)>>);
}
// Duplicate valid extensions cause an error.
TEST(ChainUtilsTests, ValidateAndUnpackDuplicate) {
// DawnTogglesDescriptor is a valid extension for InstanceDescriptor.
InstanceDescriptor desc;
DawnTogglesDescriptor chain1;
DawnTogglesDescriptor chain2;
desc.nextInChain = &chain1;
chain1.nextInChain = &chain2;
EXPECT_THAT(ValidateAndUnpack(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Duplicate"));
}
// Duplicate valid extensions cause an error.
TEST(ChainUtilsTests, ValidateAndUnpackOutDuplicate) {
// DawnAdapterPropertiesPowerPreference is a valid extension for AdapterProperties.
AdapterProperties properties;
DawnAdapterPropertiesPowerPreference chain1;
DawnAdapterPropertiesPowerPreference chain2;
properties.nextInChain = &chain1;
chain1.nextInChain = &chain2;
EXPECT_THAT(ValidateAndUnpack(&properties).AcquireError()->GetFormattedMessage(),
HasSubstr("Duplicate"));
}
// Additional extensions added via template specialization and not specified in the JSON unpack
// properly.
TEST(ChainUtilsTests, ValidateAndUnpackAdditionalExtensions) {
// DawnInstanceDescriptor is an extension on InstanceDescriptor added in ChainUtilsImpl.inl.
InstanceDescriptor desc;
DawnInstanceDescriptor chain;
desc.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_EQ(unpacked.Get<DawnInstanceDescriptor>(), &chain);
}
// Duplicate additional extensions added via template specialization should cause an error.
TEST(ChainUtilsTests, ValidateAndUnpackDuplicateAdditionalExtensions) {
// DawnInstanceDescriptor is an extension on InstanceDescriptor added in ChainUtilsImpl.inl.
InstanceDescriptor desc;
DawnInstanceDescriptor chain1;
DawnInstanceDescriptor chain2;
desc.nextInChain = &chain1;
chain1.nextInChain = &chain2;
EXPECT_THAT(ValidateAndUnpack(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Duplicate"));
}
using B1 = Branch<ShaderModuleWGSLDescriptor>;
using B2 = Branch<ShaderModuleSPIRVDescriptor>;
using B2Ext = Branch<ShaderModuleSPIRVDescriptor, DawnShaderModuleSPIRVOptionsDescriptor>;
// Validates exacly 1 branch and ensures that there are no other extensions.
TEST(ChainUtilsTests, ValidateBranchesOneValidBranch) {
ShaderModuleDescriptor desc;
// Either allowed branches should validate successfully and return the expected enum.
{
ShaderModuleWGSLDescriptor chain;
desc.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_EQ((unpacked.ValidateBranches<B1, B2>().AcquireSuccess()),
wgpu::SType::ShaderModuleWGSLDescriptor);
}
{
ShaderModuleSPIRVDescriptor chain;
desc.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_EQ((unpacked.ValidateBranches<B1, B2>().AcquireSuccess()),
wgpu::SType::ShaderModuleSPIRVDescriptor);
// Extensions are optional so validation should still pass when the extension is not
// provided.
EXPECT_EQ((unpacked.ValidateBranches<B1, B2Ext>().AcquireSuccess()),
wgpu::SType::ShaderModuleSPIRVDescriptor);
}
}
// An allowed chain that is not one of the branches causes an error.
TEST(ChainUtilsTests, ValidateBranchesInvalidBranch) {
ShaderModuleDescriptor desc;
DawnShaderModuleSPIRVOptionsDescriptor chain;
desc.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_NE((unpacked.ValidateBranches<B1, B2>().AcquireError()), nullptr);
EXPECT_NE((unpacked.ValidateBranches<B1, B2Ext>().AcquireError()), nullptr);
}
// Additional chains should cause an error when branches don't allow extensions.
TEST(ChainUtilsTests, ValidateBranchesInvalidExtension) {
ShaderModuleDescriptor desc;
{
ShaderModuleWGSLDescriptor chain1;
DawnShaderModuleSPIRVOptionsDescriptor chain2;
desc.nextInChain = &chain1;
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_NE((unpacked.ValidateBranches<B1, B2>().AcquireError()), nullptr);
EXPECT_NE((unpacked.ValidateBranches<B1, B2Ext>().AcquireError()), nullptr);
}
{
ShaderModuleSPIRVDescriptor chain1;
DawnShaderModuleSPIRVOptionsDescriptor chain2;
desc.nextInChain = &chain1;
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_NE((unpacked.ValidateBranches<B1, B2>().AcquireError()), nullptr);
}
}
// Branches that allow extensions pass successfully.
TEST(ChainUtilsTests, ValidateBranchesAllowedExtensions) {
ShaderModuleDescriptor desc;
ShaderModuleSPIRVDescriptor chain1;
DawnShaderModuleSPIRVOptionsDescriptor chain2;
desc.nextInChain = &chain1;
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_EQ((unpacked.ValidateBranches<B1, B2Ext>().AcquireSuccess()),
wgpu::SType::ShaderModuleSPIRVDescriptor);
}
// Unrealistic branching for ChainedStructOut testing. Note that this setup does not make sense.
using BOut1 = Branch<SharedFenceVkSemaphoreOpaqueFDExportInfo>;
using BOut2 = Branch<SharedFenceVkSemaphoreSyncFDExportInfo>;
using BOut2Ext =
Branch<SharedFenceVkSemaphoreSyncFDExportInfo, SharedFenceVkSemaphoreZirconHandleExportInfo>;
// Validates exacly 1 branch and ensures that there are no other extensions.
TEST(ChainUtilsTests, ValidateBranchesOneValidBranchOut) {
SharedFenceExportInfo info;
// Either allowed branches should validate successfully and return the expected enum.
{
SharedFenceVkSemaphoreOpaqueFDExportInfo chain;
info.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2>().AcquireSuccess()),
wgpu::SType::SharedFenceVkSemaphoreOpaqueFDExportInfo);
}
{
SharedFenceVkSemaphoreSyncFDExportInfo chain;
info.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2>().AcquireSuccess()),
wgpu::SType::SharedFenceVkSemaphoreSyncFDExportInfo);
// Extensions are optional so validation should still pass when the extension is not
// provided.
EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2Ext>().AcquireSuccess()),
wgpu::SType::SharedFenceVkSemaphoreSyncFDExportInfo);
}
}
// An allowed chain that is not one of the branches causes an error.
TEST(ChainUtilsTests, ValidateBranchesInvalidBranchOut) {
SharedFenceExportInfo info;
SharedFenceDXGISharedHandleExportInfo chain;
info.nextInChain = &chain;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_NE((unpacked.ValidateBranches<BOut1, BOut2>().AcquireError()), nullptr);
EXPECT_NE((unpacked.ValidateBranches<BOut1, BOut2Ext>().AcquireError()), nullptr);
}
// Additional chains should cause an error when branches don't allow extensions.
TEST(ChainUtilsTests, ValidateBranchesInvalidExtensionOut) {
SharedFenceExportInfo info;
{
SharedFenceVkSemaphoreOpaqueFDExportInfo chain1;
SharedFenceVkSemaphoreZirconHandleExportInfo chain2;
info.nextInChain = &chain1;
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_NE((unpacked.ValidateBranches<BOut1, BOut2>().AcquireError()), nullptr);
EXPECT_NE((unpacked.ValidateBranches<BOut1, BOut2Ext>().AcquireError()), nullptr);
}
{
SharedFenceVkSemaphoreSyncFDExportInfo chain1;
SharedFenceVkSemaphoreZirconHandleExportInfo chain2;
info.nextInChain = &chain1;
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_NE((unpacked.ValidateBranches<BOut1, BOut2>().AcquireError()), nullptr);
}
}
// Branches that allow extensions pass successfully.
TEST(ChainUtilsTests, ValidateBranchesAllowedExtensionsOut) {
SharedFenceExportInfo info;
SharedFenceVkSemaphoreSyncFDExportInfo chain1;
SharedFenceVkSemaphoreZirconHandleExportInfo chain2;
info.nextInChain = &chain1;
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2Ext>().AcquireSuccess()),
wgpu::SType::SharedFenceVkSemaphoreSyncFDExportInfo);
}
// Valid subsets should pass successfully, while invalid ones should error.
TEST(ChainUtilsTests, ValidateSubset) {
DeviceDescriptor desc;
DawnTogglesDescriptor chain1;
DawnCacheDeviceDescriptor chain2;
// With none set, subset for anything should work.
{
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_TRUE(unpacked.ValidateSubset<>().IsSuccess());
EXPECT_TRUE(unpacked.ValidateSubset<DawnTogglesDescriptor>().IsSuccess());
EXPECT_TRUE(unpacked.ValidateSubset<DawnCacheDeviceDescriptor>().IsSuccess());
EXPECT_TRUE((unpacked.ValidateSubset<DawnTogglesDescriptor, DawnCacheDeviceDescriptor>()
.IsSuccess()));
}
// With one set, subset with that allow that one should pass. Otherwise it should fail.
{
desc.nextInChain = &chain1;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_NE(unpacked.ValidateSubset<>().AcquireError(), nullptr);
EXPECT_TRUE(unpacked.ValidateSubset<DawnTogglesDescriptor>().IsSuccess());
EXPECT_NE(unpacked.ValidateSubset<DawnCacheDeviceDescriptor>().AcquireError(), nullptr);
EXPECT_TRUE((unpacked.ValidateSubset<DawnTogglesDescriptor, DawnCacheDeviceDescriptor>()
.IsSuccess()));
}
// With both set, single subsets should all fail.
{
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess();
EXPECT_NE(unpacked.ValidateSubset<>().AcquireError(), nullptr);
EXPECT_NE(unpacked.ValidateSubset<DawnTogglesDescriptor>().AcquireError(), nullptr);
EXPECT_NE(unpacked.ValidateSubset<DawnCacheDeviceDescriptor>().AcquireError(), nullptr);
EXPECT_TRUE((unpacked.ValidateSubset<DawnTogglesDescriptor, DawnCacheDeviceDescriptor>()
.IsSuccess()));
}
}
// Valid subsets should pass successfully, while invalid ones should error.
TEST(ChainUtilsTests, ValidateSubsetOut) {
SharedFenceExportInfo info;
SharedFenceVkSemaphoreOpaqueFDExportInfo chain1;
SharedFenceVkSemaphoreZirconHandleExportInfo chain2;
// With none set, subset for anything should work.
{
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_TRUE(unpacked.ValidateSubset<>().IsSuccess());
EXPECT_TRUE(
unpacked.ValidateSubset<SharedFenceVkSemaphoreOpaqueFDExportInfo>().IsSuccess());
EXPECT_TRUE(
unpacked.ValidateSubset<SharedFenceVkSemaphoreZirconHandleExportInfo>().IsSuccess());
EXPECT_TRUE((unpacked
.ValidateSubset<SharedFenceVkSemaphoreOpaqueFDExportInfo,
SharedFenceVkSemaphoreZirconHandleExportInfo>()
.IsSuccess()));
}
// With one set, subset with that allow that one should pass. Otherwise it should fail.
{
info.nextInChain = &chain1;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_NE(unpacked.ValidateSubset<>().AcquireError(), nullptr);
EXPECT_TRUE(
unpacked.ValidateSubset<SharedFenceVkSemaphoreOpaqueFDExportInfo>().IsSuccess());
EXPECT_NE(
unpacked.ValidateSubset<SharedFenceVkSemaphoreZirconHandleExportInfo>().AcquireError(),
nullptr);
EXPECT_TRUE((unpacked
.ValidateSubset<SharedFenceVkSemaphoreOpaqueFDExportInfo,
SharedFenceVkSemaphoreZirconHandleExportInfo>()
.IsSuccess()));
}
// With both set, single subsets should all fail.
{
chain1.nextInChain = &chain2;
auto unpacked = ValidateAndUnpack(&info).AcquireSuccess();
EXPECT_NE(unpacked.ValidateSubset<>().AcquireError(), nullptr);
EXPECT_NE(
unpacked.ValidateSubset<SharedFenceVkSemaphoreOpaqueFDExportInfo>().AcquireError(),
nullptr);
EXPECT_NE(
unpacked.ValidateSubset<SharedFenceVkSemaphoreZirconHandleExportInfo>().AcquireError(),
nullptr);
EXPECT_TRUE((unpacked
.ValidateSubset<SharedFenceVkSemaphoreOpaqueFDExportInfo,
SharedFenceVkSemaphoreZirconHandleExportInfo>()
.IsSuccess()));
}
}
} // anonymous namespace
} // namespace dawn::native