diff --git a/src/dawn/native/Limits.cpp b/src/dawn/native/Limits.cpp index 63295aea28..6f73a687ee 100644 --- a/src/dawn/native/Limits.cpp +++ b/src/dawn/native/Limits.cpp @@ -14,9 +14,11 @@ #include "dawn/native/Limits.h" +#include #include #include "dawn/common/Assert.h" +#include "dawn/common/Constants.h" #include "dawn/common/Math.h" // clang-format off @@ -35,12 +37,13 @@ X(Maximum, maxComputeWorkgroupSizeZ, 64, 64) \ X(Maximum, maxComputeWorkgroupsPerDimension, 65535, 65535) +// Tiers are 128MB, 1GB, 2GB-4, 4GB-4. #define LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \ - X(Maximum, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483647, 4294967295) + X(Maximum, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483644, 4294967292) -// Tiers are 256Mb, 1Gb, 2Gb. +// Tiers are 256MB, 1GB, 2GB, 4GB. #define LIMITS_MAX_BUFFER_SIZE(X) \ - X(Maximum, maxBufferSize, 0x10000000, 0x40000000, 0x80000000) + X(Maximum, maxBufferSize, 0x10000000, 0x40000000, 0x80000000, 0x100000000) // Tiers for limits related to resource bindings. // TODO(crbug.com/dawn/685): Define these better. For now, use two tiers where one @@ -230,7 +233,18 @@ Limits ApplyLimitTiers(Limits limits) { } LIMITS_EACH_GROUP(X_EACH_GROUP) -#undef X_CHECK_BETTER + + // After tiering all limit values, enforce additional restriction by calling NormalizeLimits. + // Since maxStorageBufferBindingSize and maxBufferSize tiers are not exactly aligned, it is + // possible that tiered maxStorageBufferBindingSize is larger than tiered maxBufferSize. For + // example, on a hypothetical device with both maxStorageBufferBindingSize and maxBufferSize + // being 4GB-1, the tiered maxStorageBufferBindingSize would be 4GB-4 while the tiered + // maxBufferSize being 2GB. NormalizeLimits will clamp the maxStorageBufferBindingSize to + // maxBufferSize in such cases, although the result may or may not be one of predefined + // maxStorageBufferBindingSize tiers. + NormalizeLimits(&limits); + +#undef X_CHECK_BETTER_AND_CLAMP #undef X_EACH_GROUP #undef GET_TIER_COUNT #undef X_TIER_COUNT @@ -255,4 +269,35 @@ void stream::Stream::Write(Sink* s, t.VisitAll([&](const auto&... members) { StreamIn(s, members...); }); } +void NormalizeLimits(Limits* limits) { + // Enforce internal Dawn constants for some limits to ensure they don't go over fixed-size + // arrays in Dawn's internal code. + limits->maxVertexBufferArrayStride = + std::min(limits->maxVertexBufferArrayStride, kMaxVertexBufferArrayStride); + limits->maxColorAttachments = + std::min(limits->maxColorAttachments, uint32_t(kMaxColorAttachments)); + limits->maxBindGroups = std::min(limits->maxBindGroups, kMaxBindGroups); + limits->maxVertexAttributes = + std::min(limits->maxVertexAttributes, uint32_t(kMaxVertexAttributes)); + limits->maxVertexBuffers = std::min(limits->maxVertexBuffers, uint32_t(kMaxVertexBuffers)); + limits->maxInterStageShaderComponents = + std::min(limits->maxInterStageShaderComponents, kMaxInterStageShaderComponents); + limits->maxSampledTexturesPerShaderStage = + std::min(limits->maxSampledTexturesPerShaderStage, kMaxSampledTexturesPerShaderStage); + limits->maxSamplersPerShaderStage = + std::min(limits->maxSamplersPerShaderStage, kMaxSamplersPerShaderStage); + limits->maxStorageBuffersPerShaderStage = + std::min(limits->maxStorageBuffersPerShaderStage, kMaxStorageBuffersPerShaderStage); + limits->maxStorageTexturesPerShaderStage = + std::min(limits->maxStorageTexturesPerShaderStage, kMaxStorageTexturesPerShaderStage); + limits->maxUniformBuffersPerShaderStage = + std::min(limits->maxUniformBuffersPerShaderStage, kMaxUniformBuffersPerShaderStage); + + // Additional enforcement for dependent limits. + limits->maxStorageBufferBindingSize = + std::min(limits->maxStorageBufferBindingSize, limits->maxBufferSize); + limits->maxUniformBufferBindingSize = + std::min(limits->maxUniformBufferBindingSize, limits->maxBufferSize); +} + } // namespace dawn::native diff --git a/src/dawn/native/Limits.h b/src/dawn/native/Limits.h index 8091a31cd2..266f2d2448 100644 --- a/src/dawn/native/Limits.h +++ b/src/dawn/native/Limits.h @@ -53,6 +53,13 @@ struct LimitsForCompilationRequest { DAWN_VISITABLE_MEMBERS(LIMITS_FOR_COMPILATION_REQUEST_MEMBERS) }; +// Enforce restriction for limit values, including: +// 1. Enforce internal Dawn constants for some limits to ensure they don't go over fixed-size +// arrays in Dawn's internal code; +// 2. Additional enforcement for dependent limits, e.g. maxStorageBufferBindingSize and +// maxUniformBufferBindingSize must not be larger than maxBufferSize. +void NormalizeLimits(Limits* limits); + } // namespace dawn::native #endif // SRC_DAWN_NATIVE_LIMITS_H_ diff --git a/src/dawn/native/PhysicalDevice.cpp b/src/dawn/native/PhysicalDevice.cpp index f6e6ebaca2..76104043ef 100644 --- a/src/dawn/native/PhysicalDevice.cpp +++ b/src/dawn/native/PhysicalDevice.cpp @@ -51,35 +51,7 @@ MaybeError PhysicalDeviceBase::Initialize() { "backend=%s type=%s)", mName, mDriverDescription, mVendorId, mDeviceId, mBackend, mAdapterType); - // Enforce internal Dawn constants for some limits to ensure they don't go over fixed-size - // arrays in Dawn's internal code. - mLimits.v1.maxVertexBufferArrayStride = - std::min(mLimits.v1.maxVertexBufferArrayStride, kMaxVertexBufferArrayStride); - mLimits.v1.maxColorAttachments = - std::min(mLimits.v1.maxColorAttachments, uint32_t(kMaxColorAttachments)); - mLimits.v1.maxBindGroups = std::min(mLimits.v1.maxBindGroups, kMaxBindGroups); - mLimits.v1.maxVertexAttributes = - std::min(mLimits.v1.maxVertexAttributes, uint32_t(kMaxVertexAttributes)); - mLimits.v1.maxVertexBuffers = - std::min(mLimits.v1.maxVertexBuffers, uint32_t(kMaxVertexBuffers)); - mLimits.v1.maxInterStageShaderComponents = - std::min(mLimits.v1.maxInterStageShaderComponents, kMaxInterStageShaderComponents); - mLimits.v1.maxSampledTexturesPerShaderStage = - std::min(mLimits.v1.maxSampledTexturesPerShaderStage, kMaxSampledTexturesPerShaderStage); - mLimits.v1.maxSamplersPerShaderStage = - std::min(mLimits.v1.maxSamplersPerShaderStage, kMaxSamplersPerShaderStage); - mLimits.v1.maxStorageBuffersPerShaderStage = - std::min(mLimits.v1.maxStorageBuffersPerShaderStage, kMaxStorageBuffersPerShaderStage); - mLimits.v1.maxStorageTexturesPerShaderStage = - std::min(mLimits.v1.maxStorageTexturesPerShaderStage, kMaxStorageTexturesPerShaderStage); - mLimits.v1.maxUniformBuffersPerShaderStage = - std::min(mLimits.v1.maxUniformBuffersPerShaderStage, kMaxUniformBuffersPerShaderStage); - - // Additional enforcement for dependent limits. - mLimits.v1.maxStorageBufferBindingSize = - std::min(mLimits.v1.maxStorageBufferBindingSize, mLimits.v1.maxBufferSize); - mLimits.v1.maxUniformBufferBindingSize = - std::min(mLimits.v1.maxUniformBufferBindingSize, mLimits.v1.maxBufferSize); + NormalizeLimits(&mLimits.v1); return {}; } diff --git a/src/dawn/tests/unittests/LimitsTests.cpp b/src/dawn/tests/unittests/LimitsTests.cpp index b0b0044928..dbac9e7d1b 100644 --- a/src/dawn/tests/unittests/LimitsTests.cpp +++ b/src/dawn/tests/unittests/LimitsTests.cpp @@ -14,6 +14,7 @@ #include +#include "dawn/common/Constants.h" #include "dawn/native/Limits.h" // Test |GetDefaultLimits| returns the default. @@ -123,14 +124,22 @@ TEST(Limits, ValidateLimits) { // Test that |ApplyLimitTiers| degrades limits to the next best tier. TEST(Limits, ApplyLimitTiers) { auto SetLimitsStorageBufferBindingSizeTier2 = [](dawn::native::Limits* limits) { + // Tier 2 of maxStorageBufferBindingSize is 1GB limits->maxStorageBufferBindingSize = 1073741824; + // Also set the maxBufferSize to be large enough, as ApplyLimitTiers ensures tired + // maxStorageBufferBindingSize no larger than tiered maxBufferSize. + limits->maxBufferSize = 2147483648; }; dawn::native::Limits limitsStorageBufferBindingSizeTier2; dawn::native::GetDefaultLimits(&limitsStorageBufferBindingSizeTier2); SetLimitsStorageBufferBindingSizeTier2(&limitsStorageBufferBindingSizeTier2); auto SetLimitsStorageBufferBindingSizeTier3 = [](dawn::native::Limits* limits) { - limits->maxStorageBufferBindingSize = 2147483647; + // Tier 3 of maxStorageBufferBindingSize is 2GB-4 + limits->maxStorageBufferBindingSize = 2147483644; + // Also set the maxBufferSize to be large enough, as ApplyLimitTiers ensures tired + // maxStorageBufferBindingSize no larger than tiered maxBufferSize. + limits->maxBufferSize = 2147483648; }; dawn::native::Limits limitsStorageBufferBindingSizeTier3; dawn::native::GetDefaultLimits(&limitsStorageBufferBindingSizeTier3); @@ -207,3 +216,228 @@ TEST(Limits, ApplyLimitTiers) { EXPECT_EQ(tiered, expected); } } + +// Test that |ApplyLimitTiers| will hold the maxStorageBufferBindingSize no larger than +// maxBufferSize restriction. +TEST(Limits, TieredMaxStorageBufferBindingSizeNoLargerThanMaxBufferSize) { + // Start with the default for supported. + dawn::native::Limits defaults; + dawn::native::GetDefaultLimits(&defaults); + + // Test reported maxStorageBufferBindingSize around 128MB, 1GB, 2GB-4 and 4GB-4. + constexpr uint64_t storageSizeTier1 = 134217728ull; // 128MB + constexpr uint64_t storageSizeTier2 = 1073741824ull; // 1GB + constexpr uint64_t storageSizeTier3 = 2147483644ull; // 2GB-4 + constexpr uint64_t storageSizeTier4 = 4294967292ull; // 4GB-4 + constexpr uint64_t possibleReportedMaxStorageBufferBindingSizes[] = { + storageSizeTier1, storageSizeTier1 + 1, storageSizeTier2 - 1, storageSizeTier2, + storageSizeTier2 + 1, storageSizeTier3 - 1, storageSizeTier3, storageSizeTier3 + 1, + storageSizeTier4 - 1, storageSizeTier4, storageSizeTier4 + 1}; + // Test reported maxBufferSize around 256MB, 1GB, 2GB and 4GB, and a large 256GB. + constexpr uint64_t bufferSizeTier1 = 0x10000000ull; // 256MB + constexpr uint64_t bufferSizeTier2 = 0x40000000ull; // 1GB + constexpr uint64_t bufferSizeTier3 = 0x80000000ull; // 2GB + constexpr uint64_t bufferSizeTier4 = 0x100000000ull; // 4GB + constexpr uint64_t bufferSizeLarge = 0x4000000000ull; // 256GB + constexpr uint64_t possibleReportedMaxBufferSizes[] = { + bufferSizeTier1, bufferSizeTier1 + 1, bufferSizeTier2 - 1, bufferSizeTier2, + bufferSizeTier2 + 1, bufferSizeTier3 - 1, bufferSizeTier3, bufferSizeTier3 + 1, + bufferSizeTier4 - 1, bufferSizeTier4, bufferSizeTier4 + 1, bufferSizeLarge}; + + // Test that tiered maxStorageBufferBindingSize is no larger than tiered maxBufferSize. + for (uint64_t reportedMaxStorageBufferBindingSizes : + possibleReportedMaxStorageBufferBindingSizes) { + for (uint64_t reportedMaxBufferSizes : possibleReportedMaxBufferSizes) { + dawn::native::Limits limits = defaults; + limits.maxStorageBufferBindingSize = reportedMaxStorageBufferBindingSizes; + limits.maxBufferSize = reportedMaxBufferSizes; + + dawn::native::Limits tiered = ApplyLimitTiers(limits); + + EXPECT_LE(tiered.maxStorageBufferBindingSize, tiered.maxBufferSize); + } + } +} + +// Test that |ApplyLimitTiers| will hold the maxUniformBufferBindingSize no larger than +// maxBufferSize restriction. +TEST(Limits, TieredMaxUniformBufferBindingSizeNoLargerThanMaxBufferSize) { + // Start with the default for supported. + dawn::native::Limits defaults; + dawn::native::GetDefaultLimits(&defaults); + + // Test reported maxStorageBufferBindingSize around 64KB, and a large 1GB. + constexpr uint64_t uniformSizeTier1 = 65536ull; // 64KB + constexpr uint64_t uniformSizeLarge = 1073741824ull; // 1GB + constexpr uint64_t possibleReportedMaxUniformBufferBindingSizes[] = { + uniformSizeTier1, uniformSizeTier1 + 1, uniformSizeLarge}; + // Test reported maxBufferSize around 256MB, 1GB, 2GB and 4GB, and a large 256GB. + constexpr uint64_t bufferSizeTier1 = 0x10000000ull; // 256MB + constexpr uint64_t bufferSizeTier2 = 0x40000000ull; // 1GB + constexpr uint64_t bufferSizeTier3 = 0x80000000ull; // 2GB + constexpr uint64_t bufferSizeTier4 = 0x100000000ull; // 4GB + constexpr uint64_t bufferSizeLarge = 0x4000000000ull; // 256GB + constexpr uint64_t possibleReportedMaxBufferSizes[] = { + bufferSizeTier1, bufferSizeTier1 + 1, bufferSizeTier2 - 1, bufferSizeTier2, + bufferSizeTier2 + 1, bufferSizeTier3 - 1, bufferSizeTier3, bufferSizeTier3 + 1, + bufferSizeTier4 - 1, bufferSizeTier4, bufferSizeTier4 + 1, bufferSizeLarge}; + + // Test that tiered maxUniformBufferBindingSize is no larger than tiered maxBufferSize. + for (uint64_t reportedMaxUniformBufferBindingSizes : + possibleReportedMaxUniformBufferBindingSizes) { + for (uint64_t reportedMaxBufferSizes : possibleReportedMaxBufferSizes) { + dawn::native::Limits limits = defaults; + limits.maxUniformBufferBindingSize = reportedMaxUniformBufferBindingSizes; + limits.maxBufferSize = reportedMaxBufferSizes; + + dawn::native::Limits tiered = ApplyLimitTiers(limits); + + EXPECT_LE(tiered.maxUniformBufferBindingSize, tiered.maxBufferSize); + } + } +} + +// Test |NormalizeLimits| works to enforce restriction of limits. +TEST(Limits, NormalizeLimits) { + // Start with the default for supported. + dawn::native::Limits defaults; + dawn::native::GetDefaultLimits(&defaults); + + // Test specific limit values are clamped to internal Dawn constants. + { + dawn::native::Limits limits = defaults; + limits.maxVertexBufferArrayStride = kMaxVertexBufferArrayStride + 1; + limits.maxColorAttachments = uint32_t(kMaxColorAttachments) + 1; + limits.maxBindGroups = kMaxBindGroups + 1; + limits.maxVertexAttributes = uint32_t(kMaxVertexAttributes) + 1; + limits.maxVertexBuffers = uint32_t(kMaxVertexBuffers) + 1; + limits.maxInterStageShaderComponents = kMaxInterStageShaderComponents + 1; + limits.maxSampledTexturesPerShaderStage = kMaxSampledTexturesPerShaderStage + 1; + limits.maxSamplersPerShaderStage = kMaxSamplersPerShaderStage + 1; + limits.maxStorageBuffersPerShaderStage = kMaxStorageBuffersPerShaderStage + 1; + limits.maxStorageTexturesPerShaderStage = kMaxStorageTexturesPerShaderStage + 1; + limits.maxUniformBuffersPerShaderStage = kMaxUniformBuffersPerShaderStage + 1; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxVertexBufferArrayStride, kMaxVertexBufferArrayStride); + EXPECT_EQ(limits.maxColorAttachments, uint32_t(kMaxColorAttachments)); + EXPECT_EQ(limits.maxBindGroups, kMaxBindGroups); + EXPECT_EQ(limits.maxVertexAttributes, uint32_t(kMaxVertexAttributes)); + EXPECT_EQ(limits.maxVertexBuffers, uint32_t(kMaxVertexBuffers)); + EXPECT_EQ(limits.maxInterStageShaderComponents, kMaxInterStageShaderComponents); + EXPECT_EQ(limits.maxSampledTexturesPerShaderStage, kMaxSampledTexturesPerShaderStage); + EXPECT_EQ(limits.maxSamplersPerShaderStage, kMaxSamplersPerShaderStage); + EXPECT_EQ(limits.maxStorageBuffersPerShaderStage, kMaxStorageBuffersPerShaderStage); + EXPECT_EQ(limits.maxStorageTexturesPerShaderStage, kMaxStorageTexturesPerShaderStage); + EXPECT_EQ(limits.maxUniformBuffersPerShaderStage, kMaxUniformBuffersPerShaderStage); + } + + // Test maxStorageBufferBindingSize is clamped to maxBufferSize. + // maxStorageBufferBindingSize is no larger than maxBufferSize + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxStorageBufferBindingSize = reportedMaxBufferSize; + dawn::native::Limits limits = defaults; + limits.maxStorageBufferBindingSize = reportedMaxStorageBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxStorageBufferBindingSize, reportedMaxStorageBufferBindingSize); + } + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxStorageBufferBindingSize = reportedMaxBufferSize - 1; + dawn::native::Limits limits = defaults; + limits.maxStorageBufferBindingSize = reportedMaxStorageBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxStorageBufferBindingSize, reportedMaxStorageBufferBindingSize); + } + // maxStorageBufferBindingSize is equal to maxBufferSize+1, expect clamping to maxBufferSize + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxStorageBufferBindingSize = reportedMaxBufferSize + 1; + dawn::native::Limits limits = defaults; + limits.maxStorageBufferBindingSize = reportedMaxStorageBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxStorageBufferBindingSize, reportedMaxBufferSize); + } + // maxStorageBufferBindingSize is much larger than maxBufferSize, expect clamping to + // maxBufferSize + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxStorageBufferBindingSize = 4294967295; + dawn::native::Limits limits = defaults; + limits.maxStorageBufferBindingSize = reportedMaxStorageBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxStorageBufferBindingSize, reportedMaxBufferSize); + } + + // Test maxUniformBufferBindingSize is clamped to maxBufferSize. + // maxUniformBufferBindingSize is no larger than maxBufferSize + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxUniformBufferBindingSize = reportedMaxBufferSize - 1; + dawn::native::Limits limits = defaults; + limits.maxUniformBufferBindingSize = reportedMaxUniformBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxUniformBufferBindingSize, reportedMaxUniformBufferBindingSize); + } + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxUniformBufferBindingSize = reportedMaxBufferSize; + dawn::native::Limits limits = defaults; + limits.maxUniformBufferBindingSize = reportedMaxUniformBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxUniformBufferBindingSize, reportedMaxUniformBufferBindingSize); + } + // maxUniformBufferBindingSize is larger than maxBufferSize, expect clamping to maxBufferSize + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxUniformBufferBindingSize = reportedMaxBufferSize + 1; + dawn::native::Limits limits = defaults; + limits.maxUniformBufferBindingSize = reportedMaxUniformBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxUniformBufferBindingSize, reportedMaxBufferSize); + } + // maxUniformBufferBindingSize is much larger than maxBufferSize, expect clamping to + // maxBufferSize + { + constexpr uint64_t reportedMaxBufferSize = 2147483648; + constexpr uint64_t reportedMaxUniformBufferBindingSize = 4294967295; + dawn::native::Limits limits = defaults; + limits.maxUniformBufferBindingSize = reportedMaxUniformBufferBindingSize; + limits.maxBufferSize = reportedMaxBufferSize; + + NormalizeLimits(&limits); + + EXPECT_EQ(limits.maxBufferSize, reportedMaxBufferSize); + EXPECT_EQ(limits.maxUniformBufferBindingSize, reportedMaxBufferSize); + } +}