Home | History | Annotate | Download | only in iOSSampleApp
      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 
     29 class SkiOSDeviceManager : public SampleWindow::DeviceManager {
     30 public:
     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 
    191 private:
    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 
    216 struct 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 
    259 static 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