Add validation, storage, and querying of limits
Adds a way to store the limits on the Adapter and the Device. For now, adapter limits are always the default limits, and device limits are stored but not used. This CL also adds usage of an ErrorObjectIdResolver and Provider in the WGPUDeviceProperties serialization and deserialization helpers. Serializing/deserializing this struct should never have objects. Bug: dawn:685 Change-Id: I1479b4407b0f9ec9f9b2bff62cad7caa693c99d7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63983 Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
caf6e8b3f0
commit
02fbf168e0
35
dawn.json
35
dawn.json
|
@ -883,7 +883,8 @@
|
||||||
{"name": "multi planar formats", "type": "bool", "default": "false"},
|
{"name": "multi planar formats", "type": "bool", "default": "false"},
|
||||||
{"name": "depth clamping", "type": "bool", "default": "false"},
|
{"name": "depth clamping", "type": "bool", "default": "false"},
|
||||||
{"name": "invalid extension", "type": "bool", "default": "false"},
|
{"name": "invalid extension", "type": "bool", "default": "false"},
|
||||||
{"name": "dawn internal usages", "type": "bool", "default": "false"}
|
{"name": "dawn internal usages", "type": "bool", "default": "false"},
|
||||||
|
{"name": "limits", "type": "limits"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"double": {
|
"double": {
|
||||||
|
@ -897,6 +898,38 @@
|
||||||
{"name": "userdata", "type": "void", "annotation": "*"}
|
{"name": "userdata", "type": "void", "annotation": "*"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"limits": {
|
||||||
|
"category": "structure",
|
||||||
|
"extensible": true,
|
||||||
|
"members": [
|
||||||
|
{"name": "max texture dimension 1D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max texture dimension 2D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max texture dimension 3D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max texture array layers", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max bind groups", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max dynamic uniform buffers per pipeline layout", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max dynamic storage buffers per pipeline layout", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max sampled textures per shader stage", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max samplers per shader stage", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max storage buffers per shader stage", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max storage textures per shader stage", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max uniform buffers per shader stage", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max uniform buffer binding size", "type": "uint64_t", "default": "WGPU_LIMIT_U64_UNDEFINED"},
|
||||||
|
{"name": "max storage buffer binding size", "type": "uint64_t", "default": "WGPU_LIMIT_U64_UNDEFINED"},
|
||||||
|
{"name": "min uniform buffer offset alignment", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "min storage buffer offset alignment", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max vertex buffers", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max vertex attributes", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max vertex buffer array stride", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max inter stage shader components", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max compute workgroup storage size", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max compute invocations per workgroup", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max compute workgroup size x", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max compute workgroup size y", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max compute workgroup size z", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||||
|
{"name": "max compute workgroups per dimension", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"}
|
||||||
|
]
|
||||||
|
},
|
||||||
"logging callback": {
|
"logging callback": {
|
||||||
"category": "callback",
|
"category": "callback",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
|
@ -711,7 +711,8 @@ namespace dawn_wire {
|
||||||
WireResult result = serializeBuffer.Next(&transfer);
|
WireResult result = serializeBuffer.Next(&transfer);
|
||||||
ASSERT(result == WireResult::Success);
|
ASSERT(result == WireResult::Success);
|
||||||
|
|
||||||
result = WGPUDevicePropertiesSerialize(*deviceProperties, transfer, &serializeBuffer);
|
ErrorObjectIdProvider provider;
|
||||||
|
result = WGPUDevicePropertiesSerialize(*deviceProperties, transfer, &serializeBuffer, provider);
|
||||||
ASSERT(result == WireResult::Success);
|
ASSERT(result == WireResult::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,8 +725,9 @@ namespace dawn_wire {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorObjectIdResolver resolver;
|
||||||
return WGPUDevicePropertiesDeserialize(deviceProperties, transfer, &deserializeBuffer,
|
return WGPUDevicePropertiesDeserialize(deviceProperties, transfer, &deserializeBuffer,
|
||||||
nullptr) == WireResult::Success;
|
nullptr, resolver) == WireResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dawn_wire
|
} // namespace dawn_wire
|
||||||
|
|
|
@ -77,6 +77,8 @@
|
||||||
// TODO(crbug.com/dawn/520): Remove WGPU_STRIDE_UNDEFINED in favor of WGPU_COPY_STRIDE_UNDEFINED.
|
// TODO(crbug.com/dawn/520): Remove WGPU_STRIDE_UNDEFINED in favor of WGPU_COPY_STRIDE_UNDEFINED.
|
||||||
#define WGPU_STRIDE_UNDEFINED (0xffffffffUL)
|
#define WGPU_STRIDE_UNDEFINED (0xffffffffUL)
|
||||||
#define WGPU_COPY_STRIDE_UNDEFINED (0xffffffffUL)
|
#define WGPU_COPY_STRIDE_UNDEFINED (0xffffffffUL)
|
||||||
|
#define WGPU_LIMIT_U32_UNDEFINED (0xffffffffUL)
|
||||||
|
#define WGPU_LIMIT_U64_UNDEFINED (0xffffffffffffffffULL)
|
||||||
|
|
||||||
typedef uint32_t WGPUFlags;
|
typedef uint32_t WGPUFlags;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ namespace wgpu {
|
||||||
// TODO(crbug.com/520): Remove kStrideUndefined in favor of kCopyStrideUndefined.
|
// TODO(crbug.com/520): Remove kStrideUndefined in favor of kCopyStrideUndefined.
|
||||||
static constexpr uint32_t kStrideUndefined = WGPU_STRIDE_UNDEFINED;
|
static constexpr uint32_t kStrideUndefined = WGPU_STRIDE_UNDEFINED;
|
||||||
static constexpr uint32_t kCopyStrideUndefined = WGPU_COPY_STRIDE_UNDEFINED;
|
static constexpr uint32_t kCopyStrideUndefined = WGPU_COPY_STRIDE_UNDEFINED;
|
||||||
|
static constexpr uint32_t kLimitU32Undefined = WGPU_LIMIT_U32_UNDEFINED;
|
||||||
|
static constexpr uint64_t kLimitU64Undefined = WGPU_LIMIT_U64_UNDEFINED;
|
||||||
|
|
||||||
{% for type in by_category["enum"] %}
|
{% for type in by_category["enum"] %}
|
||||||
enum class {{as_cppType(type.name)}} : uint32_t {
|
enum class {{as_cppType(type.name)}} : uint32_t {
|
||||||
|
|
|
@ -65,7 +65,7 @@ static constexpr float kLodMax = 1000.0;
|
||||||
static constexpr uint32_t kMaxTextureDimension1D = 8192u;
|
static constexpr uint32_t kMaxTextureDimension1D = 8192u;
|
||||||
static constexpr uint32_t kMaxTextureDimension2D = 8192u;
|
static constexpr uint32_t kMaxTextureDimension2D = 8192u;
|
||||||
static constexpr uint32_t kMaxTextureDimension3D = 2048u;
|
static constexpr uint32_t kMaxTextureDimension3D = 2048u;
|
||||||
static constexpr uint32_t kMaxTextureArrayLayers = 2048u;
|
static constexpr uint32_t kMaxTextureArrayLayers = 256u;
|
||||||
static constexpr uint32_t kMaxTexture2DMipLevels = 14u;
|
static constexpr uint32_t kMaxTexture2DMipLevels = 14u;
|
||||||
static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureDimension2D,
|
static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureDimension2D,
|
||||||
"kMaxTexture2DMipLevels and kMaxTextureDimension2D size mismatch");
|
"kMaxTexture2DMipLevels and kMaxTextureDimension2D size mismatch");
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace dawn_native {
|
||||||
|
|
||||||
AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend)
|
AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend)
|
||||||
: mInstance(instance), mBackend(backend) {
|
: mInstance(instance), mBackend(backend) {
|
||||||
|
mLimits.v1.nextInChain = nullptr;
|
||||||
|
GetDefaultLimits(&mLimits.v1);
|
||||||
mSupportedExtensions.EnableExtension(Extension::DawnInternalUsages);
|
mSupportedExtensions.EnableExtension(Extension::DawnInternalUsages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +67,24 @@ namespace dawn_native {
|
||||||
WGPUDeviceProperties adapterProperties = {};
|
WGPUDeviceProperties adapterProperties = {};
|
||||||
|
|
||||||
mSupportedExtensions.InitializeDeviceProperties(&adapterProperties);
|
mSupportedExtensions.InitializeDeviceProperties(&adapterProperties);
|
||||||
|
// This is OK for now because there are no limit extension structs.
|
||||||
|
// If we add additional structs, the caller will need to provide memory
|
||||||
|
// to store them (ex. by calling GetLimits directly instead). Currently,
|
||||||
|
// we keep this function as it's only used internally in Chromium to
|
||||||
|
// send the adapter properties across the wire.
|
||||||
|
GetLimits(reinterpret_cast<wgpu::Limits*>(&adapterProperties.limits));
|
||||||
return adapterProperties;
|
return adapterProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AdapterBase::GetLimits(wgpu::Limits* limits) const {
|
||||||
|
ASSERT(limits != nullptr);
|
||||||
|
if (limits->nextInChain != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*limits = mLimits.v1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
DeviceBase* AdapterBase::CreateDevice(const DeviceDescriptor* descriptor) {
|
DeviceBase* AdapterBase::CreateDevice(const DeviceDescriptor* descriptor) {
|
||||||
DeviceBase* result = nullptr;
|
DeviceBase* result = nullptr;
|
||||||
|
|
||||||
|
@ -104,6 +121,15 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor != nullptr && descriptor->requiredLimits != nullptr) {
|
||||||
|
DAWN_TRY(ValidateLimits(
|
||||||
|
mLimits.v1, *reinterpret_cast<const wgpu::Limits*>(descriptor->requiredLimits)));
|
||||||
|
|
||||||
|
if (descriptor->requiredLimits->nextInChain != nullptr) {
|
||||||
|
return DAWN_VALIDATION_ERROR("Unsupported limit extension struct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DAWN_TRY_ASSIGN(*result, CreateDeviceImpl(descriptor));
|
DAWN_TRY_ASSIGN(*result, CreateDeviceImpl(descriptor));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "dawn_native/Error.h"
|
#include "dawn_native/Error.h"
|
||||||
#include "dawn_native/Extensions.h"
|
#include "dawn_native/Extensions.h"
|
||||||
|
#include "dawn_native/Limits.h"
|
||||||
#include "dawn_native/dawn_platform.h"
|
#include "dawn_native/dawn_platform.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -51,6 +52,8 @@ namespace dawn_native {
|
||||||
const std::vector<const char*>& requestedExtensions) const;
|
const std::vector<const char*>& requestedExtensions) const;
|
||||||
WGPUDeviceProperties GetAdapterProperties() const;
|
WGPUDeviceProperties GetAdapterProperties() const;
|
||||||
|
|
||||||
|
bool GetLimits(wgpu::Limits* limits) const;
|
||||||
|
|
||||||
virtual bool SupportsExternalImages() const = 0;
|
virtual bool SupportsExternalImages() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -67,6 +70,7 @@ namespace dawn_native {
|
||||||
virtual MaybeError ResetInternalDeviceForTestingImpl();
|
virtual MaybeError ResetInternalDeviceForTestingImpl();
|
||||||
InstanceBase* mInstance = nullptr;
|
InstanceBase* mInstance = nullptr;
|
||||||
wgpu::BackendType mBackend;
|
wgpu::BackendType mBackend;
|
||||||
|
CombinedLimits mLimits;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -239,6 +239,8 @@ source_set("dawn_native_sources") {
|
||||||
"Instance.h",
|
"Instance.h",
|
||||||
"IntegerTypes.h",
|
"IntegerTypes.h",
|
||||||
"InternalPipelineStore.h",
|
"InternalPipelineStore.h",
|
||||||
|
"Limits.cpp",
|
||||||
|
"Limits.h",
|
||||||
"ObjectBase.cpp",
|
"ObjectBase.cpp",
|
||||||
"ObjectBase.h",
|
"ObjectBase.h",
|
||||||
"ObjectContentHasher.cpp",
|
"ObjectContentHasher.cpp",
|
||||||
|
|
|
@ -105,6 +105,8 @@ target_sources(dawn_native PRIVATE
|
||||||
"Instance.h"
|
"Instance.h"
|
||||||
"InternalPipelineStore.h"
|
"InternalPipelineStore.h"
|
||||||
"IntegerTypes.h"
|
"IntegerTypes.h"
|
||||||
|
"Limits.cpp"
|
||||||
|
"Limits.h"
|
||||||
"ObjectBase.cpp"
|
"ObjectBase.cpp"
|
||||||
"ObjectBase.h"
|
"ObjectBase.h"
|
||||||
"PassResourceUsage.h"
|
"PassResourceUsage.h"
|
||||||
|
|
|
@ -106,6 +106,10 @@ namespace dawn_native {
|
||||||
return mImpl->GetAdapterProperties();
|
return mImpl->GetAdapterProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Adapter::GetLimits(WGPULimits* limits) const {
|
||||||
|
return mImpl->GetLimits(reinterpret_cast<wgpu::Limits*>(limits));
|
||||||
|
}
|
||||||
|
|
||||||
bool Adapter::SupportsExternalImages() const {
|
bool Adapter::SupportsExternalImages() const {
|
||||||
return mImpl->SupportsExternalImages();
|
return mImpl->SupportsExternalImages();
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,6 +178,13 @@ namespace dawn_native {
|
||||||
ApplyExtensions(descriptor);
|
ApplyExtensions(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor != nullptr && descriptor->requiredLimits != nullptr) {
|
||||||
|
mLimits.v1 = ReifyDefaultLimits(
|
||||||
|
*reinterpret_cast<const wgpu::Limits*>(descriptor->requiredLimits));
|
||||||
|
} else {
|
||||||
|
GetDefaultLimits(&mLimits.v1);
|
||||||
|
}
|
||||||
|
|
||||||
mFormatTable = BuildFormatTable(this);
|
mFormatTable = BuildFormatTable(this);
|
||||||
SetDefaultToggles();
|
SetDefaultToggles();
|
||||||
if ((adapter->GetBackendType() == wgpu::BackendType::Metal ||
|
if ((adapter->GetBackendType() == wgpu::BackendType::Metal ||
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "dawn_native/Extensions.h"
|
#include "dawn_native/Extensions.h"
|
||||||
#include "dawn_native/Format.h"
|
#include "dawn_native/Format.h"
|
||||||
#include "dawn_native/Forward.h"
|
#include "dawn_native/Forward.h"
|
||||||
|
#include "dawn_native/Limits.h"
|
||||||
#include "dawn_native/ObjectBase.h"
|
#include "dawn_native/ObjectBase.h"
|
||||||
#include "dawn_native/Toggles.h"
|
#include "dawn_native/Toggles.h"
|
||||||
|
|
||||||
|
@ -447,6 +448,7 @@ namespace dawn_native {
|
||||||
size_t mLazyClearCountForTesting = 0;
|
size_t mLazyClearCountForTesting = 0;
|
||||||
std::atomic_uint64_t mNextPipelineCompatibilityToken;
|
std::atomic_uint64_t mNextPipelineCompatibilityToken;
|
||||||
|
|
||||||
|
CombinedLimits mLimits;
|
||||||
ExtensionsSet mEnabledExtensions;
|
ExtensionsSet mEnabledExtensions;
|
||||||
|
|
||||||
std::unique_ptr<InternalPipelineStore> mInternalPipelineStore;
|
std::unique_ptr<InternalPipelineStore> mInternalPipelineStore;
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
// 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 "dawn_native/Limits.h"
|
||||||
|
|
||||||
|
#include "common/Assert.h"
|
||||||
|
|
||||||
|
#define LIMITS(X) \
|
||||||
|
X(Higher, maxTextureDimension1D, 8192) \
|
||||||
|
X(Higher, maxTextureDimension2D, 8192) \
|
||||||
|
X(Higher, maxTextureDimension3D, 2048) \
|
||||||
|
X(Higher, maxTextureArrayLayers, 256) \
|
||||||
|
X(Higher, maxBindGroups, 4) \
|
||||||
|
X(Higher, maxDynamicUniformBuffersPerPipelineLayout, 8) \
|
||||||
|
X(Higher, maxDynamicStorageBuffersPerPipelineLayout, 4) \
|
||||||
|
X(Higher, maxSampledTexturesPerShaderStage, 16) \
|
||||||
|
X(Higher, maxSamplersPerShaderStage, 16) \
|
||||||
|
X(Higher, maxStorageBuffersPerShaderStage, 8) \
|
||||||
|
X(Higher, maxStorageTexturesPerShaderStage, 4) \
|
||||||
|
X(Higher, maxUniformBuffersPerShaderStage, 12) \
|
||||||
|
X(Higher, maxUniformBufferBindingSize, 16384) \
|
||||||
|
X(Higher, maxStorageBufferBindingSize, 134217728) \
|
||||||
|
X(Lower, minUniformBufferOffsetAlignment, 256) \
|
||||||
|
X(Lower, minStorageBufferOffsetAlignment, 256) \
|
||||||
|
X(Higher, maxVertexBuffers, 8) \
|
||||||
|
X(Higher, maxVertexAttributes, 16) \
|
||||||
|
X(Higher, maxVertexBufferArrayStride, 2048) \
|
||||||
|
X(Higher, maxInterStageShaderComponents, 60) \
|
||||||
|
X(Higher, maxComputeWorkgroupStorageSize, 16352) \
|
||||||
|
X(Higher, maxComputeInvocationsPerWorkgroup, 256) \
|
||||||
|
X(Higher, maxComputeWorkgroupSizeX, 256) \
|
||||||
|
X(Higher, maxComputeWorkgroupSizeY, 256) \
|
||||||
|
X(Higher, maxComputeWorkgroupSizeZ, 64) \
|
||||||
|
X(Higher, maxComputeWorkgroupsPerDimension, 65535)
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
namespace {
|
||||||
|
enum class LimitBetterDirection {
|
||||||
|
Lower,
|
||||||
|
Higher,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <LimitBetterDirection Better>
|
||||||
|
struct CheckLimit;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CheckLimit<LimitBetterDirection::Lower> {
|
||||||
|
template <typename T>
|
||||||
|
static MaybeError Invoke(T supported, T required) {
|
||||||
|
if (required < supported) {
|
||||||
|
return DAWN_VALIDATION_ERROR("requiredLimit lower than supported limit");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CheckLimit<LimitBetterDirection::Higher> {
|
||||||
|
template <typename T>
|
||||||
|
static MaybeError Invoke(T supported, T required) {
|
||||||
|
if (required > supported) {
|
||||||
|
return DAWN_VALIDATION_ERROR("requiredLimit greater than supported limit");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool IsLimitUndefined(T value) {
|
||||||
|
static_assert(sizeof(T) != sizeof(T), "IsLimitUndefined not implemented for this type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool IsLimitUndefined<uint32_t>(uint32_t value) {
|
||||||
|
return value == wgpu::kLimitU32Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool IsLimitUndefined<uint64_t>(uint64_t value) {
|
||||||
|
return value == wgpu::kLimitU64Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void GetDefaultLimits(wgpu::Limits* limits) {
|
||||||
|
ASSERT(limits != nullptr);
|
||||||
|
#define X(Better, limitName, defaultValue) limits->limitName = defaultValue;
|
||||||
|
LIMITS(X)
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::Limits ReifyDefaultLimits(const wgpu::Limits& limits) {
|
||||||
|
wgpu::Limits out;
|
||||||
|
#define X(Better, limitName, defaultValue) \
|
||||||
|
if (!IsLimitUndefined(limits.limitName)) { \
|
||||||
|
out.limitName = limits.limitName; \
|
||||||
|
} else { \
|
||||||
|
out.limitName = defaultValue; \
|
||||||
|
}
|
||||||
|
LIMITS(X)
|
||||||
|
#undef X
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeError ValidateLimits(const wgpu::Limits& supportedLimits,
|
||||||
|
const wgpu::Limits& requiredLimits) {
|
||||||
|
#define X(Better, limitName, defaultValue) \
|
||||||
|
if (!IsLimitUndefined(requiredLimits.limitName)) { \
|
||||||
|
DAWN_TRY(CheckLimit<LimitBetterDirection::Better>::Invoke(supportedLimits.limitName, \
|
||||||
|
requiredLimits.limitName)); \
|
||||||
|
}
|
||||||
|
LIMITS(X)
|
||||||
|
#undef X
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn_native
|
|
@ -0,0 +1,40 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef DAWNNATIVE_LIMITS_H_
|
||||||
|
#define DAWNNATIVE_LIMITS_H_
|
||||||
|
|
||||||
|
#include "dawn_native/Error.h"
|
||||||
|
#include "dawn_native/dawn_platform.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
|
||||||
|
struct CombinedLimits {
|
||||||
|
wgpu::Limits v1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Populate |limits| with the default limits.
|
||||||
|
void GetDefaultLimits(wgpu::Limits* limits);
|
||||||
|
|
||||||
|
// Returns a copy of |limits| where all undefined values are replaced
|
||||||
|
// with their defaults.
|
||||||
|
wgpu::Limits ReifyDefaultLimits(const wgpu::Limits& limits);
|
||||||
|
|
||||||
|
// Validate that |requiredLimits| are no better than |supportedLimits|.
|
||||||
|
MaybeError ValidateLimits(const wgpu::Limits& supportedLimits,
|
||||||
|
const wgpu::Limits& requiredLimits);
|
||||||
|
|
||||||
|
} // namespace dawn_native
|
||||||
|
|
||||||
|
#endif // DAWNNATIVE_LIMITS_H_
|
|
@ -66,6 +66,8 @@ namespace dawn_native {
|
||||||
std::vector<const char*> requiredExtensions;
|
std::vector<const char*> requiredExtensions;
|
||||||
std::vector<const char*> forceEnabledToggles;
|
std::vector<const char*> forceEnabledToggles;
|
||||||
std::vector<const char*> forceDisabledToggles;
|
std::vector<const char*> forceDisabledToggles;
|
||||||
|
|
||||||
|
const WGPULimits* requiredLimits = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A struct to record the information of a toggle. A toggle is a code path in Dawn device that
|
// A struct to record the information of a toggle. A toggle is a code path in Dawn device that
|
||||||
|
@ -108,6 +110,7 @@ namespace dawn_native {
|
||||||
|
|
||||||
std::vector<const char*> GetSupportedExtensions() const;
|
std::vector<const char*> GetSupportedExtensions() const;
|
||||||
WGPUDeviceProperties GetAdapterProperties() const;
|
WGPUDeviceProperties GetAdapterProperties() const;
|
||||||
|
bool GetLimits(WGPULimits* limits) const;
|
||||||
|
|
||||||
// Check that the Adapter is able to support importing external images. This is necessary
|
// Check that the Adapter is able to support importing external images. This is necessary
|
||||||
// to implement the swapchain and interop APIs in Chromium.
|
// to implement the swapchain and interop APIs in Chromium.
|
||||||
|
|
|
@ -214,6 +214,7 @@ test("dawn_unittests") {
|
||||||
"unittests/validation/RenderBundleValidationTests.cpp",
|
"unittests/validation/RenderBundleValidationTests.cpp",
|
||||||
"unittests/validation/RenderPassDescriptorValidationTests.cpp",
|
"unittests/validation/RenderPassDescriptorValidationTests.cpp",
|
||||||
"unittests/validation/RenderPipelineValidationTests.cpp",
|
"unittests/validation/RenderPipelineValidationTests.cpp",
|
||||||
|
"unittests/validation/RequestDeviceValidationTests.cpp",
|
||||||
"unittests/validation/ResourceUsageTrackingTests.cpp",
|
"unittests/validation/ResourceUsageTrackingTests.cpp",
|
||||||
"unittests/validation/SamplerValidationTests.cpp",
|
"unittests/validation/SamplerValidationTests.cpp",
|
||||||
"unittests/validation/ShaderModuleValidationTests.cpp",
|
"unittests/validation/ShaderModuleValidationTests.cpp",
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
// 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 "tests/unittests/validation/ValidationTest.h"
|
||||||
|
|
||||||
|
class RequestDeviceValidationTest : public ValidationTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() {
|
||||||
|
DAWN_SKIP_TEST_IF(UsesWire());
|
||||||
|
ValidationTest::SetUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ExpectRequestDeviceSuccess(WGPURequestDeviceStatus status,
|
||||||
|
WGPUDevice cDevice,
|
||||||
|
const char* message,
|
||||||
|
void* userdata) {
|
||||||
|
wgpu::Device device = wgpu::Device::Acquire(cDevice);
|
||||||
|
EXPECT_EQ(status, WGPURequestDeviceStatus_Success);
|
||||||
|
EXPECT_NE(device, nullptr);
|
||||||
|
EXPECT_STREQ(message, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ExpectRequestDeviceError(WGPURequestDeviceStatus status,
|
||||||
|
WGPUDevice cDevice,
|
||||||
|
const char* message,
|
||||||
|
void* userdata) {
|
||||||
|
wgpu::Device device = wgpu::Device::Acquire(cDevice);
|
||||||
|
EXPECT_EQ(status, WGPURequestDeviceStatus_Error);
|
||||||
|
EXPECT_EQ(device, nullptr);
|
||||||
|
EXPECT_STRNE(message, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that requesting a device without specifying limits is valid.
|
||||||
|
TEST_F(RequestDeviceValidationTest, NoRequiredLimits) {
|
||||||
|
dawn_native::DeviceDescriptor descriptor;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that requesting a device with the default limits is valid.
|
||||||
|
TEST_F(RequestDeviceValidationTest, DefaultLimits) {
|
||||||
|
wgpu::Limits limits = {};
|
||||||
|
dawn_native::DeviceDescriptor descriptor;
|
||||||
|
descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that requesting a device where a required limit is above the maximum value.
|
||||||
|
TEST_F(RequestDeviceValidationTest, HigherIsBetter) {
|
||||||
|
wgpu::Limits limits = {};
|
||||||
|
dawn_native::DeviceDescriptor descriptor;
|
||||||
|
descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
|
||||||
|
|
||||||
|
wgpu::Limits supportedLimits;
|
||||||
|
EXPECT_TRUE(adapter.GetLimits(reinterpret_cast<WGPULimits*>(&supportedLimits)));
|
||||||
|
|
||||||
|
// Test below the max.
|
||||||
|
limits.maxBindGroups = supportedLimits.maxBindGroups - 1;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
|
||||||
|
|
||||||
|
// Test the max.
|
||||||
|
limits.maxBindGroups = supportedLimits.maxBindGroups;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
|
||||||
|
|
||||||
|
// Test above the max.
|
||||||
|
limits.maxBindGroups = supportedLimits.maxBindGroups + 1;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that requesting a device where a required limit is below the minimum value.
|
||||||
|
TEST_F(RequestDeviceValidationTest, LowerIsBetter) {
|
||||||
|
wgpu::Limits limits = {};
|
||||||
|
dawn_native::DeviceDescriptor descriptor;
|
||||||
|
descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
|
||||||
|
|
||||||
|
wgpu::Limits supportedLimits;
|
||||||
|
EXPECT_TRUE(adapter.GetLimits(reinterpret_cast<WGPULimits*>(&supportedLimits)));
|
||||||
|
|
||||||
|
// Test below the min.
|
||||||
|
limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment / 2;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
|
||||||
|
|
||||||
|
// Test the min.
|
||||||
|
limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
|
||||||
|
|
||||||
|
// Test above the min.
|
||||||
|
limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment * 2;
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that it is an error to request limits with an invalid chained struct
|
||||||
|
TEST_F(RequestDeviceValidationTest, InvalidChainedStruct) {
|
||||||
|
wgpu::PrimitiveDepthClampingState depthClamp = {};
|
||||||
|
wgpu::Limits limits = {};
|
||||||
|
limits.nextInChain = &depthClamp;
|
||||||
|
|
||||||
|
dawn_native::DeviceDescriptor descriptor;
|
||||||
|
descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
|
||||||
|
adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
|
||||||
|
}
|
Loading…
Reference in New Issue