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 "dawn/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; 52private: 53 NSView* fMainView; 54 id<MTLDevice> fMTLDevice; 55 CAMetalLayer* fLayer; 56}; 57 58class SwapChainImplMTL { 59public: 60 typedef void WSIContext; 61 static DawnSwapChainImplementation Create(id<MTLDevice> device, CAMetalLayer* layer) { 62 auto impl = new SwapChainImplMTL(device, layer); 63 return CreateSwapChainImplementation<SwapChainImplMTL>(impl); 64 } 65 66 void Init(WSIContext* ctx) {} 67 68 SwapChainImplMTL(id<MTLDevice> device, CAMetalLayer* layer) 69 : fQueue([device newCommandQueue]) 70 , fLayer(layer) {} 71 72 ~SwapChainImplMTL() {} 73 74 DawnSwapChainError Configure(WGPUTextureFormat format, WGPUTextureUsage, 75 uint32_t width, uint32_t height) { 76 if (format != WGPUTextureFormat::WGPUTextureFormat_RGBA8Unorm) { 77 return "unsupported format"; 78 } 79 SkASSERT(width > 0); 80 SkASSERT(height > 0); 81 82 return DAWN_SWAP_CHAIN_NO_ERROR; 83 } 84 85 DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture) { 86 fCurrentDrawable = [fLayer nextDrawable]; 87 88 nextTexture->texture.ptr = reinterpret_cast<void*>(fCurrentDrawable.texture); 89 90 return DAWN_SWAP_CHAIN_NO_ERROR; 91 } 92 93 DawnSwapChainError Present() { 94 id<MTLCommandBuffer> commandBuffer = [fQueue commandBuffer]; 95 [commandBuffer presentDrawable: fCurrentDrawable]; 96 [commandBuffer commit]; 97 return DAWN_SWAP_CHAIN_NO_ERROR; 98 } 99private: 100 id<MTLCommandQueue> fQueue; 101 CAMetalLayer* fLayer; 102 id<CAMetalDrawable> fCurrentDrawable = nil; 103}; 104 105DawnMTLWindowContext::DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params) 106 : DawnWindowContext(params, wgpu::TextureFormat::BGRA8Unorm) 107 , fMainView(info.fMainView) { 108 CGSize size = fMainView.bounds.size; 109 this->initializeContext(size.width, size.height); 110} 111 112DawnMTLWindowContext::~DawnMTLWindowContext() { 113 this->destroyContext(); 114} 115 116DawnSwapChainImplementation DawnMTLWindowContext::createSwapChainImplementation( 117 int width, int height, const DisplayParams& params) { 118 return SwapChainImplMTL::Create(fMTLDevice, fLayer); 119} 120 121wgpu::Device DawnMTLWindowContext::onInitializeContext() { 122 wgpu::Device device = this->createDevice(dawn_native::BackendType::Metal); 123 if (!device) { 124 return nullptr; 125 } 126 127 fMTLDevice = dawn_native::metal::GetMetalDevice(device.Get()); 128 129 CGSize size; 130 size.width = width(); 131 size.height = height(); 132 133 fLayer = [CAMetalLayer layer]; 134 [fLayer setDevice:fMTLDevice]; 135 [fLayer setPixelFormat: MTLPixelFormatBGRA8Unorm]; 136 [fLayer setFramebufferOnly: YES]; 137 [fLayer setDrawableSize: size]; 138 [fLayer setColorspace: CGColorSpaceCreateDeviceRGB()]; 139 140 [fMainView setWantsLayer: YES]; 141 [fMainView setLayer: fLayer]; 142 143 return device; 144} 145 146void DawnMTLWindowContext::onDestroyContext() { 147} 148 149void DawnMTLWindowContext::onSwapBuffers() { 150} 151 152namespace window_context_factory { 153 154std::unique_ptr<WindowContext> MakeDawnMTLForMac(const MacWindowInfo& winInfo, 155 const DisplayParams& params) { 156 std::unique_ptr<WindowContext> ctx(new DawnMTLWindowContext(winInfo, params)); 157 if (!ctx->isValid()) { 158 return nullptr; 159 } 160 return ctx; 161} 162 163} 164 165} //namespace sk_app 166