• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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