mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-10 14:08:04 +00:00
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:
@@ -65,7 +65,7 @@ static constexpr float kLodMax = 1000.0;
|
||||
static constexpr uint32_t kMaxTextureDimension1D = 8192u;
|
||||
static constexpr uint32_t kMaxTextureDimension2D = 8192u;
|
||||
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_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureDimension2D,
|
||||
"kMaxTexture2DMipLevels and kMaxTextureDimension2D size mismatch");
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace dawn_native {
|
||||
|
||||
AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend)
|
||||
: mInstance(instance), mBackend(backend) {
|
||||
mLimits.v1.nextInChain = nullptr;
|
||||
GetDefaultLimits(&mLimits.v1);
|
||||
mSupportedExtensions.EnableExtension(Extension::DawnInternalUsages);
|
||||
}
|
||||
|
||||
@@ -65,9 +67,24 @@ namespace dawn_native {
|
||||
WGPUDeviceProperties 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;
|
||||
}
|
||||
|
||||
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* 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));
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/Extensions.h"
|
||||
#include "dawn_native/Limits.h"
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
||||
#include <string>
|
||||
@@ -51,6 +52,8 @@ namespace dawn_native {
|
||||
const std::vector<const char*>& requestedExtensions) const;
|
||||
WGPUDeviceProperties GetAdapterProperties() const;
|
||||
|
||||
bool GetLimits(wgpu::Limits* limits) const;
|
||||
|
||||
virtual bool SupportsExternalImages() const = 0;
|
||||
|
||||
protected:
|
||||
@@ -67,6 +70,7 @@ namespace dawn_native {
|
||||
virtual MaybeError ResetInternalDeviceForTestingImpl();
|
||||
InstanceBase* mInstance = nullptr;
|
||||
wgpu::BackendType mBackend;
|
||||
CombinedLimits mLimits;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
||||
@@ -239,6 +239,8 @@ source_set("dawn_native_sources") {
|
||||
"Instance.h",
|
||||
"IntegerTypes.h",
|
||||
"InternalPipelineStore.h",
|
||||
"Limits.cpp",
|
||||
"Limits.h",
|
||||
"ObjectBase.cpp",
|
||||
"ObjectBase.h",
|
||||
"ObjectContentHasher.cpp",
|
||||
|
||||
@@ -105,6 +105,8 @@ target_sources(dawn_native PRIVATE
|
||||
"Instance.h"
|
||||
"InternalPipelineStore.h"
|
||||
"IntegerTypes.h"
|
||||
"Limits.cpp"
|
||||
"Limits.h"
|
||||
"ObjectBase.cpp"
|
||||
"ObjectBase.h"
|
||||
"PassResourceUsage.h"
|
||||
|
||||
@@ -106,6 +106,10 @@ namespace dawn_native {
|
||||
return mImpl->GetAdapterProperties();
|
||||
}
|
||||
|
||||
bool Adapter::GetLimits(WGPULimits* limits) const {
|
||||
return mImpl->GetLimits(reinterpret_cast<wgpu::Limits*>(limits));
|
||||
}
|
||||
|
||||
bool Adapter::SupportsExternalImages() const {
|
||||
return mImpl->SupportsExternalImages();
|
||||
}
|
||||
|
||||
@@ -178,6 +178,13 @@ namespace dawn_native {
|
||||
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);
|
||||
SetDefaultToggles();
|
||||
if ((adapter->GetBackendType() == wgpu::BackendType::Metal ||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "dawn_native/Extensions.h"
|
||||
#include "dawn_native/Format.h"
|
||||
#include "dawn_native/Forward.h"
|
||||
#include "dawn_native/Limits.h"
|
||||
#include "dawn_native/ObjectBase.h"
|
||||
#include "dawn_native/Toggles.h"
|
||||
|
||||
@@ -447,6 +448,7 @@ namespace dawn_native {
|
||||
size_t mLazyClearCountForTesting = 0;
|
||||
std::atomic_uint64_t mNextPipelineCompatibilityToken;
|
||||
|
||||
CombinedLimits mLimits;
|
||||
ExtensionsSet mEnabledExtensions;
|
||||
|
||||
std::unique_ptr<InternalPipelineStore> mInternalPipelineStore;
|
||||
|
||||
129
src/dawn_native/Limits.cpp
Normal file
129
src/dawn_native/Limits.cpp
Normal file
@@ -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
|
||||
40
src/dawn_native/Limits.h
Normal file
40
src/dawn_native/Limits.h
Normal file
@@ -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*> forceEnabledToggles;
|
||||
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
|
||||
@@ -108,6 +110,7 @@ namespace dawn_native {
|
||||
|
||||
std::vector<const char*> GetSupportedExtensions() const;
|
||||
WGPUDeviceProperties GetAdapterProperties() const;
|
||||
bool GetLimits(WGPULimits* limits) const;
|
||||
|
||||
// Check that the Adapter is able to support importing external images. This is necessary
|
||||
// to implement the swapchain and interop APIs in Chromium.
|
||||
|
||||
@@ -214,6 +214,7 @@ test("dawn_unittests") {
|
||||
"unittests/validation/RenderBundleValidationTests.cpp",
|
||||
"unittests/validation/RenderPassDescriptorValidationTests.cpp",
|
||||
"unittests/validation/RenderPipelineValidationTests.cpp",
|
||||
"unittests/validation/RequestDeviceValidationTests.cpp",
|
||||
"unittests/validation/ResourceUsageTrackingTests.cpp",
|
||||
"unittests/validation/SamplerValidationTests.cpp",
|
||||
"unittests/validation/ShaderModuleValidationTests.cpp",
|
||||
|
||||
112
src/tests/unittests/validation/RequestDeviceValidationTests.cpp
Normal file
112
src/tests/unittests/validation/RequestDeviceValidationTests.cpp
Normal file
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user