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