Add maxBufferSize limit
Also emits a warning when create buffer size exceeds the maxBufferSize limit during the deprecation period. Bug: dawn:1525 Change-Id: I7b47ae5c85b116035fdcea8b68fb574c0a550729 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/103660 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Auto-Submit: Shrek Shao <shrekshao@google.com> Commit-Queue: Shrek Shao <shrekshao@google.com>
This commit is contained in:
parent
06844a5e24
commit
d1a5f93630
|
@ -1263,6 +1263,7 @@
|
|||
{"name": "min uniform buffer offset alignment", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||
{"name": "min storage buffer offset alignment", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||
{"name": "max vertex buffers", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||
{"name": "max buffer size", "type": "uint64_t", "default": "WGPU_LIMIT_U64_UNDEFINED"},
|
||||
{"name": "max vertex attributes", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||
{"name": "max vertex buffer array stride", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||
{"name": "max inter stage shader components", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
|
||||
|
|
|
@ -10,7 +10,7 @@ Vulkan is supported as best effort on other platforms (e.g. Windows and macOS).
|
|||
|
||||
**Required features**: `depthBiasClamp`, `fragmentStoresAndAtomics`, `fullDrawIndexUint32`, `imageCubeArray`, `independentBlend`, `sampleRateShading`, and either `textureCompressionBC` or both of `textureCompressionETC` and `textureCompressionASTC_LDR`.
|
||||
|
||||
**Required limites**: they are too detailed to describe here, but in general should be wildly supported.
|
||||
**Required limits**: they are too detailed to describe here, but in general should be wildly supported.
|
||||
See the [WebGPU limits](https://gpuweb.github.io/gpuweb/#limits) that mostly correspond to Vulkan limits.
|
||||
|
||||
**Operating system support**:
|
||||
|
|
|
@ -27,6 +27,8 @@ static constexpr uint8_t kMaxColorAttachments = 8u;
|
|||
static constexpr uint32_t kTextureBytesPerRowAlignment = 256u;
|
||||
static constexpr uint32_t kMaxInterStageShaderComponents = 60u;
|
||||
static constexpr uint32_t kMaxInterStageShaderVariables = 16u;
|
||||
static constexpr uint64_t kAssumedMaxBufferSize =
|
||||
0x80000000u; // Use 2 GB when the limit is unavailable
|
||||
|
||||
// Per stage limits
|
||||
static constexpr uint32_t kMaxSampledTexturesPerShaderStage = 16;
|
||||
|
|
|
@ -49,7 +49,8 @@ MaybeError AdapterBase::Initialize() {
|
|||
"backend=%s type=%s)",
|
||||
mName, mDriverDescription, mVendorId, mDeviceId, mBackend, mAdapterType);
|
||||
|
||||
// Enforce internal Dawn constants.
|
||||
// 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 =
|
||||
|
|
|
@ -1432,6 +1432,14 @@ ResultOrError<Ref<BufferBase>> DeviceBase::CreateBuffer(const BufferDescriptor*
|
|||
DAWN_TRY(ValidateIsAlive());
|
||||
if (IsValidationEnabled()) {
|
||||
DAWN_TRY_CONTEXT(ValidateBufferDescriptor(this, descriptor), "validating %s", descriptor);
|
||||
|
||||
// TODO(dawn:1525): Change to validation error after the deprecation period.
|
||||
if (descriptor->size > mLimits.v1.maxBufferSize) {
|
||||
std::string warning =
|
||||
absl::StrFormat("Buffer size (%u) exceeds the max buffer size limit (%u).",
|
||||
descriptor->size, mLimits.v1.maxBufferSize);
|
||||
EmitDeprecationWarning(warning.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Ref<BufferBase> buffer;
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
#define LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \
|
||||
X(Maximum, maxStorageBufferBindingSize, 134217728, 1073741824, 2147483647, 4294967295)
|
||||
|
||||
// Tiers are 256Mb, 1Gb, 2Gb.
|
||||
#define LIMITS_MAX_BUFFER_SIZE(X) \
|
||||
X(Maximum, maxBufferSize, 0x10000000, 0x40000000, 0x80000000)
|
||||
|
||||
// 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.
|
||||
|
@ -49,6 +53,7 @@
|
|||
X(Alignment, minUniformBufferOffsetAlignment, 256, 256) \
|
||||
X(Alignment, minStorageBufferOffsetAlignment, 256, 256) \
|
||||
X(Maximum, maxVertexBuffers, 8, 8) \
|
||||
X(Maximum, maxBufferSize, 268435456, 268435456) \
|
||||
X(Maximum, maxVertexAttributes, 16, 16) \
|
||||
X(Maximum, maxVertexBufferArrayStride, 2048, 2048) \
|
||||
X(Maximum, maxInterStageShaderComponents, 60, 60) \
|
||||
|
@ -64,11 +69,13 @@
|
|||
#define LIMITS_EACH_GROUP(X) \
|
||||
X(LIMITS_WORKGROUP_STORAGE_SIZE) \
|
||||
X(LIMITS_STORAGE_BUFFER_BINDING_SIZE) \
|
||||
X(LIMITS_MAX_BUFFER_SIZE) \
|
||||
X(LIMITS_OTHER)
|
||||
|
||||
#define LIMITS(X) \
|
||||
LIMITS_WORKGROUP_STORAGE_SIZE(X) \
|
||||
LIMITS_STORAGE_BUFFER_BINDING_SIZE(X) \
|
||||
LIMITS_MAX_BUFFER_SIZE(X) \
|
||||
LIMITS_OTHER(X)
|
||||
|
||||
namespace dawn::native {
|
||||
|
|
|
@ -304,6 +304,8 @@ MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) {
|
|||
limits->v1.maxUniformBufferBindingSize = D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16;
|
||||
// D3D12 has no documented limit on the size of a storage buffer binding.
|
||||
limits->v1.maxStorageBufferBindingSize = 4294967295;
|
||||
// D3D12 has no documented limit on the buffer size.
|
||||
limits->v1.maxBufferSize = kAssumedMaxBufferSize;
|
||||
|
||||
// Using base limits for:
|
||||
// TODO(crbug.com/dawn/685):
|
||||
|
|
|
@ -598,6 +598,7 @@ class Adapter : public AdapterBase {
|
|||
limits->v1.minStorageBufferOffsetAlignment = mtlLimits.minBufferOffsetAlignment;
|
||||
|
||||
uint64_t maxBufferSize = Buffer::QueryMaxBufferLength(*mDevice);
|
||||
limits->v1.maxBufferSize = maxBufferSize;
|
||||
|
||||
// Metal has no documented limit on the size of a binding. Use the maximum
|
||||
// buffer size.
|
||||
|
|
|
@ -309,6 +309,16 @@ MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) {
|
|||
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for framebufferDepthSampleCounts");
|
||||
}
|
||||
|
||||
if (mDeviceInfo.HasExt(DeviceExt::Maintenance3)) {
|
||||
limits->v1.maxBufferSize = mDeviceInfo.propertiesMaintenance3.maxMemoryAllocationSize;
|
||||
if (mDeviceInfo.propertiesMaintenance3.maxMemoryAllocationSize <
|
||||
baseLimits.v1.maxBufferSize) {
|
||||
return DAWN_INTERNAL_ERROR("Insufficient Vulkan maxBufferSize limit");
|
||||
}
|
||||
} else {
|
||||
limits->v1.maxBufferSize = kAssumedMaxBufferSize;
|
||||
}
|
||||
|
||||
// Only check maxFragmentCombinedOutputResources on mobile GPUs. Desktop GPUs drivers seem
|
||||
// to put incorrect values for this limit with things like 8 or 16 when they can do bindless
|
||||
// storage buffers. Mesa llvmpipe driver also puts 8 here.
|
||||
|
|
|
@ -134,6 +134,8 @@ static constexpr std::array<DeviceExtInfo, kDeviceExtCount> sDeviceExtInfos{{
|
|||
//
|
||||
{DeviceExt::BindMemory2, "VK_KHR_bind_memory2", VulkanVersion_1_1},
|
||||
{DeviceExt::Maintenance1, "VK_KHR_maintenance1", VulkanVersion_1_1},
|
||||
{DeviceExt::Maintenance2, "VK_KHR_maintenance2", VulkanVersion_1_1},
|
||||
{DeviceExt::Maintenance3, "VK_KHR_maintenance3", VulkanVersion_1_1},
|
||||
{DeviceExt::StorageBufferStorageClass, "VK_KHR_storage_buffer_storage_class",
|
||||
VulkanVersion_1_1},
|
||||
{DeviceExt::GetPhysicalDeviceProperties2, "VK_KHR_get_physical_device_properties2",
|
||||
|
@ -210,6 +212,7 @@ DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts,
|
|||
case DeviceExt::BindMemory2:
|
||||
case DeviceExt::GetMemoryRequirements2:
|
||||
case DeviceExt::Maintenance1:
|
||||
case DeviceExt::Maintenance2:
|
||||
case DeviceExt::ImageFormatList:
|
||||
case DeviceExt::StorageBufferStorageClass:
|
||||
hasDependencies = true;
|
||||
|
@ -224,6 +227,7 @@ DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts,
|
|||
// advertises the extension. So if we didn't have this check, we'd risk a calling
|
||||
// a nullptr.
|
||||
case DeviceExt::GetPhysicalDeviceProperties2:
|
||||
case DeviceExt::Maintenance3:
|
||||
hasDependencies = instanceExts[InstanceExt::GetPhysicalDeviceProperties2];
|
||||
break;
|
||||
case DeviceExt::ExternalMemoryCapabilities:
|
||||
|
|
|
@ -76,6 +76,8 @@ enum class DeviceExt {
|
|||
// Promoted to 1.1
|
||||
BindMemory2,
|
||||
Maintenance1,
|
||||
Maintenance2,
|
||||
Maintenance3,
|
||||
StorageBufferStorageClass,
|
||||
GetPhysicalDeviceProperties2,
|
||||
GetMemoryRequirements2,
|
||||
|
|
|
@ -223,9 +223,12 @@ ResultOrError<VulkanDeviceInfo> GatherDeviceInfo(const Adapter& adapter) {
|
|||
|
||||
VkPhysicalDeviceProperties2 properties2 = {};
|
||||
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
features2.pNext = nullptr;
|
||||
|
||||
PNextChainBuilder propertiesChain(&properties2);
|
||||
|
||||
propertiesChain.Add(&info.propertiesMaintenance3,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES);
|
||||
|
||||
if (info.extensions[DeviceExt::ShaderFloat16Int8]) {
|
||||
featuresChain.Add(&info.shaderFloat16Int8Features,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR);
|
||||
|
|
|
@ -61,6 +61,7 @@ struct VulkanDeviceKnobs {
|
|||
|
||||
struct VulkanDeviceInfo : VulkanDeviceKnobs {
|
||||
VkPhysicalDeviceProperties properties;
|
||||
VkPhysicalDeviceMaintenance3Properties propertiesMaintenance3;
|
||||
VkPhysicalDeviceDriverProperties driverProperties;
|
||||
VkPhysicalDeviceSubgroupSizeControlPropertiesEXT subgroupSizeControlProperties;
|
||||
VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR shaderIntegerDotProductProperties;
|
||||
|
|
|
@ -812,11 +812,13 @@ TEST_P(BufferTests, CreateBufferOOM) {
|
|||
descriptor.usage = wgpu::BufferUsage::CopyDst;
|
||||
|
||||
descriptor.size = std::numeric_limits<uint64_t>::max();
|
||||
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
|
||||
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
||||
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
||||
|
||||
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
||||
descriptor.size = 1ull << 50;
|
||||
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
|
||||
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
||||
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
||||
}
|
||||
|
||||
// Test that a very large buffer mappedAtCreation fails gracefully.
|
||||
|
@ -846,7 +848,8 @@ TEST_P(BufferTests, BufferMappedAtCreationOOM) {
|
|||
|
||||
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
||||
descriptor.size = 1ull << 50;
|
||||
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
|
||||
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
||||
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
||||
}
|
||||
|
||||
// Test mappable buffer
|
||||
|
@ -865,7 +868,8 @@ TEST_P(BufferTests, BufferMappedAtCreationOOM) {
|
|||
|
||||
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
||||
descriptor.size = 1ull << 50;
|
||||
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
|
||||
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
||||
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -878,7 +882,8 @@ TEST_P(BufferTests, CreateBufferOOMMapAsync) {
|
|||
|
||||
auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) {
|
||||
wgpu::Buffer buffer;
|
||||
ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor));
|
||||
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
||||
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(buffer = device.CreateBuffer(&descriptor)));
|
||||
|
||||
bool done = false;
|
||||
ASSERT_DEVICE_ERROR(buffer.MapAsync(
|
||||
|
|
|
@ -168,6 +168,22 @@ TEST_P(DeprecationTests, Dispatch) {
|
|||
pass.End();
|
||||
}
|
||||
|
||||
// Test that creating a buffer with size exceeding the maximum buffer size limit should emits a
|
||||
// warning. (dawn:1525)
|
||||
TEST_P(DeprecationTests, MaxBufferSizeValidation) {
|
||||
wgpu::BufferDescriptor descriptor;
|
||||
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
||||
|
||||
descriptor.size = 256;
|
||||
device.CreateBuffer(&descriptor);
|
||||
|
||||
descriptor.size = GetSupportedLimits().limits.maxBufferSize;
|
||||
device.CreateBuffer(&descriptor);
|
||||
|
||||
descriptor.size = GetSupportedLimits().limits.maxBufferSize + 1;
|
||||
EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor));
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(DeprecationTests,
|
||||
D3D12Backend(),
|
||||
MetalBackend(),
|
||||
|
|
|
@ -992,8 +992,9 @@ TEST_F(BufferValidationTest, CreationParameterReflectionForOOMBuffer) {
|
|||
desc.size = kAmazinglyLargeSize;
|
||||
|
||||
// OOM!
|
||||
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
||||
wgpu::Buffer buf;
|
||||
ASSERT_DEVICE_ERROR(buf = device.CreateBuffer(&desc));
|
||||
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(buf = device.CreateBuffer(&desc)));
|
||||
|
||||
// Reflection data is still exactly what was in the descriptor.
|
||||
EXPECT_EQ(wgpu::BufferUsage::Storage, buf.GetUsage());
|
||||
|
|
Loading…
Reference in New Issue