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/sk_app/DawnWindowContext.h" 9#include "tools/sk_app/mac/WindowContextFactory_mac.h" 10#include "webgpu/webgpu_cpp.h" 11#include "dawn/dawn_wsi.h" 12#include "dawn/native/DawnNative.h" 13#include "dawn/native/MetalBackend.h" 14 15#import <Metal/Metal.h> 16#import <QuartzCore/CAMetalLayer.h> 17#import <Cocoa/Cocoa.h> 18 19namespace sk_app { 20 21using sk_app::window_context_factory::MacWindowInfo; 22 23template <typename T> 24DawnSwapChainImplementation CreateSwapChainImplementation(T* swapChain) { 25 DawnSwapChainImplementation impl = {}; 26 impl.userData = swapChain; 27 impl.Init = [](void* userData, void* wsiContext) { 28 auto* ctx = static_cast<typename T::WSIContext*>(wsiContext); 29 reinterpret_cast<T*>(userData)->Init(ctx); 30 }; 31 impl.Destroy = [](void* userData) { delete reinterpret_cast<T*>(userData); }; 32 impl.Configure = [](void* userData, WGPUTextureFormat format, WGPUTextureUsage allowedUsage, 33 uint32_t width, uint32_t height) { 34 return static_cast<T*>(userData)->Configure(format, allowedUsage, width, height); 35 }; 36 impl.GetNextTexture = [](void* userData, DawnSwapChainNextTexture* nextTexture) { 37 return static_cast<T*>(userData)->GetNextTexture(nextTexture); 38 }; 39 impl.Present = [](void* userData) { return static_cast<T*>(userData)->Present(); }; 40 return impl; 41} 42 43class DawnMTLWindowContext : public DawnWindowContext { 44public: 45 DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params); 46 ~DawnMTLWindowContext() override; 47 wgpu::Device onInitializeContext() override; 48 void onDestroyContext() override; 49 DawnSwapChainImplementation createSwapChainImplementation(int width, int height, 50 const DisplayParams& params) override; 51 void onSwapBuffers() override; 52 void resize(int width, int height) override; 53private: 54 NSView* fMainView; 55 id<MTLDevice> fMTLDevice; 56 CAMetalLayer* fLayer; 57}; 58 59class SwapChainImplMTL { 60public: 61 typedef void WSIContext; 62 static DawnSwapChainImplementation Create(id<MTLDevice> device, CAMetalLayer* layer) { 63 auto impl = new SwapChainImplMTL(device, layer); 64 return CreateSwapChainImplementation<SwapChainImplMTL>(impl); 65 } 66 67 void Init(WSIContext* ctx) {} 68 69 SwapChainImplMTL(id<MTLDevice> device, CAMetalLayer* layer) 70 : fQueue([device newCommandQueue]) 71 , fLayer(layer) {} 72 73 ~SwapChainImplMTL() {} 74 75 DawnSwapChainError Configure(WGPUTextureFormat format, WGPUTextureUsage, 76 uint32_t width, uint32_t height) { 77 if (format != WGPUTextureFormat::WGPUTextureFormat_RGBA8Unorm) { 78 return "unsupported format"; 79 } 80 SkASSERT(width > 0); 81 SkASSERT(height > 0); 82 83 return DAWN_SWAP_CHAIN_NO_ERROR; 84 } 85 86 DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture) { 87 fCurrentDrawable = [fLayer nextDrawable]; 88 89 nextTexture->texture.ptr = reinterpret_cast<void*>(fCurrentDrawable.texture); 90 91 return DAWN_SWAP_CHAIN_NO_ERROR; 92 } 93 94 DawnSwapChainError Present() { 95 id<MTLCommandBuffer> commandBuffer = [fQueue commandBuffer]; 96 [commandBuffer presentDrawable: fCurrentDrawable]; 97 [commandBuffer commit]; 98 return DAWN_SWAP_CHAIN_NO_ERROR; 99 } 100private: 101 id<MTLCommandQueue> fQueue; 102 CAMetalLayer* fLayer; 103 id<CAMetalDrawable> fCurrentDrawable = nil; 104}; 105 106DawnMTLWindowContext::DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params) 107 : DawnWindowContext(params, wgpu::TextureFormat::BGRA8Unorm) 108 , fMainView(info.fMainView) { 109 CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(fMainView); 110 CGSize size = fMainView.bounds.size; 111 size.width *= backingScaleFactor; 112 size.height *= backingScaleFactor; 113 this->initializeContext(size.width, size.height); 114} 115 116DawnMTLWindowContext::~DawnMTLWindowContext() { 117 this->destroyContext(); 118} 119 120DawnSwapChainImplementation DawnMTLWindowContext::createSwapChainImplementation( 121 int width, int height, const DisplayParams& params) { 122 return SwapChainImplMTL::Create(fMTLDevice, fLayer); 123} 124 125wgpu::Device DawnMTLWindowContext::onInitializeContext() { 126 wgpu::Device device = this->createDevice(wgpu::BackendType::Metal); 127 if (!device) { 128 return nullptr; 129 } 130 131 // We assume that Dawn is using the default device. This could be wrong on multi-GPU systems. 132 fMTLDevice = MTLCreateSystemDefaultDevice(); 133 134 CGSize size; 135 size.width = width(); 136 size.height = height(); 137 138 fLayer = [CAMetalLayer layer]; 139 [fLayer setDevice:fMTLDevice]; 140 [fLayer setPixelFormat: MTLPixelFormatBGRA8Unorm]; 141 [fLayer setFramebufferOnly: YES]; 142 [fLayer setDrawableSize: size]; 143 [fLayer setColorspace: CGColorSpaceCreateDeviceRGB()]; 144 [fLayer setContentsScale: sk_app::GetBackingScaleFactor(fMainView)]; 145 [fLayer setContentsGravity: kCAGravityTopLeft]; 146 [fLayer setAutoresizingMask: kCALayerHeightSizable | kCALayerWidthSizable]; 147 148 [fMainView setWantsLayer: YES]; 149 [fMainView setLayer: fLayer]; 150 151 return device; 152} 153 154void DawnMTLWindowContext::onDestroyContext() { 155} 156 157void DawnMTLWindowContext::onSwapBuffers() { 158} 159 160void DawnMTLWindowContext::resize(int w, int h) { 161 CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(fMainView); 162 CGSize size = fMainView.bounds.size; 163 size.width *= backingScaleFactor; 164 size.height *= backingScaleFactor; 165 166 fLayer.drawableSize = size; 167 fLayer.contentsScale = backingScaleFactor; 168 169 DawnWindowContext::resize(size.width, size.height); 170} 171 172namespace window_context_factory { 173 174std::unique_ptr<WindowContext> MakeDawnMTLForMac(const MacWindowInfo& winInfo, 175 const DisplayParams& params) { 176 std::unique_ptr<WindowContext> ctx(new DawnMTLWindowContext(winInfo, params)); 177 if (!ctx->isValid()) { 178 return nullptr; 179 } 180 return ctx; 181} 182 183} 184 185} //namespace sk_app 186