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 "tools/window/MetalWindowContext.h" 9 10#include "include/core/SkCanvas.h" 11#include "include/core/SkSurface.h" 12#include "include/gpu/ganesh/GrBackendSurface.h" 13#include "include/gpu/ganesh/GrDirectContext.h" 14#include "include/gpu/ganesh/SkSurfaceGanesh.h" 15#include "include/gpu/ganesh/mtl/GrMtlBackendContext.h" 16#include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h" 17#include "include/gpu/ganesh/mtl/GrMtlDirectContext.h" 18#include "include/gpu/ganesh/mtl/GrMtlTypes.h" 19#include "include/gpu/ganesh/mtl/SkSurfaceMetal.h" 20#include "src/base/SkMathPriv.h" 21#include "src/gpu/ganesh/GrCaps.h" 22#include "src/gpu/ganesh/GrDirectContextPriv.h" 23#include "src/image/SkImage_Base.h" 24 25using skwindow::DisplayParams; 26using skwindow::internal::MetalWindowContext; 27 28namespace skwindow::internal { 29 30MetalWindowContext::MetalWindowContext(std::unique_ptr<const DisplayParams> params) 31 : WindowContext(DisplayParamsBuilder(params.get()).roundUpMSAA().build()) 32 , fValid(false) 33 , fDrawableHandle(nil) {} 34 35void MetalWindowContext::initializeContext() { 36 SkASSERT(!fContext); 37 38 fDevice.reset(MTLCreateSystemDefaultDevice()); 39 fQueue.reset([*fDevice newCommandQueue]); 40 41 if (fDisplayParams->msaaSampleCount() > 1) { 42 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 43 if (![*fDevice supportsTextureSampleCount:fDisplayParams->msaaSampleCount()]) { 44 return; 45 } 46 } else { 47 return; 48 } 49 } 50 fSampleCount = fDisplayParams->msaaSampleCount(); 51 fStencilBits = 8; 52 53 fValid = this->onInitializeContext(); 54 55 GrMtlBackendContext backendContext = {}; 56 backendContext.fDevice.retain((GrMTLHandle)fDevice.get()); 57 backendContext.fQueue.retain((GrMTLHandle)fQueue.get()); 58 fContext = GrDirectContexts::MakeMetal(backendContext, fDisplayParams->grContextOptions()); 59 if (!fContext && fDisplayParams->msaaSampleCount() > 1) { 60 auto newParams = DisplayParamsBuilder(fDisplayParams.get()); 61 newParams.msaaSampleCount(fDisplayParams->msaaSampleCount() / 2); 62 // Don't call this->setDisplayParams because that also calls 63 // destroyContext() and initializeContext(). 64 fDisplayParams = newParams.build(); 65 this->initializeContext(); 66 return; 67 } 68} 69 70void MetalWindowContext::destroyContext() { 71 if (fContext) { 72 // in case we have outstanding refs to this (lua?) 73 fContext->abandonContext(); 74 fContext.reset(); 75 } 76 77 this->onDestroyContext(); 78 79 fMetalLayer = nil; 80 fValid = false; 81 82 fQueue.reset(); 83 fDevice.reset(); 84} 85 86sk_sp<SkSurface> MetalWindowContext::getBackbufferSurface() { 87 sk_sp<SkSurface> surface; 88 if (fContext) { 89 if (fDisplayParams->delayDrawableAcquisition()) { 90 surface = SkSurfaces::WrapCAMetalLayer(fContext.get(), 91 (__bridge GrMTLHandle)fMetalLayer, 92 kTopLeft_GrSurfaceOrigin, 93 fSampleCount, 94 kBGRA_8888_SkColorType, 95 fDisplayParams->colorSpace(), 96 &fDisplayParams->surfaceProps(), 97 &fDrawableHandle); 98 } else { 99 id<CAMetalDrawable> currentDrawable = [fMetalLayer nextDrawable]; 100 if (currentDrawable == nil) { 101 return nullptr; 102 } 103 104 GrMtlTextureInfo fbInfo; 105 fbInfo.fTexture.retain(currentDrawable.texture); 106 107 GrBackendRenderTarget backendRT = 108 GrBackendRenderTargets::MakeMtl(fWidth, fHeight, fbInfo); 109 110 surface = SkSurfaces::WrapBackendRenderTarget(fContext.get(), 111 backendRT, 112 kTopLeft_GrSurfaceOrigin, 113 kBGRA_8888_SkColorType, 114 fDisplayParams->colorSpace(), 115 &fDisplayParams->surfaceProps()); 116 117 fDrawableHandle = CFRetain((GrMTLHandle) currentDrawable); 118 } 119 } 120 121 return surface; 122} 123 124void MetalWindowContext::onSwapBuffers() { 125 id<CAMetalDrawable> currentDrawable = (id<CAMetalDrawable>)fDrawableHandle; 126 127 id<MTLCommandBuffer> commandBuffer([*fQueue commandBuffer]); 128 commandBuffer.label = @"Present"; 129 130 [commandBuffer presentDrawable:currentDrawable]; 131 [commandBuffer commit]; 132 // ARC is off in sk_app, so we need to release the CF ref manually 133 CFRelease(fDrawableHandle); 134 fDrawableHandle = nil; 135} 136 137void MetalWindowContext::setDisplayParams(std::unique_ptr<const DisplayParams> params) { 138 this->destroyContext(); 139 fDisplayParams = std::move(params); 140 this->initializeContext(); 141} 142 143} //namespace skwindow::internal 144