Graphite: Enable MultiPlanarRenderTargets feature for D3D11/12

Enable MultiPlanarRenderTargets feature for D3D11/12. As part of that
pass the correct planeSlice to D3D12_RENDER_TARGET_VIEW_DESC and
binding for the VideoViewsTests.

Bug: dawn:1337
Change-Id: I3ab3f4432a9670f162154a967345c52d31e72373
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/154820
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Saifuddin Hitawala <hitawala@chromium.org>
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
index d1efb23..337f5ae 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -196,6 +196,7 @@
     if (mDeviceInfo.supportsSharedResourceCapabilityTier2) {
         EnableFeature(Feature::DawnMultiPlanarFormats);
         EnableFeature(Feature::MultiPlanarFormatP010);
+        EnableFeature(Feature::MultiPlanarRenderTargets);
     }
     if (mDeviceInfo.supportsROV) {
         EnableFeature(Feature::PixelLocalStorageCoherent);
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index 204f704..34c0bcc 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -145,6 +145,7 @@
     EnableFeature(Feature::Norm16TextureFormats);
     EnableFeature(Feature::AdapterPropertiesMemoryHeaps);
     EnableFeature(Feature::AdapterPropertiesD3D);
+    EnableFeature(Feature::MultiPlanarRenderTargets);
 
     if (AreTimestampQueriesSupported()) {
         EnableFeature(Feature::TimestampQuery);
diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp
index 921c0fa..1847a02 100644
--- a/src/dawn/native/d3d12/TextureD3D12.cpp
+++ b/src/dawn/native/d3d12/TextureD3D12.cpp
@@ -689,7 +689,8 @@
 D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(const Format& format,
                                                         uint32_t mipLevel,
                                                         uint32_t baseSlice,
-                                                        uint32_t sliceCount) const {
+                                                        uint32_t sliceCount,
+                                                        uint32_t planeSlice) const {
     D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
     rtvDesc.Format = d3d::DXGITextureFormat(format.format);
     if (IsMultisampledTexture()) {
@@ -713,7 +714,7 @@
             rtvDesc.Texture2DArray.FirstArraySlice = baseSlice;
             rtvDesc.Texture2DArray.ArraySize = sliceCount;
             rtvDesc.Texture2DArray.MipSlice = mipLevel;
-            rtvDesc.Texture2DArray.PlaneSlice = 0;
+            rtvDesc.Texture2DArray.PlaneSlice = planeSlice;
             break;
         case wgpu::TextureDimension::e3D:
             rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
@@ -849,7 +850,8 @@
                 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc =
                     GetRTVDescriptor(GetFormat(), level, layer,
                                      GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color)
-                                         .depthOrArrayLayers);
+                                         .depthOrArrayLayers,
+                                     GetAspectIndex(range.aspects));
                 device->GetD3D12Device()->CreateRenderTargetView(GetD3D12Resource(), &rtvDesc,
                                                                  rtvHandle);
                 commandList->ClearRenderTargetView(rtvHandle, clearColorRGBA, 0, nullptr);
@@ -1126,7 +1128,7 @@
     // view's dimension.
     return ToBackend(GetTexture())
         ->GetRTVDescriptor(GetFormat(), GetBaseMipLevel(), GetBaseArrayLayer() + depthSlice,
-                           GetLayerCount());
+                           GetLayerCount(), GetAspectIndex(GetAspects()));
 }
 
 D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor(bool depthReadOnly,
diff --git a/src/dawn/native/d3d12/TextureD3D12.h b/src/dawn/native/d3d12/TextureD3D12.h
index fe3cc45..6274a30 100644
--- a/src/dawn/native/d3d12/TextureD3D12.h
+++ b/src/dawn/native/d3d12/TextureD3D12.h
@@ -81,7 +81,8 @@
     D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(const Format& format,
                                                    uint32_t mipLevel,
                                                    uint32_t baseSlice,
-                                                   uint32_t sliceCount) const;
+                                                   uint32_t sliceCount,
+                                                   uint32_t planeSlice) const;
     D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(uint32_t mipLevel,
                                                    uint32_t baseArrayLayer,
                                                    uint32_t layerCount,
diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp
index f2acfd6..8f3932a 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests.cpp
@@ -1552,6 +1552,142 @@
 
         mBackend->DestroyVideoTextureForTest(std::move(destVideoTexture));
     }
+
+    // Tests for rendering to a chroma texture view from a luma texture view, both of which created
+    // from a multiplanar video texture. The test then copies back from chroma view to plane 0
+    // texture view and compares the result same as expected texture data from first plane.
+    template <typename T>
+    void RenderFromLumaToChromaPlane() {
+        // Create plane texture initialized with data.
+        auto CreatePlaneTexWithData = [this](int planeIndex, bool hasAlpha) -> wgpu::Texture {
+            auto kSubsampleFactor = planeIndex == kYUVAChromaPlaneIndex ? 2 : 1;
+            wgpu::Extent3D size = {kYUVAImageDataWidthInTexels / kSubsampleFactor,
+                                   kYUVAImageDataHeightInTexels / kSubsampleFactor, 1};
+
+            // Create source texture with plane format
+            wgpu::TextureDescriptor planeTextureDesc;
+            planeTextureDesc.size = size;
+            planeTextureDesc.format = GetPlaneFormat(planeIndex);
+            planeTextureDesc.usage = wgpu::TextureUsage::CopyDst |
+                                     wgpu::TextureUsage::TextureBinding |
+                                     wgpu::TextureUsage::RenderAttachment;
+            wgpu::Texture planeTexture = device.CreateTexture(&planeTextureDesc);
+
+            // Copy plane (Y/UV/A) data to the plane source texture.
+            size_t bytesPerRow = kYUVAImageDataWidthInTexels * sizeof(T);
+            std::vector<T> planeSrcData = VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<T>(
+                planeIndex, bytesPerRow, kYUVAImageDataHeightInTexels / kSubsampleFactor, false,
+                hasAlpha);
+            wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(planeTexture);
+            wgpu::TextureDataLayout textureDataLayout =
+                utils::CreateTextureDataLayout(0, bytesPerRow);
+            wgpu::Queue queue = device.GetQueue();
+            queue.WriteTexture(&imageCopyTexture, planeSrcData.data(),
+                               planeSrcData.size() * sizeof(T), &textureDataLayout, &size);
+
+            return planeTexture;
+        };
+
+        const bool hasAlpha = NumPlanes(GetFormat()) > 2;
+        // Create source texture with plane 0 format i.e. R8/R16Unorm.
+        wgpu::Texture plane0Texture = CreatePlaneTexWithData(kYUVALumaPlaneIndex, hasAlpha);
+        ASSERT_NE(plane0Texture.Get(), nullptr);
+
+        // Create a video texture to be rendered into with multiplanar format.
+        auto destVideoTexture = mBackend->CreateVideoTextureForTest(
+            GetFormat(), wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
+            /*isCheckerboard*/ false,
+            /*initialized*/ true);
+        ASSERT_NE(destVideoTexture.get(), nullptr);
+        if (!destVideoTexture->CanWrapAsWGPUTexture()) {
+            mBackend->DestroyVideoTextureForTest(std::move(destVideoTexture));
+            GTEST_SKIP() << "Skipped because not supported.";
+        }
+        auto destVideoWGPUTexture = destVideoTexture->wgpuTexture;
+
+        // Create luma plane texture view from multiplanar video texture.
+        wgpu::TextureViewDescriptor lumaViewDesc;
+        lumaViewDesc.format = GetPlaneFormat(kYUVALumaPlaneIndex);
+        lumaViewDesc.aspect = GetPlaneAspect(kYUVALumaPlaneIndex);
+        wgpu::TextureView lumaTextureView = destVideoWGPUTexture.CreateView(&lumaViewDesc);
+
+        // Create chroma plane texture view from multiplanar video texture.
+        wgpu::TextureViewDescriptor chromaViewDesc;
+        chromaViewDesc.format = GetPlaneFormat(kYUVAChromaPlaneIndex);
+        chromaViewDesc.aspect = GetPlaneAspect(kYUVAChromaPlaneIndex);
+        wgpu::TextureView chromaTextureView = destVideoWGPUTexture.CreateView(&chromaViewDesc);
+
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::Sampler sampler = device.CreateSampler();
+
+        auto CreateRenderPipeline = [this](int planeIndex, wgpu::TextureView srcTextureView,
+                                           wgpu::TextureView destTextureView,
+                                           wgpu::CommandEncoder encoder,
+                                           wgpu::Sampler sampler) -> wgpu::RenderPipeline {
+            utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
+            renderPipelineDescriptor.vertex.module = GetTestVertexShaderModule();
+            renderPipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
+                @group(0) @binding(0) var sampler0 : sampler;
+                @group(0) @binding(1) var texture : texture_2d<f32>;
+
+                @fragment
+                fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f {
+                    return textureSample(texture, sampler0, texCoord);
+                })");
+            renderPipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
+            renderPipelineDescriptor.cTargets[0].format = GetPlaneFormat(planeIndex);
+            wgpu::RenderPipeline renderPipeline =
+                device.CreateRenderPipeline(&renderPipelineDescriptor);
+
+            utils::ComboRenderPassDescriptor renderPass({destTextureView});
+            wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+            pass.SetPipeline(renderPipeline);
+            pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
+                                                      {{0, sampler}, {1, srcTextureView}}));
+            pass.Draw(6);
+            pass.End();
+
+            return renderPipeline;
+        };
+
+        // Render pass operations for reading plane0Texture with data into lumaTextureView created
+        // from the multiplanar video texture.
+        wgpu::RenderPipeline renderPipeline1 = CreateRenderPipeline(
+            kYUVALumaPlaneIndex, plane0Texture.CreateView(), lumaTextureView, encoder, sampler);
+
+        // Render pass operations for reading lumaTextureView into chromaTextureView created from
+        // the multiplanar video texture.
+        wgpu::RenderPipeline renderPipeline2 = CreateRenderPipeline(
+            kYUVAChromaPlaneIndex, lumaTextureView, chromaTextureView, encoder, sampler);
+
+        // Another render pass for reading the chromaTextureView into a texture of the luma plane's
+        // format (i.e. R8/R16Unorm).
+        utils::BasicRenderPass basicRenderPass = utils::CreateBasicRenderPass(
+            device, kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels,
+            GetPlaneFormat(kYUVALumaPlaneIndex));
+        wgpu::RenderPassEncoder secondPass =
+            encoder.BeginRenderPass(&basicRenderPass.renderPassInfo);
+        secondPass.SetPipeline(renderPipeline1);
+        secondPass.SetBindGroup(0,
+                                utils::MakeBindGroup(device, renderPipeline1.GetBindGroupLayout(0),
+                                                     {{0, sampler}, {1, chromaTextureView}}));
+        secondPass.Draw(6);
+        secondPass.End();
+
+        // Submit all commands for the encoder.
+        wgpu::CommandBuffer commands = encoder.Finish();
+        queue.Submit(1, &commands);
+
+        // Compare expected data from luma values to that from basicRenderPass.
+        std::vector<T> expectedData = VideoViewsTestsBase::GetTestTextureDataWithPlaneIndex<T>(
+            kYUVALumaPlaneIndex, kYUVAImageDataWidthInTexels * sizeof(T),
+            kYUVAImageDataHeightInTexels, false, hasAlpha);
+        EXPECT_TEXTURE_EQ(expectedData.data(), basicRenderPass.color, {0, 0},
+                          {kYUVAImageDataWidthInTexels, kYUVAImageDataHeightInTexels},
+                          GetPlaneFormat(kYUVALumaPlaneIndex));
+
+        mBackend->DestroyVideoTextureForTest(std::move(destVideoTexture));
+    }
 };
 
 // Tests creating a texture with a multi-plane format.
@@ -1638,6 +1774,18 @@
     }
 }
 
+// Tests for rendering to one plane while reading from another plane.
+TEST_P(VideoViewsRenderTargetTests, RenderFromLumaToChromaPlane) {
+    if (GetFormat() == wgpu::TextureFormat::R8BG8Biplanar420Unorm ||
+        GetFormat() == wgpu::TextureFormat::R8BG8A8Triplanar420Unorm) {
+        RenderFromLumaToChromaPlane<uint8_t>();
+    } else if (GetFormat() == wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm) {
+        RenderFromLumaToChromaPlane<uint16_t>();
+    } else {
+        DAWN_UNREACHABLE();
+    }
+}
+
 class VideoViewsExtendedUsagesTests : public VideoViewsTestsBase {
   protected:
     void SetUp() override {
diff --git a/src/dawn/tests/end2end/VideoViewsTests_win.cpp b/src/dawn/tests/end2end/VideoViewsTests_win.cpp
index fb7c493..7705644 100644
--- a/src/dawn/tests/end2end/VideoViewsTests_win.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests_win.cpp
@@ -106,6 +106,20 @@
         }
     }
 
+    static UINT GetD3D11TextureBindFlags(wgpu::TextureUsage usage) {
+        UINT bindFlags = 0;
+        if (usage & wgpu::TextureUsage::TextureBinding) {
+            bindFlags |= D3D11_BIND_SHADER_RESOURCE;
+        }
+        if (usage & wgpu::TextureUsage::StorageBinding) {
+            bindFlags |= D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
+        }
+        if (usage & wgpu::TextureUsage::RenderAttachment) {
+            bindFlags |= D3D11_BIND_RENDER_TARGET;
+        }
+        return bindFlags;
+    }
+
     std::unique_ptr<VideoViewsTestBackend::PlatformTexture> CreateVideoTextureForTest(
         wgpu::TextureFormat format,
         wgpu::TextureUsage usage,
@@ -128,7 +142,7 @@
         d3dDescriptor.SampleDesc.Count = 1;
         d3dDescriptor.SampleDesc.Quality = 0;
         d3dDescriptor.Usage = D3D11_USAGE_DEFAULT;
-        d3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+        d3dDescriptor.BindFlags = GetD3D11TextureBindFlags(usage);
         d3dDescriptor.CPUAccessFlags = 0;
         d3dDescriptor.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;