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