1// Copyright 2017 The Dawn Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include "utils/BackendBinding.h" 16 17#include "common/Assert.h" 18#include "common/SwapChainUtils.h" 19#include "dawn_native/MetalBackend.h" 20 21#define GLFW_EXPOSE_NATIVE_COCOA 22#include "GLFW/glfw3.h" 23#include "GLFW/glfw3native.h" 24 25#import <QuartzCore/CAMetalLayer.h> 26 27namespace utils { 28 class SwapChainImplMTL { 29 public: 30 using WSIContext = DawnWSIContextMetal; 31 32 SwapChainImplMTL(id nsWindow) : mNsWindow(nsWindow) { 33 } 34 35 ~SwapChainImplMTL() { 36 [mCurrentTexture release]; 37 [mCurrentDrawable release]; 38 } 39 40 void Init(DawnWSIContextMetal* ctx) { 41 mMtlDevice = ctx->device; 42 mCommandQueue = ctx->queue; 43 } 44 45 DawnSwapChainError Configure(WGPUTextureFormat format, 46 WGPUTextureUsage usage, 47 uint32_t width, 48 uint32_t height) { 49 if (format != WGPUTextureFormat_BGRA8Unorm) { 50 return "unsupported format"; 51 } 52 ASSERT(width > 0); 53 ASSERT(height > 0); 54 55 NSView* contentView = [mNsWindow contentView]; 56 [contentView setWantsLayer:YES]; 57 58 CGSize size = {}; 59 size.width = width; 60 size.height = height; 61 62 mLayer = [CAMetalLayer layer]; 63 [mLayer setDevice:mMtlDevice]; 64 [mLayer setPixelFormat:MTLPixelFormatBGRA8Unorm]; 65 [mLayer setDrawableSize:size]; 66 67 constexpr uint32_t kFramebufferOnlyTextureUsages = 68 WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_Present; 69 bool hasOnlyFramebufferUsages = !(usage & (~kFramebufferOnlyTextureUsages)); 70 if (hasOnlyFramebufferUsages) { 71 [mLayer setFramebufferOnly:YES]; 72 } 73 74 [contentView setLayer:mLayer]; 75 76 return DAWN_SWAP_CHAIN_NO_ERROR; 77 } 78 79 DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture) { 80 [mCurrentDrawable release]; 81 mCurrentDrawable = [mLayer nextDrawable]; 82 [mCurrentDrawable retain]; 83 84 [mCurrentTexture release]; 85 mCurrentTexture = mCurrentDrawable.texture; 86 [mCurrentTexture retain]; 87 88 nextTexture->texture.ptr = reinterpret_cast<void*>(mCurrentTexture); 89 90 return DAWN_SWAP_CHAIN_NO_ERROR; 91 } 92 93 DawnSwapChainError Present() { 94 id<MTLCommandBuffer> commandBuffer = [mCommandQueue commandBuffer]; 95 [commandBuffer presentDrawable:mCurrentDrawable]; 96 [commandBuffer commit]; 97 98 return DAWN_SWAP_CHAIN_NO_ERROR; 99 } 100 101 private: 102 id mNsWindow = nil; 103 id<MTLDevice> mMtlDevice = nil; 104 id<MTLCommandQueue> mCommandQueue = nil; 105 106 CAMetalLayer* mLayer = nullptr; 107 id<CAMetalDrawable> mCurrentDrawable = nil; 108 id<MTLTexture> mCurrentTexture = nil; 109 }; 110 111 class MetalBinding : public BackendBinding { 112 public: 113 MetalBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { 114 } 115 116 uint64_t GetSwapChainImplementation() override { 117 if (mSwapchainImpl.userData == nullptr) { 118 mSwapchainImpl = CreateSwapChainImplementation( 119 new SwapChainImplMTL(glfwGetCocoaWindow(mWindow))); 120 } 121 return reinterpret_cast<uint64_t>(&mSwapchainImpl); 122 } 123 124 WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { 125 return WGPUTextureFormat_BGRA8Unorm; 126 } 127 128 private: 129 DawnSwapChainImplementation mSwapchainImpl = {}; 130 }; 131 132 BackendBinding* CreateMetalBinding(GLFWwindow* window, WGPUDevice device) { 133 return new MetalBinding(window, device); 134 } 135} 136