Changeset - r24778:ede24664c30a
[Not reviewed]
master
0 2 0
Michael Lutz - 3 years ago 2021-02-06 21:58:51
michi@icosahedron.de
Change: [OSX] Use a layer-backed view to speed up drawing.
2 files changed with 19 insertions and 72 deletions:
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
@@ -128,12 +128,13 @@ if(NOT WIN32)
 
    else()
 
        find_package(Iconv)
 

	
 
        find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox)
 
        find_library(AUDIOUNIT_LIBRARY AudioUnit)
 
        find_library(COCOA_LIBRARY Cocoa)
 
        find_library(QUARTZCORE_LIBRARY QuartzCore)
 
    endif()
 
endif()
 

	
 
if(MSVC)
 
    find_package(Editbin REQUIRED)
 
endif()
 
@@ -160,12 +161,15 @@ if(APPLE)
 
    if(NOT AUDIOUNIT_LIBRARY)
 
        message(FATAL_ERROR "AudioUnit is required for this platform")
 
    endif()
 
    if(NOT COCOA_LIBRARY)
 
        message(FATAL_ERROR "Cocoa is required for this platform")
 
    endif()
 
    if(NOT QUARTZCORE_LIBRARY)
 
        message(FATAL_ERROR "QuartzCore is required for this platform")
 
    endif()
 
endif()
 

	
 
if(OPTION_PACKAGE_DEPENDENCIES)
 
    if(NOT UNIX)
 
        message(FATAL_ERROR "Can only package dependencies on Linux")
 
    endif()
 
@@ -247,12 +251,13 @@ if(APPLE)
 
    link_package(Iconv TARGET Iconv::Iconv)
 

	
 
    target_link_libraries(openttd
 
        ${AUDIOTOOLBOX_LIBRARY}
 
        ${AUDIOUNIT_LIBRARY}
 
        ${COCOA_LIBRARY}
 
        ${QUARTZCORE_LIBRARY}
 
    )
 

	
 
    add_definitions(
 
        -DWITH_COCOA
 
    )
 
endif()
src/video/cocoa/cocoa_v.mm
Show inline comments
 
@@ -97,14 +97,12 @@ static uint32 GetTick()
 

	
 
/* Subclass of OTTD_CocoaView to fix Quartz rendering */
 
@interface OTTD_QuartzView : NSView {
 
	VideoDriver_Cocoa *driver;
 
}
 
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv;
 

	
 
- (void)drawRect:(NSRect)invalidRect;
 
@end
 

	
 

	
 
VideoDriver_Cocoa::VideoDriver_Cocoa()
 
{
 
	this->window_width  = 0;
 
@@ -705,12 +703,18 @@ void VideoDriver_Cocoa::GameLoop()
 
@implementation OTTD_QuartzView
 

	
 
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv
 
{
 
	if (self = [ super initWithFrame:frameRect ]) {
 
		self->driver = drv;
 

	
 
		/* We manage our content updates ourselves. */
 
		self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
 
		self.wantsLayer = YES;
 

	
 
		self.layer.magnificationFilter = kCAFilterNearest;
 
	}
 
	return self;
 
}
 

	
 
- (BOOL)acceptsFirstResponder
 
{
 
@@ -719,86 +723,24 @@ void VideoDriver_Cocoa::GameLoop()
 

	
 
- (BOOL)isOpaque
 
{
 
	return YES;
 
}
 

	
 
- (void)drawRect:(NSRect)invalidRect
 
- (BOOL)wantsUpdateLayer
 
{
 
	return YES;
 
}
 

	
 
- (void)updateLayer
 
{
 
	if (driver->cgcontext == nullptr) return;
 

	
 
	NSGraphicsContext *ctx = [ NSGraphicsContext currentContext ];
 
	CGContextRef viewContext = [ ctx respondsToSelector:@selector(CGContext) ] ? [ ctx CGContext ] : (CGContextRef)[ ctx graphicsPort ];
 
	CGContextSetShouldAntialias(viewContext, FALSE);
 
	CGContextSetInterpolationQuality(viewContext, kCGInterpolationNone);
 

	
 
	/* The obtained 'rect' is actually a union of all dirty rects, let's ask for an explicit list of rects instead */
 
	const NSRect *dirtyRects;
 
	NSInteger     dirtyRectCount;
 
	[ self getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount ];
 

	
 
	/* We need an Image in order to do blitting, but as we don't touch the context between this call and drawing no copying will actually be done here */
 
	/* Set layer contents to our backing buffer, which avoids needless copying. */
 
	CGImageRef fullImage = CGBitmapContextCreateImage(driver->cgcontext);
 

	
 
	/* Calculate total area we are blitting */
 
	uint32 blitArea = 0;
 
	for (int n = 0; n < dirtyRectCount; n++) {
 
		blitArea += (uint32)(dirtyRects[n].size.width * dirtyRects[n].size.height);
 
	}
 

	
 
	/*
 
	 * This might be completely stupid, but in my extremely subjective opinion it feels faster
 
	 * The point is, if we're blitting less than 50% of the dirty rect union then it's still a good idea to blit each dirty
 
	 * rect separately but if we blit more than that, it's just cheaper to blit the entire union in one pass.
 
	 * Feel free to remove or find an even better value than 50% ... / blackis
 
	 */
 
	NSRect frameRect = [ self frame ];
 
	if (blitArea / (float)(invalidRect.size.width * invalidRect.size.height) > 0.5f) {
 
		NSRect rect = invalidRect;
 
		CGRect clipRect;
 
		CGRect blitRect;
 

	
 
		blitRect.origin.x = rect.origin.x;
 
		blitRect.origin.y = rect.origin.y;
 
		blitRect.size.width = rect.size.width;
 
		blitRect.size.height = rect.size.height;
 

	
 
		clipRect.origin.x = rect.origin.x;
 
		clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height;
 

	
 
		clipRect.size.width = rect.size.width;
 
		clipRect.size.height = rect.size.height;
 

	
 
		/* Blit dirty part of image */
 
		CGImageRef clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect);
 
		CGContextDrawImage(viewContext, blitRect, clippedImage);
 
		CGImageRelease(clippedImage);
 
	} else {
 
		for (int n = 0; n < dirtyRectCount; n++) {
 
			NSRect rect = dirtyRects[n];
 
			CGRect clipRect;
 
			CGRect blitRect;
 

	
 
			blitRect.origin.x = rect.origin.x;
 
			blitRect.origin.y = rect.origin.y;
 
			blitRect.size.width = rect.size.width;
 
			blitRect.size.height = rect.size.height;
 

	
 
			clipRect.origin.x = rect.origin.x;
 
			clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height;
 

	
 
			clipRect.size.width = rect.size.width;
 
			clipRect.size.height = rect.size.height;
 

	
 
			/* Blit dirty part of image */
 
			CGImageRef clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect);
 
			CGContextDrawImage(viewContext, blitRect, clippedImage);
 
			CGImageRelease(clippedImage);
 
		}
 
	}
 

	
 
	self.layer.contents = (__bridge id)fullImage;
 
	CGImageRelease(fullImage);
 
}
 

	
 
@end
 

	
 
#endif /* WITH_COCOA */
0 comments (0 inline, 0 general)