From e17a13c60ca04c381f8265a24621dfd51ede670e Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Fri, 8 Apr 2022 07:04:26 +0000 Subject: [PATCH] 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 Commit-Queue: Corentin Wallez --- src/dawn/native/Limits.cpp | 81 ++++++++++++------------ src/dawn/tests/unittests/LimitsTests.cpp | 17 +++-- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/dawn/native/Limits.cpp b/src/dawn/native/Limits.cpp index a7b8ec9bcc..3b20a1bc99 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 @@ -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 @@ namespace dawn::native { return I; } - enum class LimitBetterDirection { - Lower, - Higher, + enum class LimitClass { + Alignment, + Maximum, }; - template + template struct CheckLimit; template <> - struct CheckLimit { + struct CheckLimit { template static bool IsBetter(T lhs, T rhs) { return lhs < rhs; @@ -101,12 +102,14 @@ namespace dawn::native { 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 { + struct CheckLimit { template static bool IsBetter(T lhs, T rhs) { return lhs > rhs; @@ -148,9 +151,9 @@ namespace dawn::native { Limits ReifyDefaultLimits(const Limits& limits) { Limits out; -#define X(Better, limitName, base, ...) \ +#define X(Class, limitName, base, ...) \ if (IsLimitUndefined(limits.limitName) || \ - CheckLimit::IsBetter( \ + CheckLimit::IsBetter( \ static_cast(base), limits.limitName)) { \ /* If the limit is undefined or the default is better, use the default */ \ out.limitName = base; \ @@ -163,9 +166,9 @@ namespace dawn::native { } MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) { -#define X(Better, limitName, ...) \ +#define X(Class, limitName, ...) \ if (!IsLimitUndefined(requiredLimits.limitName)) { \ - DAWN_TRY_CONTEXT(CheckLimit::Validate( \ + DAWN_TRY_CONTEXT(CheckLimit::Validate( \ supportedLimits.limitName, requiredLimits.limitName), \ "validating " #limitName); \ } @@ -189,11 +192,11 @@ namespace dawn::native { } \ } -#define X_CHECK_BETTER_AND_CLAMP(Better, limitName, ...) \ +#define X_CHECK_BETTER_AND_CLAMP(Class, limitName, ...) \ { \ constexpr std::array tiers{__VA_ARGS__}; \ decltype(Limits::limitName) tierValue = tiers[i - 1]; \ - if (CheckLimit::IsBetter(tierValue, limits.limitName)) { \ + if (CheckLimit::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 544c0c5680..3cc3cbe6ad 100644 --- a/src/dawn/tests/unittests/LimitsTests.cpp +++ b/src/dawn/tests/unittests/LimitsTests.cpp @@ -78,7 +78,7 @@ TEST(Limits, ValidateLimits) { 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 @@ TEST(Limits, ValidateLimits) { 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 @@ TEST(Limits, ValidateLimits) { 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.