Create texture view with descriptor on D3D12 and Metal - Part I

This patch is the first part to implement creating a texture view
with a texture view descriptor on D3D12 and Metal back-ends. With
this patch the texture views created with descriptor can be bound
as sampledTextures on D3D12 and Metal back-ends.

Note that the support of rendering into a layer or a mipmap level of
a texture on D3D12 and Metal back-ends is not included in this patch.

BUG=dawn:16
TEST=dawn_end2end_tests

Change-Id: I62473ec5a4bb6b84d797ef7fd9cb98689ff763f4
Reviewed-on: https://dawn-review.googlesource.com/c/1940
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 0a52364..f200a46 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -187,28 +187,28 @@
         mLastUsage = usage;
     }
 
-    // TODO(jiawei.shao@intel.com): create texture view by TextureViewDescriptor
     TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
         : TextureViewBase(texture, descriptor) {
-        mSrvDesc.Format = D3D12TextureFormat(GetTexture()->GetFormat());
+        mSrvDesc.Format = D3D12TextureFormat(descriptor->format);
         mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
-        switch (GetTexture()->GetDimension()) {
-            case dawn::TextureDimension::e2D:
-                if (GetTexture()->GetArrayLayers() == 1) {
-                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
-                    mSrvDesc.Texture2D.MostDetailedMip = 0;
-                    mSrvDesc.Texture2D.MipLevels = GetTexture()->GetNumMipLevels();
-                    mSrvDesc.Texture2D.PlaneSlice = 0;
-                    mSrvDesc.Texture2D.ResourceMinLODClamp = 0;
-                } else {
-                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
-                    mSrvDesc.Texture2DArray.ArraySize = GetTexture()->GetArrayLayers();
-                    mSrvDesc.Texture2DArray.FirstArraySlice = 0;
-                    mSrvDesc.Texture2DArray.MipLevels = GetTexture()->GetNumMipLevels();
-                    mSrvDesc.Texture2DArray.MostDetailedMip = 0;
-                    mSrvDesc.Texture2DArray.PlaneSlice = 0;
-                    mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
-                }
+
+        // Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer
+        // and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D
+        // array textures.
+        // 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.
+        switch (descriptor->dimension) {
+            case dawn::TextureViewDimension::e2D:
+            case dawn::TextureViewDimension::e2DArray:
+                ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D);
+                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+                mSrvDesc.Texture2DArray.ArraySize = descriptor->layerCount;
+                mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer;
+                mSrvDesc.Texture2DArray.MipLevels = descriptor->levelCount;
+                mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel;
+                mSrvDesc.Texture2DArray.PlaneSlice = 0;
+                mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
                 break;
 
             default:
@@ -220,6 +220,7 @@
         return mSrvDesc;
     }
 
+    // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture.
     D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() {
         D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
         rtvDesc.Format = ToBackend(GetTexture())->GetD3D12Format();
@@ -229,6 +230,7 @@
         return rtvDesc;
     }
 
+    // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture.
     D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() {
         D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
         dsvDesc.Format = ToBackend(GetTexture())->GetD3D12Format();
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 746f298..3aff378 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -63,6 +63,7 @@
                     descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad;
                 }
 
+                // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture.
                 descriptor.colorAttachments[i].texture =
                     ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture();
                 descriptor.colorAttachments[i].storeAction = MTLStoreActionStore;
@@ -71,6 +72,7 @@
             if (desc->HasDepthStencilAttachment()) {
                 auto& attachmentInfo = desc->GetDepthStencilAttachment();
 
+                // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture.
                 id<MTLTexture> texture =
                     ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture();
                 dawn::TextureFormat format = attachmentInfo.view->GetTexture()->GetFormat();
@@ -187,16 +189,17 @@
                     } break;
 
                     case dawn::BindingType::SampledTexture: {
-                        auto texture =
-                            ToBackend(group->GetBindingAsTextureView(binding)->GetTexture());
+                        auto textureView = ToBackend(group->GetBindingAsTextureView(binding));
                         if (hasVertStage) {
-                            [render setVertexTexture:texture->GetMTLTexture() atIndex:vertIndex];
+                            [render setVertexTexture:textureView->GetMTLTexture()
+                                             atIndex:vertIndex];
                         }
                         if (hasFragStage) {
-                            [render setFragmentTexture:texture->GetMTLTexture() atIndex:fragIndex];
+                            [render setFragmentTexture:textureView->GetMTLTexture()
+                                               atIndex:fragIndex];
                         }
                         if (hasComputeStage) {
-                            [compute setTexture:texture->GetMTLTexture() atIndex:computeIndex];
+                            [compute setTexture:textureView->GetMTLTexture() atIndex:computeIndex];
                         }
                     } break;
                 }
diff --git a/src/dawn_native/metal/TextureMTL.h b/src/dawn_native/metal/TextureMTL.h
index 01efb21..c002f56 100644
--- a/src/dawn_native/metal/TextureMTL.h
+++ b/src/dawn_native/metal/TextureMTL.h
@@ -40,6 +40,12 @@
     class TextureView : public TextureViewBase {
       public:
         TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor);
+        ~TextureView();
+
+        id<MTLTexture> GetMTLTexture();
+
+      private:
+        id<MTLTexture> mMtlTextureView = nil;
     };
 
 }}  // namespace dawn_native::metal
diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm
index 7d6d024..5102055 100644
--- a/src/dawn_native/metal/TextureMTL.mm
+++ b/src/dawn_native/metal/TextureMTL.mm
@@ -55,6 +55,10 @@
                 result |= MTLTextureUsageRenderTarget;
             }
 
+            // TODO(jiawei.shao@intel.com): investigate if we should skip setting this flag when the
+            // texture is only used as a render target.
+            result |= MTLTextureUsagePixelFormatView;
+
             return result;
         }
 
@@ -65,6 +69,18 @@
                     return (arrayLayers > 1) ? MTLTextureType2DArray : MTLTextureType2D;
             }
         }
+
+        MTLTextureType MetalTextureViewType(dawn::TextureViewDimension dimension) {
+            switch (dimension) {
+                case dawn::TextureViewDimension::e2D:
+                    return MTLTextureType2D;
+                case dawn::TextureViewDimension::e2DArray:
+                    return MTLTextureType2DArray;
+                default:
+                    UNREACHABLE();
+                    return MTLTextureType2D;
+            }
+        }
     }
 
     Texture::Texture(Device* device, const TextureDescriptor* descriptor)
@@ -101,9 +117,27 @@
         return mMtlTexture;
     }
 
-    // TODO(jiawei.shao@intel.com): create texture view by texture view descriptor
+    // TODO(jiawei.shao@intel.com): use the original texture directly when the descriptor covers the
+    // whole texture in the same format (for example, when CreateDefaultTextureView() is called).
     TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
         : TextureViewBase(texture, descriptor) {
+        MTLPixelFormat format = MetalPixelFormat(descriptor->format);
+        MTLTextureType textureViewType = MetalTextureViewType(descriptor->dimension);
+        auto mipLevelRange = NSMakeRange(descriptor->baseMipLevel, descriptor->levelCount);
+        auto arrayLayerRange = NSMakeRange(descriptor->baseArrayLayer, descriptor->layerCount);
+
+        id<MTLTexture> mtlTexture = ToBackend(texture)->GetMTLTexture();
+        mMtlTextureView = [mtlTexture newTextureViewWithPixelFormat:format
+                                                        textureType:textureViewType
+                                                             levels:mipLevelRange
+                                                             slices:arrayLayerRange];
     }
 
+    TextureView::~TextureView() {
+        [mMtlTextureView release];
+    }
+
+    id<MTLTexture> TextureView::GetMTLTexture() {
+        return mMtlTextureView;
+    }
 }}  // namespace dawn_native::metal
diff --git a/src/tests/end2end/TextureViewTests.cpp b/src/tests/end2end/TextureViewTests.cpp
index 8e3abd8..1877cf4 100644
--- a/src/tests/end2end/TextureViewTests.cpp
+++ b/src/tests/end2end/TextureViewTests.cpp
@@ -164,8 +164,8 @@
                            uint32_t textureViewBaseLayer,
                            uint32_t textureViewBaseMipLevel) {
         // TODO(jiawei.shao@intel.com): support creating texture view with a texture view descriptor
-        // on D3D12, Metal and OpenGL.
-        DAWN_SKIP_TEST_IF(IsD3D12() || IsMetal() || IsOpenGL());
+        // on OpenGL.
+        DAWN_SKIP_TEST_IF(IsOpenGL());
 
         ASSERT(textureViewBaseLayer < textureArrayLayers);
         ASSERT(textureViewBaseMipLevel < textureMipLevels);
@@ -201,8 +201,8 @@
                                 uint32_t textureViewBaseLayer,
                                 uint32_t textureViewBaseMipLevel) {
         // TODO(jiawei.shao@intel.com): support creating texture view with a texture view descriptor
-        // on D3D12, Metal and OpenGL.
-        DAWN_SKIP_TEST_IF(IsD3D12() || IsMetal() || IsOpenGL());
+        // on OpenGL.
+        DAWN_SKIP_TEST_IF(IsOpenGL());
 
         ASSERT(textureViewBaseLayer < textureArrayLayers);
         ASSERT(textureViewBaseMipLevel < textureMipLevels);