Add multiplanar usages to SharedTextureMemory if supported by Device

Adjusts SharedTextureMemory to preserve CopyDst and/or RenderAttachment usages for multiplanar textures if the relevant Features are enabled.

Change-Id: Iffaee8bca3724c6558e2169efa39e8c840a2f749
Bug: chromium:1493854
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/162311
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Colin Blundell <blundell@chromium.org>
diff --git a/src/dawn/native/SharedTextureMemory.cpp b/src/dawn/native/SharedTextureMemory.cpp
index bd3be8a..c45fb13 100644
--- a/src/dawn/native/SharedTextureMemory.cpp
+++ b/src/dawn/native/SharedTextureMemory.cpp
@@ -88,10 +88,12 @@
     if (!internalFormat.supportsStorageUsage || internalFormat.IsMultiPlanar()) {
         mProperties.usage = mProperties.usage & ~wgpu::TextureUsage::StorageBinding;
     }
-    if (!internalFormat.isRenderable || internalFormat.IsMultiPlanar()) {
+    if (!internalFormat.isRenderable || (internalFormat.IsMultiPlanar() &&
+                                         !device->HasFeature(Feature::MultiPlanarRenderTargets))) {
         mProperties.usage = mProperties.usage & ~wgpu::TextureUsage::RenderAttachment;
     }
-    if (internalFormat.IsMultiPlanar()) {
+    if (internalFormat.IsMultiPlanar() &&
+        !device->HasFeature(Feature::MultiPlanarFormatExtendedUsages)) {
         mProperties.usage = mProperties.usage & ~wgpu::TextureUsage::CopyDst;
     }
 
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp b/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
index 521a36e..5d6dd99 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
@@ -44,9 +44,18 @@
     if (!SupportsFeatures(features)) {
         return {};
     }
-    if (SupportsFeatures({wgpu::FeatureName::TransientAttachments})) {
-        features.push_back(wgpu::FeatureName::TransientAttachments);
+
+    const wgpu::FeatureName kOptionalFeatures[] = {
+        wgpu::FeatureName::MultiPlanarFormatExtendedUsages,
+        wgpu::FeatureName::MultiPlanarRenderTargets,
+        wgpu::FeatureName::TransientAttachments,
+    };
+    for (auto feature : kOptionalFeatures) {
+        if (SupportsFeatures({feature})) {
+            features.push_back(feature);
+        }
     }
+
     return features;
 }
 
@@ -66,6 +75,22 @@
     return memories;
 }
 
+std::vector<wgpu::SharedTextureMemory>
+SharedTextureMemoryTestBackend::CreateSinglePlanarSharedTextureMemories(wgpu::Device& device) {
+    std::vector<wgpu::SharedTextureMemory> out;
+    for (auto& memory : CreateSharedTextureMemories(device)) {
+        wgpu::SharedTextureMemoryProperties properties;
+        memory.GetProperties(&properties);
+
+        if (utils::IsMultiPlanarFormat(properties.format)) {
+            continue;
+        }
+
+        out.push_back(std::move(memory));
+    }
+    return out;
+}
+
 std::vector<std::vector<wgpu::SharedTextureMemory>>
 SharedTextureMemoryTestBackend::CreatePerDeviceSharedTextureMemoriesFilterByUsage(
     const std::vector<wgpu::Device>& devices,
@@ -76,7 +101,17 @@
         memories[0].GetProperties(&properties);
 
         if ((properties.usage & requiredUsage) == requiredUsage) {
-            out.push_back(std::move(memories));
+            // Tests using RenderAttachment will get a TextureView from the
+            // texture. This currently doesn't work with multiplanar textures. The
+            // superficial problem is that the plane would need to be passed for
+            // multiplanar formats, and the deep problem is that the tests fail to
+            // create valid backing textures for some multiplanar formats (e.g.,
+            // on Apple), which results in a crash when accessing plane 0.
+            // TODO(crbug.com/dawn/2263): Fix this and remove this short-circuit.
+            if (!utils::IsMultiPlanarFormat(properties.format) &&
+                (requiredUsage & wgpu::TextureUsage::RenderAttachment)) {
+                out.push_back(std::move(memories));
+            }
         }
     }
     return out;
@@ -405,29 +440,31 @@
         wgpu::TextureUsage expectedUsage =
             wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding;
 
-        // Additional usages are potentially supported for single-planar
-        // formats.
-        if (!utils::IsMultiPlanarFormat(properties.format)) {
+        bool isSinglePlanar = !utils::IsMultiPlanarFormat(properties.format);
+
+        if (isSinglePlanar ||
+            device.HasFeature(wgpu::FeatureName::MultiPlanarFormatExtendedUsages)) {
             expectedUsage |= wgpu::TextureUsage::CopyDst;
+        }
 
-            // TODO(crbug.com/dawn/2262): RenderAttachment support on D3D11/D3D12 is
-            // additionally dependent on the flags passed to the underlying
-            // texture (the relevant flag is currently always passed in the test
-            // context). Add tests where the D3D texture is not created with the
-            // relevant flag.
-            if (utils::IsRenderableFormat(device, properties.format)) {
-                expectedUsage |= wgpu::TextureUsage::RenderAttachment;
-            }
+        // TODO(crbug.com/dawn/2262): RenderAttachment support on D3D11/D3D12 is
+        // additionally dependent on the flags passed to the underlying
+        // texture (the relevant flag is currently always passed in the test
+        // context). Add tests where the D3D texture is not created with the
+        // relevant flag.
+        if ((isSinglePlanar || device.HasFeature(wgpu::FeatureName::MultiPlanarRenderTargets)) &&
+            utils::IsRenderableFormat(device, properties.format)) {
+            expectedUsage |= wgpu::TextureUsage::RenderAttachment;
+        }
 
-            // TODO(crbug.com/dawn/2262): StorageBinding support on D3D11/D3D12 is
-            // additionally dependent on the flags passed to the underlying
-            // texture (the relevant flag is currently always passed in the test
-            // context). Add tests where the D3D texture is not created with the
-            // relevant flag.
-            if (utils::TextureFormatSupportsStorageTexture(properties.format,
-                                                           IsCompatibilityMode())) {
-                expectedUsage |= wgpu::TextureUsage::StorageBinding;
-            }
+        // TODO(crbug.com/dawn/2262): StorageBinding support on D3D11/D3D12 is
+        // additionally dependent on the flags passed to the underlying
+        // texture (the relevant flag is currently always passed in the test
+        // context). Add tests where the D3D texture is not created with the
+        // relevant flag.
+        if (isSinglePlanar &&
+            utils::TextureFormatSupportsStorageTexture(properties.format, IsCompatibilityMode())) {
+            expectedUsage |= wgpu::TextureUsage::StorageBinding;
         }
 
         EXPECT_EQ(properties.usage, expectedUsage) << properties.format;
@@ -706,8 +743,10 @@
 
 // Test that it is valid (does not crash) if the memory is dropped while a texture access has begun.
 TEST_P(SharedTextureMemoryTests, TextureAccessOutlivesMemory) {
+    // NOTE: UseInRenderPass()/UseInCopy() do not currently support multiplanar
+    // formats.
     for (wgpu::SharedTextureMemory memory :
-         GetParam().mBackend->CreateSharedTextureMemories(device)) {
+         GetParam().mBackend->CreateSinglePlanarSharedTextureMemories(device)) {
         wgpu::SharedTextureMemoryProperties properties;
         memory.GetProperties(&properties);
 
@@ -722,9 +761,7 @@
         // Use the texture on the GPU; it should not crash.
         if (properties.usage & wgpu::TextureUsage::RenderAttachment) {
             UseInRenderPass(device, texture);
-        } else if (properties.format != wgpu::TextureFormat::R8BG8Biplanar420Unorm &&
-                   properties.format != wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm &&
-                   properties.format != wgpu::TextureFormat::R8BG8A8Triplanar420Unorm) {
+        } else {
             DAWN_ASSERT(properties.usage & wgpu::TextureUsage::CopySrc);
             UseInCopy(device, texture);
         }
@@ -819,15 +856,24 @@
 
 // Test that if the texture is uninitialized, EndAccess writes the state out as uninitialized.
 TEST_P(SharedTextureMemoryTests, UninitializedOnEndAccess) {
+    // It is not possible to run these tests for multiplanar formats for
+    // multiple reasons:
+    // * Test basic begin+end access exports the state as uninitialized
+    // if it starts as uninitialized. Multiplanar formats must be initialized on import.
+    // * RenderAttachment gets a TextureView from the texture. This has a
+    // superficial problem and a deep problem: The superficial problem is that
+    // the plane would need to be passed for multiplanar formats, and the deep
+    // problem is that the tests fail to create valid backing textures for some multiplanar
+    // formats (e.g., on Apple), which results in a crash when accessing plane
+    // 0.
+    // TODO(crbug.com/dawn/2263): Fix this and change the below to
+    // CreateSharedTextureMemories().
     for (wgpu::SharedTextureMemory memory :
-         GetParam().mBackend->CreateSharedTextureMemories(device)) {
+         GetParam().mBackend->CreateSinglePlanarSharedTextureMemories(device)) {
         wgpu::SharedTextureMemoryProperties properties;
         memory.GetProperties(&properties);
 
-        // Test basic begin+end access exports the state as uninitialized
-        // if it starts as uninitialized. Skipped for multiplanar formats
-        // because those must be initialized on import.
-        if (!utils::IsMultiPlanarFormat(properties.format)) {
+        {
             wgpu::Texture texture = memory.CreateTexture();
 
             wgpu::SharedTextureMemoryBeginAccessDescriptor beginDesc = {};
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests.h b/src/dawn/tests/end2end/SharedTextureMemoryTests.h
index f6523cc..340e75e 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests.h
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests.h
@@ -63,6 +63,11 @@
     // device.
     std::vector<wgpu::SharedTextureMemory> CreateSharedTextureMemories(wgpu::Device& device);
 
+    // Wrapper around CreateSharedTextureMemories() that restricts the returned
+    // vector to only the single-planar instances.
+    std::vector<wgpu::SharedTextureMemory> CreateSinglePlanarSharedTextureMemories(
+        wgpu::Device& device);
+
     // Wrapper around CreatePerDeviceSharedTextureMemories that filters the memories by
     // usage to ensure they have `requiredUsage`.
     std::vector<std::vector<wgpu::SharedTextureMemory>>