Add a mechanism for finding the best tier for a set of limits
Multiple groups of limits may be defined via macros. ApplyLimitTiers degrades the incoming limits such that each limit value within a group is clamped to the best possible tier. A device may be in Tier 2 for one limit group, but Tier 1 for another limit group. Also adds equality operators to dawn_native generated structs for comparison of expected limits in the test. Bug: dawn:685 Change-Id: Ibdf947f2ccd44f70d66f48bed472ff5681230633 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64720 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Brandon Jones <bajones@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
f04d9d6618
commit
8411a12ab1
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include "dawn_native/wgpu_structs_autogen.h"
|
#include "dawn_native/wgpu_structs_autogen.h"
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
// error: 'offsetof' within non-standard-layout type 'wgpu::XXX' is conditionally-supported
|
// error: 'offsetof' within non-standard-layout type 'wgpu::XXX' is conditionally-supported
|
||||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
@ -47,5 +49,21 @@ namespace dawn_native {
|
||||||
"offsetof mismatch for {{CppType}}::{{memberName}}");
|
"offsetof mismatch for {{CppType}}::{{memberName}}");
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
bool {{CppType}}::operator==(const {{as_cppType(type.name)}}& rhs) const {
|
||||||
|
return {% if type.extensible or type.chained -%}
|
||||||
|
(nextInChain == rhs.nextInChain) &&
|
||||||
|
{%- endif %} std::tie(
|
||||||
|
{% for member in type.members %}
|
||||||
|
{{member.name.camelCase()-}}
|
||||||
|
{{ "," if not loop.last else "" }}
|
||||||
|
{% endfor %}
|
||||||
|
) == std::tie(
|
||||||
|
{% for member in type.members %}
|
||||||
|
rhs.{{member.name.camelCase()-}}
|
||||||
|
{{ "," if not loop.last else "" }}
|
||||||
|
{% endfor %}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,10 @@ namespace dawn_native {
|
||||||
{{member_declaration}};
|
{{member_declaration}};
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
// Equality operators, mostly for testing. Note that this tests
|
||||||
|
// strict pointer-pointer equality if the struct contains member pointers.
|
||||||
|
bool operator==(const {{as_cppType(type.name)}}& rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -83,10 +83,7 @@ namespace dawn_native {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (mUseTieredLimits) {
|
if (mUseTieredLimits) {
|
||||||
// TODO(crbug.com/dawn/685): Apply limit tiers.
|
limits->limits = ApplyLimitTiers(mLimits.v1);
|
||||||
// For now, set all limits to the defaults until tiers are
|
|
||||||
// defined.
|
|
||||||
GetDefaultLimits(&limits->limits);
|
|
||||||
} else {
|
} else {
|
||||||
limits->limits = mLimits.v1;
|
limits->limits = mLimits.v1;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +128,7 @@ namespace dawn_native {
|
||||||
|
|
||||||
if (descriptor != nullptr && descriptor->requiredLimits != nullptr) {
|
if (descriptor != nullptr && descriptor->requiredLimits != nullptr) {
|
||||||
DAWN_TRY(ValidateLimits(
|
DAWN_TRY(ValidateLimits(
|
||||||
mLimits.v1,
|
mUseTieredLimits ? ApplyLimitTiers(mLimits.v1) : mLimits.v1,
|
||||||
reinterpret_cast<const RequiredLimits*>(descriptor->requiredLimits)->limits));
|
reinterpret_cast<const RequiredLimits*>(descriptor->requiredLimits)->limits));
|
||||||
|
|
||||||
if (descriptor->requiredLimits->nextInChain != nullptr) {
|
if (descriptor->requiredLimits->nextInChain != nullptr) {
|
||||||
|
|
|
@ -16,36 +16,71 @@
|
||||||
|
|
||||||
#include "common/Assert.h"
|
#include "common/Assert.h"
|
||||||
|
|
||||||
#define LIMITS(X) \
|
#include <array>
|
||||||
X(Higher, maxTextureDimension1D, 8192) \
|
|
||||||
X(Higher, maxTextureDimension2D, 8192) \
|
// clang-format off
|
||||||
X(Higher, maxTextureDimension3D, 2048) \
|
// TODO(crbug.com/dawn/685):
|
||||||
X(Higher, maxTextureArrayLayers, 256) \
|
// For now, only expose these tiers until metrics can determine better ones.
|
||||||
X(Higher, maxBindGroups, 4) \
|
#define LIMITS_WORKGROUP_STORAGE_SIZE(X) \
|
||||||
X(Higher, maxDynamicUniformBuffersPerPipelineLayout, 8) \
|
X(Higher, maxComputeWorkgroupStorageSize, 16352, 32768, 49152, 65536)
|
||||||
X(Higher, maxDynamicStorageBuffersPerPipelineLayout, 4) \
|
|
||||||
X(Higher, maxSampledTexturesPerShaderStage, 16) \
|
#define LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \
|
||||||
X(Higher, maxSamplersPerShaderStage, 16) \
|
X(Higher, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483647, 4294967295)
|
||||||
X(Higher, maxStorageBuffersPerShaderStage, 8) \
|
|
||||||
X(Higher, maxStorageTexturesPerShaderStage, 4) \
|
// TODO(crbug.com/dawn/685):
|
||||||
X(Higher, maxUniformBuffersPerShaderStage, 12) \
|
// These limits don't have tiers yet. Define two tiers with the same values since the macros
|
||||||
X(Higher, maxUniformBufferBindingSize, 16384) \
|
// in this file expect more than one tier.
|
||||||
X(Higher, maxStorageBufferBindingSize, 134217728) \
|
#define LIMITS_OTHER(X) \
|
||||||
X(Lower, minUniformBufferOffsetAlignment, 256) \
|
X(Higher, maxTextureDimension1D, 8192, 8192) \
|
||||||
X(Lower, minStorageBufferOffsetAlignment, 256) \
|
X(Higher, maxTextureDimension2D, 8192, 8192) \
|
||||||
X(Higher, maxVertexBuffers, 8) \
|
X(Higher, maxTextureDimension3D, 2048, 2048) \
|
||||||
X(Higher, maxVertexAttributes, 16) \
|
X(Higher, maxTextureArrayLayers, 256, 256) \
|
||||||
X(Higher, maxVertexBufferArrayStride, 2048) \
|
X(Higher, maxBindGroups, 4, 4) \
|
||||||
X(Higher, maxInterStageShaderComponents, 60) \
|
X(Higher, maxDynamicUniformBuffersPerPipelineLayout, 8, 8) \
|
||||||
X(Higher, maxComputeWorkgroupStorageSize, 16352) \
|
X(Higher, maxDynamicStorageBuffersPerPipelineLayout, 4, 4) \
|
||||||
X(Higher, maxComputeInvocationsPerWorkgroup, 256) \
|
X(Higher, maxSampledTexturesPerShaderStage, 16, 16) \
|
||||||
X(Higher, maxComputeWorkgroupSizeX, 256) \
|
X(Higher, maxSamplersPerShaderStage, 16, 16) \
|
||||||
X(Higher, maxComputeWorkgroupSizeY, 256) \
|
X(Higher, maxStorageBuffersPerShaderStage, 8, 8) \
|
||||||
X(Higher, maxComputeWorkgroupSizeZ, 64) \
|
X(Higher, maxStorageTexturesPerShaderStage, 4, 4) \
|
||||||
X(Higher, maxComputeWorkgroupsPerDimension, 65535)
|
X(Higher, maxUniformBuffersPerShaderStage, 12, 12) \
|
||||||
|
X(Higher, maxUniformBufferBindingSize, 16384, 16384) \
|
||||||
|
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)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#define LIMITS_EACH_GROUP(X) \
|
||||||
|
X(LIMITS_WORKGROUP_STORAGE_SIZE) \
|
||||||
|
X(LIMITS_STORAGE_BUFFER_BINDING_SIZE) \
|
||||||
|
X(LIMITS_OTHER)
|
||||||
|
|
||||||
|
#define LIMITS(X) \
|
||||||
|
LIMITS_WORKGROUP_STORAGE_SIZE(X) \
|
||||||
|
LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \
|
||||||
|
LIMITS_OTHER(X)
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
namespace {
|
namespace {
|
||||||
|
template <uint32_t A, uint32_t B>
|
||||||
|
constexpr void StaticAssertSame() {
|
||||||
|
static_assert(A == B, "Mismatching tier count in limit group.");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint32_t I, uint32_t... Is>
|
||||||
|
constexpr uint32_t ReduceSameValue(std::integer_sequence<uint32_t, I, Is...>) {
|
||||||
|
int unused[] = {0, (StaticAssertSame<I, Is>(), 0)...};
|
||||||
|
DAWN_UNUSED(unused);
|
||||||
|
return I;
|
||||||
|
}
|
||||||
|
|
||||||
enum class LimitBetterDirection {
|
enum class LimitBetterDirection {
|
||||||
Lower,
|
Lower,
|
||||||
Higher,
|
Higher,
|
||||||
|
@ -106,21 +141,21 @@ namespace dawn_native {
|
||||||
|
|
||||||
void GetDefaultLimits(Limits* limits) {
|
void GetDefaultLimits(Limits* limits) {
|
||||||
ASSERT(limits != nullptr);
|
ASSERT(limits != nullptr);
|
||||||
#define X(Better, limitName, defaultValue) limits->limitName = defaultValue;
|
#define X(Better, limitName, base, ...) limits->limitName = base;
|
||||||
LIMITS(X)
|
LIMITS(X)
|
||||||
#undef X
|
#undef X
|
||||||
}
|
}
|
||||||
|
|
||||||
Limits ReifyDefaultLimits(const Limits& limits) {
|
Limits ReifyDefaultLimits(const Limits& limits) {
|
||||||
Limits out;
|
Limits out;
|
||||||
#define X(Better, limitName, defaultValue) \
|
#define X(Better, limitName, base, ...) \
|
||||||
if (IsLimitUndefined(limits.limitName) || \
|
if (IsLimitUndefined(limits.limitName) || \
|
||||||
CheckLimit<LimitBetterDirection::Better>::IsBetter( \
|
CheckLimit<LimitBetterDirection::Better>::IsBetter( \
|
||||||
static_cast<decltype(limits.limitName)>(defaultValue), 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 = defaultValue; \
|
out.limitName = base; \
|
||||||
} else { \
|
} else { \
|
||||||
out.limitName = limits.limitName; \
|
out.limitName = limits.limitName; \
|
||||||
}
|
}
|
||||||
LIMITS(X)
|
LIMITS(X)
|
||||||
#undef X
|
#undef X
|
||||||
|
@ -128,7 +163,7 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) {
|
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) {
|
||||||
#define X(Better, limitName, defaultValue) \
|
#define X(Better, limitName, ...) \
|
||||||
if (!IsLimitUndefined(requiredLimits.limitName)) { \
|
if (!IsLimitUndefined(requiredLimits.limitName)) { \
|
||||||
DAWN_TRY(CheckLimit<LimitBetterDirection::Better>::Validate(supportedLimits.limitName, \
|
DAWN_TRY(CheckLimit<LimitBetterDirection::Better>::Validate(supportedLimits.limitName, \
|
||||||
requiredLimits.limitName)); \
|
requiredLimits.limitName)); \
|
||||||
|
@ -138,4 +173,40 @@ namespace dawn_native {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Limits ApplyLimitTiers(Limits limits) {
|
||||||
|
#define X_TIER_COUNT(Better, limitName, ...) , std::integer_sequence<uint64_t, __VA_ARGS__>{}.size()
|
||||||
|
#define GET_TIER_COUNT(LIMIT_GROUP) \
|
||||||
|
ReduceSameValue(std::integer_sequence<uint32_t LIMIT_GROUP(X_TIER_COUNT)>{})
|
||||||
|
|
||||||
|
#define X_EACH_GROUP(LIMIT_GROUP) \
|
||||||
|
{ \
|
||||||
|
constexpr uint32_t kTierCount = GET_TIER_COUNT(LIMIT_GROUP); \
|
||||||
|
for (uint32_t i = kTierCount; i != 0; --i) { \
|
||||||
|
LIMIT_GROUP(X_CHECK_BETTER_AND_CLAMP) \
|
||||||
|
/* Limits fit in tier and have been clamped. Break. */ \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define X_CHECK_BETTER_AND_CLAMP(Better, 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)) { \
|
||||||
|
/* The tier is better. Go to the next tier. */ \
|
||||||
|
continue; \
|
||||||
|
} else if (tierValue != limits.limitName) { \
|
||||||
|
/* Better than the tier. Degrade |limits| to the tier. */ \
|
||||||
|
limits.limitName = tiers[i - 1]; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
LIMITS_EACH_GROUP(X_EACH_GROUP)
|
||||||
|
#undef X_CHECK_BETTER
|
||||||
|
#undef X_EACH_GROUP
|
||||||
|
#undef GET_TIER_COUNT
|
||||||
|
#undef X_TIER_COUNT
|
||||||
|
return limits;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -35,6 +35,9 @@ namespace dawn_native {
|
||||||
// Validate that |requiredLimits| are no better than |supportedLimits|.
|
// Validate that |requiredLimits| are no better than |supportedLimits|.
|
||||||
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits);
|
MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits);
|
||||||
|
|
||||||
|
// Returns a copy of |limits| where limit tiers are applied.
|
||||||
|
Limits ApplyLimitTiers(Limits limits);
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
||||||
#endif // DAWNNATIVE_LIMITS_H_
|
#endif // DAWNNATIVE_LIMITS_H_
|
||||||
|
|
|
@ -110,3 +110,91 @@ TEST(Limits, ValidateLimits) {
|
||||||
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
|
EXPECT_TRUE(ValidateLimits(defaults, required).IsSuccess());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that |ApplyLimitTiers| degrades limits to the next best tier.
|
||||||
|
TEST(Limits, ApplyLimitTiers) {
|
||||||
|
auto SetLimitsStorageBufferBindingSizeTier2 = [](dawn_native::Limits* limits) {
|
||||||
|
limits->maxStorageBufferBindingSize = 1073741824;
|
||||||
|
};
|
||||||
|
dawn_native::Limits limitsStorageBufferBindingSizeTier2;
|
||||||
|
dawn_native::GetDefaultLimits(&limitsStorageBufferBindingSizeTier2);
|
||||||
|
SetLimitsStorageBufferBindingSizeTier2(&limitsStorageBufferBindingSizeTier2);
|
||||||
|
|
||||||
|
auto SetLimitsStorageBufferBindingSizeTier3 = [](dawn_native::Limits* limits) {
|
||||||
|
limits->maxStorageBufferBindingSize = 2147483647;
|
||||||
|
};
|
||||||
|
dawn_native::Limits limitsStorageBufferBindingSizeTier3;
|
||||||
|
dawn_native::GetDefaultLimits(&limitsStorageBufferBindingSizeTier3);
|
||||||
|
SetLimitsStorageBufferBindingSizeTier3(&limitsStorageBufferBindingSizeTier3);
|
||||||
|
|
||||||
|
auto SetLimitsComputeWorkgroupStorageSizeTier1 = [](dawn_native::Limits* limits) {
|
||||||
|
limits->maxComputeWorkgroupStorageSize = 16352;
|
||||||
|
};
|
||||||
|
dawn_native::Limits limitsComputeWorkgroupStorageSizeTier1;
|
||||||
|
dawn_native::GetDefaultLimits(&limitsComputeWorkgroupStorageSizeTier1);
|
||||||
|
SetLimitsComputeWorkgroupStorageSizeTier1(&limitsComputeWorkgroupStorageSizeTier1);
|
||||||
|
|
||||||
|
auto SetLimitsComputeWorkgroupStorageSizeTier3 = [](dawn_native::Limits* limits) {
|
||||||
|
limits->maxComputeWorkgroupStorageSize = 65536;
|
||||||
|
};
|
||||||
|
dawn_native::Limits limitsComputeWorkgroupStorageSizeTier3;
|
||||||
|
dawn_native::GetDefaultLimits(&limitsComputeWorkgroupStorageSizeTier3);
|
||||||
|
SetLimitsComputeWorkgroupStorageSizeTier3(&limitsComputeWorkgroupStorageSizeTier3);
|
||||||
|
|
||||||
|
// Test that applying tiers to limits that are exactly
|
||||||
|
// equal to a tier returns the same values.
|
||||||
|
{
|
||||||
|
dawn_native::Limits limits = limitsStorageBufferBindingSizeTier2;
|
||||||
|
EXPECT_EQ(ApplyLimitTiers(limits), limits);
|
||||||
|
|
||||||
|
limits = limitsStorageBufferBindingSizeTier3;
|
||||||
|
EXPECT_EQ(ApplyLimitTiers(limits), limits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test all limits slightly worse than tier 3.
|
||||||
|
{
|
||||||
|
dawn_native::Limits limits = limitsStorageBufferBindingSizeTier3;
|
||||||
|
limits.maxStorageBufferBindingSize -= 1;
|
||||||
|
EXPECT_EQ(ApplyLimitTiers(limits), limitsStorageBufferBindingSizeTier2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that limits may match one tier exactly and be degraded in another tier.
|
||||||
|
// Degrading to one tier does not affect the other tier.
|
||||||
|
{
|
||||||
|
dawn_native::Limits limits = limitsComputeWorkgroupStorageSizeTier3;
|
||||||
|
// Set tier 3 and change one limit to be insufficent.
|
||||||
|
SetLimitsStorageBufferBindingSizeTier3(&limits);
|
||||||
|
limits.maxStorageBufferBindingSize -= 1;
|
||||||
|
|
||||||
|
dawn_native::Limits tiered = ApplyLimitTiers(limits);
|
||||||
|
|
||||||
|
// Check that |tiered| has the limits of memorySize tier 2
|
||||||
|
dawn_native::Limits tieredWithMemorySizeTier2 = tiered;
|
||||||
|
SetLimitsStorageBufferBindingSizeTier2(&tieredWithMemorySizeTier2);
|
||||||
|
EXPECT_EQ(tiered, tieredWithMemorySizeTier2);
|
||||||
|
|
||||||
|
// Check that |tiered| has the limits of bindingSpace tier 3
|
||||||
|
dawn_native::Limits tieredWithBindingSpaceTier3 = tiered;
|
||||||
|
SetLimitsComputeWorkgroupStorageSizeTier3(&tieredWithBindingSpaceTier3);
|
||||||
|
EXPECT_EQ(tiered, tieredWithBindingSpaceTier3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that limits may be simultaneously degraded in two tiers independently.
|
||||||
|
{
|
||||||
|
dawn_native::Limits limits;
|
||||||
|
dawn_native::GetDefaultLimits(&limits);
|
||||||
|
SetLimitsComputeWorkgroupStorageSizeTier3(&limits);
|
||||||
|
SetLimitsStorageBufferBindingSizeTier3(&limits);
|
||||||
|
limits.maxComputeWorkgroupStorageSize =
|
||||||
|
limitsComputeWorkgroupStorageSizeTier1.maxComputeWorkgroupStorageSize + 1;
|
||||||
|
limits.maxStorageBufferBindingSize =
|
||||||
|
limitsStorageBufferBindingSizeTier2.maxStorageBufferBindingSize + 1;
|
||||||
|
|
||||||
|
dawn_native::Limits tiered = ApplyLimitTiers(limits);
|
||||||
|
|
||||||
|
dawn_native::Limits expected = tiered;
|
||||||
|
SetLimitsComputeWorkgroupStorageSizeTier1(&expected);
|
||||||
|
SetLimitsStorageBufferBindingSizeTier2(&expected);
|
||||||
|
EXPECT_EQ(tiered, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue