1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/Limits.h" 16 17 #include "common/Assert.h" 18 19 #include <array> 20 21 // clang-format off 22 // TODO(crbug.com/dawn/685): 23 // For now, only expose these tiers until metrics can determine better ones. 24 #define LIMITS_WORKGROUP_STORAGE_SIZE(X) \ 25 X(Higher, maxComputeWorkgroupStorageSize, 16352, 32768, 49152, 65536) 26 27 #define LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \ 28 X(Higher, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483647, 4294967295) 29 30 // TODO(crbug.com/dawn/685): 31 // These limits don't have tiers yet. Define two tiers with the same values since the macros 32 // in this file expect more than one tier. 33 #define LIMITS_OTHER(X) \ 34 X(Higher, maxTextureDimension1D, 8192, 8192) \ 35 X(Higher, maxTextureDimension2D, 8192, 8192) \ 36 X(Higher, maxTextureDimension3D, 2048, 2048) \ 37 X(Higher, maxTextureArrayLayers, 256, 256) \ 38 X(Higher, maxBindGroups, 4, 4) \ 39 X(Higher, maxDynamicUniformBuffersPerPipelineLayout, 8, 8) \ 40 X(Higher, maxDynamicStorageBuffersPerPipelineLayout, 4, 4) \ 41 X(Higher, maxSampledTexturesPerShaderStage, 16, 16) \ 42 X(Higher, maxSamplersPerShaderStage, 16, 16) \ 43 X(Higher, maxStorageBuffersPerShaderStage, 8, 8) \ 44 X(Higher, maxStorageTexturesPerShaderStage, 4, 4) \ 45 X(Higher, maxUniformBuffersPerShaderStage, 12, 12) \ 46 X(Higher, maxUniformBufferBindingSize, 16384, 16384) \ 47 X( Lower, minUniformBufferOffsetAlignment, 256, 256) \ 48 X( Lower, minStorageBufferOffsetAlignment, 256, 256) \ 49 X(Higher, maxVertexBuffers, 8, 8) \ 50 X(Higher, maxVertexAttributes, 16, 16) \ 51 X(Higher, maxVertexBufferArrayStride, 2048, 2048) \ 52 X(Higher, maxInterStageShaderComponents, 60, 60) \ 53 X(Higher, maxComputeInvocationsPerWorkgroup, 256, 256) \ 54 X(Higher, maxComputeWorkgroupSizeX, 256, 256) \ 55 X(Higher, maxComputeWorkgroupSizeY, 256, 256) \ 56 X(Higher, maxComputeWorkgroupSizeZ, 64, 64) \ 57 X(Higher, maxComputeWorkgroupsPerDimension, 65535, 65535) 58 // clang-format on 59 60 #define LIMITS_EACH_GROUP(X) \ 61 X(LIMITS_WORKGROUP_STORAGE_SIZE) \ 62 X(LIMITS_STORAGE_BUFFER_BINDING_SIZE) \ 63 X(LIMITS_OTHER) 64 65 #define LIMITS(X) \ 66 LIMITS_WORKGROUP_STORAGE_SIZE(X) \ 67 LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \ 68 LIMITS_OTHER(X) 69 70 namespace dawn_native { 71 namespace { 72 template <uint32_t A, uint32_t B> StaticAssertSame()73 constexpr void StaticAssertSame() { 74 static_assert(A == B, "Mismatching tier count in limit group."); 75 } 76 77 template <uint32_t I, uint32_t... Is> ReduceSameValue(std::integer_sequence<uint32_t,I,Is...>)78 constexpr uint32_t ReduceSameValue(std::integer_sequence<uint32_t, I, Is...>) { 79 int unused[] = {0, (StaticAssertSame<I, Is>(), 0)...}; 80 DAWN_UNUSED(unused); 81 return I; 82 } 83 84 enum class LimitBetterDirection { 85 Lower, 86 Higher, 87 }; 88 89 template <LimitBetterDirection Better> 90 struct CheckLimit; 91 92 template <> 93 struct CheckLimit<LimitBetterDirection::Lower> { 94 template <typename T> IsBetterdawn_native::__anondafefa560111::CheckLimit95 static bool IsBetter(T lhs, T rhs) { 96 return lhs < rhs; 97 } 98 99 template <typename T> Validatedawn_native::__anondafefa560111::CheckLimit100 static MaybeError Validate(T supported, T required) { 101 DAWN_INVALID_IF(IsBetter(required, supported), 102 "Required limit (%u) is lower than the supported limit (%u).", 103 required, supported); 104 return {}; 105 } 106 }; 107 108 template <> 109 struct CheckLimit<LimitBetterDirection::Higher> { 110 template <typename T> IsBetterdawn_native::__anondafefa560111::CheckLimit111 static bool IsBetter(T lhs, T rhs) { 112 return lhs > rhs; 113 } 114 115 template <typename T> Validatedawn_native::__anondafefa560111::CheckLimit116 static MaybeError Validate(T supported, T required) { 117 DAWN_INVALID_IF(IsBetter(required, supported), 118 "Required limit (%u) is greater than the supported limit (%u).", 119 required, supported); 120 return {}; 121 } 122 }; 123 124 template <typename T> IsLimitUndefined(T value)125 bool IsLimitUndefined(T value) { 126 static_assert(sizeof(T) != sizeof(T), "IsLimitUndefined not implemented for this type"); 127 return false; 128 } 129 130 template <> IsLimitUndefined(uint32_t value)131 bool IsLimitUndefined<uint32_t>(uint32_t value) { 132 return value == wgpu::kLimitU32Undefined; 133 } 134 135 template <> IsLimitUndefined(uint64_t value)136 bool IsLimitUndefined<uint64_t>(uint64_t value) { 137 return value == wgpu::kLimitU64Undefined; 138 } 139 140 } // namespace 141 GetDefaultLimits(Limits * limits)142 void GetDefaultLimits(Limits* limits) { 143 ASSERT(limits != nullptr); 144 #define X(Better, limitName, base, ...) limits->limitName = base; 145 LIMITS(X) 146 #undef X 147 } 148 ReifyDefaultLimits(const Limits & limits)149 Limits ReifyDefaultLimits(const Limits& limits) { 150 Limits out; 151 #define X(Better, limitName, base, ...) \ 152 if (IsLimitUndefined(limits.limitName) || \ 153 CheckLimit<LimitBetterDirection::Better>::IsBetter( \ 154 static_cast<decltype(limits.limitName)>(base), limits.limitName)) { \ 155 /* If the limit is undefined or the default is better, use the default */ \ 156 out.limitName = base; \ 157 } else { \ 158 out.limitName = limits.limitName; \ 159 } 160 LIMITS(X) 161 #undef X 162 return out; 163 } 164 ValidateLimits(const Limits & supportedLimits,const Limits & requiredLimits)165 MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) { 166 #define X(Better, limitName, ...) \ 167 if (!IsLimitUndefined(requiredLimits.limitName)) { \ 168 DAWN_TRY_CONTEXT(CheckLimit<LimitBetterDirection::Better>::Validate( \ 169 supportedLimits.limitName, requiredLimits.limitName), \ 170 "validating " #limitName); \ 171 } 172 LIMITS(X) 173 #undef X 174 return {}; 175 } 176 ApplyLimitTiers(Limits limits)177 Limits ApplyLimitTiers(Limits limits) { 178 #define X_TIER_COUNT(Better, limitName, ...) , std::integer_sequence<uint64_t, __VA_ARGS__>{}.size() 179 #define GET_TIER_COUNT(LIMIT_GROUP) \ 180 ReduceSameValue(std::integer_sequence<uint32_t LIMIT_GROUP(X_TIER_COUNT)>{}) 181 182 #define X_EACH_GROUP(LIMIT_GROUP) \ 183 { \ 184 constexpr uint32_t kTierCount = GET_TIER_COUNT(LIMIT_GROUP); \ 185 for (uint32_t i = kTierCount; i != 0; --i) { \ 186 LIMIT_GROUP(X_CHECK_BETTER_AND_CLAMP) \ 187 /* Limits fit in tier and have been clamped. Break. */ \ 188 break; \ 189 } \ 190 } 191 192 #define X_CHECK_BETTER_AND_CLAMP(Better, limitName, ...) \ 193 { \ 194 constexpr std::array<decltype(Limits::limitName), kTierCount> tiers{__VA_ARGS__}; \ 195 decltype(Limits::limitName) tierValue = tiers[i - 1]; \ 196 if (CheckLimit<LimitBetterDirection::Better>::IsBetter(tierValue, limits.limitName)) { \ 197 /* The tier is better. Go to the next tier. */ \ 198 continue; \ 199 } else if (tierValue != limits.limitName) { \ 200 /* Better than the tier. Degrade |limits| to the tier. */ \ 201 limits.limitName = tiers[i - 1]; \ 202 } \ 203 } 204 205 LIMITS_EACH_GROUP(X_EACH_GROUP) 206 #undef X_CHECK_BETTER 207 #undef X_EACH_GROUP 208 #undef GET_TIER_COUNT 209 #undef X_TIER_COUNT 210 return limits; 211 } 212 213 } // namespace dawn_native 214