/* * Copyright 2019 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "tools/sk_app/DawnWindowContext.h" #include "tools/sk_app/mac/WindowContextFactory_mac.h" #include "webgpu/webgpu_cpp.h" #include "dawn/dawn_wsi.h" #include "dawn/native/DawnNative.h" #include "dawn/native/MetalBackend.h" #import #import #import namespace sk_app { using sk_app::window_context_factory::MacWindowInfo; template DawnSwapChainImplementation CreateSwapChainImplementation(T* swapChain) { DawnSwapChainImplementation impl = {}; impl.userData = swapChain; impl.Init = [](void* userData, void* wsiContext) { auto* ctx = static_cast(wsiContext); reinterpret_cast(userData)->Init(ctx); }; impl.Destroy = [](void* userData) { delete reinterpret_cast(userData); }; impl.Configure = [](void* userData, WGPUTextureFormat format, WGPUTextureUsage allowedUsage, uint32_t width, uint32_t height) { return static_cast(userData)->Configure(format, allowedUsage, width, height); }; impl.GetNextTexture = [](void* userData, DawnSwapChainNextTexture* nextTexture) { return static_cast(userData)->GetNextTexture(nextTexture); }; impl.Present = [](void* userData) { return static_cast(userData)->Present(); }; return impl; } class DawnMTLWindowContext : public DawnWindowContext { public: DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params); ~DawnMTLWindowContext() override; wgpu::Device onInitializeContext() override; void onDestroyContext() override; DawnSwapChainImplementation createSwapChainImplementation(int width, int height, const DisplayParams& params) override; void onSwapBuffers() override; void resize(int width, int height) override; private: NSView* fMainView; id fMTLDevice; CAMetalLayer* fLayer; }; class SwapChainImplMTL { public: typedef void WSIContext; static DawnSwapChainImplementation Create(id device, CAMetalLayer* layer) { auto impl = new SwapChainImplMTL(device, layer); return CreateSwapChainImplementation(impl); } void Init(WSIContext* ctx) {} SwapChainImplMTL(id device, CAMetalLayer* layer) : fQueue([device newCommandQueue]) , fLayer(layer) {} ~SwapChainImplMTL() {} DawnSwapChainError Configure(WGPUTextureFormat format, WGPUTextureUsage, uint32_t width, uint32_t height) { if (format != WGPUTextureFormat::WGPUTextureFormat_RGBA8Unorm) { return "unsupported format"; } SkASSERT(width > 0); SkASSERT(height > 0); return DAWN_SWAP_CHAIN_NO_ERROR; } DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture) { fCurrentDrawable = [fLayer nextDrawable]; nextTexture->texture.ptr = reinterpret_cast(fCurrentDrawable.texture); return DAWN_SWAP_CHAIN_NO_ERROR; } DawnSwapChainError Present() { id commandBuffer = [fQueue commandBuffer]; [commandBuffer presentDrawable: fCurrentDrawable]; [commandBuffer commit]; return DAWN_SWAP_CHAIN_NO_ERROR; } private: id fQueue; CAMetalLayer* fLayer; id fCurrentDrawable = nil; }; DawnMTLWindowContext::DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params) : DawnWindowContext(params, wgpu::TextureFormat::BGRA8Unorm) , fMainView(info.fMainView) { CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(fMainView); CGSize size = fMainView.bounds.size; size.width *= backingScaleFactor; size.height *= backingScaleFactor; this->initializeContext(size.width, size.height); } DawnMTLWindowContext::~DawnMTLWindowContext() { this->destroyContext(); } DawnSwapChainImplementation DawnMTLWindowContext::createSwapChainImplementation( int width, int height, const DisplayParams& params) { return SwapChainImplMTL::Create(fMTLDevice, fLayer); } wgpu::Device DawnMTLWindowContext::onInitializeContext() { wgpu::Device device = this->createDevice(wgpu::BackendType::Metal); if (!device) { return nullptr; } // We assume that Dawn is using the default device. This could be wrong on multi-GPU systems. fMTLDevice = MTLCreateSystemDefaultDevice(); CGSize size; size.width = width(); size.height = height(); fLayer = [CAMetalLayer layer]; [fLayer setDevice:fMTLDevice]; [fLayer setPixelFormat: MTLPixelFormatBGRA8Unorm]; [fLayer setFramebufferOnly: YES]; [fLayer setDrawableSize: size]; [fLayer setColorspace: CGColorSpaceCreateDeviceRGB()]; [fLayer setContentsScale: sk_app::GetBackingScaleFactor(fMainView)]; [fLayer setContentsGravity: kCAGravityTopLeft]; [fLayer setAutoresizingMask: kCALayerHeightSizable | kCALayerWidthSizable]; [fMainView setWantsLayer: YES]; [fMainView setLayer: fLayer]; return device; } void DawnMTLWindowContext::onDestroyContext() { } void DawnMTLWindowContext::onSwapBuffers() { } void DawnMTLWindowContext::resize(int w, int h) { CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(fMainView); CGSize size = fMainView.bounds.size; size.width *= backingScaleFactor; size.height *= backingScaleFactor; fLayer.drawableSize = size; fLayer.contentsScale = backingScaleFactor; DawnWindowContext::resize(size.width, size.height); } namespace window_context_factory { std::unique_ptr MakeDawnMTLForMac(const MacWindowInfo& winInfo, const DisplayParams& params) { std::unique_ptr ctx(new DawnMTLWindowContext(winInfo, params)); if (!ctx->isValid()) { return nullptr; } return ctx; } } } //namespace sk_app