diff --git a/src/dawn_native/Adapter.cpp b/src/dawn_native/Adapter.cpp index cb16e995b2..d83871b8e9 100644 --- a/src/dawn_native/Adapter.cpp +++ b/src/dawn_native/Adapter.cpp @@ -14,16 +14,51 @@ #include "dawn_native/Adapter.h" +#include "common/Constants.h" #include "dawn_native/Instance.h" namespace dawn_native { AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend) : mInstance(instance), mBackend(backend) { - GetDefaultLimits(&mLimits.v1); mSupportedFeatures.EnableFeature(Feature::DawnInternalUsages); } + MaybeError AdapterBase::Initialize() { + DAWN_TRY(InitializeImpl()); + DAWN_TRY(InitializeSupportedFeaturesImpl()); + DAWN_TRY(InitializeSupportedLimitsImpl(&mLimits)); + + // Enforce internal Dawn constants. + mLimits.v1.maxVertexBufferArrayStride = + std::min(mLimits.v1.maxVertexBufferArrayStride, kMaxVertexBufferArrayStride); + 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); + mLimits.v1.maxDynamicUniformBuffersPerPipelineLayout = + std::min(mLimits.v1.maxDynamicUniformBuffersPerPipelineLayout, + kMaxDynamicUniformBuffersPerPipelineLayout); + mLimits.v1.maxDynamicStorageBuffersPerPipelineLayout = + std::min(mLimits.v1.maxDynamicStorageBuffersPerPipelineLayout, + kMaxDynamicStorageBuffersPerPipelineLayout); + + return {}; + } + wgpu::BackendType AdapterBase::GetBackendType() const { return mBackend; } diff --git a/src/dawn_native/Adapter.h b/src/dawn_native/Adapter.h index adf230bf35..d2bd13962b 100644 --- a/src/dawn_native/Adapter.h +++ b/src/dawn_native/Adapter.h @@ -33,6 +33,8 @@ namespace dawn_native { AdapterBase(InstanceBase* instance, wgpu::BackendType backend); virtual ~AdapterBase() = default; + MaybeError Initialize(); + wgpu::BackendType GetBackendType() const; wgpu::AdapterType GetAdapterType() const; const std::string& GetDriverDescription() const; @@ -66,6 +68,14 @@ namespace dawn_native { private: virtual ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) = 0; + virtual MaybeError InitializeImpl() = 0; + + // Check base WebGPU features and discover supported featurees. + virtual MaybeError InitializeSupportedFeaturesImpl() = 0; + + // Check base WebGPU limits and populate supported limits. + virtual MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) = 0; + MaybeError CreateDeviceInternal(DeviceBase** result, const DeviceDescriptor* descriptor); virtual MaybeError ResetInternalDeviceForTestingImpl(); diff --git a/src/dawn_native/d3d12/AdapterD3D12.cpp b/src/dawn_native/d3d12/AdapterD3D12.cpp index 67f5066584..26ac005479 100644 --- a/src/dawn_native/d3d12/AdapterD3D12.cpp +++ b/src/dawn_native/d3d12/AdapterD3D12.cpp @@ -61,7 +61,7 @@ namespace dawn_native { namespace d3d12 { return mDriverVersion; } - MaybeError Adapter::Initialize() { + MaybeError Adapter::InitializeImpl() { // D3D12 cannot check for feature support without a device. // Create the device to populate the adapter properties then reuse it when needed for actual // rendering. @@ -104,8 +104,6 @@ namespace dawn_native { namespace d3d12 { mDriverDescription = o.str(); } - InitializeSupportedFeatures(); - return {}; } @@ -130,13 +128,19 @@ namespace dawn_native { namespace d3d12 { return true; } - void Adapter::InitializeSupportedFeatures() { + MaybeError Adapter::InitializeSupportedFeaturesImpl() { if (AreTimestampQueriesSupported()) { mSupportedFeatures.EnableFeature(Feature::TimestampQuery); } mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC); mSupportedFeatures.EnableFeature(Feature::PipelineStatisticsQuery); mSupportedFeatures.EnableFeature(Feature::MultiPlanarFormats); + return {}; + } + + MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { + GetDefaultLimits(&limits->v1); + return {}; } MaybeError Adapter::InitializeDebugLayerFilters() { diff --git a/src/dawn_native/d3d12/AdapterD3D12.h b/src/dawn_native/d3d12/AdapterD3D12.h index 9c3d8f4d21..f4044d155b 100644 --- a/src/dawn_native/d3d12/AdapterD3D12.h +++ b/src/dawn_native/d3d12/AdapterD3D12.h @@ -39,15 +39,16 @@ namespace dawn_native { namespace d3d12 { ComPtr GetDevice() const; const gpu_info::D3DDriverVersion& GetDriverVersion() const; - MaybeError Initialize(); - private: ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override; MaybeError ResetInternalDeviceForTestingImpl() override; bool AreTimestampQueriesSupported() const; - void InitializeSupportedFeatures(); + MaybeError InitializeImpl() override; + MaybeError InitializeSupportedFeaturesImpl() override; + MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override; + MaybeError InitializeDebugLayerFilters(); void CleanUpDebugLayerFilters(); diff --git a/src/dawn_native/d3d12/BackendD3D12.cpp b/src/dawn_native/d3d12/BackendD3D12.cpp index 3e5c15b5b8..8ba578f02f 100644 --- a/src/dawn_native/d3d12/BackendD3D12.cpp +++ b/src/dawn_native/d3d12/BackendD3D12.cpp @@ -163,7 +163,7 @@ namespace dawn_native { namespace d3d12 { ResultOrError> adapter = CreateAdapterFromIDXGIAdapter(this, dxgiAdapter); if (adapter.IsError()) { - adapter.AcquireError(); + GetInstance()->ConsumedError(adapter.AcquireError()); continue; } diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm index 48bbb4b2fa..caea140c9d 100644 --- a/src/dawn_native/metal/BackendMTL.mm +++ b/src/dawn_native/metal/BackendMTL.mm @@ -263,8 +263,6 @@ namespace dawn_native { namespace metal { NSString* osVersion = [[NSProcessInfo processInfo] operatingSystemVersionString]; mDriverDescription = "Metal driver on " + std::string(systemName) + [osVersion UTF8String]; - - InitializeSupportedFeatures(); } // AdapterBase Implementation @@ -278,7 +276,11 @@ namespace dawn_native { namespace metal { return Device::Create(this, mDevice, descriptor); } - void InitializeSupportedFeatures() { + MaybeError InitializeImpl() override { + return {}; + } + + MaybeError InitializeSupportedFeaturesImpl() override { #if defined(DAWN_PLATFORM_MACOS) if ([*mDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) { mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC); @@ -315,6 +317,13 @@ namespace dawn_native { namespace metal { if (@available(macOS 10.11, iOS 11.0, *)) { mSupportedFeatures.EnableFeature(Feature::DepthClamping); } + + return {}; + } + + MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override { + GetDefaultLimits(&limits->v1); + return {}; } NSPRef> mDevice; @@ -339,7 +348,10 @@ namespace dawn_native { namespace metal { NSRef>> devices = AcquireNSRef(MTLCopyAllDevices()); for (id device in devices.Get()) { - adapters.push_back(std::make_unique(GetInstance(), device)); + std::unique_ptr adapter = std::make_unique(GetInstance(), device); + if (!GetInstance()->ConsumedError(adapter->Initialize())) { + adapters.push_back(std::move(adapter)); + } } } #endif @@ -348,8 +360,10 @@ namespace dawn_native { namespace metal { if (@available(iOS 8.0, *)) { supportedVersion = YES; // iOS only has a single device so MTLCopyAllDevices doesn't exist there. - adapters.push_back( - std::make_unique(GetInstance(), MTLCreateSystemDefaultDevice())); + std::unique_ptr adapter = std::make_unique(GetInstance(), MTLCreateSystemDefaultDevice()); + if (!GetInstance()->ConsumedError(adapter->Initialize())) { + adapters.push_back(std::move(adapter)); + } } #endif if (!supportedVersion) { diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp index e9b23eba39..6b785a18a4 100644 --- a/src/dawn_native/null/DeviceNull.cpp +++ b/src/dawn_native/null/DeviceNull.cpp @@ -27,9 +27,8 @@ namespace dawn_native { namespace null { Adapter::Adapter(InstanceBase* instance) : AdapterBase(instance, wgpu::BackendType::Null) { mPCIInfo.name = "Null backend"; mAdapterType = wgpu::AdapterType::CPU; - - // Enable all features by default for the convenience of tests. - mSupportedFeatures.featuresBitSet.set(); + MaybeError err = Initialize(); + ASSERT(err.IsSuccess()); } Adapter::~Adapter() = default; @@ -43,6 +42,21 @@ namespace dawn_native { namespace null { mSupportedFeatures = GetInstance()->FeatureNamesToFeaturesSet(requiredFeatures); } + MaybeError Adapter::InitializeImpl() { + return {}; + } + + MaybeError Adapter::InitializeSupportedFeaturesImpl() { + // Enable all features by default for the convenience of tests. + mSupportedFeatures.featuresBitSet.set(); + return {}; + } + + MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { + GetDefaultLimits(&limits->v1); + return {}; + } + ResultOrError Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { return Device::Create(this, descriptor); } @@ -56,7 +70,8 @@ namespace dawn_native { namespace null { // There is always a single Null adapter because it is purely CPU based and doesn't // depend on the system. std::vector> adapters; - adapters.push_back(std::make_unique(GetInstance())); + std::unique_ptr adapter = std::make_unique(GetInstance()); + adapters.push_back(std::move(adapter)); return adapters; } }; diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h index 5192819a78..cef718d285 100644 --- a/src/dawn_native/null/DeviceNull.h +++ b/src/dawn_native/null/DeviceNull.h @@ -177,6 +177,10 @@ namespace dawn_native { namespace null { void SetSupportedFeatures(const std::vector& requiredFeatures); private: + MaybeError InitializeImpl() override; + MaybeError InitializeSupportedFeaturesImpl() override; + MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override; + ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override; }; diff --git a/src/dawn_native/opengl/BackendGL.cpp b/src/dawn_native/opengl/BackendGL.cpp index b92599ede8..9a66779a95 100644 --- a/src/dawn_native/opengl/BackendGL.cpp +++ b/src/dawn_native/opengl/BackendGL.cpp @@ -123,9 +123,21 @@ namespace dawn_native { namespace opengl { : AdapterBase(instance, backendType) { } - MaybeError Initialize(const AdapterDiscoveryOptions* options) { + MaybeError InitializeGLFunctions(void* (*getProc)(const char*)) { // Use getProc to populate the dispatch table - DAWN_TRY(mFunctions.Initialize(options->getProc)); + return mFunctions.Initialize(getProc); + } + + ~Adapter() override = default; + + // AdapterBase Implementation + bool SupportsExternalImages() const override { + // Via dawn_native::opengl::WrapExternalEGLImage + return GetBackendType() == wgpu::BackendType::OpenGLES; + } + + private: + MaybeError InitializeImpl() override { if (mFunctions.GetVersion().IsES()) { ASSERT(GetBackendType() == wgpu::BackendType::OpenGLES); } else { @@ -187,29 +199,10 @@ namespace dawn_native { namespace opengl { mAdapterType = wgpu::AdapterType::CPU; } - InitializeSupportedFeatures(); - return {}; } - ~Adapter() override = default; - - // AdapterBase Implementation - bool SupportsExternalImages() const override { - // Via dawn_native::opengl::WrapExternalEGLImage - return GetBackendType() == wgpu::BackendType::OpenGLES; - } - - private: - OpenGLFunctions mFunctions; - - ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override { - // There is no limit on the number of devices created from this adapter because they can - // all share the same backing OpenGL context. - return Device::Create(this, descriptor, mFunctions); - } - - void InitializeSupportedFeatures() { + MaybeError InitializeSupportedFeaturesImpl() override { // TextureCompressionBC { // BC1, BC2 and BC3 are not supported in OpenGL or OpenGL ES core features. @@ -252,7 +245,22 @@ namespace dawn_native { namespace opengl { mSupportedFeatures.EnableFeature(dawn_native::Feature::TextureCompressionBC); } } + + return {}; } + + MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override { + GetDefaultLimits(&limits->v1); + return {}; + } + + ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override { + // There is no limit on the number of devices created from this adapter because they can + // all share the same backing OpenGL context. + return Device::Create(this, descriptor, mFunctions); + } + + OpenGLFunctions mFunctions; }; // Implementation of the OpenGL backend's BackendConnection @@ -284,7 +292,8 @@ namespace dawn_native { namespace opengl { std::unique_ptr adapter = std::make_unique( GetInstance(), static_cast(optionsBase->backendType)); - DAWN_TRY(adapter->Initialize(options)); + DAWN_TRY(adapter->InitializeGLFunctions(options->getProc)); + DAWN_TRY(adapter->Initialize()); mCreatedAdapter = true; std::vector> adapters; diff --git a/src/dawn_native/vulkan/AdapterVk.cpp b/src/dawn_native/vulkan/AdapterVk.cpp index df488b3b03..bf2aa02693 100644 --- a/src/dawn_native/vulkan/AdapterVk.cpp +++ b/src/dawn_native/vulkan/AdapterVk.cpp @@ -40,9 +40,8 @@ namespace dawn_native { namespace vulkan { return mBackend; } - MaybeError Adapter::Initialize() { + MaybeError Adapter::InitializeImpl() { DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); - DAWN_TRY(CheckCoreWebGPUSupport()); if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) { mDriverDescription = mDeviceInfo.driverProperties.driverName; @@ -54,8 +53,6 @@ namespace dawn_native { namespace vulkan { "Vulkan driver version: " + std::to_string(mDeviceInfo.properties.driverVersion); } - InitializeSupportedFeatures(); - mPCIInfo.deviceId = mDeviceInfo.properties.deviceID; mPCIInfo.vendorId = mDeviceInfo.properties.vendorID; mPCIInfo.name = mDeviceInfo.properties.deviceName; @@ -78,10 +75,7 @@ namespace dawn_native { namespace vulkan { return {}; } - MaybeError Adapter::CheckCoreWebGPUSupport() { - Limits baseLimits; - GetDefaultLimits(&baseLimits); - + MaybeError Adapter::InitializeSupportedFeaturesImpl() { // Needed for viewport Y-flip. if (!mDeviceInfo.HasExt(DeviceExt::Maintenance1)) { return DAWN_INTERNAL_ERROR("Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 required."); @@ -120,152 +114,7 @@ namespace dawn_native { namespace vulkan { return DAWN_INTERNAL_ERROR("Vulkan sampleRateShading feature required."); } - // Check base WebGPU limits are supported. - const VkPhysicalDeviceLimits& limits = mDeviceInfo.properties.limits; - if (limits.maxImageDimension1D < baseLimits.maxTextureDimension1D) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension1D"); - } - if (limits.maxImageDimension2D < baseLimits.maxTextureDimension2D || - limits.maxImageDimensionCube < baseLimits.maxTextureDimension2D || - limits.maxFramebufferWidth < baseLimits.maxTextureDimension2D || - limits.maxFramebufferHeight < baseLimits.maxTextureDimension2D || - limits.maxViewportDimensions[0] < baseLimits.maxTextureDimension2D || - limits.maxViewportDimensions[1] < baseLimits.maxTextureDimension2D || - limits.viewportBoundsRange[1] < baseLimits.maxTextureDimension2D) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension2D"); - } - if (limits.maxImageDimension3D < baseLimits.maxTextureDimension3D) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension3D"); - } - if (limits.maxImageArrayLayers < baseLimits.maxTextureArrayLayers) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureArrayLayers"); - } - if (limits.maxBoundDescriptorSets < baseLimits.maxBindGroups) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxBindGroups"); - } - if (limits.maxDescriptorSetUniformBuffersDynamic < - baseLimits.maxDynamicUniformBuffersPerPipelineLayout) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxDynamicUniformBuffersPerPipelineLayout"); - } - if (limits.maxDescriptorSetStorageBuffersDynamic < - baseLimits.maxDynamicStorageBuffersPerPipelineLayout) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxDynamicStorageBuffersPerPipelineLayout"); - } - if (limits.maxPerStageDescriptorSampledImages < - baseLimits.maxSampledTexturesPerShaderStage) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxSampledTexturesPerShaderStage"); - } - if (limits.maxPerStageDescriptorSamplers < baseLimits.maxSamplersPerShaderStage) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxSamplersPerShaderStage"); - } - if (limits.maxPerStageDescriptorStorageBuffers < - baseLimits.maxStorageBuffersPerShaderStage) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxStorageBuffersPerShaderStage"); - } - if (limits.maxPerStageDescriptorStorageImages < - baseLimits.maxStorageTexturesPerShaderStage) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxStorageTexturesPerShaderStage"); - } - if (limits.maxPerStageDescriptorUniformBuffers < - baseLimits.maxUniformBuffersPerShaderStage) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxUniformBuffersPerShaderStage"); - } - if (limits.maxUniformBufferRange < baseLimits.maxUniformBufferBindingSize) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxUniformBufferBindingSize"); - } - if (limits.maxStorageBufferRange < baseLimits.maxStorageBufferBindingSize) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxStorageBufferBindingSize"); - } - if (limits.minUniformBufferOffsetAlignment > baseLimits.minUniformBufferOffsetAlignment) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for minUniformBufferOffsetAlignment"); - } - if (limits.minStorageBufferOffsetAlignment > baseLimits.minStorageBufferOffsetAlignment) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for minStorageBufferOffsetAlignment"); - } - if (limits.maxVertexInputBindings < baseLimits.maxVertexBuffers) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBuffers"); - } - if (limits.maxVertexInputAttributes < baseLimits.maxVertexAttributes) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexAttributes"); - } - if (limits.maxVertexInputBindingStride < baseLimits.maxVertexBufferArrayStride || - limits.maxVertexInputAttributeOffset < baseLimits.maxVertexBufferArrayStride - 1) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride"); - } - if (limits.maxVertexOutputComponents < baseLimits.maxInterStageShaderComponents || - limits.maxFragmentInputComponents < baseLimits.maxInterStageShaderComponents) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxInterStageShaderComponents"); - } - if (limits.maxComputeSharedMemorySize < baseLimits.maxComputeWorkgroupStorageSize) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxComputeWorkgroupStorageSize"); - } - if (limits.maxComputeWorkGroupInvocations < baseLimits.maxComputeInvocationsPerWorkgroup) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxComputeInvocationsPerWorkgroup"); - } - if (limits.maxComputeWorkGroupSize[0] < baseLimits.maxComputeWorkgroupSizeX || - limits.maxComputeWorkGroupSize[1] < baseLimits.maxComputeWorkgroupSizeY || - limits.maxComputeWorkGroupSize[2] < baseLimits.maxComputeWorkgroupSizeZ) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxComputeWorkgroupSize"); - } - if (limits.maxComputeWorkGroupCount[0] < baseLimits.maxComputeWorkgroupsPerDimension || - limits.maxComputeWorkGroupCount[1] < baseLimits.maxComputeWorkgroupsPerDimension || - limits.maxComputeWorkGroupCount[2] < baseLimits.maxComputeWorkgroupsPerDimension) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for maxComputeWorkgroupsPerDimension"); - } - if (limits.maxColorAttachments < kMaxColorAttachments) { - return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments"); - } - if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), - limits.framebufferColorSampleCounts)) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for framebufferColorSampleCounts"); - } - if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), - limits.framebufferDepthSampleCounts)) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan limits for framebufferDepthSampleCounts"); - } - - // 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. - uint32_t vendorId = mDeviceInfo.properties.vendorID; - if (!gpu_info::IsAMD(vendorId) && !gpu_info::IsIntel(vendorId) && - !gpu_info::IsNvidia(vendorId)) { - if (limits.maxFragmentCombinedOutputResources < - kMaxColorAttachments + baseLimits.maxStorageTexturesPerShaderStage + - baseLimits.maxStorageBuffersPerShaderStage) { - return DAWN_INTERNAL_ERROR( - "Insufficient Vulkan maxFragmentCombinedOutputResources limit"); - } - } - - return {}; - } - - bool Adapter::SupportsExternalImages() const { - // Via dawn_native::vulkan::WrapVulkanImage - return external_memory::Service::CheckSupport(mDeviceInfo) && - external_semaphore::Service::CheckSupport(mDeviceInfo, mPhysicalDevice, - mBackend->GetFunctions()); - } - - void Adapter::InitializeSupportedFeatures() { + // Initialize supported extensions if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) { mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC); } @@ -289,6 +138,186 @@ namespace dawn_native { namespace vulkan { if (mDeviceInfo.properties.limits.timestampComputeAndGraphics == VK_TRUE) { mSupportedFeatures.EnableFeature(Feature::TimestampQuery); } + + return {}; + } + + MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { + GetDefaultLimits(&limits->v1); + CombinedLimits baseLimits = *limits; + + const VkPhysicalDeviceLimits& vkLimits = mDeviceInfo.properties.limits; + +#define CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, compareOp, msgSegment) \ + do { \ + if (vkLimits.vulkanName compareOp baseLimits.v1.webgpuName) { \ + return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for " #webgpuName \ + "." \ + " VkPhysicalDeviceLimits::" #vulkanName \ + " must be at " msgSegment " " + \ + std::to_string(baseLimits.v1.webgpuName)); \ + } \ + limits->v1.webgpuName = vkLimits.vulkanName; \ + } while (false) + +#define CHECK_AND_SET_V1_MAX_LIMIT(vulkanName, webgpuName) \ + CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, <, "least") +#define CHECK_AND_SET_V1_MIN_LIMIT(vulkanName, webgpuName) \ + CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, >, "most") + + CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension1D, maxTextureDimension1D); + + CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension2D, maxTextureDimension2D); + CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimensionCube, maxTextureDimension2D); + CHECK_AND_SET_V1_MAX_LIMIT(maxFramebufferWidth, maxTextureDimension2D); + CHECK_AND_SET_V1_MAX_LIMIT(maxFramebufferHeight, maxTextureDimension2D); + CHECK_AND_SET_V1_MAX_LIMIT(maxViewportDimensions[0], maxTextureDimension2D); + CHECK_AND_SET_V1_MAX_LIMIT(maxViewportDimensions[1], maxTextureDimension2D); + CHECK_AND_SET_V1_MAX_LIMIT(viewportBoundsRange[1], maxTextureDimension2D); + limits->v1.maxTextureDimension2D = std::min({ + static_cast(vkLimits.maxImageDimension2D), + static_cast(vkLimits.maxImageDimensionCube), + static_cast(vkLimits.maxFramebufferWidth), + static_cast(vkLimits.maxFramebufferHeight), + static_cast(vkLimits.maxViewportDimensions[0]), + static_cast(vkLimits.maxViewportDimensions[1]), + static_cast(vkLimits.viewportBoundsRange[1]), + }); + + CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension3D, maxTextureDimension3D); + CHECK_AND_SET_V1_MAX_LIMIT(maxImageArrayLayers, maxTextureArrayLayers); + CHECK_AND_SET_V1_MAX_LIMIT(maxBoundDescriptorSets, maxBindGroups); + CHECK_AND_SET_V1_MAX_LIMIT(maxDescriptorSetUniformBuffersDynamic, + maxDynamicUniformBuffersPerPipelineLayout); + CHECK_AND_SET_V1_MAX_LIMIT(maxDescriptorSetStorageBuffersDynamic, + maxDynamicStorageBuffersPerPipelineLayout); + + CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorSampledImages, + maxSampledTexturesPerShaderStage); + CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorSamplers, maxSamplersPerShaderStage); + CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorStorageBuffers, + maxStorageBuffersPerShaderStage); + CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorStorageImages, + maxStorageTexturesPerShaderStage); + CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorUniformBuffers, + maxUniformBuffersPerShaderStage); + CHECK_AND_SET_V1_MAX_LIMIT(maxUniformBufferRange, maxUniformBufferBindingSize); + CHECK_AND_SET_V1_MAX_LIMIT(maxStorageBufferRange, maxStorageBufferBindingSize); + + CHECK_AND_SET_V1_MIN_LIMIT(minUniformBufferOffsetAlignment, + minUniformBufferOffsetAlignment); + CHECK_AND_SET_V1_MIN_LIMIT(minStorageBufferOffsetAlignment, + minStorageBufferOffsetAlignment); + + CHECK_AND_SET_V1_MAX_LIMIT(maxVertexInputBindings, maxVertexBuffers); + CHECK_AND_SET_V1_MAX_LIMIT(maxVertexInputAttributes, maxVertexAttributes); + + if (vkLimits.maxVertexInputBindingStride < baseLimits.v1.maxVertexBufferArrayStride || + vkLimits.maxVertexInputAttributeOffset < baseLimits.v1.maxVertexBufferArrayStride - 1) { + return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride"); + } + limits->v1.maxVertexBufferArrayStride = std::min( + vkLimits.maxVertexInputBindingStride, vkLimits.maxVertexInputAttributeOffset + 1); + + if (vkLimits.maxVertexOutputComponents < baseLimits.v1.maxInterStageShaderComponents || + vkLimits.maxFragmentInputComponents < baseLimits.v1.maxInterStageShaderComponents) { + return DAWN_INTERNAL_ERROR( + "Insufficient Vulkan limits for maxInterStageShaderComponents"); + } + limits->v1.maxInterStageShaderComponents = + std::min(vkLimits.maxVertexOutputComponents, vkLimits.maxFragmentInputComponents); + + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeSharedMemorySize, maxComputeWorkgroupStorageSize); + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupInvocations, + maxComputeInvocationsPerWorkgroup); + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[0], maxComputeWorkgroupSizeX); + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[1], maxComputeWorkgroupSizeY); + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[2], maxComputeWorkgroupSizeZ); + + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[0], maxComputeWorkgroupsPerDimension); + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[1], maxComputeWorkgroupsPerDimension); + CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[2], maxComputeWorkgroupsPerDimension); + limits->v1.maxComputeWorkgroupsPerDimension = std::min({ + vkLimits.maxComputeWorkGroupCount[0], + vkLimits.maxComputeWorkGroupCount[1], + vkLimits.maxComputeWorkGroupCount[2], + }); + + if (vkLimits.maxColorAttachments < kMaxColorAttachments) { + return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments"); + } + if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), + vkLimits.framebufferColorSampleCounts)) { + return DAWN_INTERNAL_ERROR( + "Insufficient Vulkan limits for framebufferColorSampleCounts"); + } + if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), + vkLimits.framebufferDepthSampleCounts)) { + return DAWN_INTERNAL_ERROR( + "Insufficient Vulkan limits for framebufferDepthSampleCounts"); + } + + // 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. + uint32_t vendorId = mDeviceInfo.properties.vendorID; + if (!gpu_info::IsAMD(vendorId) && !gpu_info::IsIntel(vendorId) && + !gpu_info::IsNvidia(vendorId)) { + if (vkLimits.maxFragmentCombinedOutputResources < + kMaxColorAttachments + baseLimits.v1.maxStorageTexturesPerShaderStage + + baseLimits.v1.maxStorageBuffersPerShaderStage) { + return DAWN_INTERNAL_ERROR( + "Insufficient Vulkan maxFragmentCombinedOutputResources limit"); + } + + uint32_t maxFragmentCombinedOutputResources = + kMaxColorAttachments + limits->v1.maxStorageTexturesPerShaderStage + + limits->v1.maxStorageBuffersPerShaderStage; + + if (maxFragmentCombinedOutputResources > vkLimits.maxFragmentCombinedOutputResources) { + // WebGPU's maxFragmentCombinedOutputResources exceeds the Vulkan limit. + // Decrease |maxStorageTexturesPerShaderStage| and |maxStorageBuffersPerShaderStage| + // to fit within the Vulkan limit. + uint32_t countOverLimit = maxFragmentCombinedOutputResources - + vkLimits.maxFragmentCombinedOutputResources; + + uint32_t maxStorageTexturesOverBase = + limits->v1.maxStorageTexturesPerShaderStage - + baseLimits.v1.maxStorageTexturesPerShaderStage; + uint32_t maxStorageBuffersOverBase = limits->v1.maxStorageBuffersPerShaderStage - + baseLimits.v1.maxStorageBuffersPerShaderStage; + + // Reduce the number of resources by half the overage count, but clamp to + // to ensure we don't go below the base limits. + uint32_t numFewerStorageTextures = + std::min(countOverLimit / 2, maxStorageTexturesOverBase); + uint32_t numFewerStorageBuffers = + std::min((countOverLimit + 1) / 2, maxStorageBuffersOverBase); + + if (numFewerStorageTextures == maxStorageTexturesOverBase) { + // If |numFewerStorageTextures| was clamped, subtract the remaining + // from the storage buffers. + numFewerStorageBuffers = countOverLimit - numFewerStorageTextures; + ASSERT(numFewerStorageBuffers <= maxStorageBuffersOverBase); + } else if (numFewerStorageBuffers == maxStorageBuffersOverBase) { + // If |numFewerStorageBuffers| was clamped, subtract the remaining + // from the storage textures. + numFewerStorageTextures = countOverLimit - numFewerStorageBuffers; + ASSERT(numFewerStorageTextures <= maxStorageTexturesOverBase); + } + limits->v1.maxStorageTexturesPerShaderStage -= numFewerStorageTextures; + limits->v1.maxStorageBuffersPerShaderStage -= numFewerStorageBuffers; + } + } + + return {}; + } + + bool Adapter::SupportsExternalImages() const { + // Via dawn_native::vulkan::WrapVulkanImage + return external_memory::Service::CheckSupport(mDeviceInfo) && + external_semaphore::Service::CheckSupport(mDeviceInfo, mPhysicalDevice, + mBackend->GetFunctions()); } ResultOrError Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { diff --git a/src/dawn_native/vulkan/AdapterVk.h b/src/dawn_native/vulkan/AdapterVk.h index 47679e8dd3..9d98d2c6e1 100644 --- a/src/dawn_native/vulkan/AdapterVk.h +++ b/src/dawn_native/vulkan/AdapterVk.h @@ -36,12 +36,12 @@ namespace dawn_native { namespace vulkan { VkPhysicalDevice GetPhysicalDevice() const; Backend* GetBackend() const; - MaybeError Initialize(); - private: + MaybeError InitializeImpl() override; + MaybeError InitializeSupportedFeaturesImpl() override; + MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override; + ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override; - MaybeError CheckCoreWebGPUSupport(); - void InitializeSupportedFeatures(); VkPhysicalDevice mPhysicalDevice; Backend* mBackend;