8686a5eeef
Remove Picture_DeviceType from SampleApp SampleWindow DeviceType enumeration. Use a bool variable to control whether the drawing happens via MultiPictureDraw. The MultiPictureDraw mode can be activated by 'M', and title is updated to contain "<MPD>". Previously the MPD mode was inaccessible. This works towards removing backend specific code from SampleApp and VisualBench with the aim to move the code to the common SkView framework (SkWindow in particular). The grand goal is to be able to use command buffer GPU API and NVPR in these apps. BUG=skia:4733 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1538343002 Review URL: https://codereview.chromium.org/1538343002
492 lines
16 KiB
Plaintext
492 lines
16 KiB
Plaintext
/*
|
|
* 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;
|
|
fCurRenderTarget = NULL;
|
|
fMSAASampleCount = 0;
|
|
fLayerFBO = layerFBO;
|
|
#endif
|
|
fBackend = SkOSWindow::kNone_BackEndType;
|
|
}
|
|
|
|
virtual ~SkiOSDeviceManager() {
|
|
#if SK_SUPPORT_GPU
|
|
SkSafeUnref(fCurContext);
|
|
SkSafeUnref(fCurIntf);
|
|
SkSafeUnref(fCurRenderTarget);
|
|
#endif
|
|
}
|
|
|
|
void setUpBackend(SampleWindow* win, int msaaSampleCount) 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, &info);
|
|
if (!result) {
|
|
SkDebugf("Failed to initialize GL");
|
|
return;
|
|
}
|
|
fMSAASampleCount = msaaSampleCount;
|
|
|
|
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->detach();
|
|
}
|
|
#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;
|
|
|
|
SkSafeUnref(fCurRenderTarget);
|
|
fCurRenderTarget = NULL;
|
|
#endif
|
|
win->detach();
|
|
fBackend = SampleWindow::kNone_BackEndType;
|
|
}
|
|
|
|
SkSurface* createSurface(SampleWindow::DeviceType dType, SampleWindow* win) override{
|
|
#if SK_SUPPORT_GPU
|
|
if (SampleWindow::IsGpuDeviceType(dType) && fCurContext) {
|
|
SkSurfaceProps props(win->getSurfaceProps());
|
|
return SkSurface::NewRenderTargetDirect(fCurRenderTarget, &props);
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
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 (NULL != fCurContext) {
|
|
SkOSWindow::AttachmentInfo info;
|
|
|
|
win->attach(fBackend, fMSAASampleCount, &info);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fLayerFBO);
|
|
GrBackendRenderTargetDesc desc;
|
|
desc.fWidth = SkScalarRoundToInt(win->width());
|
|
desc.fHeight = SkScalarRoundToInt(win->height());
|
|
desc.fConfig = kSkia8888_GrPixelConfig;
|
|
desc.fRenderTargetHandle = fLayerFBO;
|
|
desc.fSampleCnt = info.fSampleCount;
|
|
desc.fStencilBits = info.fStencilBits;
|
|
|
|
SkSafeUnref(fCurRenderTarget);
|
|
fCurRenderTarget = fCurContext->textureProvider()->wrapBackendRenderTarget(desc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
GrContext* getGrContext() override {
|
|
#if SK_SUPPORT_GPU
|
|
return fCurContext;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
GrRenderTarget* getGrRenderTarget() override {
|
|
#if SK_SUPPORT_GPU
|
|
return fCurRenderTarget;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
bool isUsingGL() const { return SkOSWindow::kNone_BackEndType != fBackend; }
|
|
|
|
private:
|
|
|
|
#if SK_SUPPORT_GPU
|
|
GrContext* fCurContext;
|
|
const GrGLInterface* fCurIntf;
|
|
GrRenderTarget* fCurRenderTarget;
|
|
int fMSAASampleCount;
|
|
GLint fLayerFBO;
|
|
#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,
|
|
kN32_SkColorType);
|
|
|
|
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);
|
|
|
|
|
|
SkAutoTUnref<SkSurface> surface(fWind->createSurface());
|
|
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 {
|
|
SkAutoTUnref<SkSurface> surface(fWind->createSurface());
|
|
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
|