Validate that alignment limits are powers of two.
Also changes enums to use the concept of "limit class" from the WebGPU
specification.
Fixed: dawn:1242
Change-Id: I328ce98c2eaaf3f4b7ff1c253ee5f3db5a2980f5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/84762
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/Limits.cpp b/src/dawn/native/Limits.cpp
index a7b8ec9..3b20a1b 100644
--- a/src/dawn/native/Limits.cpp
+++ b/src/dawn/native/Limits.cpp
@@ -15,6 +15,7 @@
#include "dawn/native/Limits.h"
#include "dawn/common/Assert.h"
+#include "dawn/common/Math.h"
#include <array>
@@ -22,39 +23,39 @@
// TODO(crbug.com/dawn/685):
// For now, only expose these tiers until metrics can determine better ones.
#define LIMITS_WORKGROUP_STORAGE_SIZE(X) \
- X(Higher, maxComputeWorkgroupStorageSize, 16352, 32768, 49152, 65536)
+ X(Maximum, maxComputeWorkgroupStorageSize, 16352, 32768, 49152, 65536)
#define LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \
- X(Higher, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483647, 4294967295)
+ X(Maximum, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483647, 4294967295)
// TODO(crbug.com/dawn/685):
// These limits don't have tiers yet. Define two tiers with the same values since the macros
// in this file expect more than one tier.
-#define LIMITS_OTHER(X) \
- X(Higher, maxTextureDimension1D, 8192, 8192) \
- X(Higher, maxTextureDimension2D, 8192, 8192) \
- X(Higher, maxTextureDimension3D, 2048, 2048) \
- X(Higher, maxTextureArrayLayers, 256, 256) \
- X(Higher, maxBindGroups, 4, 4) \
- X(Higher, maxDynamicUniformBuffersPerPipelineLayout, 8, 8) \
- X(Higher, maxDynamicStorageBuffersPerPipelineLayout, 4, 4) \
- X(Higher, maxSampledTexturesPerShaderStage, 16, 16) \
- X(Higher, maxSamplersPerShaderStage, 16, 16) \
- X(Higher, maxStorageBuffersPerShaderStage, 8, 8) \
- X(Higher, maxStorageTexturesPerShaderStage, 4, 4) \
- X(Higher, maxUniformBuffersPerShaderStage, 12, 12) \
- X(Higher, maxUniformBufferBindingSize, 65536, 65536) \
- X( Lower, minUniformBufferOffsetAlignment, 256, 256) \
- X( Lower, minStorageBufferOffsetAlignment, 256, 256) \
- X(Higher, maxVertexBuffers, 8, 8) \
- X(Higher, maxVertexAttributes, 16, 16) \
- X(Higher, maxVertexBufferArrayStride, 2048, 2048) \
- X(Higher, maxInterStageShaderComponents, 60, 60) \
- X(Higher, maxComputeInvocationsPerWorkgroup, 256, 256) \
- X(Higher, maxComputeWorkgroupSizeX, 256, 256) \
- X(Higher, maxComputeWorkgroupSizeY, 256, 256) \
- X(Higher, maxComputeWorkgroupSizeZ, 64, 64) \
- X(Higher, maxComputeWorkgroupsPerDimension, 65535, 65535)
+#define LIMITS_OTHER(X) \
+ X(Maximum, maxTextureDimension1D, 8192, 8192) \
+ X(Maximum, maxTextureDimension2D, 8192, 8192) \
+ X(Maximum, maxTextureDimension3D, 2048, 2048) \
+ X(Maximum, maxTextureArrayLayers, 256, 256) \
+ X(Maximum, maxBindGroups, 4, 4) \
+ X(Maximum, maxDynamicUniformBuffersPerPipelineLayout, 8, 8) \
+ X(Maximum, maxDynamicStorageBuffersPerPipelineLayout, 4, 4) \
+ X(Maximum, maxSampledTexturesPerShaderStage, 16, 16) \
+ X(Maximum, maxSamplersPerShaderStage, 16, 16) \
+ X(Maximum, maxStorageBuffersPerShaderStage, 8, 8) \
+ X(Maximum, maxStorageTexturesPerShaderStage, 4, 4) \
+ X(Maximum, maxUniformBuffersPerShaderStage, 12, 12) \
+ X(Maximum, maxUniformBufferBindingSize, 65536, 65536) \
+ X(Alignment, minUniformBufferOffsetAlignment, 256, 256) \
+ X(Alignment, minStorageBufferOffsetAlignment, 256, 256) \
+ X(Maximum, maxVertexBuffers, 8, 8) \
+ X(Maximum, maxVertexAttributes, 16, 16) \
+ X(Maximum, maxVertexBufferArrayStride, 2048, 2048) \
+ X(Maximum, maxInterStageShaderComponents, 60, 60) \
+ X(Maximum, maxComputeInvocationsPerWorkgroup, 256, 256) \
+ X(Maximum, maxComputeWorkgroupSizeX, 256, 256) \
+ X(Maximum, maxComputeWorkgroupSizeY, 256, 256) \
+ X(Maximum, maxComputeWorkgroupSizeZ, 64, 64) \
+ X(Maximum, maxComputeWorkgroupsPerDimension, 65535, 65535)
// clang-format on
#define LIMITS_EACH_GROUP(X) \
@@ -81,16 +82,16 @@
return I;
}
- enum class LimitBetterDirection {
- Lower,
- Higher,
+ enum class LimitClass {
+ Alignment,
+ Maximum,
};
- template <LimitBetterDirection Better>
+ template <LimitClass C>
struct CheckLimit;
template <>
- struct CheckLimit<LimitBetterDirection::Lower> {
+ struct CheckLimit<LimitClass::Alignment> {
template <typename T>
static bool IsBetter(T lhs, T rhs) {
return lhs < rhs;
@@ -101,12 +102,14 @@
DAWN_INVALID_IF(IsBetter(required, supported),
"Required limit (%u) is lower than the supported limit (%u).",
required, supported);
+ DAWN_INVALID_IF(!IsPowerOfTwo(required),
+ "Required limit (%u) is not a power of two.", required);
return {};
}
};
template <>
- struct CheckLimit<LimitBetterDirection::Higher> {
+ struct CheckLimit<LimitClass::Maximum> {
template <typename T>
static bool IsBetter(T lhs, T rhs) {
return lhs > rhs;
@@ -148,9 +151,9 @@
Limits ReifyDefaultLimits(const Limits& limits) {
Limits out;
-#define X(Better, limitName, base, ...) \
+#define X(Class, limitName, base, ...) \
if (IsLimitUndefined(limits.limitName) || \
- CheckLimit<LimitBetterDirection::Better>::IsBetter( \
+ CheckLimit<LimitClass::Class>::IsBetter( \
static_cast<decltype(limits.limitName)>(base), limits.limitName)) { \
/* If the limit is undefined or the default is better, use the default */ \
out.limitName = base; \
@@ -163,9 +166,9 @@
}
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) {
-#define X(Better, limitName, ...) \
+#define X(Class, limitName, ...) \
if (!IsLimitUndefined(requiredLimits.limitName)) { \
- DAWN_TRY_CONTEXT(CheckLimit<LimitBetterDirection::Better>::Validate( \
+ DAWN_TRY_CONTEXT(CheckLimit<LimitClass::Class>::Validate( \
supportedLimits.limitName, requiredLimits.limitName), \
"validating " #limitName); \
}
@@ -189,11 +192,11 @@
} \
}
-#define X_CHECK_BETTER_AND_CLAMP(Better, limitName, ...) \
+#define X_CHECK_BETTER_AND_CLAMP(Class, limitName, ...) \
{ \
constexpr std::array<decltype(Limits::limitName), kTierCount> tiers{__VA_ARGS__}; \
decltype(Limits::limitName) tierValue = tiers[i - 1]; \
- if (CheckLimit<LimitBetterDirection::Better>::IsBetter(tierValue, limits.limitName)) { \
+ if (CheckLimit<LimitClass::Class>::IsBetter(tierValue, limits.limitName)) { \
/* The tier is better. Go to the next tier. */ \
continue; \
} else if (tierValue != limits.limitName) { \
diff --git a/src/dawn/tests/unittests/LimitsTests.cpp b/src/dawn/tests/unittests/LimitsTests.cpp
index 544c0c5..3cc3cbe 100644
--- a/src/dawn/tests/unittests/LimitsTests.cpp
+++ b/src/dawn/tests/unittests/LimitsTests.cpp
@@ -78,7 +78,7 @@
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
}
- // Test that better than max is invalid.
+ // Test that better than supported is invalid for "maximum" limits.
{
dawn::native::Limits required = {};
required.maxTextureDimension3D = defaults.maxTextureDimension3D + 1;
@@ -87,14 +87,14 @@
err.AcquireError();
}
- // Test that worse than max is valid.
+ // Test that worse than supported is valid for "maximum" limits.
{
dawn::native::Limits required = {};
required.maxComputeWorkgroupSizeX = defaults.maxComputeWorkgroupSizeX - 1;
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
}
- // Test that better than min is invalid.
+ // Test that better than min is invalid for "alignment" limits.
{
dawn::native::Limits required = {};
required.minUniformBufferOffsetAlignment = defaults.minUniformBufferOffsetAlignment / 2;
@@ -103,12 +103,21 @@
err.AcquireError();
}
- // Test that worse than min is valid.
+ // Test that worse than min and a power of two is valid for "alignment" limits.
{
dawn::native::Limits required = {};
required.minStorageBufferOffsetAlignment = defaults.minStorageBufferOffsetAlignment * 2;
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
}
+
+ // Test that worse than min and not a power of two is invalid for "alignment" limits.
+ {
+ dawn::native::Limits required = {};
+ required.minStorageBufferOffsetAlignment = defaults.minStorageBufferOffsetAlignment * 3;
+ dawn::native::MaybeError err = ValidateLimits(defaults, required);
+ EXPECT_TRUE(err.IsError());
+ err.AcquireError();
+ }
}
// Test that |ApplyLimitTiers| degrades limits to the next best tier.