• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkCanvas.h"
9#include "include/core/SkSurface.h"
10#include "include/gpu/GrBackendSurface.h"
11#include "include/gpu/GrDirectContext.h"
12#include "include/gpu/mtl/GrMtlBackendContext.h"
13#include "include/gpu/mtl/GrMtlTypes.h"
14#include "src/core/SkMathPriv.h"
15#include "src/gpu/GrCaps.h"
16#include "src/gpu/GrDirectContextPriv.h"
17#include "src/image/SkImage_Base.h"
18#include "tools/sk_app/MetalWindowContext.h"
19
20using sk_app::DisplayParams;
21using sk_app::MetalWindowContext;
22
23namespace sk_app {
24
25MetalWindowContext::MetalWindowContext(const DisplayParams& params)
26        : WindowContext(params)
27        , fValid(false)
28        , fDrawableHandle(nil) {
29    fDisplayParams.fMSAASampleCount = GrNextPow2(fDisplayParams.fMSAASampleCount);
30}
31
32NSURL* MetalWindowContext::CacheURL() {
33    NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory
34                                                            inDomains:NSUserDomainMask];
35    NSURL* cachePath = [paths objectAtIndex:0];
36    return [cachePath URLByAppendingPathComponent:@"binaryArchive.metallib"];
37}
38
39void MetalWindowContext::initializeContext() {
40    SkASSERT(!fContext);
41
42    fDevice.reset(MTLCreateSystemDefaultDevice());
43    fQueue.reset([*fDevice newCommandQueue]);
44
45    if (fDisplayParams.fMSAASampleCount > 1) {
46        if (@available(macOS 10.11, iOS 9.0, *)) {
47            if (![*fDevice supportsTextureSampleCount:fDisplayParams.fMSAASampleCount]) {
48                return;
49            }
50        } else {
51            return;
52        }
53    }
54    fSampleCount = fDisplayParams.fMSAASampleCount;
55    fStencilBits = 8;
56
57    fValid = this->onInitializeContext();
58
59#if GR_METAL_SDK_VERSION >= 230
60    if (fDisplayParams.fEnableBinaryArchive) {
61        if (@available(macOS 11.0, iOS 14.0, *)) {
62            sk_cfp<MTLBinaryArchiveDescriptor*> desc([MTLBinaryArchiveDescriptor new]);
63            (*desc).url = CacheURL(); // try to load
64            NSError* error;
65            fPipelineArchive = [*fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
66            if (!fPipelineArchive) {
67                (*desc).url = nil; // create new
68                fPipelineArchive = [*fDevice newBinaryArchiveWithDescriptor:*desc error:&error];
69                if (!fPipelineArchive) {
70                    SkDebugf("Error creating MTLBinaryArchive:\n%s\n",
71                             error.debugDescription.UTF8String);
72                }
73            }
74        }
75    } else {
76        if (@available(macOS 11.0, iOS 14.0, *)) {
77            fPipelineArchive = nil;
78        }
79    }
80#endif
81
82    GrMtlBackendContext backendContext = {};
83    backendContext.fDevice.retain((GrMTLHandle)fDevice.get());
84    backendContext.fQueue.retain((GrMTLHandle)fQueue.get());
85#if GR_METAL_SDK_VERSION >= 230
86    if (@available(macOS 11.0, iOS 14.0, *)) {
87        backendContext.fBinaryArchive.retain((__bridge GrMTLHandle)fPipelineArchive);
88    }
89#endif
90    fContext = GrDirectContext::MakeMetal(backendContext, fDisplayParams.fGrContextOptions);
91    if (!fContext && fDisplayParams.fMSAASampleCount > 1) {
92        fDisplayParams.fMSAASampleCount /= 2;
93        this->initializeContext();
94        return;
95    }
96}
97
98void MetalWindowContext::destroyContext() {
99    if (fContext) {
100        // in case we have outstanding refs to this (lua?)
101        fContext->abandonContext();
102        fContext.reset();
103    }
104
105    this->onDestroyContext();
106
107    fMetalLayer = nil;
108    fValid = false;
109
110#if GR_METAL_SDK_VERSION >= 230
111    if (@available(macOS 11.0, iOS 14.0, *)) {
112        [fPipelineArchive release];
113    }
114#endif
115    fQueue.reset();
116    fDevice.reset();
117}
118
119sk_sp<SkSurface> MetalWindowContext::getBackbufferSurface() {
120    sk_sp<SkSurface> surface;
121    if (fContext) {
122        if (fDisplayParams.fDelayDrawableAcquisition) {
123            surface = SkSurface::MakeFromCAMetalLayer(fContext.get(),
124                                                      (__bridge GrMTLHandle)fMetalLayer,
125                                                      kTopLeft_GrSurfaceOrigin, fSampleCount,
126                                                      kBGRA_8888_SkColorType,
127                                                      fDisplayParams.fColorSpace,
128                                                      &fDisplayParams.fSurfaceProps,
129                                                      &fDrawableHandle);
130        } else {
131            id<CAMetalDrawable> currentDrawable = [fMetalLayer nextDrawable];
132
133            GrMtlTextureInfo fbInfo;
134            fbInfo.fTexture.retain(currentDrawable.texture);
135
136            GrBackendRenderTarget backendRT(fWidth,
137                                            fHeight,
138                                            fSampleCount,
139                                            fbInfo);
140
141            surface = SkSurface::MakeFromBackendRenderTarget(fContext.get(), backendRT,
142                                                             kTopLeft_GrSurfaceOrigin,
143                                                             kBGRA_8888_SkColorType,
144                                                             fDisplayParams.fColorSpace,
145                                                             &fDisplayParams.fSurfaceProps);
146
147            fDrawableHandle = CFRetain((GrMTLHandle) currentDrawable);
148        }
149    }
150
151    return surface;
152}
153
154void MetalWindowContext::swapBuffers() {
155    id<CAMetalDrawable> currentDrawable = (id<CAMetalDrawable>)fDrawableHandle;
156
157    id<MTLCommandBuffer> commandBuffer([*fQueue commandBuffer]);
158    commandBuffer.label = @"Present";
159
160    [commandBuffer presentDrawable:currentDrawable];
161    [commandBuffer commit];
162    // ARC is off in sk_app, so we need to release the CF ref manually
163    CFRelease(fDrawableHandle);
164    fDrawableHandle = nil;
165}
166
167void MetalWindowContext::setDisplayParams(const DisplayParams& params) {
168    this->destroyContext();
169    fDisplayParams = params;
170    this->initializeContext();
171}
172
173void MetalWindowContext::activate(bool isActive) {
174    // serialize pipeline archive
175    if (!isActive) {
176#if GR_METAL_SDK_VERSION >= 230
177        if (@available(macOS 11.0, iOS 14.0, *)) {
178            if (fPipelineArchive) {
179                NSError* error;
180                [fPipelineArchive serializeToURL:CacheURL() error:&error];
181                if (error) {
182                    SkDebugf("Error storing MTLBinaryArchive:\n%s\n",
183                             error.debugDescription.UTF8String);
184                }
185            }
186        }
187#endif
188    }
189}
190
191}   //namespace sk_app
192