[Mac] Create and hold MtlTextures in SharedTextureMemoryMTL

Per validation constraints [1], the MtlTextures that TextureMTL creates
when initialized from SharedTextureMemory must match the properties of
the backing IOSurface that the SharedTextureMemoryMTL instance was
created with. This means that we can create the MtlTextures in
SharedTextureMemoryMTL on its creation and simply vend them out to
any TextureMTL objects that that SharedTextureMemoryMTL instance is
asked to create. This CL makes that change.

[1] https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/dawn/native/SharedTextureMemory.cpp;l=182-193?q=SharedTextureMemory.cpp&ss=chromium

Change-Id: I5fa35b1e55b16436674dd3f730e613e6fcd9be9c
Bug: dawn:2152, 1493854
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/171041
Commit-Queue: Colin Blundell <blundell@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/metal/SharedTextureMemoryMTL.h b/src/dawn/native/metal/SharedTextureMemoryMTL.h
index 3504f4a..238bd7e 100644
--- a/src/dawn/native/metal/SharedTextureMemoryMTL.h
+++ b/src/dawn/native/metal/SharedTextureMemoryMTL.h
@@ -29,11 +29,14 @@
 #define SRC_DAWN_NATIVE_METAL_SHAREDTEXTUREMEMORYMTL_H_
 
 #include <IOSurface/IOSurfaceRef.h>
+#import <Metal/Metal.h>
 #include <vector>
 
 #include "dawn/common/CoreFoundationRef.h"
+#include "dawn/common/NSRef.h"
 #include "dawn/native/Error.h"
 #include "dawn/native/SharedTextureMemory.h"
+#include "dawn/native/Subresource.h"
 
 namespace dawn::native::metal {
 
@@ -48,12 +51,19 @@
         const SharedTextureMemoryIOSurfaceDescriptor* descriptor);
 
     IOSurfaceRef GetIOSurface() const;
+    const StackVector<NSPRef<id<MTLTexture>>, kMaxPlanesPerFormat>& GetMtlPlaneTextures() const;
+    MTLTextureUsage GetMtlTextureUsage() const;
+    MTLPixelFormat GetMtlPixelFormat() const;
 
   private:
     SharedTextureMemory(Device* device,
                         const char* label,
                         const SharedTextureMemoryProperties& properties,
                         IOSurfaceRef ioSurface);
+    // Performs initialization of the base class followed by Metal-specific
+    // initialization.
+    MaybeError Initialize();
+
     void DestroyImpl() override;
 
     ResultOrError<Ref<TextureBase>> CreateTextureImpl(
@@ -62,7 +72,11 @@
                                const UnpackedPtr<BeginAccessDescriptor>& descriptor) override;
     ResultOrError<FenceAndSignalValue> EndAccessImpl(TextureBase* texture,
                                                      UnpackedPtr<EndAccessState>& state) override;
+    MaybeError CreateMtlTextures();
 
+    StackVector<NSPRef<id<MTLTexture>>, kMaxPlanesPerFormat> mMtlPlaneTextures;
+    MTLPixelFormat mMtlFormat = MTLPixelFormatInvalid;
+    MTLTextureUsage mMtlUsage;
     CFRef<IOSurfaceRef> mIOSurface;
 };
 
diff --git a/src/dawn/native/metal/SharedTextureMemoryMTL.mm b/src/dawn/native/metal/SharedTextureMemoryMTL.mm
index ac15fc8..e9c291a 100644
--- a/src/dawn/native/metal/SharedTextureMemoryMTL.mm
+++ b/src/dawn/native/metal/SharedTextureMemoryMTL.mm
@@ -35,10 +35,19 @@
 #include "dawn/native/metal/QueueMTL.h"
 #include "dawn/native/metal/SharedFenceMTL.h"
 #include "dawn/native/metal/TextureMTL.h"
+#include "dawn/native/metal/UtilsMetal.h"
 
 namespace dawn::native::metal {
 
 namespace {
+// NOTE: When creating MTLTextures, we pass all Metal texture usages. See
+// discussion in https://bugs.chromium.org/p/dawn/issues/detail?id=2152#c14 and
+// following comments for both (a) why this is necessary and (b) why it is not
+// harmful to performance.
+const MTLTextureUsage kMetalTextureUsage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead |
+                                           MTLTextureUsagePixelFormatView |
+                                           MTLTextureUsageRenderTarget;
+
 ResultOrError<wgpu::TextureFormat> GetFormatEquivalentToIOSurfaceFormat(uint32_t format) {
     switch (format) {
         case kCVPixelFormatType_64RGBAHalf:
@@ -111,7 +120,8 @@
     properties.size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1};
 
     auto result = AcquireRef(new SharedTextureMemory(device, label, properties, ioSurface));
-    result->Initialize();
+    DAWN_TRY(result->Initialize());
+
     return result;
 }
 
@@ -126,10 +136,30 @@
     mIOSurface = nullptr;
 }
 
+MaybeError SharedTextureMemory::Initialize() {
+    SharedTextureMemoryBase::Initialize();
+
+    DAWN_TRY(CreateMtlTextures());
+    return {};
+}
+
 IOSurfaceRef SharedTextureMemory::GetIOSurface() const {
     return mIOSurface.Get();
 }
 
+const StackVector<NSPRef<id<MTLTexture>>, kMaxPlanesPerFormat>&
+SharedTextureMemory::GetMtlPlaneTextures() const {
+    return mMtlPlaneTextures;
+}
+
+MTLTextureUsage SharedTextureMemory::GetMtlTextureUsage() const {
+    return mMtlUsage;
+}
+
+MTLPixelFormat SharedTextureMemory::GetMtlPixelFormat() const {
+    return mMtlFormat;
+}
+
 ResultOrError<Ref<TextureBase>> SharedTextureMemory::CreateTextureImpl(
     const UnpackedPtr<TextureDescriptor>& descriptor) {
     return Texture::CreateFromSharedTextureMemory(this, descriptor);
@@ -184,4 +214,57 @@
     DAWN_UNREACHABLE();
 }
 
+MaybeError SharedTextureMemory::CreateMtlTextures() {
+    auto* device = static_cast<Device*>(GetDevice());
+
+    SharedTextureMemoryProperties properties;
+    APIGetProperties(&properties);
+    const Format* format;
+    DAWN_TRY_ASSIGN(format, device->GetInternalFormat(properties.format));
+
+    // NOTE: Per SharedTextureMemory semantics and validation, textures that it
+    // is asked to create must be 2D/single-sampled/array length of 1/single
+    // mipmap level.
+    if (!format->IsMultiPlanar()) {
+        // Create the descriptor for the Metal texture.
+        NSRef<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
+        MTLTextureDescriptor* mtlDesc = mtlDescRef.Get();
+
+        mtlDesc.storageMode = IOSurfaceStorageMode();
+        mtlDesc.width = properties.size.width;
+        mtlDesc.height = properties.size.height;
+        // NOTE: MetalTextureDescriptor defaults to the values mentioned above
+        // for the given parameters, so none of these need to be set explicitly.
+
+        // Metal only allows format reinterpretation to happen on swizzle pattern or conversion
+        // between linear space and sRGB. For example, creating bgra8Unorm texture view on
+        // rgba8Unorm texture or creating rgba8Unorm_srgb texture view on rgab8Unorm texture.
+        mtlDesc.usage = kMetalTextureUsage;
+        mtlDesc.pixelFormat = MetalPixelFormat(device, format->format);
+
+        mMtlUsage = mtlDesc.usage;
+        mMtlFormat = mtlDesc.pixelFormat;
+        mMtlPlaneTextures->resize(1);
+        mMtlPlaneTextures[0] =
+            AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc
+                                                                 iosurface:mIOSurface.Get()
+                                                                     plane:0]);
+    } else {
+        mMtlUsage = kMetalTextureUsage;
+        // Multiplanar format doesn't have equivalent MTLPixelFormat so just set it to invalid.
+        mMtlFormat = MTLPixelFormatInvalid;
+        const size_t numPlanes = IOSurfaceGetPlaneCount(mIOSurface.Get());
+        mMtlPlaneTextures->resize(numPlanes);
+        for (size_t plane = 0; plane < numPlanes; ++plane) {
+            mMtlPlaneTextures[plane] = AcquireNSPRef(CreateTextureMtlForPlane(
+                mMtlUsage, *format, plane, device, /*sampleCount=*/1, mIOSurface.Get()));
+            if (mMtlPlaneTextures[plane] == nil) {
+                return DAWN_INTERNAL_ERROR("Failed to create MTLTexture plane view for IOSurface.");
+            }
+        }
+    }
+
+    return {};
+}
+
 }  // namespace dawn::native::metal
diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm
index 86616d1..0b1cec6 100644
--- a/src/dawn/native/metal/TextureMTL.mm
+++ b/src/dawn/native/metal/TextureMTL.mm
@@ -49,16 +49,6 @@
 
 namespace {
 
-// NOTE: When creating MTLTextures from IOSurfaces vended by
-// SharedTextureMemory, we pass all Metal texture usages. This will facilitate an
-// upcoming change to have SharedTextureMemory cache MTLTextures. See
-// discussion in https://bugs.chromium.org/p/dawn/issues/detail?id=2152#c14 and
-// following comments for both (a) why this is necessary and (b) why it is not
-// harmful to performance.
-const MTLTextureUsage kMetalTextureUsageForSharedTextureMemoryIOSurface =
-    MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView |
-    MTLTextureUsageRenderTarget;
-
 MTLTextureUsage MetalTextureUsage(const Format& format, wgpu::TextureUsage usage) {
     MTLTextureUsage result = MTLTextureUsageUnknown;  // This is 0
 
@@ -412,49 +402,9 @@
         GetInternalUsage(), wgpu::TextureUsage::TransientAttachment);
 
     mIOSurface = memory->GetIOSurface();
-
-    Device* device = ToBackend(GetDevice());
-
-    // NOTE: The texture is guaranteed to be 2D/single-sampled/array length of
-    // 1/single mipmap level per SharedTextureMemory semantics and validation.
-    if (!GetFormat().IsMultiPlanar()) {
-        // Create the descriptor for the Metal texture.
-        NSRef<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
-        MTLTextureDescriptor* mtlDesc = mtlDescRef.Get();
-
-        mtlDesc.storageMode = IOSurfaceStorageMode();
-        mtlDesc.width = GetBaseSize().width;
-        mtlDesc.height = GetBaseSize().height;
-        // NOTE: MetalTextureDescriptor defaults to the values mentioned above
-        // for the given parameters, so none of these need to be set explicitly.
-
-        // Metal only allows format reinterpretation to happen on swizzle pattern or conversion
-        // between linear space and sRGB. For example, creating bgra8Unorm texture view on
-        // rgba8Unorm texture or creating rgba8Unorm_srgb texture view on rgab8Unorm texture.
-        mtlDesc.usage = kMetalTextureUsageForSharedTextureMemoryIOSurface;
-        mtlDesc.pixelFormat = MetalPixelFormat(GetDevice(), GetFormat().format);
-
-        mMtlUsage = mtlDesc.usage;
-        mMtlFormat = mtlDesc.pixelFormat;
-        mMtlPlaneTextures->resize(1);
-        mMtlPlaneTextures[0] =
-            AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc
-                                                                 iosurface:mIOSurface.Get()
-                                                                     plane:0]);
-    } else {
-        mMtlUsage = kMetalTextureUsageForSharedTextureMemoryIOSurface;
-        // Multiplanar format doesn't have equivalent MTLPixelFormat so just set it to invalid.
-        mMtlFormat = MTLPixelFormatInvalid;
-        const size_t numPlanes = IOSurfaceGetPlaneCount(GetIOSurface());
-        mMtlPlaneTextures->resize(numPlanes);
-        for (size_t plane = 0; plane < numPlanes; ++plane) {
-            mMtlPlaneTextures[plane] = AcquireNSPRef(CreateTextureMtlForPlane(
-                mMtlUsage, GetFormat(), plane, device, /*sampleCount=*/1, GetIOSurface()));
-            if (mMtlPlaneTextures[plane] == nil) {
-                return DAWN_INTERNAL_ERROR("Failed to create MTLTexture plane view for IOSurface.");
-            }
-        }
-    }
+    mMtlUsage = memory->GetMtlTextureUsage();
+    mMtlFormat = memory->GetMtlPixelFormat();
+    mMtlPlaneTextures = memory->GetMtlPlaneTextures();
 
     SetLabelImpl();