• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// mtl_resources.mm:
7//    Implements wrapper classes for Metal's MTLTexture and MTLBuffer.
8//
9
10#include "libANGLE/renderer/metal/mtl_resources.h"
11
12#include <TargetConditionals.h>
13
14#include <algorithm>
15
16#include "common/debug.h"
17#include "libANGLE/renderer/metal/ContextMtl.h"
18#include "libANGLE/renderer/metal/DisplayMtl.h"
19#include "libANGLE/renderer/metal/mtl_command_buffer.h"
20#include "libANGLE/renderer/metal/mtl_context_device.h"
21#include "libANGLE/renderer/metal/mtl_format_utils.h"
22#include "libANGLE/renderer/metal/mtl_utils.h"
23
24namespace rx
25{
26namespace mtl
27{
28namespace
29{
30inline NSUInteger GetMipSize(NSUInteger baseSize, const MipmapNativeLevel level)
31{
32    return std::max<NSUInteger>(1, baseSize >> level.get());
33}
34
35// Asynchronously synchronize the content of a resource between GPU memory and its CPU cache.
36// NOTE: This operation doesn't finish immediately upon function's return.
37template <class T>
38void InvokeCPUMemSync(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder, T *resource)
39{
40#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
41    if (blitEncoder)
42    {
43        blitEncoder->synchronizeResource(resource);
44
45        resource->resetCPUReadMemNeedSync();
46        resource->setCPUReadMemSyncPending(true);
47    }
48#endif
49}
50
51template <class T>
52void EnsureCPUMemWillBeSynced(ContextMtl *context, T *resource)
53{
54#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
55    // Make sure GPU & CPU contents are synchronized.
56    // NOTE: Only MacOS has separated storage for resource on CPU and GPU and needs explicit
57    // synchronization
58    if (resource->get().storageMode == MTLStorageModeManaged && resource->isCPUReadMemNeedSync())
59    {
60        mtl::BlitCommandEncoder *blitEncoder = context->getBlitCommandEncoder();
61        InvokeCPUMemSync(context, blitEncoder, resource);
62    }
63#endif
64    resource->resetCPUReadMemNeedSync();
65}
66
67MTLResourceOptions resourceOptionsForStorageMode(MTLStorageMode storageMode)
68{
69    switch (storageMode)
70    {
71        case MTLStorageModeShared:
72            return MTLResourceStorageModeShared;
73#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
74        case MTLStorageModeManaged:
75            return MTLResourceStorageModeManaged;
76#endif
77        case MTLStorageModePrivate:
78            return MTLResourceStorageModePrivate;
79        case MTLStorageModeMemoryless:
80            return MTLResourceStorageModeMemoryless;
81#if TARGET_OS_SIMULATOR
82        default:
83            // TODO(http://anglebug.com/8012): Remove me once hacked SDKs are fixed.
84            UNREACHABLE();
85            return MTLResourceStorageModeShared;
86#endif
87    }
88}
89
90}  // namespace
91
92// Resource implementation
93Resource::Resource() : mUsageRef(std::make_shared<UsageRef>()) {}
94
95// Share the GPU usage ref with other resource
96Resource::Resource(Resource *other) : Resource(other->mUsageRef) {}
97Resource::Resource(std::shared_ptr<UsageRef> otherUsageRef) : mUsageRef(std::move(otherUsageRef))
98{
99    ASSERT(mUsageRef);
100}
101
102void Resource::reset()
103{
104    mUsageRef->cmdBufferQueueSerial = 0;
105    resetCPUReadMemDirty();
106    resetCPUReadMemNeedSync();
107    resetCPUReadMemSyncPending();
108}
109
110bool Resource::isBeingUsedByGPU(Context *context) const
111{
112    return context->cmdQueue().isResourceBeingUsedByGPU(this);
113}
114
115bool Resource::hasPendingWorks(Context *context) const
116{
117    return context->cmdQueue().resourceHasPendingWorks(this);
118}
119
120bool Resource::hasPendingRenderWorks(Context *context) const
121{
122    return context->cmdQueue().resourceHasPendingRenderWorks(this);
123}
124
125void Resource::setUsedByCommandBufferWithQueueSerial(uint64_t serial,
126                                                     bool writing,
127                                                     bool isRenderCommand)
128{
129    if (writing)
130    {
131        mUsageRef->cpuReadMemNeedSync = true;
132        mUsageRef->cpuReadMemDirty    = true;
133    }
134
135    mUsageRef->cmdBufferQueueSerial = std::max(mUsageRef->cmdBufferQueueSerial, serial);
136
137    if (isRenderCommand)
138    {
139        if (writing)
140        {
141            mUsageRef->lastWritingRenderEncoderSerial = mUsageRef->cmdBufferQueueSerial;
142        }
143        else
144        {
145            mUsageRef->lastReadingRenderEncoderSerial = mUsageRef->cmdBufferQueueSerial;
146        }
147    }
148}
149
150// Texture implemenetation
151/** static */
152angle::Result Texture::Make2DTexture(ContextMtl *context,
153                                     const Format &format,
154                                     uint32_t width,
155                                     uint32_t height,
156                                     uint32_t mips,
157                                     bool renderTargetOnly,
158                                     bool allowFormatView,
159                                     TextureRef *refOut)
160{
161    ANGLE_MTL_OBJC_SCOPE
162    {
163        MTLTextureDescriptor *desc =
164            [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat
165                                                               width:width
166                                                              height:height
167                                                           mipmapped:mips == 0 || mips > 1];
168        return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
169    }  // ANGLE_MTL_OBJC_SCOPE
170}
171
172/** static */
173angle::Result Texture::MakeMemoryLess2DTexture(ContextMtl *context,
174                                               const Format &format,
175                                               uint32_t width,
176                                               uint32_t height,
177                                               TextureRef *refOut)
178{
179    ANGLE_MTL_OBJC_SCOPE
180    {
181        MTLTextureDescriptor *desc =
182            [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat
183                                                               width:width
184                                                              height:height
185                                                           mipmapped:NO];
186
187        return MakeTexture(context, format, desc, 1, true, false, true, refOut);
188    }  // ANGLE_MTL_OBJC_SCOPE
189}
190/** static */
191angle::Result Texture::MakeCubeTexture(ContextMtl *context,
192                                       const Format &format,
193                                       uint32_t size,
194                                       uint32_t mips,
195                                       bool renderTargetOnly,
196                                       bool allowFormatView,
197                                       TextureRef *refOut)
198{
199    ANGLE_MTL_OBJC_SCOPE
200    {
201        MTLTextureDescriptor *desc =
202            [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:format.metalFormat
203                                                                  size:size
204                                                             mipmapped:mips == 0 || mips > 1];
205
206        return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
207    }  // ANGLE_MTL_OBJC_SCOPE
208}
209
210/** static */
211angle::Result Texture::Make2DMSTexture(ContextMtl *context,
212                                       const Format &format,
213                                       uint32_t width,
214                                       uint32_t height,
215                                       uint32_t samples,
216                                       bool renderTargetOnly,
217                                       bool allowFormatView,
218                                       TextureRef *refOut)
219{
220    ANGLE_MTL_OBJC_SCOPE
221    {
222        MTLTextureDescriptor *desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE];
223        desc.textureType           = MTLTextureType2DMultisample;
224        desc.pixelFormat           = format.metalFormat;
225        desc.width                 = width;
226        desc.height                = height;
227        desc.mipmapLevelCount      = 1;
228        desc.sampleCount           = samples;
229
230        return MakeTexture(context, format, desc, 1, renderTargetOnly, allowFormatView, refOut);
231    }  // ANGLE_MTL_OBJC_SCOPE
232}
233
234/** static */
235angle::Result Texture::Make2DArrayTexture(ContextMtl *context,
236                                          const Format &format,
237                                          uint32_t width,
238                                          uint32_t height,
239                                          uint32_t mips,
240                                          uint32_t arrayLength,
241                                          bool renderTargetOnly,
242                                          bool allowFormatView,
243                                          TextureRef *refOut)
244{
245    ANGLE_MTL_OBJC_SCOPE
246    {
247        // Use texture2DDescriptorWithPixelFormat to calculate full range mipmap range:
248        MTLTextureDescriptor *desc =
249            [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat
250                                                               width:width
251                                                              height:height
252                                                           mipmapped:mips == 0 || mips > 1];
253
254        desc.textureType = MTLTextureType2DArray;
255        desc.arrayLength = arrayLength;
256
257        return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
258    }  // ANGLE_MTL_OBJC_SCOPE
259}
260
261/** static */
262angle::Result Texture::Make3DTexture(ContextMtl *context,
263                                     const Format &format,
264                                     uint32_t width,
265                                     uint32_t height,
266                                     uint32_t depth,
267                                     uint32_t mips,
268                                     bool renderTargetOnly,
269                                     bool allowFormatView,
270                                     TextureRef *refOut)
271{
272    ANGLE_MTL_OBJC_SCOPE
273    {
274        // Use texture2DDescriptorWithPixelFormat to calculate full range mipmap range:
275        const uint32_t maxDimen = std::max({width, height, depth});
276        MTLTextureDescriptor *desc =
277            [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format.metalFormat
278                                                               width:maxDimen
279                                                              height:maxDimen
280                                                           mipmapped:mips == 0 || mips > 1];
281
282        desc.textureType = MTLTextureType3D;
283        desc.width       = width;
284        desc.height      = height;
285        desc.depth       = depth;
286
287        return MakeTexture(context, format, desc, mips, renderTargetOnly, allowFormatView, refOut);
288    }  // ANGLE_MTL_OBJC_SCOPE
289}
290
291/** static */
292angle::Result Texture::MakeTexture(ContextMtl *context,
293                                   const Format &mtlFormat,
294                                   MTLTextureDescriptor *desc,
295                                   uint32_t mips,
296                                   bool renderTargetOnly,
297                                   bool allowFormatView,
298                                   TextureRef *refOut)
299{
300    return MakeTexture(context, mtlFormat, desc, mips, renderTargetOnly, allowFormatView, false,
301                       refOut);
302}
303
304angle::Result Texture::MakeTexture(ContextMtl *context,
305                                   const Format &mtlFormat,
306                                   MTLTextureDescriptor *desc,
307                                   uint32_t mips,
308                                   bool renderTargetOnly,
309                                   bool allowFormatView,
310                                   bool memoryLess,
311                                   TextureRef *refOut)
312{
313    if (desc.pixelFormat == MTLPixelFormatInvalid)
314    {
315        return angle::Result::Stop;
316    }
317
318    ASSERT(refOut);
319    Texture *newTexture =
320        new Texture(context, desc, mips, renderTargetOnly, allowFormatView, memoryLess);
321    ANGLE_MTL_CHECK(context, newTexture->valid(), GL_OUT_OF_MEMORY);
322    refOut->reset(newTexture);
323
324    if (!mtlFormat.hasDepthAndStencilBits())
325    {
326        refOut->get()->setColorWritableMask(GetEmulatedColorWriteMask(mtlFormat));
327    }
328
329    size_t estimatedBytes = EstimateTextureSizeInBytes(
330        mtlFormat, desc.width, desc.height, desc.depth, desc.sampleCount, desc.mipmapLevelCount);
331    if (refOut)
332    {
333        refOut->get()->setEstimatedByteSize(memoryLess ? 0 : estimatedBytes);
334    }
335
336    return angle::Result::Continue;
337}
338
339angle::Result Texture::MakeTexture(ContextMtl *context,
340                                   const Format &mtlFormat,
341                                   MTLTextureDescriptor *desc,
342                                   IOSurfaceRef surfaceRef,
343                                   NSUInteger slice,
344                                   bool renderTargetOnly,
345                                   TextureRef *refOut)
346{
347
348    ASSERT(refOut);
349    Texture *newTexture = new Texture(context, desc, surfaceRef, slice, renderTargetOnly);
350    ANGLE_MTL_CHECK(context, newTexture->valid(), GL_OUT_OF_MEMORY);
351    refOut->reset(newTexture);
352    if (!mtlFormat.hasDepthAndStencilBits())
353    {
354        refOut->get()->setColorWritableMask(GetEmulatedColorWriteMask(mtlFormat));
355    }
356
357    size_t estimatedBytes = EstimateTextureSizeInBytes(
358        mtlFormat, desc.width, desc.height, desc.depth, desc.sampleCount, desc.mipmapLevelCount);
359    refOut->get()->setEstimatedByteSize(estimatedBytes);
360
361    return angle::Result::Continue;
362}
363
364bool needMultisampleColorFormatShaderReadWorkaround(ContextMtl *context, MTLTextureDescriptor *desc)
365{
366    return desc.sampleCount > 1 &&
367           context->getDisplay()
368               ->getFeatures()
369               .multisampleColorFormatShaderReadWorkaround.enabled &&
370           context->getNativeFormatCaps(desc.pixelFormat).colorRenderable;
371}
372
373/** static */
374TextureRef Texture::MakeFromMetal(id<MTLTexture> metalTexture)
375{
376    ANGLE_MTL_OBJC_SCOPE
377    {
378        return TextureRef(new Texture(metalTexture));
379    }
380}
381
382Texture::Texture(id<MTLTexture> metalTexture)
383    : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll))
384{
385    set(metalTexture);
386}
387
388Texture::Texture(std::shared_ptr<UsageRef> usageRef,
389                 id<MTLTexture> metalTexture,
390                 std::shared_ptr<MTLColorWriteMask> colorWriteMask)
391    : Resource(std::move(usageRef)), mColorWritableMask(std::move(colorWriteMask))
392{
393    set(metalTexture);
394}
395
396Texture::Texture(ContextMtl *context,
397                 MTLTextureDescriptor *desc,
398                 uint32_t mips,
399                 bool renderTargetOnly,
400                 bool allowFormatView)
401    : Texture(context, desc, mips, renderTargetOnly, allowFormatView, false)
402{}
403
404Texture::Texture(ContextMtl *context,
405                 MTLTextureDescriptor *desc,
406                 uint32_t mips,
407                 bool renderTargetOnly,
408                 bool allowFormatView,
409                 bool memoryLess)
410    : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll))
411{
412    ANGLE_MTL_OBJC_SCOPE
413    {
414        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
415
416        if (mips > 1 && mips < desc.mipmapLevelCount)
417        {
418            desc.mipmapLevelCount = mips;
419        }
420
421        // Every texture will support being rendered for now
422        desc.usage = 0;
423
424        if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable())
425        {
426            desc.usage |= MTLTextureUsageRenderTarget;
427        }
428
429        if (memoryLess)
430        {
431#if (TARGET_OS_IOS || TARGET_OS_TV) && !TARGET_OS_MACCATALYST
432            desc.resourceOptions = MTLResourceStorageModeMemoryless;
433#else
434            desc.resourceOptions = MTLResourceStorageModePrivate;
435#endif
436        }
437        else if (context->getNativeFormatCaps(desc.pixelFormat).depthRenderable ||
438                 desc.textureType == MTLTextureType2DMultisample)
439        {
440            // Metal doesn't support host access to depth stencil texture's data
441            desc.resourceOptions = MTLResourceStorageModePrivate;
442        }
443
444        if (!renderTargetOnly || needMultisampleColorFormatShaderReadWorkaround(context, desc))
445        {
446            desc.usage = desc.usage | MTLTextureUsageShaderRead;
447            if (context->getNativeFormatCaps(desc.pixelFormat).writable)
448            {
449                desc.usage = desc.usage | MTLTextureUsageShaderWrite;
450            }
451        }
452        if (desc.pixelFormat == MTLPixelFormatDepth32Float_Stencil8)
453        {
454            ASSERT(allowFormatView);
455        }
456#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
457        if (desc.pixelFormat == MTLPixelFormatDepth24Unorm_Stencil8)
458        {
459            ASSERT(allowFormatView);
460        }
461#endif
462
463        if (allowFormatView)
464        {
465            desc.usage = desc.usage | MTLTextureUsagePixelFormatView;
466        }
467
468        set(metalDevice.newTextureWithDescriptor(desc));
469
470        mCreationDesc.retainAssign(desc);
471    }
472}
473
474Texture::Texture(ContextMtl *context,
475                 MTLTextureDescriptor *desc,
476                 IOSurfaceRef iosurface,
477                 NSUInteger plane,
478                 bool renderTargetOnly)
479    : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll))
480{
481    ANGLE_MTL_OBJC_SCOPE
482    {
483        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
484
485        // Every texture will support being rendered for now
486        desc.usage = MTLTextureUsagePixelFormatView;
487
488        if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable())
489        {
490            desc.usage |= MTLTextureUsageRenderTarget;
491        }
492
493#if (TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH) && !TARGET_OS_MACCATALYST
494        desc.resourceOptions = MTLResourceStorageModeShared;
495#else
496        desc.resourceOptions = MTLResourceStorageModeManaged;
497#endif
498
499        if (!renderTargetOnly)
500        {
501            desc.usage = desc.usage | MTLTextureUsageShaderRead;
502            if (context->getNativeFormatCaps(desc.pixelFormat).writable)
503            {
504                desc.usage = desc.usage | MTLTextureUsageShaderWrite;
505            }
506        }
507        set(metalDevice.newTextureWithDescriptor(desc, iosurface, plane));
508    }
509}
510
511Texture::Texture(Texture *original, MTLPixelFormat pixelFormat)
512    : Resource(original),
513      mColorWritableMask(original->mColorWritableMask)  // Share color write mask property
514{
515    ANGLE_MTL_OBJC_SCOPE
516    {
517        auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat];
518
519        set([view ANGLE_MTL_AUTORELEASE]);
520        // Texture views consume no additional memory
521        mEstimatedByteSize = 0;
522    }
523}
524
525Texture::Texture(Texture *original,
526                 MTLPixelFormat pixelFormat,
527                 MTLTextureType textureType,
528                 NSRange levels,
529                 NSRange slices)
530    : Resource(original),
531      mColorWritableMask(original->mColorWritableMask)  // Share color write mask property
532{
533    ANGLE_MTL_OBJC_SCOPE
534    {
535        auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat
536                                                       textureType:textureType
537                                                            levels:levels
538                                                            slices:slices];
539
540        set([view ANGLE_MTL_AUTORELEASE]);
541        // Texture views consume no additional memory
542        mEstimatedByteSize = 0;
543    }
544}
545
546Texture::Texture(Texture *original,
547                 MTLPixelFormat pixelFormat,
548                 MTLTextureType textureType,
549                 NSRange levels,
550                 NSRange slices,
551                 const TextureSwizzleChannels &swizzle)
552    : Resource(original),
553      mColorWritableMask(original->mColorWritableMask)  // Share color write mask property
554{
555#if ANGLE_MTL_SWIZZLE_AVAILABLE
556    ANGLE_MTL_OBJC_SCOPE
557    {
558        auto view = [original->get() newTextureViewWithPixelFormat:pixelFormat
559                                                       textureType:textureType
560                                                            levels:levels
561                                                            slices:slices
562                                                           swizzle:swizzle];
563
564        set([view ANGLE_MTL_AUTORELEASE]);
565        // Texture views consume no additional memory
566        mEstimatedByteSize = 0;
567    }
568#else
569    UNREACHABLE();
570#endif
571}
572
573void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder)
574{
575    InvokeCPUMemSync(context, blitEncoder, this);
576}
577
578void Texture::syncContentIfNeeded(ContextMtl *context)
579{
580    EnsureCPUMemWillBeSynced(context, this);
581}
582
583bool Texture::isCPUAccessible() const
584{
585#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
586    if (get().storageMode == MTLStorageModeManaged)
587    {
588        return true;
589    }
590#endif
591    return get().storageMode == MTLStorageModeShared;
592}
593
594bool Texture::isShaderReadable() const
595{
596    return get().usage & MTLTextureUsageShaderRead;
597}
598
599bool Texture::isShaderWritable() const
600{
601    return get().usage & MTLTextureUsageShaderWrite;
602}
603
604bool Texture::supportFormatView() const
605{
606    return get().usage & MTLTextureUsagePixelFormatView;
607}
608
609void Texture::replace2DRegion(ContextMtl *context,
610                              const MTLRegion &region,
611                              const MipmapNativeLevel &mipmapLevel,
612                              uint32_t slice,
613                              const uint8_t *data,
614                              size_t bytesPerRow)
615{
616    ASSERT(region.size.depth == 1);
617    replaceRegion(context, region, mipmapLevel, slice, data, bytesPerRow, 0);
618}
619
620void Texture::replaceRegion(ContextMtl *context,
621                            const MTLRegion &region,
622                            const MipmapNativeLevel &mipmapLevel,
623                            uint32_t slice,
624                            const uint8_t *data,
625                            size_t bytesPerRow,
626                            size_t bytesPer2DImage)
627{
628    if (mipmapLevel.get() >= this->mipmapLevels())
629    {
630        return;
631    }
632
633    ASSERT(isCPUAccessible());
634
635    CommandQueue &cmdQueue = context->cmdQueue();
636
637    syncContentIfNeeded(context);
638
639    // NOTE(hqle): what if multiple contexts on multiple threads are using this texture?
640    if (this->isBeingUsedByGPU(context))
641    {
642        context->flushCommandBuffer(mtl::NoWait);
643    }
644
645    cmdQueue.ensureResourceReadyForCPU(this);
646
647    if (textureType() != MTLTextureType3D)
648    {
649        bytesPer2DImage = 0;
650    }
651
652    [get() replaceRegion:region
653             mipmapLevel:mipmapLevel.get()
654                   slice:slice
655               withBytes:data
656             bytesPerRow:bytesPerRow
657           bytesPerImage:bytesPer2DImage];
658}
659
660void Texture::getBytes(ContextMtl *context,
661                       size_t bytesPerRow,
662                       size_t bytesPer2DInage,
663                       const MTLRegion &region,
664                       const MipmapNativeLevel &mipmapLevel,
665                       uint32_t slice,
666                       uint8_t *dataOut)
667{
668    ASSERT(isCPUAccessible());
669
670    CommandQueue &cmdQueue = context->cmdQueue();
671
672    syncContentIfNeeded(context);
673
674    // NOTE(hqle): what if multiple contexts on multiple threads are using this texture?
675    if (this->isBeingUsedByGPU(context))
676    {
677        context->flushCommandBuffer(mtl::NoWait);
678    }
679
680    cmdQueue.ensureResourceReadyForCPU(this);
681
682    [get() getBytes:dataOut
683          bytesPerRow:bytesPerRow
684        bytesPerImage:bytesPer2DInage
685           fromRegion:region
686          mipmapLevel:mipmapLevel.get()
687                slice:slice];
688}
689
690TextureRef Texture::createCubeFaceView(uint32_t face)
691{
692    ANGLE_MTL_OBJC_SCOPE
693    {
694        switch (textureType())
695        {
696            case MTLTextureTypeCube:
697                return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D,
698                                              NSMakeRange(0, mipmapLevels()),
699                                              NSMakeRange(face, 1)));
700            default:
701                UNREACHABLE();
702                return nullptr;
703        }
704    }
705}
706
707TextureRef Texture::createSliceMipView(uint32_t slice, const MipmapNativeLevel &level)
708{
709    ANGLE_MTL_OBJC_SCOPE
710    {
711        switch (textureType())
712        {
713            case MTLTextureTypeCube:
714            case MTLTextureType2D:
715            case MTLTextureType2DArray:
716                return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D,
717                                              NSMakeRange(level.get(), 1), NSMakeRange(slice, 1)));
718            default:
719                UNREACHABLE();
720                return nullptr;
721        }
722    }
723}
724
725TextureRef Texture::createMipView(const MipmapNativeLevel &level)
726{
727    ANGLE_MTL_OBJC_SCOPE
728    {
729        NSUInteger slices = cubeFacesOrArrayLength();
730        return TextureRef(new Texture(this, pixelFormat(), textureType(),
731                                      NSMakeRange(level.get(), 1), NSMakeRange(0, slices)));
732    }
733}
734
735TextureRef Texture::createMipsView(const MipmapNativeLevel &baseLevel, uint32_t levels)
736{
737    ANGLE_MTL_OBJC_SCOPE
738    {
739        NSUInteger slices = cubeFacesOrArrayLength();
740        return TextureRef(new Texture(this, pixelFormat(), textureType(),
741                                      NSMakeRange(baseLevel.get(), levels),
742                                      NSMakeRange(0, slices)));
743    }
744}
745
746TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format)
747{
748    ASSERT(supportFormatView());
749    return TextureRef(new Texture(this, format));
750}
751
752TextureRef Texture::createShaderImageView2D(const MipmapNativeLevel &level,
753                                            int layer,
754                                            MTLPixelFormat format)
755{
756    ASSERT(isShaderReadable());
757    ASSERT(isShaderWritable());
758    ASSERT(format == pixelFormat() || supportFormatView());
759    ASSERT(textureType() != MTLTextureType3D);
760    return TextureRef(new Texture(this, format, MTLTextureType2D, NSMakeRange(level.get(), 1),
761                                  NSMakeRange(layer, 1)));
762}
763
764TextureRef Texture::createViewWithCompatibleFormat(MTLPixelFormat format)
765{
766    return TextureRef(new Texture(this, format));
767}
768
769TextureRef Texture::createMipsSwizzleView(const MipmapNativeLevel &baseLevel,
770                                          uint32_t levels,
771                                          MTLPixelFormat format,
772                                          const TextureSwizzleChannels &swizzle)
773{
774#if ANGLE_MTL_SWIZZLE_AVAILABLE
775    return TextureRef(new Texture(this, format, textureType(), NSMakeRange(baseLevel.get(), levels),
776                                  NSMakeRange(0, cubeFacesOrArrayLength()), swizzle));
777#else
778    WARN() << "Texture swizzle is not supported on pre iOS 13.0 and macOS 15.0";
779    UNIMPLEMENTED();
780    return createMipsView(baseLevel, levels);
781#endif
782}
783
784MTLPixelFormat Texture::pixelFormat() const
785{
786    return get().pixelFormat;
787}
788
789MTLTextureType Texture::textureType() const
790{
791    return get().textureType;
792}
793
794uint32_t Texture::mipmapLevels() const
795{
796    return static_cast<uint32_t>(get().mipmapLevelCount);
797}
798
799uint32_t Texture::arrayLength() const
800{
801    return static_cast<uint32_t>(get().arrayLength);
802}
803
804uint32_t Texture::cubeFaces() const
805{
806    if (textureType() == MTLTextureTypeCube)
807    {
808        return 6;
809    }
810    return 1;
811}
812
813uint32_t Texture::cubeFacesOrArrayLength() const
814{
815    if (textureType() == MTLTextureTypeCube)
816    {
817        return 6;
818    }
819    return arrayLength();
820}
821
822uint32_t Texture::width(const MipmapNativeLevel &level) const
823{
824    return static_cast<uint32_t>(GetMipSize(get().width, level));
825}
826
827uint32_t Texture::height(const MipmapNativeLevel &level) const
828{
829    return static_cast<uint32_t>(GetMipSize(get().height, level));
830}
831
832uint32_t Texture::depth(const MipmapNativeLevel &level) const
833{
834    return static_cast<uint32_t>(GetMipSize(get().depth, level));
835}
836
837gl::Extents Texture::size(const MipmapNativeLevel &level) const
838{
839    gl::Extents re;
840
841    re.width  = width(level);
842    re.height = height(level);
843    re.depth  = depth(level);
844
845    return re;
846}
847
848gl::Extents Texture::size(const ImageNativeIndex &index) const
849{
850    gl::Extents extents = size(index.getNativeLevel());
851
852    if (index.hasLayer())
853    {
854        extents.depth = 1;
855    }
856
857    return extents;
858}
859
860uint32_t Texture::samples() const
861{
862    return static_cast<uint32_t>(get().sampleCount);
863}
864
865bool Texture::hasIOSurface() const
866{
867    return (get().iosurface) != nullptr;
868}
869
870bool Texture::sameTypeAndDimemsionsAs(const TextureRef &other) const
871{
872    return textureType() == other->textureType() && pixelFormat() == other->pixelFormat() &&
873           mipmapLevels() == other->mipmapLevels() &&
874           cubeFacesOrArrayLength() == other->cubeFacesOrArrayLength() &&
875           widthAt0() == other->widthAt0() && heightAt0() == other->heightAt0() &&
876           depthAt0() == other->depthAt0();
877}
878
879angle::Result Texture::resize(ContextMtl *context, uint32_t width, uint32_t height)
880{
881    // Resizing texture view is not supported.
882    ASSERT(mCreationDesc);
883
884    ANGLE_MTL_OBJC_SCOPE
885    {
886        MTLTextureDescriptor *newDesc = [[mCreationDesc.get() copy] ANGLE_MTL_AUTORELEASE];
887        newDesc.width                 = width;
888        newDesc.height                = height;
889        auto newTexture               = context->getMetalDevice().newTextureWithDescriptor(newDesc);
890        ANGLE_CHECK_GL_ALLOC(context, newTexture);
891        mCreationDesc.retainAssign(newDesc);
892        set(newTexture);
893        // Reset reference counter
894        Resource::reset();
895    }
896
897    return angle::Result::Continue;
898}
899
900TextureRef Texture::getLinearColorView()
901{
902    if (mLinearColorView)
903    {
904        return mLinearColorView;
905    }
906
907    switch (pixelFormat())
908    {
909        case MTLPixelFormatRGBA8Unorm_sRGB:
910            mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatRGBA8Unorm);
911            break;
912        case MTLPixelFormatBGRA8Unorm_sRGB:
913            mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatBGRA8Unorm);
914            break;
915        default:
916            // NOTE(hqle): Not all sRGB formats are supported yet.
917            UNREACHABLE();
918    }
919
920    return mLinearColorView;
921}
922
923TextureRef Texture::getReadableCopy(ContextMtl *context,
924                                    mtl::BlitCommandEncoder *encoder,
925                                    const uint32_t levelToCopy,
926                                    const uint32_t sliceToCopy,
927                                    const MTLRegion &areaToCopy)
928{
929    gl::Extents firstLevelSize = size(kZeroNativeMipLevel);
930    if (!mReadCopy || mReadCopy->get().width < static_cast<size_t>(firstLevelSize.width) ||
931        mReadCopy->get().height < static_cast<size_t>(firstLevelSize.height))
932    {
933        // Create a texture that big enough to store the first level data and any smaller level
934        ANGLE_MTL_OBJC_SCOPE
935        {
936            auto desc            = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE];
937            desc.textureType     = get().textureType;
938            desc.pixelFormat     = get().pixelFormat;
939            desc.width           = firstLevelSize.width;
940            desc.height          = firstLevelSize.height;
941            desc.depth           = 1;
942            desc.arrayLength     = 1;
943            desc.resourceOptions = MTLResourceStorageModePrivate;
944            desc.sampleCount     = get().sampleCount;
945            desc.usage           = MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView;
946            mReadCopy.reset(new Texture(context->getMetalDevice().newTextureWithDescriptor(desc)));
947        }  // ANGLE_MTL_OBJC_SCOPE
948    }
949
950    ASSERT(encoder);
951
952    encoder->copyTexture(shared_from_this(), sliceToCopy, mtl::MipmapNativeLevel(levelToCopy),
953                         mReadCopy, 0, mtl::kZeroNativeMipLevel, 1, 1);
954
955    return mReadCopy;
956}
957
958void Texture::releaseReadableCopy()
959{
960    mReadCopy = nullptr;
961}
962
963TextureRef Texture::getStencilView()
964{
965    if (mStencilView)
966    {
967        return mStencilView;
968    }
969
970    switch (pixelFormat())
971    {
972        case MTLPixelFormatStencil8:
973        case MTLPixelFormatX32_Stencil8:
974#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
975        case MTLPixelFormatX24_Stencil8:
976#endif
977            return mStencilView = shared_from_this();
978#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
979        case MTLPixelFormatDepth24Unorm_Stencil8:
980            mStencilView = createViewWithDifferentFormat(MTLPixelFormatX24_Stencil8);
981            break;
982#endif
983        case MTLPixelFormatDepth32Float_Stencil8:
984            mStencilView = createViewWithDifferentFormat(MTLPixelFormatX32_Stencil8);
985            break;
986        default:
987            UNREACHABLE();
988    }
989
990    return mStencilView;
991}
992
993TextureRef Texture::parentTexture()
994{
995    if (mParentTexture)
996    {
997        return mParentTexture;
998    }
999
1000    if (!get().parentTexture)
1001    {
1002        // Doesn't have parent.
1003        return nullptr;
1004    }
1005
1006    // Lazily construct parent's Texture object from parent's MTLTexture.
1007    // Note that the constructed Texture object is not the same as the same original object that
1008    // creates this view. However, it will share the same usageRef and MTLTexture with the
1009    // original Texture object. We do this to avoid cyclic reference between original Texture
1010    // and its view.
1011    //
1012    // For example, the original Texture object might keep a ref to its stencil view. Had we
1013    // kept the original object's ref in the stencil view, there would have been a cyclic
1014    // reference.
1015    //
1016    // This is OK because even though the Texture objects are not the same, they refer to same
1017    // MTLTexture and usageRef.
1018    mParentTexture.reset(new Texture(mUsageRef, get().parentTexture, mColorWritableMask));
1019
1020    return mParentTexture;
1021}
1022MipmapNativeLevel Texture::parentRelativeLevel()
1023{
1024    return mtl::GetNativeMipLevel(static_cast<uint32_t>(get().parentRelativeLevel), 0);
1025}
1026uint32_t Texture::parentRelativeSlice()
1027{
1028    return static_cast<uint32_t>(get().parentRelativeSlice);
1029}
1030
1031void Texture::set(id<MTLTexture> metalTexture)
1032{
1033    ParentClass::set(metalTexture);
1034    // Reset stencil view
1035    mStencilView     = nullptr;
1036    mLinearColorView = nullptr;
1037
1038    mParentTexture = nullptr;
1039}
1040
1041// Buffer implementation
1042
1043MTLStorageMode Buffer::getStorageModeForSharedBuffer(ContextMtl *contextMtl)
1044{
1045#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1046    if (ANGLE_UNLIKELY(contextMtl->getDisplay()->getFeatures().forceBufferGPUStorage.enabled))
1047    {
1048        return MTLStorageModeManaged;
1049    }
1050#endif
1051    return MTLStorageModeShared;
1052}
1053
1054MTLStorageMode Buffer::getStorageModeForUsage(ContextMtl *contextMtl, Usage usage)
1055{
1056#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1057    bool hasCpuAccess = false;
1058    switch (usage)
1059    {
1060        case Usage::StaticCopy:
1061        case Usage::StaticDraw:
1062        case Usage::StaticRead:
1063        case Usage::DynamicRead:
1064        case Usage::StreamRead:
1065            hasCpuAccess = true;
1066            break;
1067        default:
1068            break;
1069    }
1070    const auto &features = contextMtl->getDisplay()->getFeatures();
1071    if (hasCpuAccess)
1072    {
1073        if (features.alwaysUseManagedStorageModeForBuffers.enabled ||
1074            ANGLE_UNLIKELY(features.forceBufferGPUStorage.enabled))
1075        {
1076            return MTLStorageModeManaged;
1077        }
1078        return MTLStorageModeShared;
1079    }
1080    if (contextMtl->getMetalDevice().hasUnifiedMemory() ||
1081        features.alwaysUseSharedStorageModeForBuffers.enabled)
1082    {
1083        return MTLStorageModeShared;
1084    }
1085    return MTLStorageModeManaged;
1086#else
1087    ANGLE_UNUSED_VARIABLE(contextMtl);
1088    ANGLE_UNUSED_VARIABLE(usage);
1089    return MTLStorageModeShared;
1090#endif
1091}
1092
1093angle::Result Buffer::MakeBuffer(ContextMtl *context,
1094                                 size_t size,
1095                                 const uint8_t *data,
1096                                 BufferRef *bufferOut)
1097{
1098    auto storageMode = getStorageModeForUsage(context, Usage::DynamicDraw);
1099    return MakeBufferWithStorageMode(context, storageMode, size, data, bufferOut);
1100}
1101
1102angle::Result Buffer::MakeBufferWithStorageMode(ContextMtl *context,
1103                                                MTLStorageMode storageMode,
1104                                                size_t size,
1105                                                const uint8_t *data,
1106                                                BufferRef *bufferOut)
1107{
1108    bufferOut->reset(new Buffer(context, storageMode, size, data));
1109
1110    if (!(*bufferOut) || !(*bufferOut)->get())
1111    {
1112        ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY);
1113    }
1114
1115    return angle::Result::Continue;
1116}
1117
1118Buffer::Buffer(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data)
1119{
1120    (void)reset(context, storageMode, size, data);
1121}
1122
1123angle::Result Buffer::reset(ContextMtl *context,
1124                            MTLStorageMode storageMode,
1125                            size_t size,
1126                            const uint8_t *data)
1127{
1128    auto options = resourceOptionsForStorageMode(storageMode);
1129    set([&]() -> AutoObjCPtr<id<MTLBuffer>> {
1130        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
1131        if (size > [metalDevice maxBufferLength])
1132        {
1133            return nullptr;
1134        }
1135        if (data)
1136        {
1137            return metalDevice.newBufferWithBytes(data, size, options);
1138        }
1139        return metalDevice.newBufferWithLength(size, options);
1140    }());
1141    // Reset command buffer's reference serial
1142    Resource::reset();
1143
1144    return angle::Result::Continue;
1145}
1146
1147void Buffer::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder)
1148{
1149    InvokeCPUMemSync(context, blitEncoder, this);
1150}
1151
1152const uint8_t *Buffer::mapReadOnly(ContextMtl *context)
1153{
1154    return mapWithOpt(context, true, false);
1155}
1156
1157uint8_t *Buffer::map(ContextMtl *context)
1158{
1159    return mapWithOpt(context, false, false);
1160}
1161
1162uint8_t *Buffer::mapWithOpt(ContextMtl *context, bool readonly, bool noSync)
1163{
1164    mMapReadOnly = readonly;
1165
1166    if (!noSync && (isCPUReadMemSyncPending() || isCPUReadMemNeedSync() || !readonly))
1167    {
1168        CommandQueue &cmdQueue = context->cmdQueue();
1169
1170        EnsureCPUMemWillBeSynced(context, this);
1171
1172        if (this->isBeingUsedByGPU(context))
1173        {
1174            context->flushCommandBuffer(mtl::NoWait);
1175        }
1176
1177        cmdQueue.ensureResourceReadyForCPU(this);
1178        resetCPUReadMemSyncPending();
1179    }
1180
1181    return reinterpret_cast<uint8_t *>([get() contents]);
1182}
1183
1184void Buffer::unmap(ContextMtl *context)
1185{
1186    flush(context, 0, size());
1187
1188    // Reset read only flag
1189    mMapReadOnly = true;
1190}
1191
1192void Buffer::unmapNoFlush(ContextMtl *context)
1193{
1194    mMapReadOnly = true;
1195}
1196
1197void Buffer::unmapAndFlushSubset(ContextMtl *context, size_t offsetWritten, size_t sizeWritten)
1198{
1199#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1200    flush(context, offsetWritten, sizeWritten);
1201#endif
1202    mMapReadOnly = true;
1203}
1204
1205void Buffer::flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten)
1206{
1207#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1208    if (!mMapReadOnly)
1209    {
1210        if (get().storageMode == MTLStorageModeManaged)
1211        {
1212            size_t bufferSize  = size();
1213            size_t startOffset = std::min(offsetWritten, bufferSize);
1214            size_t endOffset   = std::min(offsetWritten + sizeWritten, bufferSize);
1215            size_t clampedSize = endOffset - startOffset;
1216            if (clampedSize > 0)
1217            {
1218                [get() didModifyRange:NSMakeRange(startOffset, clampedSize)];
1219            }
1220        }
1221    }
1222#endif
1223}
1224
1225size_t Buffer::size() const
1226{
1227    return get().length;
1228}
1229
1230MTLStorageMode Buffer::storageMode() const
1231{
1232    return get().storageMode;
1233}
1234}  // namespace mtl
1235}  // namespace rx
1236