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>
This commit is contained in:
Corentin Wallez 2022-04-08 07:04:26 +00:00 committed by Dawn LUCI CQ
parent b7411603ad
commit e17a13c60c
2 changed files with 55 additions and 43 deletions

View File

@ -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)
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 <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 @@ 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<LimitBetterDirection::Higher> {
struct CheckLimit<LimitClass::Maximum> {
template <typename T>
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<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 @@ 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<LimitBetterDirection::Better>::Validate( \
DAWN_TRY_CONTEXT(CheckLimit<LimitClass::Class>::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<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) { \

View File

@ -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.