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:
parent
b7411603ad
commit
e17a13c60c
|
@ -15,6 +15,7 @@
|
||||||
#include "dawn/native/Limits.h"
|
#include "dawn/native/Limits.h"
|
||||||
|
|
||||||
#include "dawn/common/Assert.h"
|
#include "dawn/common/Assert.h"
|
||||||
|
#include "dawn/common/Math.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
@ -22,39 +23,39 @@
|
||||||
// TODO(crbug.com/dawn/685):
|
// TODO(crbug.com/dawn/685):
|
||||||
// For now, only expose these tiers until metrics can determine better ones.
|
// For now, only expose these tiers until metrics can determine better ones.
|
||||||
#define LIMITS_WORKGROUP_STORAGE_SIZE(X) \
|
#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) \
|
#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):
|
// TODO(crbug.com/dawn/685):
|
||||||
// These limits don't have tiers yet. Define two tiers with the same values since the macros
|
// 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.
|
// in this file expect more than one tier.
|
||||||
#define LIMITS_OTHER(X) \
|
#define LIMITS_OTHER(X) \
|
||||||
X(Higher, maxTextureDimension1D, 8192, 8192) \
|
X(Maximum, maxTextureDimension1D, 8192, 8192) \
|
||||||
X(Higher, maxTextureDimension2D, 8192, 8192) \
|
X(Maximum, maxTextureDimension2D, 8192, 8192) \
|
||||||
X(Higher, maxTextureDimension3D, 2048, 2048) \
|
X(Maximum, maxTextureDimension3D, 2048, 2048) \
|
||||||
X(Higher, maxTextureArrayLayers, 256, 256) \
|
X(Maximum, maxTextureArrayLayers, 256, 256) \
|
||||||
X(Higher, maxBindGroups, 4, 4) \
|
X(Maximum, maxBindGroups, 4, 4) \
|
||||||
X(Higher, maxDynamicUniformBuffersPerPipelineLayout, 8, 8) \
|
X(Maximum, maxDynamicUniformBuffersPerPipelineLayout, 8, 8) \
|
||||||
X(Higher, maxDynamicStorageBuffersPerPipelineLayout, 4, 4) \
|
X(Maximum, maxDynamicStorageBuffersPerPipelineLayout, 4, 4) \
|
||||||
X(Higher, maxSampledTexturesPerShaderStage, 16, 16) \
|
X(Maximum, maxSampledTexturesPerShaderStage, 16, 16) \
|
||||||
X(Higher, maxSamplersPerShaderStage, 16, 16) \
|
X(Maximum, maxSamplersPerShaderStage, 16, 16) \
|
||||||
X(Higher, maxStorageBuffersPerShaderStage, 8, 8) \
|
X(Maximum, maxStorageBuffersPerShaderStage, 8, 8) \
|
||||||
X(Higher, maxStorageTexturesPerShaderStage, 4, 4) \
|
X(Maximum, maxStorageTexturesPerShaderStage, 4, 4) \
|
||||||
X(Higher, maxUniformBuffersPerShaderStage, 12, 12) \
|
X(Maximum, maxUniformBuffersPerShaderStage, 12, 12) \
|
||||||
X(Higher, maxUniformBufferBindingSize, 65536, 65536) \
|
X(Maximum, maxUniformBufferBindingSize, 65536, 65536) \
|
||||||
X( Lower, minUniformBufferOffsetAlignment, 256, 256) \
|
X(Alignment, minUniformBufferOffsetAlignment, 256, 256) \
|
||||||
X( Lower, minStorageBufferOffsetAlignment, 256, 256) \
|
X(Alignment, minStorageBufferOffsetAlignment, 256, 256) \
|
||||||
X(Higher, maxVertexBuffers, 8, 8) \
|
X(Maximum, maxVertexBuffers, 8, 8) \
|
||||||
X(Higher, maxVertexAttributes, 16, 16) \
|
X(Maximum, maxVertexAttributes, 16, 16) \
|
||||||
X(Higher, maxVertexBufferArrayStride, 2048, 2048) \
|
X(Maximum, maxVertexBufferArrayStride, 2048, 2048) \
|
||||||
X(Higher, maxInterStageShaderComponents, 60, 60) \
|
X(Maximum, maxInterStageShaderComponents, 60, 60) \
|
||||||
X(Higher, maxComputeInvocationsPerWorkgroup, 256, 256) \
|
X(Maximum, maxComputeInvocationsPerWorkgroup, 256, 256) \
|
||||||
X(Higher, maxComputeWorkgroupSizeX, 256, 256) \
|
X(Maximum, maxComputeWorkgroupSizeX, 256, 256) \
|
||||||
X(Higher, maxComputeWorkgroupSizeY, 256, 256) \
|
X(Maximum, maxComputeWorkgroupSizeY, 256, 256) \
|
||||||
X(Higher, maxComputeWorkgroupSizeZ, 64, 64) \
|
X(Maximum, maxComputeWorkgroupSizeZ, 64, 64) \
|
||||||
X(Higher, maxComputeWorkgroupsPerDimension, 65535, 65535)
|
X(Maximum, maxComputeWorkgroupsPerDimension, 65535, 65535)
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#define LIMITS_EACH_GROUP(X) \
|
#define LIMITS_EACH_GROUP(X) \
|
||||||
|
@ -81,16 +82,16 @@ namespace dawn::native {
|
||||||
return I;
|
return I;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class LimitBetterDirection {
|
enum class LimitClass {
|
||||||
Lower,
|
Alignment,
|
||||||
Higher,
|
Maximum,
|
||||||
};
|
};
|
||||||
|
|
||||||
template <LimitBetterDirection Better>
|
template <LimitClass C>
|
||||||
struct CheckLimit;
|
struct CheckLimit;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct CheckLimit<LimitBetterDirection::Lower> {
|
struct CheckLimit<LimitClass::Alignment> {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static bool IsBetter(T lhs, T rhs) {
|
static bool IsBetter(T lhs, T rhs) {
|
||||||
return lhs < rhs;
|
return lhs < rhs;
|
||||||
|
@ -101,12 +102,14 @@ namespace dawn::native {
|
||||||
DAWN_INVALID_IF(IsBetter(required, supported),
|
DAWN_INVALID_IF(IsBetter(required, supported),
|
||||||
"Required limit (%u) is lower than the supported limit (%u).",
|
"Required limit (%u) is lower than the supported limit (%u).",
|
||||||
required, supported);
|
required, supported);
|
||||||
|
DAWN_INVALID_IF(!IsPowerOfTwo(required),
|
||||||
|
"Required limit (%u) is not a power of two.", required);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct CheckLimit<LimitBetterDirection::Higher> {
|
struct CheckLimit<LimitClass::Maximum> {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static bool IsBetter(T lhs, T rhs) {
|
static bool IsBetter(T lhs, T rhs) {
|
||||||
return lhs > rhs;
|
return lhs > rhs;
|
||||||
|
@ -148,9 +151,9 @@ namespace dawn::native {
|
||||||
|
|
||||||
Limits ReifyDefaultLimits(const Limits& limits) {
|
Limits ReifyDefaultLimits(const Limits& limits) {
|
||||||
Limits out;
|
Limits out;
|
||||||
#define X(Better, limitName, base, ...) \
|
#define X(Class, limitName, base, ...) \
|
||||||
if (IsLimitUndefined(limits.limitName) || \
|
if (IsLimitUndefined(limits.limitName) || \
|
||||||
CheckLimit<LimitBetterDirection::Better>::IsBetter( \
|
CheckLimit<LimitClass::Class>::IsBetter( \
|
||||||
static_cast<decltype(limits.limitName)>(base), limits.limitName)) { \
|
static_cast<decltype(limits.limitName)>(base), limits.limitName)) { \
|
||||||
/* If the limit is undefined or the default is better, use the default */ \
|
/* If the limit is undefined or the default is better, use the default */ \
|
||||||
out.limitName = base; \
|
out.limitName = base; \
|
||||||
|
@ -163,9 +166,9 @@ namespace dawn::native {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) {
|
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) {
|
||||||
#define X(Better, limitName, ...) \
|
#define X(Class, limitName, ...) \
|
||||||
if (!IsLimitUndefined(requiredLimits.limitName)) { \
|
if (!IsLimitUndefined(requiredLimits.limitName)) { \
|
||||||
DAWN_TRY_CONTEXT(CheckLimit<LimitBetterDirection::Better>::Validate( \
|
DAWN_TRY_CONTEXT(CheckLimit<LimitClass::Class>::Validate( \
|
||||||
supportedLimits.limitName, requiredLimits.limitName), \
|
supportedLimits.limitName, requiredLimits.limitName), \
|
||||||
"validating " #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__}; \
|
constexpr std::array<decltype(Limits::limitName), kTierCount> tiers{__VA_ARGS__}; \
|
||||||
decltype(Limits::limitName) tierValue = tiers[i - 1]; \
|
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. */ \
|
/* The tier is better. Go to the next tier. */ \
|
||||||
continue; \
|
continue; \
|
||||||
} else if (tierValue != limits.limitName) { \
|
} else if (tierValue != limits.limitName) { \
|
||||||
|
|
|
@ -78,7 +78,7 @@ TEST(Limits, ValidateLimits) {
|
||||||
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
|
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 = {};
|
dawn::native::Limits required = {};
|
||||||
required.maxTextureDimension3D = defaults.maxTextureDimension3D + 1;
|
required.maxTextureDimension3D = defaults.maxTextureDimension3D + 1;
|
||||||
|
@ -87,14 +87,14 @@ TEST(Limits, ValidateLimits) {
|
||||||
err.AcquireError();
|
err.AcquireError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that worse than max is valid.
|
// Test that worse than supported is valid for "maximum" limits.
|
||||||
{
|
{
|
||||||
dawn::native::Limits required = {};
|
dawn::native::Limits required = {};
|
||||||
required.maxComputeWorkgroupSizeX = defaults.maxComputeWorkgroupSizeX - 1;
|
required.maxComputeWorkgroupSizeX = defaults.maxComputeWorkgroupSizeX - 1;
|
||||||
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
|
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 = {};
|
dawn::native::Limits required = {};
|
||||||
required.minUniformBufferOffsetAlignment = defaults.minUniformBufferOffsetAlignment / 2;
|
required.minUniformBufferOffsetAlignment = defaults.minUniformBufferOffsetAlignment / 2;
|
||||||
|
@ -103,12 +103,21 @@ TEST(Limits, ValidateLimits) {
|
||||||
err.AcquireError();
|
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 = {};
|
dawn::native::Limits required = {};
|
||||||
required.minStorageBufferOffsetAlignment = defaults.minStorageBufferOffsetAlignment * 2;
|
required.minStorageBufferOffsetAlignment = defaults.minStorageBufferOffsetAlignment * 2;
|
||||||
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
|
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.
|
// Test that |ApplyLimitTiers| degrades limits to the next best tier.
|
||||||
|
|
Loading…
Reference in New Issue