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/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) { \
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue