blob: 5a68092ae88ff2750b2444d07ef68ea97e36eafa [file] [log] [blame]
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#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;
// Checks that we cannot find any structs in an empty chain
TEST(ChainUtilsTests, FindEmptyChain) {
{
const PrimitiveDepthClipControl* info = nullptr;
const ChainedStruct* chained = nullptr;
FindInChain(chained, &info);
ASSERT_EQ(nullptr, info);
}
{
DawnAdapterPropertiesPowerPreference* info = nullptr;
ChainedStructOut* chained = nullptr;
FindInChain(chained, &info);
ASSERT_EQ(nullptr, info);
}
}
// Checks that searching a chain for a present struct returns that struct
TEST(ChainUtilsTests, FindPresentInChain) {
{
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
chain1.nextInChain = &chain2;
const PrimitiveDepthClipControl* info1 = nullptr;
const ShaderModuleSPIRVDescriptor* info2 = nullptr;
FindInChain(&chain1, &info1);
FindInChain(&chain1, &info2);
ASSERT_NE(nullptr, info1);
ASSERT_NE(nullptr, info2);
}
{
DawnAdapterPropertiesPowerPreference chain;
DawnAdapterPropertiesPowerPreference* output = nullptr;
FindInChain(&chain, &output);
ASSERT_NE(nullptr, output);
}
}
// Checks that searching a chain for a struct that doesn't exist returns a nullptr
TEST(ChainUtilsTests, FindMissingInChain) {
{
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
chain1.nextInChain = &chain2;
const SurfaceDescriptorFromMetalLayer* info = nullptr;
FindInChain(&chain1, &info);
ASSERT_EQ(nullptr, info);
}
{
AdapterProperties adapterProperties;
DawnAdapterPropertiesPowerPreference* output = nullptr;
FindInChain(adapterProperties.nextInChain, &output);
ASSERT_EQ(nullptr, output);
}
}
// Checks that validation rejects chains with duplicate STypes
TEST(ChainUtilsTests, ValidateDuplicateSTypes) {
{
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
PrimitiveDepthClipControl chain3;
chain1.nextInChain = &chain2;
chain2.nextInChain = &chain3;
MaybeError result = ValidateSTypes(&chain1, {});
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
{
DawnAdapterPropertiesPowerPreference chain1;
DawnAdapterPropertiesPowerPreference chain2;
chain1.nextInChain = &chain2;
MaybeError result = ValidateSTypes(&chain1, {});
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
}
// Checks that validation rejects chains that contain unspecified STypes
TEST(ChainUtilsTests, ValidateUnspecifiedSTypes) {
{
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
ShaderModuleWGSLDescriptor chain3;
chain1.nextInChain = &chain2;
chain2.nextInChain = &chain3;
MaybeError result = ValidateSTypes(&chain1, {
{wgpu::SType::PrimitiveDepthClipControl},
{wgpu::SType::ShaderModuleSPIRVDescriptor},
});
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
{
DawnAdapterPropertiesPowerPreference chain1;
ChainedStructOut chain2;
chain2.sType = wgpu::SType::RenderPassDescriptorMaxDrawCount;
chain1.nextInChain = &chain2;
MaybeError result =
ValidateSTypes(&chain1, {{wgpu::SType::DawnAdapterPropertiesPowerPreference}});
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
}
// Checks that validation rejects chains that contain multiple STypes from the same oneof
// constraint.
TEST(ChainUtilsTests, ValidateOneOfFailure) {
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
ShaderModuleWGSLDescriptor chain3;
chain1.nextInChain = &chain2;
chain2.nextInChain = &chain3;
MaybeError result = ValidateSTypes(&chain1, {{wgpu::SType::ShaderModuleSPIRVDescriptor,
wgpu::SType::ShaderModuleWGSLDescriptor}});
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
// Checks that validation accepts chains that match the constraints.
TEST(ChainUtilsTests, ValidateSuccess) {
{
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
chain1.nextInChain = &chain2;
MaybeError result = ValidateSTypes(
&chain1,
{
{wgpu::SType::ShaderModuleSPIRVDescriptor, wgpu::SType::ShaderModuleWGSLDescriptor},
{wgpu::SType::PrimitiveDepthClipControl},
{wgpu::SType::SurfaceDescriptorFromMetalLayer},
});
ASSERT_TRUE(result.IsSuccess());
}
{
DawnAdapterPropertiesPowerPreference chain1;
MaybeError result =
ValidateSTypes(&chain1, {{wgpu::SType::DawnAdapterPropertiesPowerPreference}});
ASSERT_TRUE(result.IsSuccess());
}
}
// Checks that validation always passes on empty chains.
TEST(ChainUtilsTests, ValidateEmptyChain) {
{
const ChainedStruct* chain = nullptr;
MaybeError result = ValidateSTypes(chain, {
{wgpu::SType::ShaderModuleSPIRVDescriptor},
{wgpu::SType::PrimitiveDepthClipControl},
});
ASSERT_TRUE(result.IsSuccess());
result = ValidateSTypes(chain, {});
ASSERT_TRUE(result.IsSuccess());
}
{
ChainedStructOut* chain = nullptr;
MaybeError result =
ValidateSTypes(chain, {{wgpu::SType::DawnAdapterPropertiesPowerPreference}});
ASSERT_TRUE(result.IsSuccess());
result = ValidateSTypes(chain, {});
ASSERT_TRUE(result.IsSuccess());
}
}
// Checks that singleton validation always passes on empty chains.
TEST(ChainUtilsTests, ValidateSingleEmptyChain) {
{
const ChainedStruct* chain = nullptr;
MaybeError result = ValidateSingleSType(chain, wgpu::SType::ShaderModuleSPIRVDescriptor);
ASSERT_TRUE(result.IsSuccess());
result = ValidateSingleSType(chain, wgpu::SType::ShaderModuleSPIRVDescriptor,
wgpu::SType::PrimitiveDepthClipControl);
ASSERT_TRUE(result.IsSuccess());
}
{
ChainedStructOut* chain = nullptr;
MaybeError result =
ValidateSingleSType(chain, wgpu::SType::DawnAdapterPropertiesPowerPreference);
ASSERT_TRUE(result.IsSuccess());
result = ValidateSingleSType(chain, wgpu::SType::DawnAdapterPropertiesPowerPreference,
wgpu::SType::PrimitiveDepthClipControl);
ASSERT_TRUE(result.IsSuccess());
}
}
// Checks that singleton validation always fails on chains with multiple children.
TEST(ChainUtilsTests, ValidateSingleMultiChain) {
{
PrimitiveDepthClipControl chain1;
ShaderModuleSPIRVDescriptor chain2;
chain1.nextInChain = &chain2;
MaybeError result = ValidateSingleSType(&chain1, wgpu::SType::PrimitiveDepthClipControl);
ASSERT_TRUE(result.IsError());
result.AcquireError();
result = ValidateSingleSType(&chain1, wgpu::SType::PrimitiveDepthClipControl,
wgpu::SType::ShaderModuleSPIRVDescriptor);
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
{
DawnAdapterPropertiesPowerPreference chain1;
DawnAdapterPropertiesPowerPreference chain2;
chain1.nextInChain = &chain2;
MaybeError result =
ValidateSingleSType(&chain1, wgpu::SType::DawnAdapterPropertiesPowerPreference);
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
}
// Checks that singleton validation passes when the one of constraint is met.
TEST(ChainUtilsTests, ValidateSingleSatisfied) {
{
ShaderModuleWGSLDescriptor chain1;
MaybeError result = ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor);
ASSERT_TRUE(result.IsSuccess());
result = ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleSPIRVDescriptor,
wgpu::SType::ShaderModuleWGSLDescriptor);
ASSERT_TRUE(result.IsSuccess());
result = ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor,
wgpu::SType::ShaderModuleSPIRVDescriptor);
ASSERT_TRUE(result.IsSuccess());
}
{
DawnAdapterPropertiesPowerPreference chain1;
MaybeError result =
ValidateSingleSType(&chain1, wgpu::SType::DawnAdapterPropertiesPowerPreference);
ASSERT_TRUE(result.IsSuccess());
}
}
// Checks that singleton validation passes when the oneof constraint is not met.
TEST(ChainUtilsTests, ValidateSingleUnsatisfied) {
{
PrimitiveDepthClipControl chain1;
MaybeError result = ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor);
ASSERT_TRUE(result.IsError());
result.AcquireError();
result = ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleSPIRVDescriptor,
wgpu::SType::ShaderModuleWGSLDescriptor);
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
{
ChainedStructOut chain1;
chain1.sType = wgpu::SType::ShaderModuleWGSLDescriptor;
MaybeError result =
ValidateSingleSType(&chain1, wgpu::SType::DawnAdapterPropertiesPowerPreference);
ASSERT_TRUE(result.IsError());
result.AcquireError();
}
}
// 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 (as of when this test was written) does not have any valid chains
// in the JSON nor via additional extensions.
TextureViewDescriptor desc;
auto unpacked = ValidateAndUnpackChain(&desc).AcquireSuccess();
static_assert(std::tuple_size_v<decltype(unpacked)> == 0);
std::apply(
[](const auto*... args) {
(([&](const auto* arg) { EXPECT_EQ(args, nullptr); }(args)), ...);
},
unpacked);
}
{
// InstanceDescriptor has at least 1 valid chain extension.
InstanceDescriptor desc;
auto unpacked = ValidateAndUnpackChain(&desc).AcquireSuccess();
std::apply(
[](const auto*... args) {
(([&](const auto* arg) { EXPECT_EQ(args, nullptr); }(args)), ...);
},
unpacked);
}
}
// Invalid chain extensions should 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(ValidateAndUnpackChain(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Unexpected"));
}
{
// InstanceDescriptor has at least 1 valid chain extension.
InstanceDescriptor desc;
ChainedStruct chain;
desc.nextInChain = &chain;
EXPECT_THAT(ValidateAndUnpackChain(&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 = ValidateAndUnpackChain(&desc).AcquireSuccess();
EXPECT_EQ(std::get<const DawnTogglesDescriptor*>(unpacked), &chain);
}
// Duplicate valid extensions should 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(ValidateAndUnpackChain(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Duplicate"));
}
// Additional extensions added via template specialization and not specified in the JSON should work
// properly.
TEST(ChainUtilsTests, ValidateAndUnpackAdditionalExtensions) {
// DawnInstanceDescriptor is an extension on InstanceDescriptor added in ChainUtilsImpl.h.
InstanceDescriptor desc;
DawnInstanceDescriptor chain;
desc.nextInChain = &chain;
auto unpacked = ValidateAndUnpackChain(&desc).AcquireSuccess();
EXPECT_EQ(std::get<const DawnInstanceDescriptor*>(unpacked), &chain);
}
// Duplicate additional extensions added via template specialization should cause an error.
TEST(ChainUtilsTests, ValidateAndUnpackDuplicateAdditionalExtensions) {
// DawnInstanceDescriptor is an extension on InstanceDescriptor added in ChainUtilsImpl.h.
InstanceDescriptor desc;
DawnInstanceDescriptor chain1;
DawnInstanceDescriptor chain2;
desc.nextInChain = &chain1;
chain1.nextInChain = &chain2;
EXPECT_THAT(ValidateAndUnpackChain(&desc).AcquireError()->GetFormattedMessage(),
HasSubstr("Duplicate"));
}
} // anonymous namespace
} // namespace dawn::native