• 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/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        angle::ObjCPtr<MTLTextureDescriptor> desc = angle::adoptObjCPtr([MTLTextureDescriptor new]);
227        desc.get().textureType                    = MTLTextureType2DMultisample;
228        desc.get().pixelFormat                    = format.metalFormat;
229        desc.get().width                          = width;
230        desc.get().height                         = height;
231        desc.get().mipmapLevelCount               = 1;
232        desc.get().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_CHECK_GL_ALLOC(context, newTexture->valid());
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_CHECK_GL_ALLOC(context, newTexture->valid());
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        mCreationDesc = std::move(desc);
481    }
482}
483
484Texture::Texture(ContextMtl *context,
485                 MTLTextureDescriptor *desc,
486                 IOSurfaceRef iosurface,
487                 NSUInteger plane,
488                 bool renderTargetOnly)
489    : mColorWritableMask(std::make_shared<MTLColorWriteMask>(MTLColorWriteMaskAll))
490{
491    ANGLE_MTL_OBJC_SCOPE
492    {
493        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
494
495        // Every texture will support being rendered for now
496        desc.usage = MTLTextureUsagePixelFormatView;
497
498        if (context->getNativeFormatCaps(desc.pixelFormat).isRenderable())
499        {
500            desc.usage |= MTLTextureUsageRenderTarget;
501        }
502
503#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
504        desc.resourceOptions = MTLResourceStorageModeManaged;
505#else
506        desc.resourceOptions = MTLResourceStorageModeShared;
507#endif
508
509        if (!renderTargetOnly)
510        {
511            desc.usage = desc.usage | MTLTextureUsageShaderRead;
512            if (context->getNativeFormatCaps(desc.pixelFormat).writable)
513            {
514                desc.usage = desc.usage | MTLTextureUsageShaderWrite;
515            }
516        }
517        set(metalDevice.newTextureWithDescriptor(desc, iosurface, plane));
518    }
519}
520
521Texture::Texture(Texture *original, MTLPixelFormat pixelFormat)
522    : Resource(original),
523      mColorWritableMask(original->mColorWritableMask)  // Share color write mask property
524{
525    ANGLE_MTL_OBJC_SCOPE
526    {
527        set(angle::adoptObjCPtr([original->get() newTextureViewWithPixelFormat:pixelFormat]));
528        // Texture views consume no additional memory
529        mEstimatedByteSize = 0;
530    }
531}
532
533Texture::Texture(Texture *original,
534                 MTLPixelFormat pixelFormat,
535                 MTLTextureType textureType,
536                 NSRange levels,
537                 NSRange slices)
538    : Resource(original),
539      mColorWritableMask(original->mColorWritableMask)  // Share color write mask property
540{
541    ANGLE_MTL_OBJC_SCOPE
542    {
543        set(angle::adoptObjCPtr([original->get() newTextureViewWithPixelFormat:pixelFormat
544                                                                   textureType:textureType
545                                                                        levels:levels
546                                                                        slices:slices]));
547        // Texture views consume no additional memory
548        mEstimatedByteSize = 0;
549    }
550}
551
552Texture::Texture(Texture *original,
553                 MTLPixelFormat pixelFormat,
554                 MTLTextureType textureType,
555                 NSRange levels,
556                 NSRange slices,
557                 const MTLTextureSwizzleChannels &swizzle)
558    : Resource(original),
559      mColorWritableMask(original->mColorWritableMask)  // Share color write mask property
560{
561    ANGLE_MTL_OBJC_SCOPE
562    {
563        set(angle::adoptObjCPtr([original->get() newTextureViewWithPixelFormat:pixelFormat
564                                                                   textureType:textureType
565                                                                        levels:levels
566                                                                        slices:slices
567                                                                       swizzle:swizzle]));
568
569        // Texture views consume no additional memory
570        mEstimatedByteSize = 0;
571    }
572}
573
574void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder)
575{
576    InvokeCPUMemSync(context, blitEncoder, this);
577}
578
579void Texture::syncContentIfNeeded(ContextMtl *context)
580{
581    EnsureCPUMemWillBeSynced(context, this);
582}
583
584bool Texture::isCPUAccessible() const
585{
586#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
587    if (get().storageMode == MTLStorageModeManaged)
588    {
589        return true;
590    }
591#endif
592    return get().storageMode == MTLStorageModeShared;
593}
594
595bool Texture::isShaderReadable() const
596{
597    return get().usage & MTLTextureUsageShaderRead;
598}
599
600bool Texture::isShaderWritable() const
601{
602    return get().usage & MTLTextureUsageShaderWrite;
603}
604
605bool Texture::supportFormatView() const
606{
607    return get().usage & MTLTextureUsagePixelFormatView;
608}
609
610void Texture::replace2DRegion(ContextMtl *context,
611                              const MTLRegion &region,
612                              const MipmapNativeLevel &mipmapLevel,
613                              uint32_t slice,
614                              const uint8_t *data,
615                              size_t bytesPerRow)
616{
617    ASSERT(region.size.depth == 1);
618    replaceRegion(context, region, mipmapLevel, slice, data, bytesPerRow, 0);
619}
620
621void Texture::replaceRegion(ContextMtl *context,
622                            const MTLRegion &region,
623                            const MipmapNativeLevel &mipmapLevel,
624                            uint32_t slice,
625                            const uint8_t *data,
626                            size_t bytesPerRow,
627                            size_t bytesPer2DImage)
628{
629    if (mipmapLevel.get() >= this->mipmapLevels())
630    {
631        return;
632    }
633
634    ASSERT(isCPUAccessible());
635
636    CommandQueue &cmdQueue = context->cmdQueue();
637
638    syncContentIfNeeded(context);
639
640    // NOTE(hqle): what if multiple contexts on multiple threads are using this texture?
641    if (this->isBeingUsedByGPU(context))
642    {
643        context->flushCommandBuffer(mtl::NoWait);
644    }
645
646    cmdQueue.ensureResourceReadyForCPU(this);
647
648    if (textureType() != MTLTextureType3D)
649    {
650        bytesPer2DImage = 0;
651    }
652
653    [get() replaceRegion:region
654             mipmapLevel:mipmapLevel.get()
655                   slice:slice
656               withBytes:data
657             bytesPerRow:bytesPerRow
658           bytesPerImage:bytesPer2DImage];
659}
660
661void Texture::getBytes(ContextMtl *context,
662                       size_t bytesPerRow,
663                       size_t bytesPer2DInage,
664                       const MTLRegion &region,
665                       const MipmapNativeLevel &mipmapLevel,
666                       uint32_t slice,
667                       uint8_t *dataOut)
668{
669    ASSERT(isCPUAccessible());
670
671    CommandQueue &cmdQueue = context->cmdQueue();
672
673    syncContentIfNeeded(context);
674
675    // NOTE(hqle): what if multiple contexts on multiple threads are using this texture?
676    if (this->isBeingUsedByGPU(context))
677    {
678        context->flushCommandBuffer(mtl::NoWait);
679    }
680
681    cmdQueue.ensureResourceReadyForCPU(this);
682
683    [get() getBytes:dataOut
684          bytesPerRow:bytesPerRow
685        bytesPerImage:bytesPer2DInage
686           fromRegion:region
687          mipmapLevel:mipmapLevel.get()
688                slice:slice];
689}
690
691TextureRef Texture::createCubeFaceView(uint32_t face)
692{
693    ANGLE_MTL_OBJC_SCOPE
694    {
695        switch (textureType())
696        {
697            case MTLTextureTypeCube:
698                return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D,
699                                              NSMakeRange(0, mipmapLevels()),
700                                              NSMakeRange(face, 1)));
701            default:
702                UNREACHABLE();
703                return nullptr;
704        }
705    }
706}
707
708TextureRef Texture::createSliceMipView(uint32_t slice, const MipmapNativeLevel &level)
709{
710    ANGLE_MTL_OBJC_SCOPE
711    {
712        switch (textureType())
713        {
714            case MTLTextureTypeCube:
715            case MTLTextureType2D:
716            case MTLTextureType2DArray:
717                return TextureRef(new Texture(this, pixelFormat(), MTLTextureType2D,
718                                              NSMakeRange(level.get(), 1), NSMakeRange(slice, 1)));
719            default:
720                UNREACHABLE();
721                return nullptr;
722        }
723    }
724}
725
726TextureRef Texture::createMipView(const MipmapNativeLevel &level)
727{
728    ANGLE_MTL_OBJC_SCOPE
729    {
730        NSUInteger slices = cubeFacesOrArrayLength();
731        return TextureRef(new Texture(this, pixelFormat(), textureType(),
732                                      NSMakeRange(level.get(), 1), NSMakeRange(0, slices)));
733    }
734}
735
736TextureRef Texture::createMipsView(const MipmapNativeLevel &baseLevel, uint32_t levels)
737{
738    ANGLE_MTL_OBJC_SCOPE
739    {
740        NSUInteger slices = cubeFacesOrArrayLength();
741        return TextureRef(new Texture(this, pixelFormat(), textureType(),
742                                      NSMakeRange(baseLevel.get(), levels),
743                                      NSMakeRange(0, slices)));
744    }
745}
746
747TextureRef Texture::createViewWithDifferentFormat(MTLPixelFormat format)
748{
749    ASSERT(supportFormatView());
750    return TextureRef(new Texture(this, format));
751}
752
753TextureRef Texture::createShaderImageView2D(const MipmapNativeLevel &level,
754                                            int layer,
755                                            MTLPixelFormat format)
756{
757    ASSERT(isShaderReadable());
758    ASSERT(isShaderWritable());
759    ASSERT(format == pixelFormat() || supportFormatView());
760    ASSERT(textureType() != MTLTextureType3D);
761    return TextureRef(new Texture(this, format, MTLTextureType2D, NSMakeRange(level.get(), 1),
762                                  NSMakeRange(layer, 1)));
763}
764
765TextureRef Texture::createViewWithCompatibleFormat(MTLPixelFormat format)
766{
767    return TextureRef(new Texture(this, format));
768}
769
770TextureRef Texture::createMipsSwizzleView(const MipmapNativeLevel &baseLevel,
771                                          uint32_t levels,
772                                          MTLPixelFormat format,
773                                          const MTLTextureSwizzleChannels &swizzle)
774{
775    return TextureRef(new Texture(this, format, textureType(), NSMakeRange(baseLevel.get(), levels),
776                                  NSMakeRange(0, cubeFacesOrArrayLength()), swizzle));
777}
778
779MTLPixelFormat Texture::pixelFormat() const
780{
781    return get().pixelFormat;
782}
783
784MTLTextureType Texture::textureType() const
785{
786    return get().textureType;
787}
788
789uint32_t Texture::mipmapLevels() const
790{
791    return static_cast<uint32_t>(get().mipmapLevelCount);
792}
793
794uint32_t Texture::arrayLength() const
795{
796    return static_cast<uint32_t>(get().arrayLength);
797}
798
799uint32_t Texture::cubeFaces() const
800{
801    if (textureType() == MTLTextureTypeCube)
802    {
803        return 6;
804    }
805    return 1;
806}
807
808uint32_t Texture::cubeFacesOrArrayLength() const
809{
810    if (textureType() == MTLTextureTypeCube)
811    {
812        return 6;
813    }
814    return arrayLength();
815}
816
817uint32_t Texture::width(const MipmapNativeLevel &level) const
818{
819    return static_cast<uint32_t>(GetMipSize(get().width, level));
820}
821
822uint32_t Texture::height(const MipmapNativeLevel &level) const
823{
824    return static_cast<uint32_t>(GetMipSize(get().height, level));
825}
826
827uint32_t Texture::depth(const MipmapNativeLevel &level) const
828{
829    return static_cast<uint32_t>(GetMipSize(get().depth, level));
830}
831
832gl::Extents Texture::size(const MipmapNativeLevel &level) const
833{
834    gl::Extents re;
835
836    re.width  = width(level);
837    re.height = height(level);
838    re.depth  = depth(level);
839
840    return re;
841}
842
843gl::Extents Texture::size(const ImageNativeIndex &index) const
844{
845    gl::Extents extents = size(index.getNativeLevel());
846
847    if (index.hasLayer())
848    {
849        extents.depth = 1;
850    }
851
852    return extents;
853}
854
855uint32_t Texture::samples() const
856{
857    return static_cast<uint32_t>(get().sampleCount);
858}
859
860bool Texture::hasIOSurface() const
861{
862    return (get().iosurface) != nullptr;
863}
864
865bool Texture::sameTypeAndDimemsionsAs(const TextureRef &other) const
866{
867    return textureType() == other->textureType() && pixelFormat() == other->pixelFormat() &&
868           mipmapLevels() == other->mipmapLevels() &&
869           cubeFacesOrArrayLength() == other->cubeFacesOrArrayLength() &&
870           widthAt0() == other->widthAt0() && heightAt0() == other->heightAt0() &&
871           depthAt0() == other->depthAt0();
872}
873
874angle::Result Texture::resize(ContextMtl *context, uint32_t width, uint32_t height)
875{
876    // Resizing texture view is not supported.
877    ASSERT(mCreationDesc);
878
879    ANGLE_MTL_OBJC_SCOPE
880    {
881        angle::ObjCPtr<MTLTextureDescriptor> newDesc =
882            angle::adoptObjCPtr([mCreationDesc.get() copy]);
883        newDesc.get().width           = width;
884        newDesc.get().height          = height;
885        auto newTexture               = context->getMetalDevice().newTextureWithDescriptor(newDesc);
886        ANGLE_CHECK_GL_ALLOC(context, newTexture);
887        mCreationDesc = std::move(newDesc);
888        set(std::move(newTexture));
889        // Reset reference counter
890        Resource::reset();
891    }
892
893    return angle::Result::Continue;
894}
895
896TextureRef Texture::getLinearColorView()
897{
898    if (mLinearColorView)
899    {
900        return mLinearColorView;
901    }
902
903    switch (pixelFormat())
904    {
905        case MTLPixelFormatRGBA8Unorm_sRGB:
906            mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatRGBA8Unorm);
907            break;
908        case MTLPixelFormatBGRA8Unorm_sRGB:
909            mLinearColorView = createViewWithCompatibleFormat(MTLPixelFormatBGRA8Unorm);
910            break;
911        default:
912            // NOTE(hqle): Not all sRGB formats are supported yet.
913            UNREACHABLE();
914    }
915
916    return mLinearColorView;
917}
918
919TextureRef Texture::getReadableCopy(ContextMtl *context,
920                                    mtl::BlitCommandEncoder *encoder,
921                                    const uint32_t levelToCopy,
922                                    const uint32_t sliceToCopy,
923                                    const MTLRegion &areaToCopy)
924{
925    gl::Extents firstLevelSize = size(kZeroNativeMipLevel);
926    if (!mReadCopy || mReadCopy->get().width < static_cast<size_t>(firstLevelSize.width) ||
927        mReadCopy->get().height < static_cast<size_t>(firstLevelSize.height))
928    {
929        // Create a texture that big enough to store the first level data and any smaller level
930        ANGLE_MTL_OBJC_SCOPE
931        {
932            angle::ObjCPtr<MTLTextureDescriptor> desc =
933                angle::adoptObjCPtr([MTLTextureDescriptor new]);
934            desc.get().textureType     = get().textureType;
935            desc.get().pixelFormat     = get().pixelFormat;
936            desc.get().width           = firstLevelSize.width;
937            desc.get().height          = firstLevelSize.height;
938            desc.get().depth           = 1;
939            desc.get().arrayLength     = 1;
940            desc.get().resourceOptions = MTLResourceStorageModePrivate;
941            desc.get().sampleCount     = get().sampleCount;
942            desc.get().usage           = MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView;
943            mReadCopy.reset(new Texture(context->getMetalDevice().newTextureWithDescriptor(desc)));
944        }  // ANGLE_MTL_OBJC_SCOPE
945    }
946
947    ASSERT(encoder);
948
949    encoder->copyTexture(shared_from_this(), sliceToCopy, mtl::MipmapNativeLevel(levelToCopy),
950                         mReadCopy, 0, mtl::kZeroNativeMipLevel, 1, 1);
951
952    return mReadCopy;
953}
954
955void Texture::releaseReadableCopy()
956{
957    mReadCopy = nullptr;
958}
959
960TextureRef Texture::getStencilView()
961{
962    if (mStencilView)
963    {
964        return mStencilView;
965    }
966
967    switch (pixelFormat())
968    {
969        case MTLPixelFormatStencil8:
970        case MTLPixelFormatX32_Stencil8:
971#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
972        case MTLPixelFormatX24_Stencil8:
973#endif
974            // This texture is already a stencil texture. Return its ref directly.
975            return shared_from_this();
976#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
977        case MTLPixelFormatDepth24Unorm_Stencil8:
978            mStencilView = createViewWithDifferentFormat(MTLPixelFormatX24_Stencil8);
979            break;
980#endif
981        case MTLPixelFormatDepth32Float_Stencil8:
982            mStencilView = createViewWithDifferentFormat(MTLPixelFormatX32_Stencil8);
983            break;
984        default:
985            UNREACHABLE();
986    }
987
988    return mStencilView;
989}
990
991TextureRef Texture::parentTexture()
992{
993    if (mParentTexture)
994    {
995        return mParentTexture;
996    }
997
998    if (!get().parentTexture)
999    {
1000        // Doesn't have parent.
1001        return nullptr;
1002    }
1003
1004    // Lazily construct parent's Texture object from parent's MTLTexture.
1005    // Note that the constructed Texture object is not the same as the same original object that
1006    // creates this view. However, it will share the same usageRef and MTLTexture with the
1007    // original Texture object. We do this to avoid cyclic reference between original Texture
1008    // and its view.
1009    //
1010    // For example, the original Texture object might keep a ref to its stencil view. Had we
1011    // kept the original object's ref in the stencil view, there would have been a cyclic
1012    // reference.
1013    //
1014    // This is OK because even though the Texture objects are not the same, they refer to same
1015    // MTLTexture and usageRef.
1016    mParentTexture.reset(new Texture(mUsageRef, get().parentTexture, mColorWritableMask));
1017
1018    return mParentTexture;
1019}
1020MipmapNativeLevel Texture::parentRelativeLevel()
1021{
1022    return mtl::GetNativeMipLevel(static_cast<uint32_t>(get().parentRelativeLevel), 0);
1023}
1024uint32_t Texture::parentRelativeSlice()
1025{
1026    return static_cast<uint32_t>(get().parentRelativeSlice);
1027}
1028
1029void Texture::set(id<MTLTexture> metalTexture)
1030{
1031    ParentClass::set(metalTexture);
1032    // Reset stencil view
1033    mStencilView     = nullptr;
1034    mLinearColorView = nullptr;
1035
1036    mParentTexture = nullptr;
1037}
1038
1039// Buffer implementation
1040
1041MTLStorageMode Buffer::getStorageModeForSharedBuffer(ContextMtl *contextMtl)
1042{
1043#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1044    if (ANGLE_UNLIKELY(contextMtl->getDisplay()->getFeatures().forceBufferGPUStorage.enabled))
1045    {
1046        return MTLStorageModeManaged;
1047    }
1048#endif
1049    return MTLStorageModeShared;
1050}
1051
1052MTLStorageMode Buffer::getStorageModeForUsage(ContextMtl *contextMtl, Usage usage)
1053{
1054#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1055    bool hasCpuAccess = false;
1056    switch (usage)
1057    {
1058        case Usage::StaticCopy:
1059        case Usage::StaticDraw:
1060        case Usage::StaticRead:
1061        case Usage::DynamicRead:
1062        case Usage::StreamRead:
1063            hasCpuAccess = true;
1064            break;
1065        default:
1066            break;
1067    }
1068    const auto &features = contextMtl->getDisplay()->getFeatures();
1069    if (hasCpuAccess)
1070    {
1071        if (features.alwaysUseManagedStorageModeForBuffers.enabled ||
1072            ANGLE_UNLIKELY(features.forceBufferGPUStorage.enabled))
1073        {
1074            return MTLStorageModeManaged;
1075        }
1076        return MTLStorageModeShared;
1077    }
1078    if (contextMtl->getMetalDevice().hasUnifiedMemory() ||
1079        features.alwaysUseSharedStorageModeForBuffers.enabled)
1080    {
1081        return MTLStorageModeShared;
1082    }
1083    return MTLStorageModeManaged;
1084#else
1085    ANGLE_UNUSED_VARIABLE(contextMtl);
1086    ANGLE_UNUSED_VARIABLE(usage);
1087    return MTLStorageModeShared;
1088#endif
1089}
1090
1091angle::Result Buffer::MakeBuffer(ContextMtl *context,
1092                                 size_t size,
1093                                 const uint8_t *data,
1094                                 BufferRef *bufferOut)
1095{
1096    auto storageMode = getStorageModeForUsage(context, Usage::DynamicDraw);
1097    return MakeBufferWithStorageMode(context, storageMode, size, data, bufferOut);
1098}
1099
1100angle::Result Buffer::MakeBufferWithStorageMode(ContextMtl *context,
1101                                                MTLStorageMode storageMode,
1102                                                size_t size,
1103                                                const uint8_t *data,
1104                                                BufferRef *bufferOut)
1105{
1106    bufferOut->reset(new Buffer(context, storageMode, size, data));
1107    ANGLE_CHECK_GL_ALLOC(context, *bufferOut && (*bufferOut)->get());
1108    return angle::Result::Continue;
1109}
1110
1111Buffer::Buffer(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data)
1112{
1113    (void)reset(context, storageMode, size, data);
1114}
1115
1116angle::Result Buffer::reset(ContextMtl *context,
1117                            MTLStorageMode storageMode,
1118                            size_t size,
1119                            const uint8_t *data)
1120{
1121    auto options = resourceOptionsForStorageMode(storageMode);
1122    set([&]() -> angle::ObjCPtr<id<MTLBuffer>> {
1123        const mtl::ContextDevice &metalDevice = context->getMetalDevice();
1124        if (size > [metalDevice maxBufferLength])
1125        {
1126            return nullptr;
1127        }
1128        if (data)
1129        {
1130            return metalDevice.newBufferWithBytes(data, size, options);
1131        }
1132        return metalDevice.newBufferWithLength(size, options);
1133    }());
1134    // Reset command buffer's reference serial
1135    Resource::reset();
1136
1137    return angle::Result::Continue;
1138}
1139
1140void Buffer::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEncoder)
1141{
1142    InvokeCPUMemSync(context, blitEncoder, this);
1143}
1144
1145const uint8_t *Buffer::mapReadOnly(ContextMtl *context)
1146{
1147    return mapWithOpt(context, true, false);
1148}
1149
1150uint8_t *Buffer::map(ContextMtl *context, size_t offset)
1151{
1152    ASSERT(offset < size());
1153    uint8_t *result = mapWithOpt(context, false, false);
1154    return result != nullptr ? result + offset : nullptr;
1155}
1156
1157uint8_t *Buffer::mapWithOpt(ContextMtl *context, bool readonly, bool noSync)
1158{
1159    mMapReadOnly = readonly;
1160
1161    if (!noSync && (isCPUReadMemSyncPending() || isCPUReadMemNeedSync() || !readonly))
1162    {
1163        CommandQueue &cmdQueue = context->cmdQueue();
1164
1165        EnsureCPUMemWillBeSynced(context, this);
1166
1167        if (this->isBeingUsedByGPU(context))
1168        {
1169            context->flushCommandBuffer(mtl::NoWait);
1170        }
1171
1172        cmdQueue.ensureResourceReadyForCPU(this);
1173        resetCPUReadMemSyncPending();
1174    }
1175
1176    return reinterpret_cast<uint8_t *>([get() contents]);
1177}
1178
1179void Buffer::unmap(ContextMtl *context)
1180{
1181    flush(context, 0, size());
1182
1183    // Reset read only flag
1184    mMapReadOnly = true;
1185}
1186
1187void Buffer::unmapNoFlush(ContextMtl *context)
1188{
1189    mMapReadOnly = true;
1190}
1191
1192void Buffer::unmapAndFlushSubset(ContextMtl *context, size_t offsetWritten, size_t sizeWritten)
1193{
1194#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1195    flush(context, offsetWritten, sizeWritten);
1196#endif
1197    mMapReadOnly = true;
1198}
1199
1200void Buffer::flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten)
1201{
1202#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1203    if (!mMapReadOnly)
1204    {
1205        if (get().storageMode == MTLStorageModeManaged)
1206        {
1207            size_t bufferSize  = size();
1208            size_t startOffset = std::min(offsetWritten, bufferSize);
1209            size_t endOffset   = std::min(offsetWritten + sizeWritten, bufferSize);
1210            size_t clampedSize = endOffset - startOffset;
1211            if (clampedSize > 0)
1212            {
1213                [get() didModifyRange:NSMakeRange(startOffset, clampedSize)];
1214            }
1215        }
1216    }
1217#endif
1218}
1219
1220size_t Buffer::size() const
1221{
1222    return get().length;
1223}
1224
1225MTLStorageMode Buffer::storageMode() const
1226{
1227    return get().storageMode;
1228}
1229}  // namespace mtl
1230}  // namespace rx
1231