From 335573116ceb0e1c84c33a2cd4c8d02e673e2697 Mon Sep 17 00:00:00 2001 From: Le Hoang Quyen Date: Thu, 20 Apr 2023 20:42:05 +0000 Subject: [PATCH] Add Device::GetSupportedSurfaceUsage() method. This method will return supported usage flags that can be used to create a swap chain. Bug: dawn:1760 Change-Id: I7699c2c4ef7142c6bd06e72239d6e4f9112f15a3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127440 Kokoro: Kokoro Commit-Queue: Quyen Le Reviewed-by: Austin Eng --- dawn.json | 10 +- dawn_wire.json | 1 + docs/dawn/features/surface_capabilities.md | 20 +++ src/dawn/native/Device.cpp | 21 +++ src/dawn/native/Device.h | 7 + src/dawn/native/Features.cpp | 9 + src/dawn/native/Features.h | 1 + src/dawn/native/SwapChain.cpp | 17 +- src/dawn/native/d3d/DeviceD3D.cpp | 7 + src/dawn/native/d3d/DeviceD3D.h | 3 + src/dawn/native/d3d11/AdapterD3D11.cpp | 1 + src/dawn/native/d3d12/AdapterD3D12.cpp | 1 + src/dawn/native/metal/BackendMTL.mm | 1 + src/dawn/native/metal/DeviceMTL.h | 3 + src/dawn/native/metal/DeviceMTL.mm | 7 + src/dawn/native/null/DeviceNull.cpp | 5 + src/dawn/native/null/DeviceNull.h | 3 + src/dawn/native/opengl/DeviceGL.cpp | 7 + src/dawn/native/opengl/DeviceGL.h | 3 + src/dawn/native/vulkan/AdapterVk.cpp | 2 + src/dawn/native/vulkan/DeviceVk.cpp | 5 + src/dawn/native/vulkan/DeviceVk.h | 3 + src/dawn/native/vulkan/SwapChainVk.cpp | 29 +++- src/dawn/native/vulkan/SwapChainVk.h | 4 + src/dawn/tests/end2end/SwapChainTests.cpp | 162 +++++++++++++++++- .../tests/unittests/native/mocks/DeviceMock.h | 5 + src/dawn/wire/SupportedFeatures.cpp | 1 + src/dawn/wire/client/Device.cpp | 6 + src/dawn/wire/client/Device.h | 1 + 29 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 docs/dawn/features/surface_capabilities.md diff --git a/dawn.json b/dawn.json index d0dd885812..c81698367e 100644 --- a/dawn.json +++ b/dawn.json @@ -1251,6 +1251,13 @@ "args": [ {"name": "descriptor", "type": "texture descriptor", "annotation": "const*"} ] + }, + { + "name": "get supported surface usage", + "returns": "texture usage", + "args": [ + {"name": "surface", "type": "surface"} + ] } ] }, @@ -1467,7 +1474,8 @@ {"value": 1004, "name": "dawn native", "tags": ["dawn", "native"]}, {"value": 1005, "name": "chromium experimental dp4a", "tags": ["dawn"]}, {"value": 1006, "name": "timestamp query inside passes", "tags": ["dawn"]}, - {"value": 1007, "name": "implicit device synchronization", "tags": ["dawn", "native"]} + {"value": 1007, "name": "implicit device synchronization", "tags": ["dawn", "native"]}, + {"value": 1008, "name": "surface capabilities", "tags": ["dawn", "native"]} ] }, "filter mode": { diff --git a/dawn_wire.json b/dawn_wire.json index d8c57e97eb..96e284f915 100644 --- a/dawn_wire.json +++ b/dawn_wire.json @@ -228,6 +228,7 @@ "DeviceCreateErrorTexture", "DeviceGetAdapter", "DeviceGetQueue", + "DeviceGetSupportedSurfaceUsage", "DeviceInjectError" ], "client_special_objects": [ diff --git a/docs/dawn/features/surface_capabilities.md b/docs/dawn/features/surface_capabilities.md new file mode 100644 index 0000000000..e5c843540f --- /dev/null +++ b/docs/dawn/features/surface_capabilities.md @@ -0,0 +1,20 @@ +# Surface Capabilities + +The `surface-capabilities` feature allows querying a surface's capabilities and creating a swap chain with additional usage flags. + +Additional functionality: + - Adds `wgpu::Device::GetSupportedSurfaceUsage(wgpu::Surface)` method for querying the surface's supported usage flags. One or the combination of these flags can be used to create a swap chain. + +Example Usage: +``` +wgpu::TextureUsage supportedUsage = device.GetSupportedSurfaceUsage(surface); + +wgpu::SwapChainDescriptor desc = {}; +// set usage flags. +desc.usage = supportedUsage; + +device.CreateSwapChain(surface, &desc); +``` + +Notes: + - If this feature is not enabled, only `wgpu::TextureUsage::RenderAttachment` flag is allowed to be used in `wgpu::SwapChainDescriptor::usage`. diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp index bb1cdbbe19..33bc5e2e9d 100644 --- a/src/dawn/native/Device.cpp +++ b/src/dawn/native/Device.cpp @@ -1242,6 +1242,15 @@ TextureBase* DeviceBase::APICreateTexture(const TextureDescriptor* descriptor) { return result.Detach(); } +wgpu::TextureUsage DeviceBase::APIGetSupportedSurfaceUsage(Surface* surface) { + wgpu::TextureUsage result; + if (ConsumedError(GetSupportedSurfaceUsage(surface), &result, + "calling %s.GetSupportedSurfaceUsage().", this)) { + return wgpu::TextureUsage::None; + } + return result; +} + // For Dawn Wire BufferBase* DeviceBase::APICreateErrorBuffer(const BufferDescriptor* desc) { @@ -1814,6 +1823,18 @@ ResultOrError> DeviceBase::CreateTextureView( return CreateTextureViewImpl(texture, &desc); } +ResultOrError DeviceBase::GetSupportedSurfaceUsage( + const Surface* surface) const { + DAWN_TRY(ValidateIsAlive()); + + if (IsValidationEnabled()) { + DAWN_INVALID_IF(!HasFeature(Feature::SurfaceCapabilities), "%s is not enabled.", + wgpu::FeatureName::SurfaceCapabilities); + } + + return GetSupportedSurfaceUsageImpl(surface); +} + // Other implementation details DynamicUploader* DeviceBase::GetDynamicUploader() const { diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h index c3ff510e38..b9e8b3624a 100644 --- a/src/dawn/native/Device.h +++ b/src/dawn/native/Device.h @@ -261,6 +261,8 @@ class DeviceBase : public RefCountedWithExternalCount { ResultOrError> CreateTextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); + ResultOrError GetSupportedSurfaceUsage(const Surface* surface) const; + // Implementation of API object creation methods. DO NOT use them in a reentrant manner. BindGroupBase* APICreateBindGroup(const BindGroupDescriptor* descriptor); BindGroupLayoutBase* APICreateBindGroupLayout(const BindGroupLayoutDescriptor* descriptor); @@ -284,6 +286,8 @@ class DeviceBase : public RefCountedWithExternalCount { SwapChainBase* APICreateSwapChain(Surface* surface, const SwapChainDescriptor* descriptor); TextureBase* APICreateTexture(const TextureDescriptor* descriptor); + wgpu::TextureUsage APIGetSupportedSurfaceUsage(Surface* surface); + InternalPipelineStore* GetInternalPipelineStore(); // For Dawn Wire @@ -492,6 +496,9 @@ class DeviceBase : public RefCountedWithExternalCount { const RenderPipelineDescriptor* descriptor) = 0; virtual void SetLabelImpl(); + virtual ResultOrError GetSupportedSurfaceUsageImpl( + const Surface* surface) const = 0; + virtual MaybeError TickImpl() = 0; void FlushCallbackTaskQueue(); diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp index 3d6cdce4bd..b097e1617b 100644 --- a/src/dawn/native/Features.cpp +++ b/src/dawn/native/Features.cpp @@ -102,6 +102,11 @@ static constexpr FeatureEnumAndInfoList kFeatureNameAndInfoList = {{ "Public API methods (except encoding) will have implicit device synchronization. So they " "will be safe to be used on multiple threads.", "https://bugs.chromium.org/p/dawn/issues/detail?id=1662", FeatureInfo::FeatureState::Stable}}, + {Feature::SurfaceCapabilities, + {"surface-capabilities", + "Support querying Surface's capabilities such as supported usage flags. This feature also " + "enables swap chain to be created with usage other than RenderAttachment.", + "https://bugs.chromium.org/p/dawn/issues/detail?id=1760", FeatureInfo::FeatureState::Stable}}, }}; Feature FromAPIFeature(wgpu::FeatureName feature) { @@ -146,6 +151,8 @@ Feature FromAPIFeature(wgpu::FeatureName feature) { return Feature::BGRA8UnormStorage; case wgpu::FeatureName::ImplicitDeviceSynchronization: return Feature::ImplicitDeviceSynchronization; + case wgpu::FeatureName::SurfaceCapabilities: + return Feature::SurfaceCapabilities; } return Feature::InvalidEnum; } @@ -186,6 +193,8 @@ wgpu::FeatureName ToAPIFeature(Feature feature) { return wgpu::FeatureName::BGRA8UnormStorage; case Feature::ImplicitDeviceSynchronization: return wgpu::FeatureName::ImplicitDeviceSynchronization; + case Feature::SurfaceCapabilities: + return wgpu::FeatureName::SurfaceCapabilities; case Feature::EnumCount: break; diff --git a/src/dawn/native/Features.h b/src/dawn/native/Features.h index 44d1e7ff23..0453090ca0 100644 --- a/src/dawn/native/Features.h +++ b/src/dawn/native/Features.h @@ -46,6 +46,7 @@ enum class Feature { MultiPlanarFormats, DawnNative, ImplicitDeviceSynchronization, + SurfaceCapabilities, EnumCount, InvalidEnum = EnumCount, diff --git a/src/dawn/native/SwapChain.cpp b/src/dawn/native/SwapChain.cpp index 06dd0b15c7..97590acfb8 100644 --- a/src/dawn/native/SwapChain.cpp +++ b/src/dawn/native/SwapChain.cpp @@ -57,9 +57,20 @@ MaybeError ValidateSwapChainDescriptor(const DeviceBase* device, "Format (%s) is not %s, which is (currently) the only accepted format.", descriptor->format, kRequireSwapChainFormat); - DAWN_INVALID_IF(descriptor->usage != wgpu::TextureUsage::RenderAttachment, - "Usage (%s) is not %s, which is (currently) the only accepted usage.", - descriptor->usage, wgpu::TextureUsage::RenderAttachment); + if (device->HasFeature(Feature::SurfaceCapabilities)) { + wgpu::TextureUsage validUsage; + DAWN_TRY_ASSIGN(validUsage, device->GetSupportedSurfaceUsage(surface)); + DAWN_INVALID_IF( + (descriptor->usage | validUsage) != validUsage, + "Usage (%s) is not supported, %s are (currently) the only accepted usage flags.", + descriptor->usage, validUsage); + } else { + DAWN_INVALID_IF(descriptor->usage != wgpu::TextureUsage::RenderAttachment, + "Usage (%s) is not %s, which is (currently) the only accepted usage. Other " + "usage flags require enabling %s", + descriptor->usage, wgpu::TextureUsage::RenderAttachment, + wgpu::FeatureName::SurfaceCapabilities); + } DAWN_INVALID_IF(descriptor->width == 0 || descriptor->height == 0, "Swap Chain size (width: %u, height: %u) is empty.", descriptor->width, diff --git a/src/dawn/native/d3d/DeviceD3D.cpp b/src/dawn/native/d3d/DeviceD3D.cpp index cad63b7537..1dc884f187 100644 --- a/src/dawn/native/d3d/DeviceD3D.cpp +++ b/src/dawn/native/d3d/DeviceD3D.cpp @@ -27,6 +27,13 @@ Device::Device(AdapterBase* adapter, Device::~Device() = default; +ResultOrError Device::GetSupportedSurfaceUsageImpl( + const Surface* surface) const { + wgpu::TextureUsage usages = + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding; + return usages; +} + const PlatformFunctions* Device::GetFunctions() const { return ToBackend(GetAdapter())->GetBackend()->GetFunctions(); } diff --git a/src/dawn/native/d3d/DeviceD3D.h b/src/dawn/native/d3d/DeviceD3D.h index 82bfc5c259..60b791e4c6 100644 --- a/src/dawn/native/d3d/DeviceD3D.h +++ b/src/dawn/native/d3d/DeviceD3D.h @@ -30,6 +30,9 @@ class Device : public DeviceBase { const TogglesState& deviceToggles); ~Device() override; + ResultOrError GetSupportedSurfaceUsageImpl( + const Surface* surface) const override; + const PlatformFunctions* GetFunctions() const; ComPtr GetFactory() const; diff --git a/src/dawn/native/d3d11/AdapterD3D11.cpp b/src/dawn/native/d3d11/AdapterD3D11.cpp index 86c2731342..f3dd13495a 100644 --- a/src/dawn/native/d3d11/AdapterD3D11.cpp +++ b/src/dawn/native/d3d11/AdapterD3D11.cpp @@ -84,6 +84,7 @@ MaybeError Adapter::InitializeImpl() { void Adapter::InitializeSupportedFeaturesImpl() { EnableFeature(Feature::TextureCompressionBC); + EnableFeature(Feature::SurfaceCapabilities); } MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { diff --git a/src/dawn/native/d3d12/AdapterD3D12.cpp b/src/dawn/native/d3d12/AdapterD3D12.cpp index 497fa2052e..4313f5683f 100644 --- a/src/dawn/native/d3d12/AdapterD3D12.cpp +++ b/src/dawn/native/d3d12/AdapterD3D12.cpp @@ -121,6 +121,7 @@ void Adapter::InitializeSupportedFeaturesImpl() { EnableFeature(Feature::IndirectFirstInstance); EnableFeature(Feature::RG11B10UfloatRenderable); EnableFeature(Feature::DepthClipControl); + EnableFeature(Feature::SurfaceCapabilities); if (AreTimestampQueriesSupported()) { EnableFeature(Feature::TimestampQuery); diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm index bc4fd4ad2b..25ac48e04b 100644 --- a/src/dawn/native/metal/BackendMTL.mm +++ b/src/dawn/native/metal/BackendMTL.mm @@ -517,6 +517,7 @@ class Adapter : public AdapterBase { EnableFeature(Feature::ShaderF16); EnableFeature(Feature::RG11B10UfloatRenderable); EnableFeature(Feature::BGRA8UnormStorage); + EnableFeature(Feature::SurfaceCapabilities); } void InitializeVendorArchitectureImpl() override { diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h index 59a8c0f7c4..55385db3f1 100644 --- a/src/dawn/native/metal/DeviceMTL.h +++ b/src/dawn/native/metal/DeviceMTL.h @@ -130,6 +130,9 @@ class Device final : public DeviceBase { WGPUCreateRenderPipelineAsyncCallback callback, void* userdata) override; + ResultOrError GetSupportedSurfaceUsageImpl( + const Surface* surface) const override; + void DestroyImpl() override; MaybeError WaitForIdleForDestruction() override; bool HasPendingCommands() const override; diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm index a572991790..fdcf4d2c6a 100644 --- a/src/dawn/native/metal/DeviceMTL.mm +++ b/src/dawn/native/metal/DeviceMTL.mm @@ -240,6 +240,13 @@ void Device::InitializeRenderPipelineAsyncImpl(Ref renderPip RenderPipeline::InitializeAsync(std::move(renderPipeline), callback, userdata); } +ResultOrError Device::GetSupportedSurfaceUsageImpl( + const Surface* surface) const { + wgpu::TextureUsage usages = + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding; + return usages; +} + ResultOrError Device::CheckAndUpdateCompletedSerials() { uint64_t frontendCompletedSerial{GetCompletedCommandSerial()}; // sometimes we increase the serials, in which case the completed serial in diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp index 2cd64d820a..0bf820edf2 100644 --- a/src/dawn/native/null/DeviceNull.cpp +++ b/src/dawn/native/null/DeviceNull.cpp @@ -188,6 +188,11 @@ ResultOrError> Device::CreateTextureViewImpl( return AcquireRef(new TextureView(texture, descriptor)); } +ResultOrError Device::GetSupportedSurfaceUsageImpl( + const Surface* surface) const { + return wgpu::TextureUsage::RenderAttachment; +} + void Device::DestroyImpl() { ASSERT(GetState() == State::Disconnected); diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h index 8b93137498..927c458b7a 100644 --- a/src/dawn/native/null/DeviceNull.h +++ b/src/dawn/native/null/DeviceNull.h @@ -155,6 +155,9 @@ class Device final : public DeviceBase { TextureBase* texture, const TextureViewDescriptor* descriptor) override; + ResultOrError GetSupportedSurfaceUsageImpl( + const Surface* surface) const override; + ResultOrError CheckAndUpdateCompletedSerials() override; void DestroyImpl() override; diff --git a/src/dawn/native/opengl/DeviceGL.cpp b/src/dawn/native/opengl/DeviceGL.cpp index 8deabe1615..45346a6866 100644 --- a/src/dawn/native/opengl/DeviceGL.cpp +++ b/src/dawn/native/opengl/DeviceGL.cpp @@ -253,6 +253,13 @@ ResultOrError> Device::CreateTextureViewImpl( return AcquireRef(new TextureView(texture, descriptor)); } +ResultOrError Device::GetSupportedSurfaceUsageImpl( + const Surface* surface) const { + wgpu::TextureUsage usages = + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding; + return usages; +} + void Device::SubmitFenceSync() { if (!mHasPendingCommands) { return; diff --git a/src/dawn/native/opengl/DeviceGL.h b/src/dawn/native/opengl/DeviceGL.h index fd656dbc02..c020877bc5 100644 --- a/src/dawn/native/opengl/DeviceGL.h +++ b/src/dawn/native/opengl/DeviceGL.h @@ -125,6 +125,9 @@ class Device final : public DeviceBase { Ref CreateUninitializedRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) override; + ResultOrError GetSupportedSurfaceUsageImpl( + const Surface* surface) const override; + GLenum GetBGRAInternalFormat() const; ResultOrError CheckAndUpdateCompletedSerials() override; void DestroyImpl() override; diff --git a/src/dawn/native/vulkan/AdapterVk.cpp b/src/dawn/native/vulkan/AdapterVk.cpp index af227aff18..df4edd3745 100644 --- a/src/dawn/native/vulkan/AdapterVk.cpp +++ b/src/dawn/native/vulkan/AdapterVk.cpp @@ -263,6 +263,8 @@ void Adapter::InitializeSupportedFeaturesImpl() { // features. EnableFeature(Feature::MultiPlanarFormats); #endif // DAWN_PLATFORM_IS(ANDROID) || DAWN_PLATFORM_IS(CHROMEOS) + + EnableFeature(Feature::SurfaceCapabilities); } MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp index 2fcdfa71da..e3f0f3f95f 100644 --- a/src/dawn/native/vulkan/DeviceVk.cpp +++ b/src/dawn/native/vulkan/DeviceVk.cpp @@ -217,6 +217,11 @@ void Device::InitializeRenderPipelineAsyncImpl(Ref renderPip RenderPipeline::InitializeAsync(std::move(renderPipeline), callback, userdata); } +ResultOrError Device::GetSupportedSurfaceUsageImpl( + const Surface* surface) const { + return SwapChain::GetSupportedSurfaceUsage(this, surface); +} + MaybeError Device::TickImpl() { RecycleCompletedCommands(); diff --git a/src/dawn/native/vulkan/DeviceVk.h b/src/dawn/native/vulkan/DeviceVk.h index 30aef5c088..63b8abc203 100644 --- a/src/dawn/native/vulkan/DeviceVk.h +++ b/src/dawn/native/vulkan/DeviceVk.h @@ -155,6 +155,9 @@ class Device final : public DeviceBase { WGPUCreateRenderPipelineAsyncCallback callback, void* userdata) override; + ResultOrError GetSupportedSurfaceUsageImpl( + const Surface* surface) const override; + ResultOrError CreateDevice(VkPhysicalDevice physicalDevice); void GatherQueueFromDevice(); diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp index fe60fb0495..4944b52496 100644 --- a/src/dawn/native/vulkan/SwapChainVk.cpp +++ b/src/dawn/native/vulkan/SwapChainVk.cpp @@ -36,7 +36,7 @@ namespace dawn::native::vulkan { namespace { -ResultOrError CreateVulkanSurface(Adapter* adapter, Surface* surface) { +ResultOrError CreateVulkanSurface(const Adapter* adapter, const Surface* surface) { const VulkanGlobalInfo& info = adapter->GetVulkanInstance()->GetGlobalInfo(); const VulkanFunctions& fn = adapter->GetVulkanInstance()->GetFunctions(); VkInstance instance = adapter->GetVulkanInstance()->GetVkInstance(); @@ -205,6 +205,33 @@ uint32_t MinImageCountForPresentMode(VkPresentModeKHR mode) { } // anonymous namespace +// static +ResultOrError SwapChain::GetSupportedSurfaceUsage(const Device* device, + const Surface* surface) { + Adapter* adapter = ToBackend(device->GetAdapter()); + const VulkanFunctions& fn = adapter->GetVulkanInstance()->GetFunctions(); + VkInstance instanceVk = adapter->GetVulkanInstance()->GetVkInstance(); + VkPhysicalDevice physicalDeviceVk = adapter->GetPhysicalDevice(); + + VkSurfaceKHR surfaceVk; + VkSurfaceCapabilitiesKHR surfaceCapsVk; + DAWN_TRY_ASSIGN(surfaceVk, CreateVulkanSurface(adapter, surface)); + + DAWN_TRY(CheckVkSuccess( + fn.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDeviceVk, surfaceVk, &surfaceCapsVk), + "GetPhysicalDeviceSurfaceCapabilitiesKHR")); + + wgpu::TextureUsage supportedUsages = wgpu::TextureUsage::RenderAttachment; + + if (surfaceCapsVk.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) { + supportedUsages |= wgpu::TextureUsage::TextureBinding; + } + + fn.DestroySurfaceKHR(instanceVk, surfaceVk, nullptr); + + return supportedUsages; +} + // static ResultOrError> SwapChain::Create(Device* device, Surface* surface, diff --git a/src/dawn/native/vulkan/SwapChainVk.h b/src/dawn/native/vulkan/SwapChainVk.h index 3f5e22a243..edd5cee6b5 100644 --- a/src/dawn/native/vulkan/SwapChainVk.h +++ b/src/dawn/native/vulkan/SwapChainVk.h @@ -33,6 +33,10 @@ class SwapChain : public SwapChainBase { Surface* surface, SwapChainBase* previousSwapChain, const SwapChainDescriptor* descriptor); + + static ResultOrError GetSupportedSurfaceUsage(const Device* device, + const Surface* surface); + ~SwapChain() override; private: diff --git a/src/dawn/tests/end2end/SwapChainTests.cpp b/src/dawn/tests/end2end/SwapChainTests.cpp index b5dab3151b..d704de9ce6 100644 --- a/src/dawn/tests/end2end/SwapChainTests.cpp +++ b/src/dawn/tests/end2end/SwapChainTests.cpp @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "dawn/tests/DawnTest.h" +#include #include "dawn/common/Constants.h" #include "dawn/common/Log.h" +#include "dawn/tests/DawnTest.h" +#include "dawn/utils/ComboRenderPipelineDescriptor.h" #include "dawn/utils/WGPUHelpers.h" #include "webgpu/webgpu_glfw.h" @@ -235,4 +237,162 @@ TEST_P(SwapChainTests, SwitchingDevice) { } } +// Test that calling Device.GetSupportedSurfaceUsage() will throw an error because +// SurfaceCapabilities is not enabled. +TEST_P(SwapChainTests, ErrorGetSurfaceSupportedUsage) { + DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation")); + EXPECT_FALSE(device.HasFeature(wgpu::FeatureName::SurfaceCapabilities)); + + ASSERT_DEVICE_ERROR_MSG( + { + auto usageFlags = device.GetSupportedSurfaceUsage(surface); + EXPECT_EQ(usageFlags, wgpu::TextureUsage::None); + }, + testing::HasSubstr("FeatureName::SurfaceCapabilities is not enabled")); +} + +// Test that creating swapchain with TextureBinding usage without enabling SurfaceCapabilities +// feature should fail. +TEST_P(SwapChainTests, ErrorCreateWithTextureBindingUsage) { + DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation")); + EXPECT_FALSE(device.HasFeature(wgpu::FeatureName::SurfaceCapabilities)); + + auto desc = baseDescriptor; + desc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment; + + ASSERT_DEVICE_ERROR_MSG( + { auto swapchain = device.CreateSwapChain(surface, &desc); }, + testing::HasSubstr("require enabling FeatureName::SurfaceCapabilities")); +} + +class SwapChainWithAdditionalUsageTests : public SwapChainTests { + protected: + std::vector GetRequiredFeatures() override { + std::vector features; + if (!UsesWire() && SupportsFeatures({wgpu::FeatureName::SurfaceCapabilities})) { + features.push_back(wgpu::FeatureName::SurfaceCapabilities); + } + return features; + } + + void SetUp() override { + SwapChainTests::SetUp(); + + // If parent class skipped the test, we should skip as well. + if (surface == nullptr) { + GTEST_SKIP(); + } + + DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::SurfaceCapabilities})); + } + + void SampleTexture(wgpu::TextureView view, + uint32_t width, + uint32_t height, + utils::RGBA8 expectedColor) { + wgpu::TextureDescriptor texDescriptor; + texDescriptor.size = {width, height, 1}; + texDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + texDescriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::CopyDst; + texDescriptor.mipLevelCount = 1; + texDescriptor.sampleCount = 1; + + wgpu::Texture dstTexture = device.CreateTexture(&texDescriptor); + wgpu::TextureView dstView = dstTexture.CreateView(); + + // Create a render pipeline to blit |view| into |dstView|. + utils::ComboRenderPipelineDescriptor pipelineDesc; + pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"( + @vertex + fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { + var pos = array( + vec2f(-1.0, -1.0), + vec2f(-1.0, 1.0), + vec2f( 1.0, -1.0), + vec2f(-1.0, 1.0), + vec2f( 1.0, -1.0), + vec2f( 1.0, 1.0)); + return vec4f(pos[VertexIndex], 0.0, 1.0); + } + )"); + pipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var texture : texture_2d; + + @fragment + fn main(@builtin(position) coord: vec4f) -> @location(0) vec4f { + return textureLoad(texture, vec2i(coord.xy), 0); + } + )"); + pipelineDesc.cTargets[0].format = texDescriptor.format; + + // Submit a render pass to perform the blit from |view| to |dstView|. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, view}}); + + utils::ComboRenderPassDescriptor renderPassInfo({dstView}); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.End(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_TEXTURE_EQ(expectedColor, dstTexture, {0, 0}); + EXPECT_TEXTURE_EQ(expectedColor, dstTexture, {width - 1, height - 1}); + } +}; + +TEST_P(SwapChainWithAdditionalUsageTests, GetSurfaceSupportedUsage) { + auto usageFlags = device.GetSupportedSurfaceUsage(surface); + EXPECT_NE(usageFlags, wgpu::TextureUsage::None); +} + +// Test that sampling from swapchain is supported. +TEST_P(SwapChainWithAdditionalUsageTests, SamplingFromSwapChain) { + // Skip all tests if readable surface doesn't support texture binding + DAWN_TEST_UNSUPPORTED_IF( + (device.GetSupportedSurfaceUsage(surface) & wgpu::TextureUsage::TextureBinding) == 0); + + auto desc = baseDescriptor; + desc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment; + + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &desc); + ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0}); + + SampleTexture(swapchain.GetCurrentTextureView(), baseDescriptor.width, baseDescriptor.height, + utils::RGBA8::kRed); + + swapchain.Present(); +} + +// Test that including unsupported usage flag will result in error. +TEST_P(SwapChainWithAdditionalUsageTests, ErrorIncludeUnsupportedUsage) { + DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation")); + + auto supportedUsage = device.GetSupportedSurfaceUsage(surface); + + // Assuming StorageBinding is not supported. + DAWN_TEST_UNSUPPORTED_IF((supportedUsage & wgpu::TextureUsage::StorageBinding) != 0); + + auto desc = baseDescriptor; + desc.usage = supportedUsage | wgpu::TextureUsage::StorageBinding; + + ASSERT_DEVICE_ERROR_MSG({ auto swapchain = device.CreateSwapChain(surface, &desc); }, + testing::HasSubstr("is not supported")); +} + DAWN_INSTANTIATE_TEST(SwapChainTests, MetalBackend(), VulkanBackend()); +DAWN_INSTANTIATE_TEST(SwapChainWithAdditionalUsageTests, + D3D12Backend(), + MetalBackend(), + NullBackend(), + VulkanBackend()); diff --git a/src/dawn/tests/unittests/native/mocks/DeviceMock.h b/src/dawn/tests/unittests/native/mocks/DeviceMock.h index d852f6b1d1..76e9475b8c 100644 --- a/src/dawn/tests/unittests/native/mocks/DeviceMock.h +++ b/src/dawn/tests/unittests/native/mocks/DeviceMock.h @@ -121,6 +121,11 @@ class DeviceMock : public DeviceBase { (TextureBase*, const TextureViewDescriptor*), (override)); + MOCK_METHOD(ResultOrError, + GetSupportedSurfaceUsageImpl, + (const Surface*), + (const, override)); + MOCK_METHOD(MaybeError, TickImpl, (), (override)); MOCK_METHOD(ResultOrError, CheckAndUpdateCompletedSerials, (), (override)); diff --git a/src/dawn/wire/SupportedFeatures.cpp b/src/dawn/wire/SupportedFeatures.cpp index c4d02c5f14..3bad7c624b 100644 --- a/src/dawn/wire/SupportedFeatures.cpp +++ b/src/dawn/wire/SupportedFeatures.cpp @@ -25,6 +25,7 @@ bool IsFeatureSupported(WGPUFeatureName feature) { case WGPUFeatureName_DawnNative: case WGPUFeatureName_DawnShaderFloat16: // Deprecated case WGPUFeatureName_ImplicitDeviceSynchronization: + case WGPUFeatureName_SurfaceCapabilities: return false; case WGPUFeatureName_Depth32FloatStencil8: case WGPUFeatureName_TimestampQuery: diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp index f63875f8af..91fd99cbdc 100644 --- a/src/dawn/wire/client/Device.cpp +++ b/src/dawn/wire/client/Device.cpp @@ -221,6 +221,12 @@ WGPUAdapter Device::GetAdapter() { return nullptr; } +WGPUTextureUsage Device::GetSupportedSurfaceUsage(WGPUSurface) { + // Not implemented in the wire. + UNREACHABLE(); + return WGPUTextureUsage_RenderAttachment; +} + WGPUQueue Device::GetQueue() { // The queue is lazily created because if a Device is created by // Reserve/Inject, we cannot send the GetQueue message until diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h index 9ca7b318a2..7a7a176786 100644 --- a/src/dawn/wire/client/Device.h +++ b/src/dawn/wire/client/Device.h @@ -70,6 +70,7 @@ class Device final : public ObjectBase { void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount); WGPUAdapter GetAdapter(); // Not implemented in the wire. + WGPUTextureUsage GetSupportedSurfaceUsage(WGPUSurface); // Not implemented in the wire. WGPUQueue GetQueue(); void CancelCallbacksForDisconnect() override;