Skip to content

Commit

Permalink
WebGPU: properly handle enabled feature status
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Jan 5, 2025
1 parent 9cde409 commit 951dba2
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 119 deletions.
22 changes: 13 additions & 9 deletions Graphics/GraphicsEngineWebGPU/include/RenderDeviceWebGPUImpl.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 Diligent Graphics LLC
* Copyright 2023-2025 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,14 +52,18 @@ class RenderDeviceWebGPUImpl final : public RenderDeviceBase<EngineWebGPUImplTra
public:
using TRenderDeviceBase = RenderDeviceBase<EngineWebGPUImplTraits>;

RenderDeviceWebGPUImpl(IReferenceCounters* pRefCounters,
IMemoryAllocator& RawMemAllocator,
IEngineFactory* pEngineFactory,
const EngineWebGPUCreateInfo& EngineCI,
const GraphicsAdapterInfo& AdapterInfo,
WGPUInstance wgpuInstance,
WGPUAdapter wgpuAdapter,
WGPUDevice wgpuDevice) noexcept(false);
struct CreateInfo
{
IMemoryAllocator& RawMemAllocator;
IEngineFactory* const pEngineFactory;
const EngineWebGPUCreateInfo& EngineCI;
const GraphicsAdapterInfo& AdapterInfo;
const DeviceFeatures& EnabledFeatures;
WGPUInstance wgpuInstance = {};
WGPUAdapter wgpuAdapter = {};
WGPUDevice wgpuDevice = {};
};
RenderDeviceWebGPUImpl(IReferenceCounters* pRefCounters, const CreateInfo& CI) noexcept(false);

~RenderDeviceWebGPUImpl() override;

Expand Down
211 changes: 118 additions & 93 deletions Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,51 +237,45 @@ static void UncapturedErrorCallback2(WGPUDevice const* device,
}
#endif

WebGPUDeviceWrapper CreateDeviceForAdapter(EngineWebGPUCreateInfo const& EngineCI, WGPUInstance wgpuInstance, WGPUAdapter wgpuAdapter)
WebGPUDeviceWrapper CreateDeviceForAdapter(const DeviceFeatures& Features, WGPUInstance wgpuInstance, WGPUAdapter wgpuAdapter)
{
WGPUSupportedLimits SupportedLimits{};
wgpuAdapterGetLimits(wgpuAdapter, &SupportedLimits);

std::vector<WGPUFeatureName> Features{};
std::vector<WGPUFeatureName> wgpuFeatures{};
{
if (EngineCI.Features.DepthBiasClamp && wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_DepthClipControl))
Features.push_back(WGPUFeatureName_DepthClipControl);

if (EngineCI.Features.TimestampQueries && wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_TimestampQuery))
Features.push_back(WGPUFeatureName_TimestampQuery);

if (EngineCI.Features.TimestampQueries && wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_ChromiumExperimentalTimestampQueryInsidePasses))
Features.push_back(WGPUFeatureName_ChromiumExperimentalTimestampQueryInsidePasses);

if (EngineCI.Features.TextureCompressionBC && wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_TextureCompressionBC))
Features.push_back(WGPUFeatureName_TextureCompressionBC);

if (EngineCI.Features.TextureCompressionETC2 && wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_TextureCompressionETC2))
Features.push_back(WGPUFeatureName_TextureCompressionETC2);
auto AddWGPUFeature = [wgpuAdapter, &wgpuFeatures](DEVICE_FEATURE_STATE FeatureState, WGPUFeatureName wgpuFeature) {
if (FeatureState && wgpuAdapterHasFeature(wgpuAdapter, wgpuFeature))
wgpuFeatures.push_back(wgpuFeature);
};

if (EngineCI.Features.ShaderFloat16 && wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_ShaderF16))
Features.push_back(WGPUFeatureName_ShaderF16);
AddWGPUFeature(Features.DepthBiasClamp, WGPUFeatureName_DepthClipControl);
AddWGPUFeature(Features.TimestampQueries, WGPUFeatureName_TimestampQuery);
AddWGPUFeature(Features.DurationQueries, WGPUFeatureName_ChromiumExperimentalTimestampQueryInsidePasses);
AddWGPUFeature(Features.TextureCompressionBC, WGPUFeatureName_TextureCompressionBC);
AddWGPUFeature(Features.TextureCompressionETC2, WGPUFeatureName_TextureCompressionETC2);
AddWGPUFeature(Features.ShaderFloat16, WGPUFeatureName_ShaderF16);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_Depth32FloatStencil8))
Features.push_back(WGPUFeatureName_Depth32FloatStencil8);
wgpuFeatures.push_back(WGPUFeatureName_Depth32FloatStencil8);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_Float32Filterable))
Features.push_back(WGPUFeatureName_Float32Filterable);
wgpuFeatures.push_back(WGPUFeatureName_Float32Filterable);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_IndirectFirstInstance))
Features.push_back(WGPUFeatureName_IndirectFirstInstance);
wgpuFeatures.push_back(WGPUFeatureName_IndirectFirstInstance);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_RG11B10UfloatRenderable))
Features.push_back(WGPUFeatureName_RG11B10UfloatRenderable);
wgpuFeatures.push_back(WGPUFeatureName_RG11B10UfloatRenderable);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_BGRA8UnormStorage))
Features.push_back(WGPUFeatureName_BGRA8UnormStorage);
wgpuFeatures.push_back(WGPUFeatureName_BGRA8UnormStorage);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_Unorm16TextureFormats))
Features.push_back(WGPUFeatureName_Unorm16TextureFormats);
wgpuFeatures.push_back(WGPUFeatureName_Unorm16TextureFormats);

if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_Snorm16TextureFormats))
Features.push_back(WGPUFeatureName_Snorm16TextureFormats);
wgpuFeatures.push_back(WGPUFeatureName_Snorm16TextureFormats);
}

struct CallbackUserData
Expand Down Expand Up @@ -320,8 +314,8 @@ WebGPUDeviceWrapper CreateDeviceForAdapter(EngineWebGPUCreateInfo const& EngineC

WGPUDeviceDescriptor DeviceDesc{};
DeviceDesc.requiredLimits = &RequiredLimits;
DeviceDesc.requiredFeatureCount = Features.size();
DeviceDesc.requiredFeatures = Features.data();
DeviceDesc.requiredFeatureCount = wgpuFeatures.size();
DeviceDesc.requiredFeatures = wgpuFeatures.data();
#if PLATFORM_EMSCRIPTEN
DeviceDesc.deviceLostCallback = DeviceLostCallback;
#else
Expand All @@ -340,7 +334,88 @@ WebGPUDeviceWrapper CreateDeviceForAdapter(EngineWebGPUCreateInfo const& EngineC
return WebGPUDeviceWrapper{UserData.Device};
}

GraphicsAdapterInfo GetGraphicsAdapterInfo(WGPUAdapter wgpuAdapter, WGPUDevice wgpuDevice = nullptr /*Hack for Emscripten*/)
bool FeatureSupported(WGPUAdapter wgpuAdapter, WGPUDevice wgpuDevice, WGPUFeatureName Feature)
{
if (wgpuAdapter != nullptr)
{
return wgpuAdapterHasFeature(wgpuAdapter, Feature);
}
else if (wgpuDevice != nullptr)
{
return wgpuDeviceHasFeature(wgpuDevice, Feature);
}
else
{
UNEXPECTED("Either adapter or device must not be null");
return DEVICE_FEATURE_STATE_DISABLED;
}
}

DeviceFeatures GetSupportedFeatures(WGPUAdapter wgpuAdapter, WGPUDevice wgpuDevice = nullptr)
{
auto CheckFeature = [wgpuAdapter, wgpuDevice](WGPUFeatureName Feature) {
return FeatureSupported(wgpuAdapter, wgpuDevice, Feature) ? DEVICE_FEATURE_STATE_ENABLED : DEVICE_FEATURE_STATE_DISABLED;
};

DeviceFeatures Features;
Features.SeparablePrograms = DEVICE_FEATURE_STATE_ENABLED;
Features.ShaderResourceQueries = DEVICE_FEATURE_STATE_ENABLED;
Features.WireframeFill = DEVICE_FEATURE_STATE_DISABLED;
Features.MultithreadedResourceCreation = DEVICE_FEATURE_STATE_DISABLED;
Features.ComputeShaders = DEVICE_FEATURE_STATE_ENABLED;
Features.GeometryShaders = DEVICE_FEATURE_STATE_DISABLED;
Features.Tessellation = DEVICE_FEATURE_STATE_DISABLED;
Features.MeshShaders = DEVICE_FEATURE_STATE_DISABLED;
Features.RayTracing = DEVICE_FEATURE_STATE_DISABLED;
Features.BindlessResources = DEVICE_FEATURE_STATE_DISABLED;
Features.OcclusionQueries = DEVICE_FEATURE_STATE_ENABLED;
Features.BinaryOcclusionQueries = DEVICE_FEATURE_STATE_DISABLED;
Features.PipelineStatisticsQueries = DEVICE_FEATURE_STATE_DISABLED;
Features.DepthBiasClamp = DEVICE_FEATURE_STATE_ENABLED;
Features.DepthClamp = CheckFeature(WGPUFeatureName_DepthClipControl);
Features.IndependentBlend = DEVICE_FEATURE_STATE_ENABLED;
Features.DualSourceBlend = CheckFeature(WGPUFeatureName_DualSourceBlending);
Features.MultiViewport = DEVICE_FEATURE_STATE_DISABLED;
Features.TextureCompressionBC = CheckFeature(WGPUFeatureName_TextureCompressionBC);
Features.TextureCompressionETC2 = CheckFeature(WGPUFeatureName_TextureCompressionETC2);
Features.VertexPipelineUAVWritesAndAtomics = DEVICE_FEATURE_STATE_ENABLED;
Features.PixelUAVWritesAndAtomics = DEVICE_FEATURE_STATE_ENABLED;
Features.TextureUAVExtendedFormats = DEVICE_FEATURE_STATE_ENABLED;
Features.ShaderFloat16 = CheckFeature(WGPUFeatureName_ShaderF16);
Features.ResourceBuffer16BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.UniformBuffer16BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderInputOutput16 = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderInt8 = DEVICE_FEATURE_STATE_DISABLED;
Features.ResourceBuffer8BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.UniformBuffer8BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderResourceStaticArrays = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderResourceRuntimeArrays = DEVICE_FEATURE_STATE_DISABLED;
Features.WaveOp = DEVICE_FEATURE_STATE_DISABLED;
Features.InstanceDataStepRate = DEVICE_FEATURE_STATE_DISABLED;
Features.NativeFence = DEVICE_FEATURE_STATE_DISABLED;
Features.TileShaders = DEVICE_FEATURE_STATE_DISABLED;
Features.TransferQueueTimestampQueries = DEVICE_FEATURE_STATE_DISABLED;
Features.VariableRateShading = DEVICE_FEATURE_STATE_DISABLED;
Features.SparseResources = DEVICE_FEATURE_STATE_DISABLED;
Features.SubpassFramebufferFetch = DEVICE_FEATURE_STATE_DISABLED;
Features.TextureComponentSwizzle = DEVICE_FEATURE_STATE_DISABLED;
Features.TextureSubresourceViews = DEVICE_FEATURE_STATE_ENABLED;
Features.NativeMultiDraw = DEVICE_FEATURE_STATE_DISABLED;
Features.AsyncShaderCompilation = DEVICE_FEATURE_STATE_ENABLED;
Features.FormattedBuffers = DEVICE_FEATURE_STATE_DISABLED;

Features.TimestampQueries = CheckFeature(WGPUFeatureName_TimestampQuery);
Features.DurationQueries = Features.TimestampQueries ?
CheckFeature(WGPUFeatureName_ChromiumExperimentalTimestampQueryInsidePasses) :
DEVICE_FEATURE_STATE_DISABLED;

ASSERT_SIZEOF(DeviceFeatures, 47, "Did you add a new feature to DeviceFeatures? Please handle its status here.");

return Features;
}


GraphicsAdapterInfo GetGraphicsAdapterInfo(WGPUAdapter wgpuAdapter, WGPUDevice wgpuDevice = nullptr)
{
WGPUAdapterInfo wgpuAdapterInfo{};
if (wgpuAdapter)
Expand Down Expand Up @@ -377,68 +452,7 @@ GraphicsAdapterInfo GetGraphicsAdapterInfo(WGPUAdapter wgpuAdapter, WGPUDevice w
AdapterInfo.NumOutputs = 0;
}

auto CheckWebGPUFeature = [wgpuAdapter, wgpuDevice](WGPUFeatureName Feature) {
return ((wgpuAdapter != nullptr && wgpuAdapterHasFeature(wgpuAdapter, Feature)) ||
(wgpuDevice != nullptr && wgpuDeviceHasFeature(wgpuDevice, Feature))) ?
DEVICE_FEATURE_STATE_ENABLED :
DEVICE_FEATURE_STATE_DISABLED;
};

// Enable features
{
DeviceFeatures& Features{AdapterInfo.Features};
Features.SeparablePrograms = DEVICE_FEATURE_STATE_ENABLED;
Features.ShaderResourceQueries = DEVICE_FEATURE_STATE_ENABLED;
Features.WireframeFill = DEVICE_FEATURE_STATE_DISABLED;
Features.MultithreadedResourceCreation = DEVICE_FEATURE_STATE_DISABLED;
Features.ComputeShaders = DEVICE_FEATURE_STATE_ENABLED;
Features.GeometryShaders = DEVICE_FEATURE_STATE_DISABLED;
Features.Tessellation = DEVICE_FEATURE_STATE_DISABLED;
Features.MeshShaders = DEVICE_FEATURE_STATE_DISABLED;
Features.RayTracing = DEVICE_FEATURE_STATE_DISABLED;
Features.BindlessResources = DEVICE_FEATURE_STATE_DISABLED;
Features.OcclusionQueries = DEVICE_FEATURE_STATE_ENABLED;
Features.BinaryOcclusionQueries = DEVICE_FEATURE_STATE_DISABLED;
Features.PipelineStatisticsQueries = DEVICE_FEATURE_STATE_DISABLED;
Features.DepthBiasClamp = DEVICE_FEATURE_STATE_ENABLED;
Features.DepthClamp = CheckWebGPUFeature(WGPUFeatureName_DepthClipControl);
Features.IndependentBlend = DEVICE_FEATURE_STATE_ENABLED;
Features.DualSourceBlend = CheckWebGPUFeature(WGPUFeatureName_DualSourceBlending);
Features.MultiViewport = DEVICE_FEATURE_STATE_DISABLED;
Features.TextureCompressionBC = CheckWebGPUFeature(WGPUFeatureName_TextureCompressionBC);
Features.TextureCompressionETC2 = CheckWebGPUFeature(WGPUFeatureName_TextureCompressionETC2);
Features.VertexPipelineUAVWritesAndAtomics = DEVICE_FEATURE_STATE_ENABLED;
Features.PixelUAVWritesAndAtomics = DEVICE_FEATURE_STATE_ENABLED;
Features.TextureUAVExtendedFormats = DEVICE_FEATURE_STATE_ENABLED;
Features.ShaderFloat16 = CheckWebGPUFeature(WGPUFeatureName_ShaderF16);
Features.ResourceBuffer16BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.UniformBuffer16BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderInputOutput16 = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderInt8 = DEVICE_FEATURE_STATE_DISABLED;
Features.ResourceBuffer8BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.UniformBuffer8BitAccess = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderResourceStaticArrays = DEVICE_FEATURE_STATE_DISABLED;
Features.ShaderResourceRuntimeArrays = DEVICE_FEATURE_STATE_DISABLED;
Features.WaveOp = DEVICE_FEATURE_STATE_DISABLED;
Features.InstanceDataStepRate = DEVICE_FEATURE_STATE_DISABLED;
Features.NativeFence = DEVICE_FEATURE_STATE_DISABLED;
Features.TileShaders = DEVICE_FEATURE_STATE_DISABLED;
Features.TransferQueueTimestampQueries = DEVICE_FEATURE_STATE_DISABLED;
Features.VariableRateShading = DEVICE_FEATURE_STATE_DISABLED;
Features.SparseResources = DEVICE_FEATURE_STATE_DISABLED;
Features.SubpassFramebufferFetch = DEVICE_FEATURE_STATE_DISABLED;
Features.TextureComponentSwizzle = DEVICE_FEATURE_STATE_DISABLED;
Features.TextureSubresourceViews = DEVICE_FEATURE_STATE_ENABLED;
Features.NativeMultiDraw = DEVICE_FEATURE_STATE_DISABLED;
Features.AsyncShaderCompilation = DEVICE_FEATURE_STATE_ENABLED;
Features.FormattedBuffers = DEVICE_FEATURE_STATE_DISABLED;

Features.TimestampQueries = CheckWebGPUFeature(WGPUFeatureName_TimestampQuery);
Features.DurationQueries = Features.TimestampQueries ?
CheckWebGPUFeature(WGPUFeatureName_ChromiumExperimentalTimestampQueryInsidePasses) :
DEVICE_FEATURE_STATE_DISABLED;
}
ASSERT_SIZEOF(DeviceFeatures, 47, "Did you add a new feature to DeviceFeatures? Please handle its status here.");
AdapterInfo.Features = GetSupportedFeatures(wgpuAdapter, wgpuDevice);

WGPUSupportedLimits wgpuSupportedLimits{};
if (wgpuAdapter)
Expand All @@ -459,7 +473,7 @@ GraphicsAdapterInfo GetGraphicsAdapterInfo(WGPUAdapter wgpuAdapter, WGPUDevice w
DrawCommandInfo.MaxDrawIndirectCount = ~0u;
DrawCommandInfo.CapFlags = DRAW_COMMAND_CAP_FLAG_DRAW_INDIRECT;

if (CheckWebGPUFeature(WGPUFeatureName_IndirectFirstInstance))
if (FeatureSupported(wgpuAdapter, wgpuDevice, WGPUFeatureName_IndirectFirstInstance))
DrawCommandInfo.CapFlags |= DRAW_COMMAND_CAP_FLAG_DRAW_INDIRECT_FIRST_INSTANCE;
}

Expand Down Expand Up @@ -581,7 +595,7 @@ void EngineFactoryWebGPUImpl::CreateDeviceAndContextsWebGPU(const EngineWebGPUCr
SpecificAdapter = std::move(wgpuAdapters[0]);
}

WebGPUDeviceWrapper Device = CreateDeviceForAdapter(EngineCI, wgpuInstance.Get(), SpecificAdapter.Get());
WebGPUDeviceWrapper Device = CreateDeviceForAdapter(EngineCI.Features, wgpuInstance, SpecificAdapter);
AttachToWebGPUDevice(wgpuInstance.Detach(), SpecificAdapter.Detach(), Device.Detach(), EngineCI, ppDevice, ppImmediateContext);
}
catch (const std::runtime_error&)
Expand Down Expand Up @@ -659,12 +673,23 @@ void EngineFactoryWebGPUImpl::AttachToWebGPUDevice(void*
const GraphicsAdapterInfo AdapterInfo = GetGraphicsAdapterInfo(static_cast<WGPUAdapter>(wgpuAdapter), static_cast<WGPUDevice>(wgpuDevice));
VerifyEngineCreateInfo(EngineCI, AdapterInfo);

const DeviceFeatures EnabledFeatures = GetSupportedFeatures(nullptr, static_cast<WGPUDevice>(wgpuDevice));

SetRawAllocator(EngineCI.pRawMemAllocator);
IMemoryAllocator& RawMemAllocator = GetRawAllocator();

RenderDeviceWebGPUImpl* pRenderDeviceWebGPU{
NEW_RC_OBJ(RawMemAllocator, "RenderDeviceWebGPUImpl instance", RenderDeviceWebGPUImpl)(
RawMemAllocator, this, EngineCI, AdapterInfo, static_cast<WGPUInstance>(wgpuInstance), static_cast<WGPUAdapter>(wgpuAdapter), static_cast<WGPUDevice>(wgpuDevice))};
RenderDeviceWebGPUImpl::CreateInfo{
RawMemAllocator,
this,
EngineCI,
AdapterInfo,
EnabledFeatures,
static_cast<WGPUInstance>(wgpuInstance),
static_cast<WGPUAdapter>(wgpuAdapter),
static_cast<WGPUDevice>(wgpuDevice),
})};
pRenderDeviceWebGPU->QueryInterface(IID_RenderDevice, reinterpret_cast<IObject**>(ppDevice));

DeviceContextWebGPUImpl* pDeviceContextWebGPU{
Expand Down
Loading

0 comments on commit 951dba2

Please sign in to comment.