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