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 "SkCanvas.h"
11#include "SkColorFilter.h"
12#include "WindowContextFactory_mac.h"
13#include "gl/GrGLInterface.h"
14#include "sk_tool_utils.h"
15
16#include <OpenGL/gl.h>
17
18#include <Cocoa/Cocoa.h>
19
20using sk_app::DisplayParams;
21using sk_app::window_context_factory::MacWindowInfo;
22using sk_app::GLWindowContext;
23
24namespace {
25
26// TODO: This still uses GL to handle the update rather than using a purely raster backend,
27// for historical reasons. Writing a pure raster backend would be better in the long run.
28
29class RasterWindowContext_mac : public GLWindowContext {
30public:
31    RasterWindowContext_mac(const MacWindowInfo&, const DisplayParams&);
32
33    ~RasterWindowContext_mac() override;
34
35    sk_sp<SkSurface> getBackbufferSurface() override;
36
37    void onSwapBuffers() override;
38
39    sk_sp<const GrGLInterface> onInitializeContext() override;
40    void onDestroyContext() override;
41
42private:
43    NSView*              fMainView;
44    NSOpenGLView*        fRasterView;
45    NSOpenGLContext*     fGLContext;
46    NSOpenGLPixelFormat* fPixelFormat;
47    sk_sp<SkSurface>     fBackbufferSurface;
48
49    typedef GLWindowContext INHERITED;
50};
51
52RasterWindowContext_mac::RasterWindowContext_mac(const MacWindowInfo& info,
53                                                 const DisplayParams& params)
54    : INHERITED(params)
55    , fMainView(info.fMainView) {
56
57    // any config code here (particularly for msaa)?
58
59    this->initializeContext();
60}
61
62RasterWindowContext_mac::~RasterWindowContext_mac() {
63    this->destroyContext();
64}
65
66sk_sp<const GrGLInterface> RasterWindowContext_mac::onInitializeContext() {
67    SkASSERT(nil != fMainView);
68
69    // set up pixel format
70    constexpr int kMaxAttributes = 18;
71    NSOpenGLPixelFormatAttribute attributes[kMaxAttributes];
72    int numAttributes = 0;
73    attributes[numAttributes++] = NSOpenGLPFAAccelerated;
74    attributes[numAttributes++] = NSOpenGLPFAClosestPolicy;
75    attributes[numAttributes++] = NSOpenGLPFAOpenGLProfile;
76    attributes[numAttributes++] = NSOpenGLProfileVersion3_2Core;
77    attributes[numAttributes++] = NSOpenGLPFAColorSize;
78    attributes[numAttributes++] = 24;
79    attributes[numAttributes++] = NSOpenGLPFAAlphaSize;
80    attributes[numAttributes++] = 8;
81    attributes[numAttributes++] = NSOpenGLPFADepthSize;
82    attributes[numAttributes++] = 0;
83    attributes[numAttributes++] = NSOpenGLPFAStencilSize;
84    attributes[numAttributes++] = 8;
85    attributes[numAttributes++] = NSOpenGLPFADoubleBuffer;
86    if (fDisplayParams.fMSAASampleCount > 1) {
87        attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
88        attributes[numAttributes++] = 1;
89        attributes[numAttributes++] = NSOpenGLPFASamples;
90        attributes[numAttributes++] = fDisplayParams.fMSAASampleCount;
91    } else {
92        attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
93        attributes[numAttributes++] = 0;
94    }
95    attributes[numAttributes++] = 0;
96    SkASSERT(numAttributes <= kMaxAttributes);
97
98    fPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
99    if (nil == fPixelFormat) {
100        return nullptr;
101    }
102
103    // create context
104    fGLContext = [[NSOpenGLContext alloc] initWithFormat:fPixelFormat shareContext:nil];
105    if (nil == fGLContext) {
106        [fPixelFormat release];
107        fPixelFormat = nil;
108        return nullptr;
109    }
110
111    // create view
112    NSRect rect = fMainView.bounds;
113    fRasterView = [[NSOpenGLView alloc] initWithFrame:rect];
114    if (nil == fRasterView) {
115        [fGLContext release];
116        fGLContext = nil;
117        [fPixelFormat release];
118        fPixelFormat = nil;
119        return nullptr;
120    }
121    [fRasterView setTranslatesAutoresizingMaskIntoConstraints:NO];
122
123    // attach OpenGL view to main view
124    [fMainView addSubview:fRasterView];
125    NSDictionary *views = NSDictionaryOfVariableBindings(fRasterView);
126
127    [fMainView addConstraints:
128     [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[fRasterView]|"
129                                             options:0
130                                             metrics:nil
131                                               views:views]];
132
133    [fMainView addConstraints:
134     [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[fRasterView]|"
135                                             options:0
136                                             metrics:nil
137                                               views:views]];
138
139    // make context current
140    GLint swapInterval = 1;
141    [fGLContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
142    [fRasterView setOpenGLContext:fGLContext];
143    [fRasterView setPixelFormat:fPixelFormat];
144    // TODO: support Retina displays
145    [fRasterView setWantsBestResolutionOpenGLSurface:NO];
146    [fGLContext setView:fRasterView];
147
148    [fGLContext makeCurrentContext];
149
150    glClearStencil(0);
151    glClearColor(0, 0, 0, 0);
152    glStencilMask(0xffffffff);
153    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
154
155    GLint stencilBits;
156    [fPixelFormat getValues:&stencilBits forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:0];
157    fStencilBits = stencilBits;
158    GLint sampleCount;
159    [fPixelFormat getValues:&sampleCount forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
160    fSampleCount = sampleCount;
161    fSampleCount = SkTMax(fSampleCount, 1);
162
163    const NSRect viewportRect = [fRasterView bounds];
164    fWidth = viewportRect.size.width;
165    fHeight = viewportRect.size.height;
166    glViewport(0, 0, fWidth, fHeight);
167
168    // make the offscreen image
169    SkImageInfo info = SkImageInfo::Make(fWidth, fHeight, fDisplayParams.fColorType,
170                                         kPremul_SkAlphaType, fDisplayParams.fColorSpace);
171    fBackbufferSurface = SkSurface::MakeRaster(info);
172    return GrGLMakeNativeInterface();
173}
174
175void RasterWindowContext_mac::onDestroyContext() {
176    fBackbufferSurface.reset(nullptr);
177
178    [fRasterView removeFromSuperview];
179    [fRasterView release];
180    fRasterView = nil;
181    [fGLContext release];
182    fGLContext = nil;
183    [fPixelFormat release];
184    fPixelFormat = nil;
185}
186
187sk_sp<SkSurface> RasterWindowContext_mac::getBackbufferSurface() { return fBackbufferSurface; }
188
189void RasterWindowContext_mac::onSwapBuffers() {
190    if (fBackbufferSurface) {
191        // We made/have an off-screen surface. Get the contents as an SkImage:
192        sk_sp<SkImage> snapshot = fBackbufferSurface->makeImageSnapshot();
193
194        sk_sp<SkSurface> gpuSurface = INHERITED::getBackbufferSurface();
195        SkCanvas* gpuCanvas = gpuSurface->getCanvas();
196        gpuCanvas->drawImage(snapshot, 0, 0);
197        gpuCanvas->flush();
198
199        [fGLContext flushBuffer];
200    }
201}
202
203}  // anonymous namespace
204
205namespace sk_app {
206namespace window_context_factory {
207
208WindowContext* NewRasterForMac(const MacWindowInfo& info, const DisplayParams& params) {
209    WindowContext* ctx = new RasterWindowContext_mac(info, params);
210    if (!ctx->isValid()) {
211        delete ctx;
212        return nullptr;
213    }
214    return ctx;
215}
216
217}  // namespace window_context_factory
218}  // namespace sk_app
219