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