1
2/*
3 * Copyright 2019 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "../GLWindowContext.h"
10#include "WindowContextFactory_mac.h"
11#include "gl/GrGLInterface.h"
12
13#include <OpenGL/gl.h>
14#include <Cocoa/Cocoa.h>
15
16using sk_app::DisplayParams;
17using sk_app::window_context_factory::MacWindowInfo;
18using sk_app::GLWindowContext;
19
20namespace {
21
22class GLWindowContext_mac : public GLWindowContext {
23public:
24    GLWindowContext_mac(const MacWindowInfo&, const DisplayParams&);
25
26    ~GLWindowContext_mac() override;
27
28    void onSwapBuffers() override;
29
30    sk_sp<const GrGLInterface> onInitializeContext() override;
31    void onDestroyContext() override;
32
33private:
34    NSView*              fMainView;
35    NSOpenGLView*        fGLView;
36    NSOpenGLContext*     fGLContext;
37    NSOpenGLPixelFormat* fPixelFormat;
38
39    typedef GLWindowContext INHERITED;
40};
41
42GLWindowContext_mac::GLWindowContext_mac(const MacWindowInfo& info, const DisplayParams& params)
43    : INHERITED(params)
44    , fMainView(info.fMainView) {
45
46    // any config code here (particularly for msaa)?
47
48    this->initializeContext();
49}
50
51GLWindowContext_mac::~GLWindowContext_mac() {
52    this->destroyContext();
53}
54
55sk_sp<const GrGLInterface> GLWindowContext_mac::onInitializeContext() {
56    SkASSERT(nil != fMainView);
57
58    // set up pixel format
59    constexpr int kMaxAttributes = 18;
60    NSOpenGLPixelFormatAttribute attributes[kMaxAttributes];
61    int numAttributes = 0;
62    attributes[numAttributes++] = NSOpenGLPFAAccelerated;
63    attributes[numAttributes++] = NSOpenGLPFAClosestPolicy;
64    attributes[numAttributes++] = NSOpenGLPFADoubleBuffer;
65    attributes[numAttributes++] = NSOpenGLPFAOpenGLProfile;
66    attributes[numAttributes++] = NSOpenGLProfileVersion3_2Core;
67    attributes[numAttributes++] = NSOpenGLPFAColorSize;
68    attributes[numAttributes++] = 24;
69    attributes[numAttributes++] = NSOpenGLPFAAlphaSize;
70    attributes[numAttributes++] = 8;
71    attributes[numAttributes++] = NSOpenGLPFADepthSize;
72    attributes[numAttributes++] = 0;
73    attributes[numAttributes++] = NSOpenGLPFAStencilSize;
74    attributes[numAttributes++] = 8;
75    if (fDisplayParams.fMSAASampleCount > 1) {
76        attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
77        attributes[numAttributes++] = 1;
78        attributes[numAttributes++] = NSOpenGLPFASamples;
79        attributes[numAttributes++] = fDisplayParams.fMSAASampleCount;
80    } else {
81        attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
82        attributes[numAttributes++] = 0;
83    }
84    attributes[numAttributes++] = 0;
85    SkASSERT(numAttributes <= kMaxAttributes);
86
87    fPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
88    if (nil == fPixelFormat) {
89        return nullptr;
90    }
91
92    // create context
93    fGLContext = [[NSOpenGLContext alloc] initWithFormat:fPixelFormat shareContext:nil];
94    if (nil == fGLContext) {
95        [fPixelFormat release];
96        fPixelFormat = nil;
97        return nullptr;
98    }
99
100    // create view
101    NSRect rect = fMainView.bounds;
102    fGLView = [[NSOpenGLView alloc] initWithFrame:rect];
103    if (nil == fGLView) {
104        [fGLContext release];
105        fGLContext = nil;
106        [fPixelFormat release];
107        fPixelFormat = nil;
108        return nullptr;
109    }
110    [fGLView setTranslatesAutoresizingMaskIntoConstraints:NO];
111
112    // attach OpenGL view to main view
113    [fMainView addSubview:fGLView];
114    NSDictionary *views = NSDictionaryOfVariableBindings(fGLView);
115
116    [fMainView addConstraints:
117     [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[fGLView]|"
118                                             options:0
119                                             metrics:nil
120                                               views:views]];
121
122    [fMainView addConstraints:
123     [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[fGLView]|"
124                                             options:0
125                                             metrics:nil
126                                               views:views]];
127
128    // make context current
129    GLint swapInterval = 1;
130    [fGLContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
131    [fGLView setOpenGLContext:fGLContext];
132    [fGLView setPixelFormat:fPixelFormat];
133    // TODO: support Retina displays
134    [fGLView setWantsBestResolutionOpenGLSurface:NO];
135    [fGLContext setView:fGLView];
136
137    [fGLContext makeCurrentContext];
138
139    glClearStencil(0);
140    glClearColor(0, 0, 0, 255);
141    glStencilMask(0xffffffff);
142    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
143
144    GLint stencilBits;
145    [fPixelFormat getValues:&stencilBits forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:0];
146    fStencilBits = stencilBits;
147    GLint sampleCount;
148    [fPixelFormat getValues:&sampleCount forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
149    fSampleCount = sampleCount;
150    fSampleCount = SkTMax(fSampleCount, 1);
151
152    const NSRect viewportRect = [fGLView bounds];
153    fWidth = viewportRect.size.width;
154    fHeight = viewportRect.size.height;
155    glViewport(0, 0, fWidth, fHeight);
156
157    return GrGLMakeNativeInterface();
158}
159
160void GLWindowContext_mac::onDestroyContext() {
161    [fGLView removeFromSuperview];
162    [fGLView release];
163    fGLView = nil;
164    [fGLContext release];
165    fGLContext = nil;
166    [fPixelFormat release];
167    fPixelFormat = nil;
168}
169
170void GLWindowContext_mac::onSwapBuffers() {
171    [fGLContext flushBuffer];
172}
173
174}  // anonymous namespace
175
176namespace sk_app {
177namespace window_context_factory {
178
179WindowContext* NewGLForMac(const MacWindowInfo& info, const DisplayParams& params) {
180    WindowContext* ctx = new GLWindowContext_mac(info, params);
181    if (!ctx->isValid()) {
182        delete ctx;
183        return nullptr;
184    }
185    return ctx;
186}
187
188}  // namespace window_context_factory
189}  // namespace sk_app
190