blob: b1e04873cd35d5e8e5e14650bcb2837fb62b48a8 [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#import "SkSampleUIView.h"
//#define SKGL_CONFIG kEAGLColorFormatRGB565
#define SKGL_CONFIG kEAGLColorFormatRGBA8
#define FORCE_REDRAW
#include "SkCanvas.h"
#include "SkCGUtils.h"
#include "SkSurface.h"
#include "SampleApp.h"
#if SK_SUPPORT_GPU
//#define USE_GL_1
#define USE_GL_2
#include "gl/GrGLInterface.h"
#include "GrContext.h"
#include "SkGpuDevice.h"
#endif
class SkiOSDeviceManager : public SampleWindow::DeviceManager {
public:
SkiOSDeviceManager(GLint layerFBO) {
#if SK_SUPPORT_GPU
fCurContext = NULL;
fCurIntf = NULL;
fMSAASampleCount = 0;
fDeepColor = false;
fActualColorBits = 0;
#endif
fBackend = SkOSWindow::kNone_BackEndType;
}
virtual ~SkiOSDeviceManager() {
#if SK_SUPPORT_GPU
SkSafeUnref(fCurContext);
SkSafeUnref(fCurIntf);
#endif
}
void setUpBackend(SampleWindow* win, int msaaSampleCount, bool deepColor) override {
SkASSERT(SkOSWindow::kNone_BackEndType == fBackend);
fBackend = SkOSWindow::kNone_BackEndType;
#if SK_SUPPORT_GPU
switch (win->getDeviceType()) {
case SampleWindow::kRaster_DeviceType:
break;
// these guys use the native backend
case SampleWindow::kGPU_DeviceType:
fBackend = SkOSWindow::kNativeGL_BackEndType;
break;
default:
SkASSERT(false);
break;
}
SkOSWindow::AttachmentInfo info;
bool result = win->attach(fBackend, msaaSampleCount, false, &info);
if (!result) {
SkDebugf("Failed to initialize GL");
return;
}
fMSAASampleCount = msaaSampleCount;
fDeepColor = deepColor;
// Assume that we have at least 24-bit output, for backends that don't supply this data
fActualColorBits = SkTMax(info.fColorBits, 24);
SkASSERT(NULL == fCurIntf);
switch (win->getDeviceType()) {
case SampleWindow::kRaster_DeviceType:
fCurIntf = NULL;
break;
case SampleWindow::kGPU_DeviceType:
fCurIntf = GrGLCreateNativeInterface();
break;
default:
SkASSERT(false);
break;
}
SkASSERT(NULL == fCurContext);
if (SkOSWindow::kNone_BackEndType != fBackend) {
fCurContext = GrContext::Create(kOpenGL_GrBackend,
(GrBackendContext) fCurIntf);
}
if ((NULL == fCurContext || NULL == fCurIntf) &&
SkOSWindow::kNone_BackEndType != fBackend) {
// We need some context and interface to see results if we're using a GL backend
SkSafeUnref(fCurContext);
SkSafeUnref(fCurIntf);
SkDebugf("Failed to setup 3D");
win->release();
}
#endif // SK_SUPPORT_GPU
// call windowSizeChanged to create the render target
this->windowSizeChanged(win);
}
void tearDownBackend(SampleWindow *win) override {
#if SK_SUPPORT_GPU
SkSafeUnref(fCurContext);
fCurContext = NULL;
SkSafeUnref(fCurIntf);
fCurIntf = NULL;
fGpuSurface = nullptr;
#endif
win->release();
fBackend = SampleWindow::kNone_BackEndType;
}
sk_sp<SkSurface> makeSurface(SampleWindow::DeviceType dType, SampleWindow* win) override {
#if SK_SUPPORT_GPU
if (SampleWindow::IsGpuDeviceType(dType) && fCurContext) {
SkSurfaceProps props(win->getSurfaceProps());
if (kRGBA_F16_SkColorType == win->info().colorType() || fActualColorBits > 24) {
// If we're rendering to F16, we need an off-screen surface - the current render
// target is most likely the wrong format.
//
// If we're using a deep (10-bit or higher) surface, we probably need an off-screen
// surface. 10-bit, in particular, has strange gamma behavior.
return SkSurface::MakeRenderTarget(fCurContext, SkBudgeted::kNo, win->info(),
fMSAASampleCount, &props);
} else {
return fGpuSurface;
}
}
#endif
return nullptr;
}
virtual void publishCanvas(SampleWindow::DeviceType dType,
SkCanvas* canvas,
SampleWindow* win) override {
#if SK_SUPPORT_GPU
if (NULL != fCurContext) {
fCurContext->flush();
}
#endif
win->present();
}
void windowSizeChanged(SampleWindow* win) override {
#if SK_SUPPORT_GPU
if (fCurContext) {
SampleWindow::AttachmentInfo attachmentInfo;
win->attach(fBackend, fMSAASampleCount, fDeepColor, &attachmentInfo);
fActualColorBits = SkTMax(attachmentInfo.fColorBits, 24);
fGpuSurface = win->makeGpuBackedSurface(attachmentInfo, fCurIntf, fCurContext);
}
#endif
}
GrContext* getGrContext() override {
#if SK_SUPPORT_GPU
return fCurContext;
#else
return NULL;
#endif
}
int numColorSamples() const override {
#if SK_SUPPORT_GPU
return fMSAASampleCount;
#else
return 0;
#endif
}
int getColorBits() override {
#if SK_SUPPORT_GPU
return fActualColorBits;
#else
return 24;
#endif
}
bool isUsingGL() const { return SkOSWindow::kNone_BackEndType != fBackend; }
private:
#if SK_SUPPORT_GPU
GrContext* fCurContext;
const GrGLInterface* fCurIntf;
sk_sp<SkSurface> fGpuSurface;
int fMSAASampleCount;
bool fDeepColor;
int fActualColorBits;
#endif
SkOSWindow::SkBackEndTypes fBackend;
typedef SampleWindow::DeviceManager INHERITED;
};
////////////////////////////////////////////////////////////////////////////////
@implementation SkSampleUIView
@synthesize fTitle, fRasterLayer, fGLLayer;
#include "SkApplication.h"
#include "SkEvent.h"
#include "SkWindow.h"
struct FPSState {
static const int FRAME_COUNT = 60;
CFTimeInterval fNow0, fNow1;
CFTimeInterval fTime0, fTime1, fTotalTime;
int fFrameCounter;
SkString str;
FPSState() {
fTime0 = fTime1 = fTotalTime = 0;
fFrameCounter = 0;
}
void startDraw() {
fNow0 = CACurrentMediaTime();
}
void endDraw() {
fNow1 = CACurrentMediaTime();
}
void flush(SkOSWindow* hwnd) {
CFTimeInterval now2 = CACurrentMediaTime();
fTime0 += fNow1 - fNow0;
fTime1 += now2 - fNow1;
if (++fFrameCounter == FRAME_COUNT) {
CFTimeInterval totalNow = CACurrentMediaTime();
fTotalTime = totalNow - fTotalTime;
//SkMSec ms0 = (int)(1000 * fTime0 / FRAME_COUNT);
//SkMSec msTotal = (int)(1000 * fTotalTime / FRAME_COUNT);
//str.printf(" ms: %d [%d], fps: %3.1f", msTotal, ms0,
// FRAME_COUNT / fTotalTime);
str.printf(" fps:%3.1f", FRAME_COUNT / fTotalTime);
hwnd->setTitle(NULL);
fTotalTime = totalNow;
fTime0 = fTime1 = 0;
fFrameCounter = 0;
}
}
};
static FPSState gFPS;
#define FPS_StartDraw() gFPS.startDraw()
#define FPS_EndDraw() gFPS.endDraw()
#define FPS_Flush(wind) gFPS.flush(wind)
///////////////////////////////////////////////////////////////////////////////
- (id)initWithDefaults {
if (self = [super initWithDefaults]) {
fRedrawRequestPending = false;
fFPSState = new FPSState;
#ifdef USE_GL_1
fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
#else
fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
#endif
if (!fGL.fContext || ![EAGLContext setCurrentContext:fGL.fContext])
{
[self release];
return nil;
}
// Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
glGenFramebuffers(1, &fGL.fFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
glGenRenderbuffers(1, &fGL.fRenderbuffer);
glGenRenderbuffers(1, &fGL.fStencilbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fGL.fRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fGL.fStencilbuffer);
self.fGLLayer = [CAEAGLLayer layer];
fGLLayer.bounds = self.bounds;
fGLLayer.anchorPoint = CGPointMake(0, 0);
fGLLayer.opaque = TRUE;
[self.layer addSublayer:fGLLayer];
fGLLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO],
kEAGLDrawablePropertyRetainedBacking,
SKGL_CONFIG,
kEAGLDrawablePropertyColorFormat,
nil];
self.fRasterLayer = [CALayer layer];
fRasterLayer.anchorPoint = CGPointMake(0, 0);
fRasterLayer.opaque = TRUE;
[self.layer addSublayer:fRasterLayer];
NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",
[NSNull null], @"sublayers",
[NSNull null], @"contents",
[NSNull null], @"bounds",
nil];
fGLLayer.actions = newActions;
fRasterLayer.actions = newActions;
[newActions release];
// rebuild argc and argv from process info
NSArray* arguments = [[NSProcessInfo processInfo] arguments];
int argc = [arguments count];
char** argv = new char*[argc];
for (int i = 0; i < argc; ++i) {
NSString* arg = [arguments objectAtIndex:i];
int strlen = [arg lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
argv[i] = new char[strlen+1];
[arg getCString:argv[i] maxLength:strlen+1 encoding:NSUTF8StringEncoding];
}
fDevManager = new SkiOSDeviceManager(fGL.fFramebuffer);
fWind = new SampleWindow(self, argc, argv, fDevManager);
fWind->resize(self.frame.size.width, self.frame.size.height);
for (int i = 0; i < argc; ++i) {
delete [] argv[i];
}
delete [] argv;
}
return self;
}
- (void)dealloc {
delete fDevManager;
delete fFPSState;
self.fRasterLayer = nil;
self.fGLLayer = nil;
[fGL.fContext release];
[super dealloc];
}
- (void)layoutSubviews {
int W, H;
// Allocate color buffer backing based on the current layer size
glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
[fGL.fContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:fGLLayer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &fGL.fWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &fGL.fHeight);
glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fGL.fWidth, fGL.fHeight);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
if (fDevManager->isUsingGL()) {
W = fGL.fWidth;
H = fGL.fHeight;
CGRect rect = CGRectMake(0, 0, W, H);
fGLLayer.bounds = rect;
}
else {
CGRect rect = self.bounds;
W = (int)CGRectGetWidth(rect);
H = (int)CGRectGetHeight(rect);
fRasterLayer.bounds = rect;
}
printf("---- layoutSubviews %d %d\n", W, H);
fWind->resize(W, H);
fWind->inval(NULL);
}
///////////////////////////////////////////////////////////////////////////////
- (void)drawWithCanvas:(SkCanvas*)canvas {
fRedrawRequestPending = false;
fFPSState->startDraw();
fWind->draw(canvas);
fFPSState->endDraw();
#ifdef FORCE_REDRAW
fWind->inval(NULL);
#endif
fFPSState->flush(fWind);
}
- (void)drawInGL {
// This application only creates a single context which is already set current at this point.
// This call is redundant, but needed if dealing with multiple contexts.
[EAGLContext setCurrentContext:fGL.fContext];
// This application only creates a single default framebuffer which is already bound at this point.
// This call is redundant, but needed if dealing with multiple framebuffers.
glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
GLint scissorEnable;
glGetIntegerv(GL_SCISSOR_TEST, &scissorEnable);
glDisable(GL_SCISSOR_TEST);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
if (scissorEnable) {
glEnable(GL_SCISSOR_TEST);
}
glViewport(0, 0, fGL.fWidth, fGL.fHeight);
sk_sp<SkSurface> surface(fWind->makeSurface());
SkCanvas* canvas = surface->getCanvas();
// if we're not "retained", then we have to always redraw everything.
// This call forces us to ignore the fDirtyRgn, and draw everywhere.
// If we are "retained", we can skip this call (as the raster case does)
fWind->forceInvalAll();
[self drawWithCanvas:canvas];
// This application only creates a single color renderbuffer which is already bound at this point.
// This call is redundant, but needed if dealing with multiple renderbuffers.
glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
[fGL.fContext presentRenderbuffer:GL_RENDERBUFFER];
}
- (void)drawInRaster {
sk_sp<SkSurface> surface(fWind->makeSurface());
SkCanvas* canvas = surface->getCanvas();
[self drawWithCanvas:canvas];
CGImageRef cgimage = SkCreateCGImageRef(fWind->getBitmap());
fRasterLayer.contents = (id)cgimage;
CGImageRelease(cgimage);
}
- (void)forceRedraw {
if (fDevManager->isUsingGL())
[self drawInGL];
else
[self drawInRaster];
}
///////////////////////////////////////////////////////////////////////////////
- (void)setSkTitle:(const char *)title {
NSString* text = [NSString stringWithUTF8String:title];
if ([text length] > 0)
self.fTitle = text;
if (fTitleItem && fTitle) {
fTitleItem.title = [NSString stringWithFormat:@"%@%@", fTitle,
[NSString stringWithUTF8String:fFPSState->str.c_str()]];
}
}
- (void)postInvalWithRect:(const SkIRect*)r {
if (!fRedrawRequestPending) {
fRedrawRequestPending = true;
bool gl = fDevManager->isUsingGL();
[CATransaction begin];
[CATransaction setAnimationDuration:0];
fRasterLayer.hidden = gl;
fGLLayer.hidden = !gl;
[CATransaction commit];
if (gl) {
[self performSelector:@selector(drawInGL) withObject:nil afterDelay:0];
}
else {
[self performSelector:@selector(drawInRaster) withObject:nil afterDelay:0];
[self setNeedsDisplay];
}
}
}
- (void)getAttachmentInfo:(SkOSWindow::AttachmentInfo*)info {
glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_STENCIL_SIZE,
&info->fStencilBits);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_SAMPLES_APPLE,
&info->fSampleCount);
}
@end