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