Populate supported Vulkan limits from the backend
This commit also unifies the initialization process for Adapters. InitializeImpl() initializes the actual backend adapter. InitializeSupportedFeaturesImpl() checks base WebGPU features and discovers additional supported features. InitializeSupportedLimitsImpl() checks base WebGPU limits and queries the adapter's maximum supported limits. Some of these limits from the backend are still overriden in the frontend because they are limited by internal Dawn constants. Bug: dawn:685 Change-Id: I43efb0b678dd45f8f89cd62d13104dd00b197da1 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64980 Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Brandon Jones <bajones@chromium.org>
This commit is contained in:
parent
bf9b3cc5a9
commit
d97b29cd86
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<DeviceBase*> 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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -39,15 +39,16 @@ namespace dawn_native { namespace d3d12 {
|
|||
ComPtr<ID3D12Device> GetDevice() const;
|
||||
const gpu_info::D3DDriverVersion& GetDriverVersion() const;
|
||||
|
||||
MaybeError Initialize();
|
||||
|
||||
private:
|
||||
ResultOrError<DeviceBase*> 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();
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
ResultOrError<std::unique_ptr<AdapterBase>> adapter =
|
||||
CreateAdapterFromIDXGIAdapter(this, dxgiAdapter);
|
||||
if (adapter.IsError()) {
|
||||
adapter.AcquireError();
|
||||
GetInstance()->ConsumedError(adapter.AcquireError());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<id<MTLDevice>> mDevice;
|
||||
|
@ -339,7 +348,10 @@ namespace dawn_native { namespace metal {
|
|||
NSRef<NSArray<id<MTLDevice>>> devices = AcquireNSRef(MTLCopyAllDevices());
|
||||
|
||||
for (id<MTLDevice> device in devices.Get()) {
|
||||
adapters.push_back(std::make_unique<Adapter>(GetInstance(), device));
|
||||
std::unique_ptr<Adapter> adapter = std::make_unique<Adapter>(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<Adapter>(GetInstance(), MTLCreateSystemDefaultDevice()));
|
||||
std::unique_ptr<Adapter> adapter = std::make_unique<Adapter>(GetInstance(), MTLCreateSystemDefaultDevice());
|
||||
if (!GetInstance()->ConsumedError(adapter->Initialize())) {
|
||||
adapters.push_back(std::move(adapter));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!supportedVersion) {
|
||||
|
|
|
@ -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<DeviceBase*> 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<std::unique_ptr<AdapterBase>> adapters;
|
||||
adapters.push_back(std::make_unique<Adapter>(GetInstance()));
|
||||
std::unique_ptr<Adapter> adapter = std::make_unique<Adapter>(GetInstance());
|
||||
adapters.push_back(std::move(adapter));
|
||||
return adapters;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -177,6 +177,10 @@ namespace dawn_native { namespace null {
|
|||
void SetSupportedFeatures(const std::vector<const char*>& requiredFeatures);
|
||||
|
||||
private:
|
||||
MaybeError InitializeImpl() override;
|
||||
MaybeError InitializeSupportedFeaturesImpl() override;
|
||||
MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override;
|
||||
|
||||
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<DeviceBase*> 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<DeviceBase*> 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> adapter = std::make_unique<Adapter>(
|
||||
GetInstance(), static_cast<wgpu::BackendType>(optionsBase->backendType));
|
||||
DAWN_TRY(adapter->Initialize(options));
|
||||
DAWN_TRY(adapter->InitializeGLFunctions(options->getProc));
|
||||
DAWN_TRY(adapter->Initialize());
|
||||
|
||||
mCreatedAdapter = true;
|
||||
std::vector<std::unique_ptr<AdapterBase>> adapters;
|
||||
|
|
|
@ -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<uint32_t>(vkLimits.maxImageDimension2D),
|
||||
static_cast<uint32_t>(vkLimits.maxImageDimensionCube),
|
||||
static_cast<uint32_t>(vkLimits.maxFramebufferWidth),
|
||||
static_cast<uint32_t>(vkLimits.maxFramebufferHeight),
|
||||
static_cast<uint32_t>(vkLimits.maxViewportDimensions[0]),
|
||||
static_cast<uint32_t>(vkLimits.maxViewportDimensions[1]),
|
||||
static_cast<uint32_t>(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<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {
|
||||
|
|
|
@ -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<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override;
|
||||
MaybeError CheckCoreWebGPUSupport();
|
||||
void InitializeSupportedFeatures();
|
||||
|
||||
VkPhysicalDevice mPhysicalDevice;
|
||||
Backend* mBackend;
|
||||
|
|
Loading…
Reference in New Issue