Ken Thomases : winemac: Stop the CVDisplayLink when there are no more changes to flush.

Alexandre Julliard julliard at wine.codeweavers.com
Tue Nov 10 10:36:20 CST 2015


Module: wine
Branch: master
Commit: b4fc81bdf28264c381719966ead058fcafe299ce
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=b4fc81bdf28264c381719966ead058fcafe299ce

Author: Ken Thomases <ken at codeweavers.com>
Date:   Mon Nov  9 20:31:22 2015 -0600

winemac: Stop the CVDisplayLink when there are no more changes to flush.

The change to a CVDisplayLink-driven display mechanism introduced a problem: a
Wine process never went completely idle for long periods.  The display link
would fire for every refresh cycle of the display, waking a CPU from idle and
wasting energy.

To fix that, I have the display link stop itself when it determines that none
of its windows need to be displayed.  When a window is subsequently marked as
needing display, it either temporarily re-enables Cocoa's normal autodisplay
mechanism so that it displays at the end of the current turn of the run loop,
or it restarts the display link.  It chooses the former if it's been a long
time since the window was last displayed so that the display is done more
immediately.

Signed-off-by: Ken Thomases <ken at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/winemac.drv/cocoa_window.h |   1 +
 dlls/winemac.drv/cocoa_window.m | 116 ++++++++++++++++++++++++++++++++++------
 2 files changed, 102 insertions(+), 15 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index 5d581b2..19e5778 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -44,6 +44,7 @@
     pthread_mutex_t* surface_mutex;
 
     CGDirectDisplayID _lastDisplayID;
+    NSTimeInterval _lastDisplayTime;
 
     NSBezierPath* shape;
     NSData* shapeData;
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 49afd18..15fa16f 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -161,6 +161,9 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
     CGDirectDisplayID _displayID;
     CVDisplayLinkRef _link;
     NSMutableSet* _windows;
+
+    NSTimeInterval _actualRefreshPeriod;
+    NSTimeInterval _nominalRefreshPeriod;
 }
 
     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
@@ -168,6 +171,10 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
     - (void) addWindow:(WineWindow*)window;
     - (void) removeWindow:(WineWindow*)window;
 
+    - (NSTimeInterval) refreshPeriod;
+
+    - (void) start;
+
 @end
 
 @implementation WineDisplayLink
@@ -234,12 +241,41 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
             windows = [_windows copy];
         }
         dispatch_async(dispatch_get_main_queue(), ^{
+            BOOL anyDisplayed = FALSE;
             for (WineWindow* window in windows)
-                [window displayIfNeeded];
+            {
+                if ([window viewsNeedDisplay])
+                {
+                    [window displayIfNeeded];
+                    anyDisplayed = YES;
+                }
+            }
+            if (!anyDisplayed)
+                CVDisplayLinkStop(_link);
         });
         [windows release];
     }
 
+    - (NSTimeInterval) refreshPeriod
+    {
+        if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
+            return _actualRefreshPeriod;
+
+        if (_nominalRefreshPeriod)
+            return _nominalRefreshPeriod;
+
+        CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
+        if (time.flags & kCVTimeIsIndefinite)
+            return 1.0 / 60.0;
+        _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
+        return _nominalRefreshPeriod;
+    }
+
+    - (void) start
+    {
+        CVDisplayLinkStart(_link);
+    }
+
 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
 {
     WineDisplayLink* link = displayLinkContext;
@@ -690,6 +726,7 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
         window->savedContentMinSize = NSZeroSize;
         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
         window->resizable = wf->resizable;
+        window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
 
         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
                                                                   (NSString*)kUTTypeContent,
@@ -1663,21 +1700,8 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
         }
     }
 
-    - (void) checkWineDisplayLink
+    - (NSMutableDictionary*) displayIDToDisplayLinkMap
     {
-        NSScreen* screen = self.screen;
-        if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
-            screen = nil;
-#if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
-        if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
-            screen = nil;
-#endif
-
-        NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
-        CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
-        if (displayID == _lastDisplayID)
-            return;
-
         static NSMutableDictionary* displayIDToDisplayLinkMap;
         if (!displayIDToDisplayLinkMap)
         {
@@ -1693,6 +1717,34 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
             }];
         }
+        return displayIDToDisplayLinkMap;
+    }
+
+    - (WineDisplayLink*) wineDisplayLink
+    {
+        if (!_lastDisplayID)
+            return nil;
+
+        NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
+        return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
+    }
+
+    - (void) checkWineDisplayLink
+    {
+        NSScreen* screen = self.screen;
+        if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
+            screen = nil;
+#if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
+        if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
+            screen = nil;
+#endif
+
+        NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
+        CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
+        if (displayID == _lastDisplayID)
+            return;
+
+        NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
 
         if (_lastDisplayID)
         {
@@ -2026,6 +2078,40 @@ static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTi
             [super toggleFullScreen:sender];
     }
 
+    - (void) setViewsNeedDisplay:(BOOL)value
+    {
+        if (value && ![self viewsNeedDisplay])
+        {
+            WineDisplayLink* link = [self wineDisplayLink];
+            if (link)
+            {
+                NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
+                if (_lastDisplayTime + [link refreshPeriod] < now)
+                    [self setAutodisplay:YES];
+                else
+                {
+                    [link start];
+                    _lastDisplayTime = now;
+                }
+            }
+        }
+        [super setViewsNeedDisplay:value];
+    }
+
+    - (void) display
+    {
+        _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
+        [super display];
+        [self setAutodisplay:NO];
+    }
+
+    - (void) displayIfNeeded
+    {
+        _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
+        [super displayIfNeeded];
+        [self setAutodisplay:NO];
+    }
+
     - (NSArray*) childWineWindows
     {
         NSArray* childWindows = self.childWindows;




More information about the wine-cvs mailing list