Support multisampled rendering on D3D12

This patch adds the basic support of multisampled rendering on D3D12
backends and enables the related end2end test on D3D12 backends.

D3D12 render pass is not used in this patch. We plan to make use of it
in the future.

BUG=dawn:56
TEST=dawn_end2end_tests

Change-Id: I63759431654ec0abe9d21157f679d4c971cd92a8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/6200
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 9509049..25437cd 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -50,7 +50,7 @@
             D3D12_TEXTURE_COPY_LOCATION copyLocation;
             copyLocation.pResource = texture.GetD3D12Resource();
             copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
-            copyLocation.SubresourceIndex = texture.GetNumMipLevels() * slice + level;
+            copyLocation.SubresourceIndex = texture.GetSubresourceIndex(level, slice);
 
             return copyLocation;
         }
@@ -346,6 +346,37 @@
             }
         }
 
+        void ResolveMultisampledRenderPass(ComPtr<ID3D12GraphicsCommandList> commandList,
+                                           BeginRenderPassCmd* renderPass) {
+            ASSERT(renderPass != nullptr);
+
+            for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
+                TextureViewBase* resolveTarget =
+                    renderPass->colorAttachments[i].resolveTarget.Get();
+                if (resolveTarget == nullptr) {
+                    continue;
+                }
+
+                Texture* colorTexture =
+                    ToBackend(renderPass->colorAttachments[i].view->GetTexture());
+                Texture* resolveTexture = ToBackend(resolveTarget->GetTexture());
+
+                // Transition the usages of the color attachment and resolve target.
+                colorTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
+                resolveTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_DEST);
+
+                // Do MSAA resolve with ResolveSubResource().
+                ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource();
+                ID3D12Resource* resolveTextureHandle = resolveTexture->GetD3D12Resource();
+                const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex(
+                    resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer());
+                constexpr uint32_t kColorTextureSubresourceIndex = 0;
+                commandList->ResolveSubresource(
+                    resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle,
+                    kColorTextureSubresourceIndex, colorTexture->GetD3D12Format());
+            }
+        }
+
     }  // anonymous namespace
 
     CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder)
@@ -734,6 +765,12 @@
             switch (type) {
                 case Command::EndRenderPass: {
                     mCommands.NextCommand<EndRenderPassCmd>();
+
+                    // TODO(brandon1.jones@intel.com): avoid calling this function and enable MSAA
+                    // resolve in D3D12 render pass on the platforms that support this feature.
+                    if (renderPass->sampleCount > 1) {
+                        ResolveMultisampledRenderPass(commandList, renderPass);
+                    }
                     return;
                 } break;
 
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
index 0b0aa06..2cc719b 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
@@ -351,7 +351,7 @@
         descriptorD3D12.RasterizerState.SlopeScaledDepthBias =
             D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
         descriptorD3D12.RasterizerState.DepthClipEnable = TRUE;
-        descriptorD3D12.RasterizerState.MultisampleEnable = FALSE;
+        descriptorD3D12.RasterizerState.MultisampleEnable = (GetSampleCount() > 1) ? TRUE : FALSE;
         descriptorD3D12.RasterizerState.AntialiasedLineEnable = FALSE;
         descriptorD3D12.RasterizerState.ForcedSampleCount = 0;
         descriptorD3D12.RasterizerState.ConservativeRaster =
@@ -376,7 +376,8 @@
 
         descriptorD3D12.SampleMask = UINT_MAX;
         descriptorD3D12.PrimitiveTopologyType = D3D12PrimitiveTopologyType(GetPrimitiveTopology());
-        descriptorD3D12.SampleDesc.Count = 1;
+        descriptorD3D12.SampleDesc.Count = GetSampleCount();
+        descriptorD3D12.SampleDesc.Quality = 0;
 
         ASSERT_SUCCESS(device->GetD3D12Device()->CreateGraphicsPipelineState(
             &descriptorD3D12, IID_PPV_ARGS(&mPipelineState)));
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 63a0533..5e9a63b 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -54,13 +54,19 @@
         }
 
         D3D12_RESOURCE_FLAGS D3D12ResourceFlags(dawn::TextureUsageBit usage,
-                                                dawn::TextureFormat format) {
+                                                dawn::TextureFormat format,
+                                                bool isMultisampledTexture) {
             D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
 
             if (usage & dawn::TextureUsageBit::Storage) {
                 flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
             }
-            if (usage & dawn::TextureUsageBit::OutputAttachment) {
+
+            // A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or
+            // D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags.
+            // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource
+            // _desc
+            if ((usage & dawn::TextureUsageBit::OutputAttachment) || isMultisampledTexture) {
                 if (TextureFormatHasDepth(format) || TextureFormatHasStencil(format)) {
                     flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
                 } else {
@@ -120,10 +126,12 @@
         resourceDescriptor.DepthOrArraySize = GetDepthOrArraySize();
         resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
         resourceDescriptor.Format = D3D12TextureFormat(GetFormat());
-        resourceDescriptor.SampleDesc.Count = 1;
+        resourceDescriptor.SampleDesc.Count = descriptor->sampleCount;
+        // TODO(bryan.bernhart@intel.com): investigate how to specify standard MSAA sample pattern.
         resourceDescriptor.SampleDesc.Quality = 0;
         resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
-        resourceDescriptor.Flags = D3D12ResourceFlags(GetUsage(), GetFormat());
+        resourceDescriptor.Flags =
+            D3D12ResourceFlags(GetUsage(), GetFormat(), IsMultisampledTexture());
 
         mResource = ToBackend(GetDevice())
                         ->GetResourceAllocator()
@@ -169,26 +177,32 @@
 
     void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
                                      dawn::TextureUsageBit usage) {
+        TransitionUsageNow(commandList, D3D12TextureUsage(usage, GetFormat()));
+    }
+
+    void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
+                                     D3D12_RESOURCE_STATES newState) {
         // Avoid transitioning the texture when it isn't needed.
         // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
-        if (usage == mLastUsage) {
+        if (mLastState == newState) {
             return;
         }
 
-        D3D12_RESOURCE_STATES lastState = D3D12TextureUsage(mLastUsage, GetFormat());
-        D3D12_RESOURCE_STATES newState = D3D12TextureUsage(usage, GetFormat());
-
         D3D12_RESOURCE_BARRIER barrier;
         barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
         barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
         barrier.Transition.pResource = mResourcePtr;
-        barrier.Transition.StateBefore = lastState;
+        barrier.Transition.StateBefore = mLastState;
         barrier.Transition.StateAfter = newState;
         barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
 
         commandList->ResourceBarrier(1, &barrier);
 
-        mLastUsage = usage;
+        mLastState = newState;
+    }
+
+    uint32_t Texture::GetSubresourceIndex(uint32_t mipmapLevel, uint32_t arraySlice) const {
+        return GetNumMipLevels() * arraySlice + mipmapLevel;
     }
 
     TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
@@ -202,6 +216,7 @@
         // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv
         // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv
         // TODO(jiawei.shao@intel.com): support more texture view dimensions.
+        // TODO(jiawei.shao@intel.com): support creating SRV on multisampled textures.
         switch (descriptor->dimension) {
             case dawn::TextureViewDimension::e2D:
             case dawn::TextureViewDimension::e2DArray:
@@ -242,26 +257,43 @@
         ASSERT(GetTexture()->GetDimension() == dawn::TextureDimension::e2D);
         D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
         rtvDesc.Format = GetD3D12Format();
-        // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array layer
-        // and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as 1-layer 2D
-        // array textures. (Just like how we treat SRVs)
-        // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv
-        // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_rtv
-        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
-        rtvDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer();
-        rtvDesc.Texture2DArray.ArraySize = GetLayerCount();
-        rtvDesc.Texture2DArray.MipSlice = GetBaseMipLevel();
-        rtvDesc.Texture2DArray.PlaneSlice = 0;
+        if (GetTexture()->IsMultisampledTexture()) {
+            ASSERT(GetTexture()->GetArrayLayers() == 1 && GetTexture()->GetNumMipLevels() == 1 &&
+                   GetBaseArrayLayer() == 0 && GetBaseMipLevel() == 0);
+            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
+        } else {
+            // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array
+            // layer and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as
+            // 1-layer 2D array textures. (Just like how we treat SRVs)
+            // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv
+            // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array
+            // _rtv
+            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
+            rtvDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer();
+            rtvDesc.Texture2DArray.ArraySize = GetLayerCount();
+            rtvDesc.Texture2DArray.MipSlice = GetBaseMipLevel();
+            rtvDesc.Texture2DArray.PlaneSlice = 0;
+        }
+
         return rtvDesc;
     }
 
-    // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture.
     D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const {
         D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
         dsvDesc.Format = ToBackend(GetTexture())->GetD3D12Format();
-        dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
-        dsvDesc.Texture2D.MipSlice = 0;
         dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
+
+        // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture.
+        ASSERT(GetTexture()->GetArrayLayers() == 1 && GetTexture()->GetNumMipLevels() == 1 &&
+               GetBaseArrayLayer() == 0 && GetBaseMipLevel() == 0);
+
+        if (GetTexture()->IsMultisampledTexture()) {
+            dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
+        } else {
+            dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
+            dsvDesc.Texture2D.MipSlice = 0;
+        }
+
         return dsvDesc;
     }
 
diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h
index 5167612..33b6be3 100644
--- a/src/dawn_native/d3d12/TextureD3D12.h
+++ b/src/dawn_native/d3d12/TextureD3D12.h
@@ -36,6 +36,10 @@
 
         void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
                                 dawn::TextureUsageBit usage);
+        void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
+                                D3D12_RESOURCE_STATES newState);
+
+        uint32_t GetSubresourceIndex(uint32_t mipmapLevel, uint32_t arraySlice) const;
 
       private:
         // Dawn API
@@ -45,7 +49,7 @@
 
         ComPtr<ID3D12Resource> mResource = {};
         ID3D12Resource* mResourcePtr = nullptr;
-        dawn::TextureUsageBit mLastUsage = dawn::TextureUsageBit::None;
+        D3D12_RESOURCE_STATES mLastState = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON;
     };
 
     class TextureView : public TextureViewBase {
diff --git a/src/tests/end2end/MultisampledRenderingTests.cpp b/src/tests/end2end/MultisampledRenderingTests.cpp
index e22b561..e1ceb83 100644
--- a/src/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/tests/end2end/MultisampledRenderingTests.cpp
@@ -466,4 +466,4 @@
 }
 
 // TODO(jiawei.shao@intel.com): enable multisampled rendering on all Dawn backends.
-DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, OpenGLBackend);
+DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, D3D12Backend, OpenGLBackend);