1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#import "SkSampleUIView.h"
9
10//#define SKGL_CONFIG         kEAGLColorFormatRGB565
11#define SKGL_CONFIG         kEAGLColorFormatRGBA8
12
13#define FORCE_REDRAW
14
15#include "SkCanvas.h"
16#include "SkCGUtils.h"
17#include "SkSurface.h"
18#include "SampleApp.h"
19
20#if SK_SUPPORT_GPU
21//#define USE_GL_1
22#define USE_GL_2
23
24#include "gl/GrGLInterface.h"
25#include "GrContext.h"
26#include "SkGpuDevice.h"
27#endif
28
29class SkiOSDeviceManager : public SampleWindow::DeviceManager {
30public:
31    SkiOSDeviceManager(GLint layerFBO) {
32#if SK_SUPPORT_GPU
33        fCurContext = NULL;
34        fCurIntf = NULL;
35        fCurRenderTarget = NULL;
36        fMSAASampleCount = 0;
37        fLayerFBO = layerFBO;
38#endif
39        fBackend = SkOSWindow::kNone_BackEndType;
40    }
41
42    virtual ~SkiOSDeviceManager() {
43#if SK_SUPPORT_GPU
44        SkSafeUnref(fCurContext);
45        SkSafeUnref(fCurIntf);
46        SkSafeUnref(fCurRenderTarget);
47#endif
48    }
49
50    void setUpBackend(SampleWindow* win, int msaaSampleCount) override {
51        SkASSERT(SkOSWindow::kNone_BackEndType == fBackend);
52
53        fBackend = SkOSWindow::kNone_BackEndType;
54
55#if SK_SUPPORT_GPU
56        switch (win->getDeviceType()) {
57            // these two don't use GL
58            case SampleWindow::kRaster_DeviceType:
59            case SampleWindow::kPicture_DeviceType:
60                break;
61            // these guys use the native backend
62            case SampleWindow::kGPU_DeviceType:
63                fBackend = SkOSWindow::kNativeGL_BackEndType;
64                break;
65            default:
66                SkASSERT(false);
67                break;
68        }
69        SkOSWindow::AttachmentInfo info;
70        bool result = win->attach(fBackend, msaaSampleCount, &info);
71        if (!result) {
72            SkDebugf("Failed to initialize GL");
73            return;
74        }
75        fMSAASampleCount = msaaSampleCount;
76
77        SkASSERT(NULL == fCurIntf);
78        switch (win->getDeviceType()) {
79            // these two don't use GL
80            case SampleWindow::kRaster_DeviceType:
81            case SampleWindow::kPicture_DeviceType:
82                fCurIntf = NULL;
83                break;
84            case SampleWindow::kGPU_DeviceType:
85                fCurIntf = GrGLCreateNativeInterface();
86                break;
87            default:
88                SkASSERT(false);
89                break;
90        }
91
92        SkASSERT(NULL == fCurContext);
93        if (SkOSWindow::kNone_BackEndType != fBackend) {
94            fCurContext = GrContext::Create(kOpenGL_GrBackend,
95                                            (GrBackendContext) fCurIntf);
96        }
97
98        if ((NULL == fCurContext || NULL == fCurIntf) &&
99            SkOSWindow::kNone_BackEndType != fBackend) {
100            // We need some context and interface to see results if we're using a GL backend
101            SkSafeUnref(fCurContext);
102            SkSafeUnref(fCurIntf);
103            SkDebugf("Failed to setup 3D");
104            win->detach();
105        }
106#endif // SK_SUPPORT_GPU
107        // call windowSizeChanged to create the render target
108        this->windowSizeChanged(win);
109    }
110
111    void tearDownBackend(SampleWindow *win) override {
112#if SK_SUPPORT_GPU
113        SkSafeUnref(fCurContext);
114        fCurContext = NULL;
115
116        SkSafeUnref(fCurIntf);
117        fCurIntf = NULL;
118
119        SkSafeUnref(fCurRenderTarget);
120        fCurRenderTarget = NULL;
121#endif
122        win->detach();
123        fBackend = SampleWindow::kNone_BackEndType;
124    }
125
126    SkSurface* createSurface(SampleWindow::DeviceType dType, SampleWindow* win) override{
127#if SK_SUPPORT_GPU
128        if (SampleWindow::IsGpuDeviceType(dType) && fCurContext) {
129            SkSurfaceProps props(win->getSurfaceProps());
130            return SkSurface::NewRenderTargetDirect(fCurRenderTarget, &props);
131        }
132#endif
133        return NULL;
134    }
135
136    virtual void publishCanvas(SampleWindow::DeviceType dType,
137                               SkCanvas* canvas,
138                               SampleWindow* win) override {
139#if SK_SUPPORT_GPU
140        if (NULL != fCurContext) {
141            fCurContext->flush();
142        }
143#endif
144        win->present();
145    }
146
147    void windowSizeChanged(SampleWindow* win) override {
148#if SK_SUPPORT_GPU
149        if (NULL != fCurContext) {
150            SkOSWindow::AttachmentInfo info;
151
152            win->attach(fBackend, fMSAASampleCount, &info);
153
154            glBindFramebuffer(GL_FRAMEBUFFER, fLayerFBO);
155            GrBackendRenderTargetDesc desc;
156            desc.fWidth = SkScalarRoundToInt(win->width());
157            desc.fHeight = SkScalarRoundToInt(win->height());
158            desc.fConfig = kSkia8888_GrPixelConfig;
159            desc.fRenderTargetHandle = fLayerFBO;
160            desc.fSampleCnt = info.fSampleCount;
161            desc.fStencilBits = info.fStencilBits;
162
163            SkSafeUnref(fCurRenderTarget);
164            fCurRenderTarget = fCurContext->wrapBackendRenderTarget(desc);
165        }
166#endif
167    }
168
169    GrContext* getGrContext() override {
170#if SK_SUPPORT_GPU
171        return fCurContext;
172#else
173        return NULL;
174#endif
175    }
176
177    GrRenderTarget* getGrRenderTarget() override {
178#if SK_SUPPORT_GPU
179        return fCurRenderTarget;
180#else
181        return NULL;
182#endif
183    }
184
185    bool isUsingGL() const { return SkOSWindow::kNone_BackEndType != fBackend; }
186
187private:
188
189#if SK_SUPPORT_GPU
190    GrContext*              fCurContext;
191    const GrGLInterface*    fCurIntf;
192    GrRenderTarget*         fCurRenderTarget;
193    int                     fMSAASampleCount;
194    GLint                   fLayerFBO;
195#endif
196
197    SkOSWindow::SkBackEndTypes fBackend;
198
199    typedef SampleWindow::DeviceManager INHERITED;
200};
201
202////////////////////////////////////////////////////////////////////////////////
203@implementation SkSampleUIView
204
205@synthesize fTitle, fRasterLayer, fGLLayer;
206
207#include "SkApplication.h"
208#include "SkEvent.h"
209#include "SkWindow.h"
210
211struct FPSState {
212    static const int FRAME_COUNT = 60;
213
214    CFTimeInterval fNow0, fNow1;
215    CFTimeInterval fTime0, fTime1, fTotalTime;
216    int fFrameCounter;
217    SkString str;
218    FPSState() {
219        fTime0 = fTime1 = fTotalTime = 0;
220        fFrameCounter = 0;
221    }
222
223    void startDraw() {
224        fNow0 = CACurrentMediaTime();
225    }
226
227    void endDraw() {
228        fNow1 = CACurrentMediaTime();
229    }
230
231    void flush(SkOSWindow* hwnd) {
232        CFTimeInterval now2 = CACurrentMediaTime();
233
234        fTime0 += fNow1 - fNow0;
235        fTime1 += now2 - fNow1;
236
237        if (++fFrameCounter == FRAME_COUNT) {
238            CFTimeInterval totalNow = CACurrentMediaTime();
239            fTotalTime = totalNow - fTotalTime;
240
241            //SkMSec ms0 = (int)(1000 * fTime0 / FRAME_COUNT);
242            //SkMSec msTotal = (int)(1000 * fTotalTime / FRAME_COUNT);
243            //str.printf(" ms: %d [%d], fps: %3.1f", msTotal, ms0,
244            //           FRAME_COUNT / fTotalTime);
245            str.printf(" fps:%3.1f", FRAME_COUNT / fTotalTime);
246            hwnd->setTitle(NULL);
247            fTotalTime = totalNow;
248            fTime0 = fTime1 = 0;
249            fFrameCounter = 0;
250        }
251    }
252};
253
254static FPSState gFPS;
255
256#define FPS_StartDraw() gFPS.startDraw()
257#define FPS_EndDraw()   gFPS.endDraw()
258#define FPS_Flush(wind) gFPS.flush(wind)
259
260///////////////////////////////////////////////////////////////////////////////
261
262- (id)initWithDefaults {
263    if (self = [super initWithDefaults]) {
264        fRedrawRequestPending = false;
265        fFPSState = new FPSState;
266
267#ifdef USE_GL_1
268        fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
269#else
270        fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
271#endif
272
273        if (!fGL.fContext || ![EAGLContext setCurrentContext:fGL.fContext])
274        {
275            [self release];
276            return nil;
277        }
278
279        // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
280        glGenFramebuffers(1, &fGL.fFramebuffer);
281        glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
282
283        glGenRenderbuffers(1, &fGL.fRenderbuffer);
284        glGenRenderbuffers(1, &fGL.fStencilbuffer);
285
286        glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
287        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fGL.fRenderbuffer);
288
289        glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
290        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fGL.fStencilbuffer);
291
292        self.fGLLayer = [CAEAGLLayer layer];
293        fGLLayer.bounds = self.bounds;
294        fGLLayer.anchorPoint = CGPointMake(0, 0);
295        fGLLayer.opaque = TRUE;
296        [self.layer addSublayer:fGLLayer];
297        fGLLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
298                                       [NSNumber numberWithBool:NO],
299                                       kEAGLDrawablePropertyRetainedBacking,
300                                       SKGL_CONFIG,
301                                       kEAGLDrawablePropertyColorFormat,
302                                       nil];
303
304        self.fRasterLayer = [CALayer layer];
305        fRasterLayer.anchorPoint = CGPointMake(0, 0);
306        fRasterLayer.opaque = TRUE;
307        [self.layer addSublayer:fRasterLayer];
308
309        NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"onOrderIn",
310                                           [NSNull null], @"onOrderOut",
311                                           [NSNull null], @"sublayers",
312                                           [NSNull null], @"contents",
313                                           [NSNull null], @"bounds",
314                                           nil];
315        fGLLayer.actions = newActions;
316        fRasterLayer.actions = newActions;
317        [newActions release];
318
319        fDevManager = new SkiOSDeviceManager(fGL.fFramebuffer);
320        static char* kDummyArgv = const_cast<char*>("dummyExecutableName");
321        fWind = new SampleWindow(self, 1, &kDummyArgv, fDevManager);
322
323        fWind->resize(self.frame.size.width, self.frame.size.height,
324                      kN32_SkColorType);
325    }
326    return self;
327}
328
329- (void)dealloc {
330    delete fDevManager;
331    delete fFPSState;
332    self.fRasterLayer = nil;
333    self.fGLLayer = nil;
334    [fGL.fContext release];
335    [super dealloc];
336}
337
338- (void)layoutSubviews {
339    int W, H;
340
341    // Allocate color buffer backing based on the current layer size
342    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
343    [fGL.fContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:fGLLayer];
344
345    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &fGL.fWidth);
346    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &fGL.fHeight);
347
348    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
349    glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fGL.fWidth, fGL.fHeight);
350
351    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
352        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
353    }
354
355    if (fDevManager->isUsingGL()) {
356        W = fGL.fWidth;
357        H = fGL.fHeight;
358        CGRect rect = CGRectMake(0, 0, W, H);
359        fGLLayer.bounds = rect;
360    }
361    else {
362        CGRect rect = self.bounds;
363        W = (int)CGRectGetWidth(rect);
364        H = (int)CGRectGetHeight(rect);
365        fRasterLayer.bounds = rect;
366    }
367
368    printf("---- layoutSubviews %d %d\n", W, H);
369    fWind->resize(W, H);
370    fWind->inval(NULL);
371}
372
373///////////////////////////////////////////////////////////////////////////////
374
375- (void)drawWithCanvas:(SkCanvas*)canvas {
376    fRedrawRequestPending = false;
377    fFPSState->startDraw();
378    fWind->draw(canvas);
379    fFPSState->endDraw();
380#ifdef FORCE_REDRAW
381    fWind->inval(NULL);
382#endif
383    fFPSState->flush(fWind);
384}
385
386- (void)drawInGL {
387    // This application only creates a single context which is already set current at this point.
388    // This call is redundant, but needed if dealing with multiple contexts.
389    [EAGLContext setCurrentContext:fGL.fContext];
390
391    // This application only creates a single default framebuffer which is already bound at this point.
392    // This call is redundant, but needed if dealing with multiple framebuffers.
393    glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
394
395    GLint scissorEnable;
396    glGetIntegerv(GL_SCISSOR_TEST, &scissorEnable);
397    glDisable(GL_SCISSOR_TEST);
398    glClearColor(0,0,0,0);
399    glClear(GL_COLOR_BUFFER_BIT);
400    if (scissorEnable) {
401        glEnable(GL_SCISSOR_TEST);
402    }
403    glViewport(0, 0, fGL.fWidth, fGL.fHeight);
404
405
406    SkAutoTUnref<SkSurface> surface(fWind->createSurface());
407    SkCanvas* canvas = surface->getCanvas();
408
409    // if we're not "retained", then we have to always redraw everything.
410    // This call forces us to ignore the fDirtyRgn, and draw everywhere.
411    // If we are "retained", we can skip this call (as the raster case does)
412    fWind->forceInvalAll();
413
414    [self drawWithCanvas:canvas];
415
416    // This application only creates a single color renderbuffer which is already bound at this point.
417    // This call is redundant, but needed if dealing with multiple renderbuffers.
418    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
419    [fGL.fContext presentRenderbuffer:GL_RENDERBUFFER];
420}
421
422- (void)drawInRaster {
423    SkAutoTUnref<SkSurface> surface(fWind->createSurface());
424    SkCanvas* canvas = surface->getCanvas();
425    [self drawWithCanvas:canvas];
426    CGImageRef cgimage = SkCreateCGImageRef(fWind->getBitmap());
427    fRasterLayer.contents = (id)cgimage;
428    CGImageRelease(cgimage);
429}
430
431- (void)forceRedraw {
432    if (fDevManager->isUsingGL())
433        [self drawInGL];
434    else
435        [self drawInRaster];
436}
437
438///////////////////////////////////////////////////////////////////////////////
439
440- (void)setSkTitle:(const char *)title {
441    NSString* text = [NSString stringWithUTF8String:title];
442    if ([text length] > 0)
443        self.fTitle = text;
444
445    if (fTitleItem && fTitle) {
446        fTitleItem.title = [NSString stringWithFormat:@"%@%@", fTitle,
447                            [NSString stringWithUTF8String:fFPSState->str.c_str()]];
448    }
449}
450
451- (void)postInvalWithRect:(const SkIRect*)r {
452    if (!fRedrawRequestPending) {
453        fRedrawRequestPending = true;
454        bool gl = fDevManager->isUsingGL();
455        [CATransaction begin];
456        [CATransaction setAnimationDuration:0];
457        fRasterLayer.hidden = gl;
458        fGLLayer.hidden = !gl;
459        [CATransaction commit];
460        if (gl) {
461            [self performSelector:@selector(drawInGL) withObject:nil afterDelay:0];
462        }
463        else {
464            [self performSelector:@selector(drawInRaster) withObject:nil afterDelay:0];
465            [self setNeedsDisplay];
466        }
467    }
468}
469
470- (void)getAttachmentInfo:(SkOSWindow::AttachmentInfo*)info {
471    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
472    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
473                                 GL_RENDERBUFFER_STENCIL_SIZE,
474                                 &info->fStencilBits);
475    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
476                                 GL_RENDERBUFFER_SAMPLES_APPLE,
477                                 &info->fSampleCount);
478}
479
480@end
481