| // 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/DawnNative.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")); | |
| } | |
| } | |
| // Inject invalid chain extensions cause an error. | |
| TEST(ChainUtilsTests, ValidateAndUnpackInjected) { | |
| { | |
| // TextureViewDescriptor (as of when this test was written) does not have any valid chains | |
| // in the JSON nor via additional extensions. | |
| TextureViewDescriptor desc; | |
| DawnInjectedInvalidSType chain; | |
| chain.invalidSType = wgpu::SType::ShaderSourceWGSL; | |
| desc.nextInChain = &chain; | |
| EXPECT_THAT(ValidateAndUnpack(&desc).AcquireError()->GetFormattedMessage(), | |
| HasSubstr("ShaderSourceWGSL")); | |
| } | |
| { | |
| // InstanceDescriptor has at least 1 valid chain extension. | |
| InstanceDescriptor desc; | |
| DawnInjectedInvalidSType chain; | |
| chain.invalidSType = wgpu::SType::ShaderSourceWGSL; | |
| desc.nextInChain = &chain; | |
| EXPECT_THAT(ValidateAndUnpack(&desc).AcquireError()->GetFormattedMessage(), | |
| HasSubstr("ShaderSourceWGSL")); | |
| } | |
| } | |
| // 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 AdapterInfo. | |
| AdapterInfo info; | |
| DawnAdapterPropertiesPowerPreference chain; | |
| info.nextInChain = &chain; | |
| auto unpacked = ValidateAndUnpack(&info).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 AdapterInfo. | |
| AdapterInfo info; | |
| DawnAdapterPropertiesPowerPreference chain1; | |
| DawnAdapterPropertiesPowerPreference chain2; | |
| info.nextInChain = &chain1; | |
| chain1.nextInChain = &chain2; | |
| EXPECT_THAT(ValidateAndUnpack(&info).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<ShaderSourceWGSL>; | |
| using B2 = Branch<ShaderSourceSPIRV>; | |
| using B2Ext = Branch<ShaderSourceSPIRV, 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. | |
| { | |
| ShaderSourceWGSL chain; | |
| desc.nextInChain = &chain; | |
| auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess(); | |
| EXPECT_EQ((unpacked.ValidateBranches<B1, B2>().AcquireSuccess()), | |
| wgpu::SType::ShaderSourceWGSL); | |
| } | |
| { | |
| ShaderSourceSPIRV chain; | |
| desc.nextInChain = &chain; | |
| auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess(); | |
| EXPECT_EQ((unpacked.ValidateBranches<B1, B2>().AcquireSuccess()), | |
| wgpu::SType::ShaderSourceSPIRV); | |
| // Extensions are optional so validation should still pass when the extension is not | |
| // provided. | |
| EXPECT_EQ((unpacked.ValidateBranches<B1, B2Ext>().AcquireSuccess()), | |
| wgpu::SType::ShaderSourceSPIRV); | |
| } | |
| } | |
| // 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; | |
| { | |
| ShaderSourceWGSL 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); | |
| } | |
| { | |
| ShaderSourceSPIRV 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; | |
| ShaderSourceSPIRV chain1; | |
| DawnShaderModuleSPIRVOptionsDescriptor chain2; | |
| desc.nextInChain = &chain1; | |
| chain1.nextInChain = &chain2; | |
| auto unpacked = ValidateAndUnpack(&desc).AcquireSuccess(); | |
| EXPECT_EQ((unpacked.ValidateBranches<B1, B2Ext>().AcquireSuccess()), | |
| wgpu::SType::ShaderSourceSPIRV); | |
| } | |
| // Unrealistic branching for ChainedStructOut testing. Note that this setup does not make sense. | |
| using BOut1 = Branch<SharedFenceVkSemaphoreOpaqueFDExportInfo>; | |
| using BOut2 = Branch<SharedFenceSyncFDExportInfo>; | |
| using BOut2Ext = Branch<SharedFenceSyncFDExportInfo, 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); | |
| } | |
| { | |
| SharedFenceSyncFDExportInfo chain; | |
| info.nextInChain = &chain; | |
| auto unpacked = ValidateAndUnpack(&info).AcquireSuccess(); | |
| EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2>().AcquireSuccess()), | |
| wgpu::SType::SharedFenceSyncFDExportInfo); | |
| // Extensions are optional so validation should still pass when the extension is not | |
| // provided. | |
| EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2Ext>().AcquireSuccess()), | |
| wgpu::SType::SharedFenceSyncFDExportInfo); | |
| } | |
| } | |
| // 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); | |
| } | |
| { | |
| SharedFenceSyncFDExportInfo 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; | |
| SharedFenceSyncFDExportInfo chain1; | |
| SharedFenceVkSemaphoreZirconHandleExportInfo chain2; | |
| info.nextInChain = &chain1; | |
| chain1.nextInChain = &chain2; | |
| auto unpacked = ValidateAndUnpack(&info).AcquireSuccess(); | |
| EXPECT_EQ((unpacked.ValidateBranches<BOut1, BOut2Ext>().AcquireSuccess()), | |
| wgpu::SType::SharedFenceSyncFDExportInfo); | |
| } | |
| // 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 |