From 4957d7be5bf9953786520754adf7da424b704de3 Mon Sep 17 00:00:00 2001 From: rogerman Date: Sat, 16 Sep 2017 00:13:52 -0700 Subject: [PATCH] Cocoa Port: Rework how Metal display views flush to screen, improving their overall performance. - Fix a bug where Metal display views can block emulation execution if the user very quickly spams inputs while the input HUD is shown. - Metal display views are no longer frame capped -- they can now run to their fullest performance potential, as fast as the host hardware will allow. This behavior is now consistent with OpenGL display views. - As a side-effect, non-layer backed OpenGL display views also have a performance improvement. --- .../src/frontend/cocoa/ClientDisplayView.cpp | 38 +- .../src/frontend/cocoa/ClientDisplayView.h | 13 +- .../project.pbxproj | 8 + .../project.pbxproj | 12 + desmume/src/frontend/cocoa/cocoa_GPU.h | 23 ++ desmume/src/frontend/cocoa/cocoa_GPU.mm | 187 +++++++++ desmume/src/frontend/cocoa/cocoa_output.h | 2 + desmume/src/frontend/cocoa/cocoa_output.mm | 18 + .../cocoa/userinterface/DisplayViewCALayer.h | 55 +++ .../cocoa/userinterface/DisplayViewCALayer.mm | 34 ++ .../userinterface/DisplayWindowController.h | 46 +-- .../userinterface/DisplayWindowController.mm | 216 ++++++----- .../cocoa/userinterface/MacMetalDisplayView.h | 18 +- .../userinterface/MacMetalDisplayView.mm | 360 ++++++++++-------- .../cocoa/userinterface/MacOGLDisplayView.h | 9 +- .../cocoa/userinterface/MacOGLDisplayView.mm | 51 ++- .../cocoa/userinterface/appDelegate.mm | 1 + 17 files changed, 788 insertions(+), 303 deletions(-) diff --git a/desmume/src/frontend/cocoa/ClientDisplayView.cpp b/desmume/src/frontend/cocoa/ClientDisplayView.cpp index cc6fec2d5..81b7dfaf0 100644 --- a/desmume/src/frontend/cocoa/ClientDisplayView.cpp +++ b/desmume/src/frontend/cocoa/ClientDisplayView.cpp @@ -81,6 +81,7 @@ void ClientDisplayView::__InstanceInit(const ClientDisplayViewProperties &props) _selectedSourceForDisplay[NDSDisplayID_Main] = NDSDisplayID_Main; _selectedSourceForDisplay[NDSDisplayID_Touch] = NDSDisplayID_Touch; + _displayViewID = 0; _useVerticalSync = false; _scaleFactor = 1.0; @@ -112,7 +113,9 @@ void ClientDisplayView::__InstanceInit(const ClientDisplayViewProperties &props) _outHudString = _hudString; _hudInputString = "<^>vABXYLRSsgf x:000 y:000"; _hudNeedsUpdate = true; + _viewNeedsFlush = false; _allowViewUpdates = true; + _allowViewFlushes = true; FT_Error error = FT_Init_FreeType(&_ftLibrary); if (error) @@ -225,6 +228,22 @@ void ClientDisplayView::Init() // Do nothing. This is implementation dependent. } +int64_t ClientDisplayView::GetDisplayViewID() +{ + return this->_displayViewID; +} + +void ClientDisplayView::SetDisplayViewID(int64_t displayViewID) +{ + // This implementation-dependent value will never be used internally. + this->_displayViewID = displayViewID; +} + +bool ClientDisplayView::GetViewNeedsFlush() +{ + return this->_viewNeedsFlush; +} + bool ClientDisplayView::GetUseVerticalSync() const { return this->_useVerticalSync; @@ -809,11 +828,21 @@ bool ClientDisplayView::GetAllowViewUpdates() const return this->_allowViewUpdates; } -void ClientDisplayView::SetAllowViewUpdates(const bool allowUpdates) +void ClientDisplayView::SetAllowViewUpdates(bool allowUpdates) { this->_allowViewUpdates = allowUpdates; } +bool ClientDisplayView::GetAllowViewFlushes() const +{ + return this->_allowViewFlushes; +} + +void ClientDisplayView::SetAllowViewFlushes(bool allowFlushes) +{ + this->_allowViewFlushes = allowFlushes; +} + void ClientDisplayView::_LoadNativeDisplayByID(const NDSDisplayID displayID) { // Do nothing. This is implementation dependent. @@ -979,6 +1008,13 @@ void ClientDisplayView::ProcessDisplays() void ClientDisplayView::UpdateView() { // Do nothing. This is implementation dependent. + this->_viewNeedsFlush = true; +} + +void ClientDisplayView::FlushView() +{ + // Do nothing. This is implementation dependent. + this->_viewNeedsFlush = false; } void ClientDisplayView::FinishFrameAtIndex(const u8 bufferIndex) diff --git a/desmume/src/frontend/cocoa/ClientDisplayView.h b/desmume/src/frontend/cocoa/ClientDisplayView.h index fda9af322..5cf5a05d8 100644 --- a/desmume/src/frontend/cocoa/ClientDisplayView.h +++ b/desmume/src/frontend/cocoa/ClientDisplayView.h @@ -152,6 +152,7 @@ protected: bool _isSelectedDisplayEnabled[2]; NDSDisplayID _selectedSourceForDisplay[2]; + int64_t _displayViewID; bool _useVerticalSync; double _scaleFactor; @@ -183,7 +184,9 @@ protected: std::string _hudInputString; std::string _outHudString; bool _hudNeedsUpdate; + bool _viewNeedsFlush; bool _allowViewUpdates; + bool _allowViewFlushes; FT_Library _ftLibrary; const char *_lastFontFilePath; @@ -218,6 +221,11 @@ public: virtual void Init(); + int64_t GetDisplayViewID(); + virtual void SetDisplayViewID(int64_t displayViewID); + + virtual bool GetViewNeedsFlush(); + bool GetUseVerticalSync() const; virtual void SetUseVerticalSync(const bool useVerticalSync); double GetScaleFactor() const; @@ -304,11 +312,14 @@ public: void SetFetchObject(GPUClientFetchObject *fetchObject); bool GetAllowViewUpdates() const; - void SetAllowViewUpdates(const bool allowUpdates); + virtual void SetAllowViewUpdates(bool allowUpdates); + bool GetAllowViewFlushes() const; + virtual void SetAllowViewFlushes(bool allowFlushes); virtual void LoadDisplays(); virtual void ProcessDisplays(); virtual void UpdateView(); + virtual void FlushView(); virtual void FinishFrameAtIndex(const u8 bufferIndex); // Emulator interface diff --git a/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj b/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj index fff0158f8..fdd734a1e 100644 --- a/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj +++ b/desmume/src/frontend/cocoa/DeSmuME (Latest).xcodeproj/project.pbxproj @@ -877,6 +877,9 @@ ABADF11F1DEA4D0000A142B1 /* Database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABADF1171DEA4C1200A142B1 /* Database.cpp */; }; ABADF1201DEA4D0000A142B1 /* Database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABADF1171DEA4C1200A142B1 /* Database.cpp */; }; ABADF1211DEA4D0000A142B1 /* Database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABADF1171DEA4C1200A142B1 /* Database.cpp */; }; + ABAE1F6F1F6873E70080EFE3 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABAE1F6E1F6873E70080EFE3 /* CoreVideo.framework */; }; + ABAE1F701F6874090080EFE3 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABAE1F6E1F6873E70080EFE3 /* CoreVideo.framework */; }; + ABAE1F711F6874090080EFE3 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABAE1F6E1F6873E70080EFE3 /* CoreVideo.framework */; }; ABAF0A411A96E67200B95B75 /* RomInfoPanel.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABAF0A401A96E67200B95B75 /* RomInfoPanel.mm */; }; ABAF0A421A96E67200B95B75 /* RomInfoPanel.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABAF0A401A96E67200B95B75 /* RomInfoPanel.mm */; }; ABAF0A431A96E67200B95B75 /* RomInfoPanel.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABAF0A401A96E67200B95B75 /* RomInfoPanel.mm */; }; @@ -1566,6 +1569,7 @@ ABADF1161DEA4C1200A142B1 /* Database.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Database.h; sourceTree = ""; }; ABADF1171DEA4C1200A142B1 /* Database.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Database.cpp; sourceTree = ""; }; ABADF11A1DEA4CF700A142B1 /* features_cpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = features_cpu.c; sourceTree = ""; }; + ABAE1F6E1F6873E70080EFE3 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; ABAF0A3F1A96E67200B95B75 /* RomInfoPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RomInfoPanel.h; sourceTree = ""; }; ABAF0A401A96E67200B95B75 /* RomInfoPanel.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RomInfoPanel.mm; sourceTree = ""; }; ABB0FBC41A9E5CEA0060C55A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; @@ -1932,6 +1936,7 @@ ABC570D5134431DA00E7B0B1 /* OpenGL.framework in Frameworks */, AB3BF4341E256309003E2B24 /* QuartzCore.framework in Frameworks */, AB4676F314AB12D60002FF94 /* libz.dylib in Frameworks */, + ABAE1F711F6874090080EFE3 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1946,6 +1951,7 @@ AB74EC8A1738499C0026C41E /* Carbon.framework in Frameworks */, AB796D6815CDCBA200C59155 /* Cocoa.framework in Frameworks */, ABB0FBC71A9E5D080060C55A /* CoreAudio.framework in Frameworks */, + ABAE1F6F1F6873E70080EFE3 /* CoreVideo.framework in Frameworks */, AB564907186E6F0C002740F4 /* ForceFeedback.framework in Frameworks */, AB796D6915CDCBA200C59155 /* Foundation.framework in Frameworks */, AB796D6A15CDCBA200C59155 /* IOKit.framework in Frameworks */, @@ -1974,6 +1980,7 @@ AB8F3D281A53AC2600A80BF6 /* OpenGL.framework in Frameworks */, AB3BF4351E256309003E2B24 /* QuartzCore.framework in Frameworks */, AB8F3D291A53AC2600A80BF6 /* libz.dylib in Frameworks */, + ABAE1F701F6874090080EFE3 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2062,6 +2069,7 @@ AB74EC891738499C0026C41E /* Carbon.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ABB0FBC41A9E5CEA0060C55A /* CoreAudio.framework */, + ABAE1F6E1F6873E70080EFE3 /* CoreVideo.framework */, AB564906186E6F0C002740F4 /* ForceFeedback.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, AB350BA41478AC96007165AC /* IOKit.framework */, diff --git a/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj b/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj index cfb085910..e23d372a4 100644 --- a/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj +++ b/desmume/src/frontend/cocoa/DeSmuME (XCode 3).xcodeproj/project.pbxproj @@ -782,6 +782,11 @@ AB43528917D5BA95007417C8 /* fsnitro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB43528617D5BA95007417C8 /* fsnitro.cpp */; }; AB43528A17D5BA95007417C8 /* fsnitro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB43528617D5BA95007417C8 /* fsnitro.cpp */; }; AB43528B17D5BA95007417C8 /* fsnitro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB43528617D5BA95007417C8 /* fsnitro.cpp */; }; + AB446DA11F69DB56002F32B6 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB446DA01F69DB56002F32B6 /* CoreVideo.framework */; }; + AB446DB31F69DB68002F32B6 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB446DA01F69DB56002F32B6 /* CoreVideo.framework */; }; + AB446DB41F69DB68002F32B6 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB446DA01F69DB56002F32B6 /* CoreVideo.framework */; }; + AB446DB51F69DB69002F32B6 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB446DA01F69DB56002F32B6 /* CoreVideo.framework */; }; + AB446DB61F69DB69002F32B6 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB446DA01F69DB56002F32B6 /* CoreVideo.framework */; }; AB4C4C3F16F55C64002E07CD /* AAFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB4C4C2A16F55C64002E07CD /* AAFilter.cpp */; }; AB4C4C4016F55C64002E07CD /* cpu_detect_x86_gcc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB4C4C2B16F55C64002E07CD /* cpu_detect_x86_gcc.cpp */; }; AB4C4C4116F55C64002E07CD /* FIFOSampleBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AB4C4C2C16F55C64002E07CD /* FIFOSampleBuffer.cpp */; }; @@ -1912,6 +1917,7 @@ AB43527117D5BA5E007417C8 /* slot1_retail_mcrom_debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = slot1_retail_mcrom_debug.cpp; sourceTree = ""; }; AB43528517D5BA95007417C8 /* fsnitro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsnitro.h; sourceTree = ""; }; AB43528617D5BA95007417C8 /* fsnitro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fsnitro.cpp; sourceTree = ""; }; + AB446DA01F69DB56002F32B6 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; AB4C4C2A16F55C64002E07CD /* AAFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AAFilter.cpp; sourceTree = ""; }; AB4C4C2B16F55C64002E07CD /* cpu_detect_x86_gcc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpu_detect_x86_gcc.cpp; sourceTree = ""; }; AB4C4C2C16F55C64002E07CD /* FIFOSampleBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIFOSampleBuffer.cpp; sourceTree = ""; }; @@ -2305,6 +2311,7 @@ AB2A9A731725F00F0062C1A1 /* OpenGL.framework in Frameworks */, AB1CC8001AA509C2008B0A16 /* CoreAudio.framework in Frameworks */, AB3E69801E25FB9800D4CC75 /* QuartzCore.framework in Frameworks */, + AB446DB61F69DB69002F32B6 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2325,6 +2332,7 @@ AB2F3C4015CF9C6000858373 /* OpenGL.framework in Frameworks */, AB1CC80A1AA509DF008B0A16 /* CoreAudio.framework in Frameworks */, AB3E697F1E25FB9700D4CC75 /* QuartzCore.framework in Frameworks */, + AB446DB51F69DB69002F32B6 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2345,6 +2353,7 @@ AB711F761481C35F009011C8 /* OpenGL.framework in Frameworks */, AB1CC80D1AA509E1008B0A16 /* CoreAudio.framework in Frameworks */, AB3E69451E25FB8400D4CC75 /* QuartzCore.framework in Frameworks */, + AB446DA11F69DB56002F32B6 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2365,6 +2374,7 @@ AB73AA2E1507C9F500A310C8 /* OpenGL.framework in Frameworks */, AB1CC80C1AA509E0008B0A16 /* CoreAudio.framework in Frameworks */, AB3E697D1E25FB9600D4CC75 /* QuartzCore.framework in Frameworks */, + AB446DB31F69DB68002F32B6 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2385,6 +2395,7 @@ ABAD104415ACE7A00000EC47 /* OpenGL.framework in Frameworks */, AB1CC80B1AA509E0008B0A16 /* CoreAudio.framework in Frameworks */, AB3E697E1E25FB9700D4CC75 /* QuartzCore.framework in Frameworks */, + AB446DB41F69DB68002F32B6 /* CoreVideo.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2453,6 +2464,7 @@ ABB6AD5C173A3F2B00EC2E8D /* Carbon.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, AB1CC7FF1AA509C2008B0A16 /* CoreAudio.framework */, + AB446DA01F69DB56002F32B6 /* CoreVideo.framework */, AB8C6E56186CD07E00E3EC64 /* ForceFeedback.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, AB350BA41478AC96007165AC /* IOKit.framework */, diff --git a/desmume/src/frontend/cocoa/cocoa_GPU.h b/desmume/src/frontend/cocoa/cocoa_GPU.h index 881b195c5..a86c3db45 100644 --- a/desmume/src/frontend/cocoa/cocoa_GPU.h +++ b/desmume/src/frontend/cocoa/cocoa_GPU.h @@ -16,8 +16,10 @@ */ #import +#import #include #include +#include #import "cocoa_util.h" #include "../../GPU.h" @@ -30,15 +32,24 @@ #define ENABLE_APPLE_METAL #endif +#define VIDEO_FLUSH_TIME_LIMIT_OFFSET 8 // The amount of time, in seconds, to wait for a flush to occur on a given CVDisplayLink before stopping it. + class GPUEventHandlerOSX; +typedef std::map DisplayLinksActiveMap; +typedef std::map DisplayLinkFlushTimeLimitMap; + @interface MacClientSharedObject : CocoaDSThread { GPUClientFetchObject *GPUFetchObject; pthread_rwlock_t *_rwlockFramebuffer[2]; pthread_mutex_t *_mutexOutputList; + pthread_mutex_t _mutexFlushVideo; NSMutableArray *_cdsOutputList; volatile int32_t numberViewsUsingDirectToCPUFiltering; + + DisplayLinksActiveMap _displayLinksActiveList; + DisplayLinkFlushTimeLimitMap _displayLinkFlushTimeList; } @property (assign, nonatomic) GPUClientFetchObject *GPUFetchObject; @@ -52,6 +63,11 @@ class GPUEventHandlerOSX; - (void) handleFetchFromBufferIndexAndPushVideo:(NSData *)indexData; - (void) pushVideoDataToAllDisplayViews; - (void) finishAllDisplayViewsAtIndex:(const u8)bufferIndex; +- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStamp:(const CVTimeStamp *)timeStamp; + +- (BOOL) isDisplayLinkRunningUsingID:(CGDirectDisplayID)displayID; +- (void) displayLinkStartUsingID:(CGDirectDisplayID)displayID; +- (void) displayLinkListUpdate; @end @@ -115,6 +131,13 @@ extern "C" { #endif +CVReturn MacDisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp *inNow, + const CVTimeStamp *inOutputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext); + bool OSXOpenGLRendererInit(); bool OSXOpenGLRendererBegin(); void OSXOpenGLRendererEnd(); diff --git a/desmume/src/frontend/cocoa/cocoa_GPU.mm b/desmume/src/frontend/cocoa/cocoa_GPU.mm index 843e52a4c..0158f64f2 100644 --- a/desmume/src/frontend/cocoa/cocoa_GPU.mm +++ b/desmume/src/frontend/cocoa/cocoa_GPU.mm @@ -876,17 +876,45 @@ public: pthread_rwlock_init(_rwlockFramebuffer[0], NULL); pthread_rwlock_init(_rwlockFramebuffer[1], NULL); + pthread_mutex_init(&_mutexFlushVideo, NULL); GPUFetchObject = nil; _mutexOutputList = NULL; _cdsOutputList = nil; numberViewsUsingDirectToCPUFiltering = 0; + _displayLinksActiveList.clear(); + _displayLinkFlushTimeList.clear(); + [self displayLinkListUpdate]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(respondToScreenChange:) + name:@"NSApplicationDidChangeScreenParametersNotification" + object:NSApp]; + return self; } - (void)dealloc { + pthread_mutex_lock(&_mutexFlushVideo); + + for (DisplayLinksActiveMap::iterator it = _displayLinksActiveList.begin(); it != _displayLinksActiveList.end(); ++it) + { + CGDirectDisplayID displayID = it->first; + CVDisplayLinkRef displayLinkRef = it->second; + if (CVDisplayLinkIsRunning(displayLinkRef)) + { + CVDisplayLinkStop(displayLinkRef); + CVDisplayLinkRelease(displayLinkRef); + } + + _displayLinksActiveList.erase(displayID); + _displayLinkFlushTimeList.erase(displayID); + } + + pthread_mutex_unlock(&_mutexFlushVideo); + pthread_mutex_t *currentMutex = _mutexOutputList; if (currentMutex != NULL) @@ -903,6 +931,7 @@ public: pthread_rwlock_destroy(_rwlockFramebuffer[0]); pthread_rwlock_destroy(_rwlockFramebuffer[1]); + pthread_mutex_destroy(&_mutexFlushVideo); [super dealloc]; } @@ -1020,6 +1049,151 @@ public: } } +- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStamp:(const CVTimeStamp *)timeStamp +{ + pthread_mutex_t *currentMutex = _mutexOutputList; + CGDirectDisplayID displayID = CVDisplayLinkGetCurrentCGDisplay(displayLink); + bool didFlushOccur = false; + + if (currentMutex != NULL) + { + pthread_mutex_lock(currentMutex); + } + + for (CocoaDSOutput *cdsOutput in _cdsOutputList) + { + if ([cdsOutput isKindOfClass:[CocoaDSDisplayVideo class]]) + { + if ([(CocoaDSDisplayVideo *)cdsOutput currentDisplayID] == displayID) + { + ClientDisplay3DView *cdv = [(CocoaDSDisplayVideo *)cdsOutput clientDisplayView]; + + if (cdv->GetViewNeedsFlush()) + { + cdv->FlushView(); + didFlushOccur = true; + } + } + } + } + + if (currentMutex != NULL) + { + pthread_mutex_unlock(currentMutex); + } + + pthread_mutex_lock(&_mutexFlushVideo); + + if (didFlushOccur) + { + // Set the new time limit to 8 seconds after the current time. + _displayLinkFlushTimeList[displayID] = timeStamp->videoTime + (timeStamp->videoTimeScale * VIDEO_FLUSH_TIME_LIMIT_OFFSET); + } + else if (timeStamp->videoTime > _displayLinkFlushTimeList[displayID]) + { + CVDisplayLinkStop(displayLink); + } + + pthread_mutex_unlock(&_mutexFlushVideo); +} + +- (BOOL) isDisplayLinkRunningUsingID:(CGDirectDisplayID)displayID +{ + CVDisplayLinkRef displayLink = NULL; + + pthread_mutex_lock(&_mutexFlushVideo); + + if (_displayLinksActiveList.find(displayID) != _displayLinksActiveList.end()) + { + displayLink = _displayLinksActiveList[displayID]; + } + + const BOOL isRunning = ( (displayLink != NULL) && CVDisplayLinkIsRunning(displayLink) ) ? YES : NO; + + pthread_mutex_unlock(&_mutexFlushVideo); + + return isRunning; +} + +- (void) displayLinkStartUsingID:(CGDirectDisplayID)displayID +{ + CVDisplayLinkRef displayLink = NULL; + + pthread_mutex_lock(&_mutexFlushVideo); + + if (_displayLinksActiveList.find(displayID) != _displayLinksActiveList.end()) + { + displayLink = _displayLinksActiveList[displayID]; + } + + if (displayLink != NULL) + { + CVDisplayLinkStart(displayLink); + } + + pthread_mutex_unlock(&_mutexFlushVideo); +} + +- (void) displayLinkListUpdate +{ + // Set up the display links + NSArray *screenList = [NSScreen screens]; + std::set screenActiveDisplayIDsList; + + pthread_mutex_lock(&_mutexFlushVideo); + + // Add new CGDirectDisplayIDs for new screens + for (size_t i = 0; i < [screenList count]; i++) + { + NSScreen *screen = [screenList objectAtIndex:i]; + NSDictionary *deviceDescription = [screen deviceDescription]; + NSNumber *idNumber = (NSNumber *)[deviceDescription valueForKey:@"NSScreenNumber"]; + + CGDirectDisplayID displayID = [idNumber unsignedIntValue]; + bool isDisplayLinkStillActive = (_displayLinksActiveList.find(displayID) != _displayLinksActiveList.end()); + + if (!isDisplayLinkStillActive) + { + CVDisplayLinkRef newDisplayLink; + CVDisplayLinkCreateWithActiveCGDisplays(&newDisplayLink); + CVDisplayLinkSetCurrentCGDisplay(newDisplayLink, displayID); + CVDisplayLinkSetOutputCallback(newDisplayLink, &MacDisplayLinkCallback, self); + + _displayLinksActiveList[displayID] = newDisplayLink; + _displayLinkFlushTimeList[displayID] = 0; + } + + // While we're iterating through NSScreens, save the CGDirectDisplayID to a temporary list for later use. + screenActiveDisplayIDsList.insert(displayID); + } + + // Remove old CGDirectDisplayIDs for screens that no longer exist + for (DisplayLinksActiveMap::iterator it = _displayLinksActiveList.begin(); it != _displayLinksActiveList.end(); ++it) + { + CGDirectDisplayID displayID = it->first; + CVDisplayLinkRef displayLinkRef = it->second; + + if (screenActiveDisplayIDsList.find(displayID) == screenActiveDisplayIDsList.end()) + { + if (CVDisplayLinkIsRunning(displayLinkRef)) + { + CVDisplayLinkStop(displayLinkRef); + CVDisplayLinkRelease(displayLinkRef); + } + + _displayLinksActiveList.erase(displayID); + _displayLinkFlushTimeList.erase(displayID); + } + } + + pthread_mutex_unlock(&_mutexFlushVideo); +} + +- (void) respondToScreenChange:(NSNotification *)aNotification +{ + [self displayLinkListUpdate]; +} + @end #pragma mark - @@ -1143,6 +1317,19 @@ CGLContextObj OSXOpenGLRendererContext = NULL; CGLPBufferObj OSXOpenGLRendererPBuffer = NULL; #pragma GCC diagnostic pop +CVReturn MacDisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp *inNow, + const CVTimeStamp *inOutputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext) +{ + MacClientSharedObject *sharedData = (MacClientSharedObject *)displayLinkContext; + [sharedData flushAllDisplaysOnDisplayLink:displayLink timeStamp:inNow]; + + return kCVReturnSuccess; +} + bool OSXOpenGLRendererInit() { static bool isContextAlreadyCreated = false; diff --git a/desmume/src/frontend/cocoa/cocoa_output.h b/desmume/src/frontend/cocoa/cocoa_output.h index b8e61eaba..b2eb1062d 100644 --- a/desmume/src/frontend/cocoa/cocoa_output.h +++ b/desmume/src/frontend/cocoa/cocoa_output.h @@ -137,6 +137,7 @@ OSSpinLock spinlockSourceDeposterize; OSSpinLock spinlockPixelScaler; OSSpinLock spinlockDisplayVideoSource; + OSSpinLock spinlockDisplayID; } @property (readonly, nonatomic) BOOL canFilterOnGPU; @@ -160,6 +161,7 @@ @property (assign) uint32_t hudColorInputPendingOnly; @property (assign) NSInteger displayMainVideoSource; @property (assign) NSInteger displayTouchVideoSource; +@property (assign) uint32_t currentDisplayID; @property (assign) BOOL useVerticalSync; @property (assign) BOOL videoFiltersPreferGPU; @property (assign) BOOL sourceDeposterize; diff --git a/desmume/src/frontend/cocoa/cocoa_output.mm b/desmume/src/frontend/cocoa/cocoa_output.mm index 06ac2f280..870da4f39 100644 --- a/desmume/src/frontend/cocoa/cocoa_output.mm +++ b/desmume/src/frontend/cocoa/cocoa_output.mm @@ -751,6 +751,7 @@ @dynamic hudColorInputPendingAndApplied; @dynamic hudColorInputAppliedOnly; @dynamic hudColorInputPendingOnly; +@dynamic currentDisplayID; @dynamic useVerticalSync; @dynamic videoFiltersPreferGPU; @dynamic sourceDeposterize; @@ -774,6 +775,7 @@ spinlockSourceDeposterize = OS_SPINLOCK_INIT; spinlockPixelScaler = OS_SPINLOCK_INIT; spinlockDisplayVideoSource = OS_SPINLOCK_INIT; + spinlockDisplayID = OS_SPINLOCK_INIT; return self; } @@ -1097,6 +1099,22 @@ return displayVideoSource; } +- (void) setCurrentDisplayID:(uint32_t)theDisplayID +{ + OSSpinLockLock(&spinlockDisplayID); + _cdv->SetDisplayViewID((int64_t)theDisplayID); + OSSpinLockUnlock(&spinlockDisplayID); +} + +- (uint32_t) currentDisplayID +{ + OSSpinLockLock(&spinlockDisplayID); + const uint32_t displayID = (uint32_t)_cdv->GetDisplayViewID(); + OSSpinLockUnlock(&spinlockDisplayID); + + return displayID; +} + - (void) setUseVerticalSync:(BOOL)theState { OSSpinLockLock(&spinlockUseVerticalSync); diff --git a/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.h b/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.h index 59639c68d..274ad1352 100644 --- a/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.h +++ b/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.h @@ -21,6 +21,10 @@ #import #import +#import "InputManager.h" + +@class CocoaDSDisplayVideo; +@class MacClientSharedObject; class ClientDisplay3DView; @protocol DisplayViewCALayer @@ -30,17 +34,68 @@ class ClientDisplay3DView; @end +@protocol CocoaDisplayViewProtocol + +@required +@property (retain) InputManager *inputManager; +@property (retain) CocoaDSDisplayVideo *cdsVideoOutput; +@property (readonly, nonatomic) ClientDisplay3DView *clientDisplay3DView; +@property (readonly) BOOL canUseShaderBasedFilters; +@property (assign, nonatomic) BOOL allowViewUpdates; +@property (assign) BOOL isHUDVisible; +@property (assign) BOOL isHUDVideoFPSVisible; +@property (assign) BOOL isHUDRender3DFPSVisible; +@property (assign) BOOL isHUDFrameIndexVisible; +@property (assign) BOOL isHUDLagFrameCountVisible; +@property (assign) BOOL isHUDCPULoadAverageVisible; +@property (assign) BOOL isHUDRealTimeClockVisible; +@property (assign) BOOL isHUDInputVisible; +@property (assign) NSColor *hudColorVideoFPS; +@property (assign) NSColor *hudColorRender3DFPS; +@property (assign) NSColor *hudColorFrameIndex; +@property (assign) NSColor *hudColorLagFrameCount; +@property (assign) NSColor *hudColorCPULoadAverage; +@property (assign) NSColor *hudColorRTC; +@property (assign) NSColor *hudColorInputPendingAndApplied; +@property (assign) NSColor *hudColorInputAppliedOnly; +@property (assign) NSColor *hudColorInputPendingOnly; +@property (assign) NSInteger displayMainVideoSource; +@property (assign) NSInteger displayTouchVideoSource; +@property (assign) BOOL useVerticalSync; +@property (assign) BOOL videoFiltersPreferGPU; +@property (assign) BOOL sourceDeposterize; +@property (assign) NSInteger outputFilter; +@property (assign) NSInteger pixelScaler; + +- (void) setupLayer; +- (void) requestScreenshot:(NSURL *)fileURL fileType:(NSBitmapImageFileType)fileType; + +@end + class DisplayViewCALayerInterface { private: + NSView *_nsView; CALayer *_frontendLayer; + bool _willRenderToCALayer; + + MacClientSharedObject *_sharedData; public: DisplayViewCALayerInterface(); + NSView* GetNSView() const; + void SetNSView(NSView *theView); + CALayer* GetFrontendLayer() const; void SetFrontendLayer(CALayer *layer); void CALayerDisplay(); + + bool GetRenderToCALayer() const; + void SetRenderToCALayer(const bool renderToLayer); + + MacClientSharedObject* GetSharedData(); + void SetSharedData(MacClientSharedObject *sharedObject); }; #endif // _DISPLAYVIEWCALAYER_H diff --git a/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.mm b/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.mm index 709236e0c..e62a350aa 100644 --- a/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.mm +++ b/desmume/src/frontend/cocoa/userinterface/DisplayViewCALayer.mm @@ -16,10 +16,24 @@ */ #import "DisplayViewCALayer.h" +#import "../cocoa_GPU.h" DisplayViewCALayerInterface::DisplayViewCALayerInterface() { + _nsView = nil; _frontendLayer = nil; + _sharedData = nil; + _willRenderToCALayer = false; +} + +NSView* DisplayViewCALayerInterface::GetNSView() const +{ + return this->_nsView; +} + +void DisplayViewCALayerInterface::SetNSView(NSView *theView) +{ + this->_nsView = theView; } CALayer* DisplayViewCALayerInterface::GetFrontendLayer() const @@ -36,3 +50,23 @@ void DisplayViewCALayerInterface::CALayerDisplay() { [this->_frontendLayer setNeedsDisplay]; } + +bool DisplayViewCALayerInterface::GetRenderToCALayer() const +{ + return this->_willRenderToCALayer; +} + +void DisplayViewCALayerInterface::SetRenderToCALayer(const bool renderToLayer) +{ + this->_willRenderToCALayer = renderToLayer; +} + +MacClientSharedObject* DisplayViewCALayerInterface::GetSharedData() +{ + return this->_sharedData; +} + +void DisplayViewCALayerInterface::SetSharedData(MacClientSharedObject *sharedObject) +{ + this->_sharedData = sharedObject; +} diff --git a/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.h b/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.h index c93e1dca3..d745ed110 100644 --- a/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.h +++ b/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.h @@ -17,11 +17,9 @@ #import #import -#include #include -#import "InputManager.h" -#import "cocoa_output.h" +#import "DisplayViewCALayer.h" #include "../ClientDisplayView.h" #undef BOOL @@ -30,7 +28,9 @@ #define DISPLAY_VIDEO_SOURCE_TOUCH_TAG_BASE 2000 @class CocoaDSController; +@class CocoaDSDisplayVideo; @class EmuControllerDelegate; +@class InputManager; class OGLVideoOutput; // Subclass NSWindow for full screen windows so that we can override some methods. @@ -38,7 +38,7 @@ class OGLVideoOutput; { } @end -@interface DisplayView : NSView +@interface DisplayView : NSView { InputManager *inputManager; CocoaDSDisplayVideo *cdsVideoOutput; @@ -46,41 +46,8 @@ class OGLVideoOutput; NSOpenGLContext *localOGLContext; } -@property (retain) InputManager *inputManager; -@property (retain) CocoaDSDisplayVideo *cdsVideoOutput; -@property (readonly, nonatomic) ClientDisplay3DView *clientDisplay3DView; -@property (readonly) BOOL canUseShaderBasedFilters; -@property (assign, nonatomic) BOOL allowViewUpdates; -@property (assign) BOOL isHUDVisible; -@property (assign) BOOL isHUDVideoFPSVisible; -@property (assign) BOOL isHUDRender3DFPSVisible; -@property (assign) BOOL isHUDFrameIndexVisible; -@property (assign) BOOL isHUDLagFrameCountVisible; -@property (assign) BOOL isHUDCPULoadAverageVisible; -@property (assign) BOOL isHUDRealTimeClockVisible; -@property (assign) BOOL isHUDInputVisible; -@property (assign) NSColor *hudColorVideoFPS; -@property (assign) NSColor *hudColorRender3DFPS; -@property (assign) NSColor *hudColorFrameIndex; -@property (assign) NSColor *hudColorLagFrameCount; -@property (assign) NSColor *hudColorCPULoadAverage; -@property (assign) NSColor *hudColorRTC; -@property (assign) NSColor *hudColorInputPendingAndApplied; -@property (assign) NSColor *hudColorInputAppliedOnly; -@property (assign) NSColor *hudColorInputPendingOnly; -@property (assign) NSInteger displayMainVideoSource; -@property (assign) NSInteger displayTouchVideoSource; -@property (assign) BOOL useVerticalSync; -@property (assign) BOOL videoFiltersPreferGPU; -@property (assign) BOOL sourceDeposterize; -@property (assign) NSInteger outputFilter; -@property (assign) NSInteger pixelScaler; - -- (void) setupLayer; - - (BOOL) handleKeyPress:(NSEvent *)theEvent keyPressed:(BOOL)keyPressed; - (BOOL) handleMouseButton:(NSEvent *)theEvent buttonPressed:(BOOL)buttonPressed; -- (void) requestScreenshot:(NSURL *)fileURL fileType:(NSBitmapImageFileType)fileType; @end @@ -101,7 +68,7 @@ class OGLVideoOutput; NSSlider *microphoneGainSlider; NSButton *microphoneMuteButton; - DisplayView *view; + NSView *view; EmuControllerDelegate *emuControl; CocoaDSDisplayVideo *cdsVideoOutput; NSScreen *assignedScreen; @@ -131,7 +98,7 @@ class OGLVideoOutput; @property (readonly) IBOutlet NSSlider *microphoneGainSlider; @property (readonly) IBOutlet NSButton *microphoneMuteButton; -@property (retain) DisplayView *view; +@property (retain) NSView *view; @property (retain) EmuControllerDelegate *emuControl; @property (retain) CocoaDSDisplayVideo *cdsVideoOutput; @property (assign) NSScreen *assignedScreen; @@ -179,6 +146,7 @@ class OGLVideoOutput; - (double) maxViewScaleInHostScreen:(double)contentBoundsWidth height:(double)contentBoundsHeight; - (void) enterFullScreen; - (void) exitFullScreen; +- (void) updateDisplayID; - (IBAction) copy:(id)sender; - (IBAction) changeHardwareMicGain:(id)sender; diff --git a/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm b/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm index 619bb2fae..9866b2af7 100644 --- a/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm +++ b/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm @@ -23,6 +23,7 @@ #import "cocoa_GPU.h" #import "cocoa_file.h" #import "cocoa_input.h" +#import "cocoa_output.h" #import "cocoa_globals.h" #import "cocoa_videofilter.h" #import "cocoa_util.h" @@ -747,6 +748,16 @@ static std::unordered_map _screenMap; // [masterWindow display]; } +- (void) updateDisplayID +{ + NSScreen *screen = [[self window] screen]; + NSDictionary *deviceDescription = [screen deviceDescription]; + NSNumber *idNumber = (NSNumber *)[deviceDescription valueForKey:@"NSScreenNumber"]; + CGDirectDisplayID displayID = [idNumber unsignedIntValue]; + + [[[self view] cdsVideoOutput] setCurrentDisplayID:displayID]; +} + - (void) respondToScreenChange:(NSNotification *)aNotification { if (_canUseMavericksFullScreen) @@ -1353,19 +1364,19 @@ static std::unordered_map _screenMap; // - (void)windowDidLoad { NSRect newViewFrameRect = NSMakeRect(0.0f, (CGFloat)_statusBarHeight, (CGFloat)_localViewProps.clientWidth, (CGFloat)_localViewProps.clientHeight); - DisplayView *newView = [[[DisplayView alloc] initWithFrame:newViewFrameRect] autorelease]; + NSView *newView = (NSView *)[[[DisplayView alloc] initWithFrame:newViewFrameRect] autorelease]; [self setView:newView]; // Set up the master window that is associated with this window controller. [self setMasterWindow:[self window]]; [masterWindow setTitle:(NSString *)[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]]; - [[masterWindow contentView] addSubview:view]; - [masterWindow setInitialFirstResponder:view]; + [[masterWindow contentView] addSubview:newView]; + [masterWindow setInitialFirstResponder:newView]; [[emuControl windowList] addObject:self]; [emuControl updateAllWindowTitles]; - [view setupLayer]; - [view setInputManager:[emuControl inputManager]]; + [newView setupLayer]; + [newView setInputManager:[emuControl inputManager]]; // Set up the scaling factor if this is a Retina window float scaleFactor = 1.0f; @@ -1403,7 +1414,7 @@ static std::unordered_map _screenMap; // } [self setCdsVideoOutput:newDisplayOutput]; - [view setCdsVideoOutput:newDisplayOutput]; + [newView setCdsVideoOutput:newDisplayOutput]; // Add the video thread to the output list. [emuControl addOutputToCore:newDisplayOutput]; @@ -1554,6 +1565,12 @@ static std::unordered_map _screenMap; // [emuControl updateDisplayPanelTitles]; } +- (void)windowDidChangeScreen:(NSNotification *)notification +{ + [self updateDisplayID]; + [[view cdsVideoOutput] clientDisplayView]->UpdateView(); +} + #if defined(MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) - (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize @@ -1749,7 +1766,63 @@ static std::unordered_map _screenMap; // [super dealloc]; } -#pragma mark Dynamic Property Methods +#pragma mark Class Methods + +- (BOOL) handleKeyPress:(NSEvent *)theEvent keyPressed:(BOOL)keyPressed +{ + BOOL isHandled = NO; + DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; + + MacInputDevicePropertiesEncoder *inputEncoder = [inputManager inputEncoder]; + const ClientInputDeviceProperties inputProperty = inputEncoder->EncodeKeyboardInput((int32_t)[theEvent keyCode], (keyPressed) ? true : false); + + if (keyPressed && [theEvent window] != nil) + { + NSString *newStatusText = [NSString stringWithFormat:@"%s:%s", inputProperty.deviceName, inputProperty.elementName]; + [[windowController emuControl] setStatusText:newStatusText]; + } + + isHandled = [inputManager dispatchCommandUsingInputProperties:&inputProperty]; + return isHandled; +} + +- (BOOL) handleMouseButton:(NSEvent *)theEvent buttonPressed:(BOOL)buttonPressed +{ + BOOL isHandled = NO; + DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; + ClientDisplayView *cdv = [(id)localLayer clientDisplay3DView]; + const ClientDisplayMode displayMode = cdv->GetMode(); + + // Convert the clicked location from window coordinates, to view coordinates, + // and finally to DS touchscreen coordinates. + const int32_t buttonNumber = (int32_t)[theEvent buttonNumber]; + uint8_t x = 0; + uint8_t y = 0; + + if (displayMode != ClientDisplayMode_Main) + { + const NSEventType eventType = [theEvent type]; + const bool isInitialMouseDown = (eventType == NSLeftMouseDown) || (eventType == NSRightMouseDown) || (eventType == NSOtherMouseDown); + + // Convert the clicked location from window coordinates, to view coordinates, and finally to NDS touchscreen coordinates. + const NSPoint clientLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + cdv->GetNDSPoint((int)buttonNumber, isInitialMouseDown, clientLoc.x, clientLoc.y, x, y); + } + + MacInputDevicePropertiesEncoder *inputEncoder = [inputManager inputEncoder]; + const ClientInputDeviceProperties inputProperty = inputEncoder->EncodeMouseInput(buttonNumber, (float)x, (float)y, (buttonPressed) ? true : false); + + if (buttonPressed && [theEvent window] != nil) + { + NSString *newStatusText = (displayMode == ClientDisplayMode_Main) ? [NSString stringWithFormat:@"%s:%s", inputProperty.deviceName, inputProperty.elementName] : [NSString stringWithFormat:@"%s:%s X:%i Y:%i", inputProperty.deviceName, inputProperty.elementName, (int)inputProperty.intCoordX, (int)inputProperty.intCoordY]; + [[windowController emuControl] setStatusText:newStatusText]; + } + + isHandled = [inputManager dispatchCommandUsingInputProperties:&inputProperty]; + return isHandled; +} + +#pragma mark CocoaDisplayView Protocol - (ClientDisplay3DView *) clientDisplay3DView { @@ -2068,12 +2141,12 @@ static std::unordered_map _screenMap; // return [[self cdsVideoOutput] pixelScaler]; } -#pragma mark Class Methods - (void) setupLayer { DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; CocoaDSCore *cdsCore = (CocoaDSCore *)[[[windowController emuControl] cdsCoreController] content]; CocoaDSGPU *cdsGPU = [cdsCore cdsGPU]; + BOOL isMetalLayer = NO; #ifdef ENABLE_APPLE_METAL MacClientSharedObject *macSharedData = [cdsGPU sharedData]; @@ -2082,15 +2155,19 @@ static std::unordered_map _screenMap; // localLayer = [[DisplayViewMetalLayer alloc] init]; [(DisplayViewMetalLayer *)localLayer setSharedData:(MetalDisplayViewSharedData *)macSharedData]; - MacMetalDisplayView *cdv = (MacMetalDisplayView *)[(id)localLayer clientDisplay3DView]; - cdv->SetFetchObject([cdsGPU fetchObject]); - cdv->Init(); + MacMetalDisplayView *macMTLCDV = (MacMetalDisplayView *)[(id)localLayer clientDisplay3DView]; + macMTLCDV->SetFetchObject([cdsGPU fetchObject]); + macMTLCDV->Init(); + macMTLCDV->SetNSView(self); + macMTLCDV->SetSharedData([cdsGPU sharedData]); if ([(DisplayViewMetalLayer *)localLayer device] == nil) { [localLayer release]; localLayer = nil; } + + isMetalLayer = YES; } #endif @@ -2100,6 +2177,8 @@ static std::unordered_map _screenMap; // MacOGLDisplayView *macOGLCDV = (MacOGLDisplayView *)[(id)localLayer clientDisplay3DView]; macOGLCDV->SetFetchObject([cdsGPU fetchObject]); macOGLCDV->Init(); + macOGLCDV->SetNSView(self); + macOGLCDV->SetSharedData([cdsGPU sharedData]); // For macOS 10.8 Mountain Lion and later, we can use the CAOpenGLLayer directly. But for // earlier versions of macOS, using the CALayer directly will cause too many strange issues, @@ -2118,28 +2197,52 @@ static std::unordered_map _screenMap; // #endif localOGLContext = macOGLCDV->GetNSContext(); [localOGLContext retain]; - macOGLCDV->SetRenderToCALayer(false); } } + ClientDisplay3DView *cdv = [(id)localLayer clientDisplay3DView]; + cdv->UpdateView(); + if (localOGLContext != nil) { // If localOGLContext isn't nil, then we will not assign the local layer // directly to the view, since the OpenGL context will already be what // is assigned. + cdv->FlushView(); return; } - [localLayer setNeedsDisplay]; - [self setLayer:localLayer]; - [self setWantsLayer:YES]; - #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) - if ([self respondsToSelector:@selector(setLayerContentsRedrawPolicy:)]) - { - [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawNever]; - } + if ([self respondsToSelector:@selector(setLayerContentsRedrawPolicy:)]) + { + [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawNever]; + } #endif + + [self setLayer:localLayer]; + [self setWantsLayer:YES]; + + if (isMetalLayer) + { + cdv->FlushView(); + } + else + { + [localLayer setNeedsDisplay]; + } +} + +- (void) requestScreenshot:(NSURL *)fileURL fileType:(NSBitmapImageFileType)fileType +{ + NSString *fileURLString = [fileURL absoluteString]; + NSData *fileURLStringData = [fileURLString dataUsingEncoding:NSUTF8StringEncoding]; + NSData *bitmapImageFileTypeData = [[NSData alloc] initWithBytes:&fileType length:sizeof(NSBitmapImageFileType)]; + NSArray *messageComponents = [[NSArray alloc] initWithObjects:fileURLStringData, bitmapImageFileTypeData, nil]; + + [CocoaDSUtil messageSendOneWayWithMessageComponents:[[self cdsVideoOutput] receivePort] msgID:MESSAGE_REQUEST_SCREENSHOT array:messageComponents]; + + [bitmapImageFileTypeData release]; + [messageComponents release]; } #pragma mark InputHIDManagerTarget Protocol @@ -2187,73 +2290,6 @@ static std::unordered_map _screenMap; // return isHandled; } -- (BOOL) handleKeyPress:(NSEvent *)theEvent keyPressed:(BOOL)keyPressed -{ - BOOL isHandled = NO; - DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; - - MacInputDevicePropertiesEncoder *inputEncoder = [inputManager inputEncoder]; - const ClientInputDeviceProperties inputProperty = inputEncoder->EncodeKeyboardInput((int32_t)[theEvent keyCode], (keyPressed) ? true : false); - - if (keyPressed && [theEvent window] != nil) - { - NSString *newStatusText = [NSString stringWithFormat:@"%s:%s", inputProperty.deviceName, inputProperty.elementName]; - [[windowController emuControl] setStatusText:newStatusText]; - } - - isHandled = [inputManager dispatchCommandUsingInputProperties:&inputProperty]; - return isHandled; -} - -- (BOOL) handleMouseButton:(NSEvent *)theEvent buttonPressed:(BOOL)buttonPressed -{ - BOOL isHandled = NO; - DisplayWindowController *windowController = (DisplayWindowController *)[[self window] delegate]; - ClientDisplayView *cdv = [(id)localLayer clientDisplay3DView]; - const ClientDisplayMode displayMode = cdv->GetMode(); - - // Convert the clicked location from window coordinates, to view coordinates, - // and finally to DS touchscreen coordinates. - const int32_t buttonNumber = (int32_t)[theEvent buttonNumber]; - uint8_t x = 0; - uint8_t y = 0; - - if (displayMode != ClientDisplayMode_Main) - { - const NSEventType eventType = [theEvent type]; - const bool isInitialMouseDown = (eventType == NSLeftMouseDown) || (eventType == NSRightMouseDown) || (eventType == NSOtherMouseDown); - - // Convert the clicked location from window coordinates, to view coordinates, and finally to NDS touchscreen coordinates. - const NSPoint clientLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; - cdv->GetNDSPoint((int)buttonNumber, isInitialMouseDown, clientLoc.x, clientLoc.y, x, y); - } - - MacInputDevicePropertiesEncoder *inputEncoder = [inputManager inputEncoder]; - const ClientInputDeviceProperties inputProperty = inputEncoder->EncodeMouseInput(buttonNumber, (float)x, (float)y, (buttonPressed) ? true : false); - - if (buttonPressed && [theEvent window] != nil) - { - NSString *newStatusText = (displayMode == ClientDisplayMode_Main) ? [NSString stringWithFormat:@"%s:%s", inputProperty.deviceName, inputProperty.elementName] : [NSString stringWithFormat:@"%s:%s X:%i Y:%i", inputProperty.deviceName, inputProperty.elementName, (int)inputProperty.intCoordX, (int)inputProperty.intCoordY]; - [[windowController emuControl] setStatusText:newStatusText]; - } - - isHandled = [inputManager dispatchCommandUsingInputProperties:&inputProperty]; - return isHandled; -} - -- (void) requestScreenshot:(NSURL *)fileURL fileType:(NSBitmapImageFileType)fileType -{ - NSString *fileURLString = [fileURL absoluteString]; - NSData *fileURLStringData = [fileURLString dataUsingEncoding:NSUTF8StringEncoding]; - NSData *bitmapImageFileTypeData = [[NSData alloc] initWithBytes:&fileType length:sizeof(NSBitmapImageFileType)]; - NSArray *messageComponents = [[NSArray alloc] initWithObjects:fileURLStringData, bitmapImageFileTypeData, nil]; - - [CocoaDSUtil messageSendOneWayWithMessageComponents:[[self cdsVideoOutput] receivePort] msgID:MESSAGE_REQUEST_SCREENSHOT array:messageComponents]; - - [bitmapImageFileTypeData release]; - [messageComponents release]; -} - #pragma mark NSView Methods - (void)lockFocus @@ -2283,12 +2319,12 @@ static std::unordered_map _screenMap; // - (void)updateLayer { - [self clientDisplay3DView]->UpdateView(); + [self clientDisplay3DView]->FlushView(); } - (void)drawRect:(NSRect)dirtyRect { - [self clientDisplay3DView]->UpdateView(); + [self clientDisplay3DView]->FlushView(); } - (void)setFrame:(NSRect)rect @@ -2327,7 +2363,7 @@ static std::unordered_map _screenMap; // [localLayer setBounds:CGRectMake(0.0f, 0.0f, props.clientWidth, props.clientHeight)]; } #ifdef ENABLE_APPLE_METAL - else if ([[self layer] isKindOfClass:[CAMetalLayer class]]) + else if ([localLayer isKindOfClass:[CAMetalLayer class]]) { [(CAMetalLayer *)localLayer setDrawableSize:CGSizeMake(props.clientWidth, props.clientHeight)]; } diff --git a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h index 3b04ee064..7b3016d8c 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h +++ b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h @@ -20,6 +20,7 @@ #import #import +#include #import "DisplayViewCALayer.h" #import "../cocoa_GPU.h" @@ -187,7 +188,15 @@ typedef DisplayViewShaderProperties DisplayViewShaderProperties; BOOL needsScreenVerticesUpdate; BOOL needsHUDVerticesUpdate; - dispatch_semaphore_t availableResources; + pthread_mutex_t _mutexDisplayTextureUpdate; + pthread_mutex_t _mutexBufferUpdate; + bool _needEncodeViewport; + MTLViewport _newViewport; + bool _willDrawDisplays; + bool _willDrawHUD; + bool _willDrawHUDInput; + size_t _hudStringLength; + size_t _hudTouchLineLength; } @property (assign, nonatomic) MetalDisplayViewSharedData *sharedData; @@ -213,7 +222,8 @@ typedef DisplayViewShaderProperties DisplayViewShaderProperties; - (void) resizeCPUPixelScalerUsingFilterID:(const VideoFilterTypeID)filterID; - (void) copyHUDFontUsingFace:(const FT_Face &)fontFace size:(const size_t)glyphSize tileSize:(const size_t)glyphTileSize info:(GlyphInfo *)glyphInfo; - (void) processDisplays; -- (void) renderToDrawable; +- (void) updateRenderBuffers; +- (void) renderAndFlushDrawable; @end @@ -246,6 +256,7 @@ class MacMetalDisplayView : public ClientDisplay3DView, public DisplayViewCALaye { protected: pthread_mutex_t *_mutexProcessPtr; + OSSpinLock _spinlockViewNeedsFlush; virtual void _UpdateNormalSize(); virtual void _UpdateOrder(); @@ -262,6 +273,8 @@ public: pthread_mutex_t* GetMutexProcessPtr() const; virtual void Init(); + virtual bool GetViewNeedsFlush(); + virtual void SetAllowViewFlushes(bool allowFlushes); virtual void CopyHUDFont(const FT_Face &fontFace, const size_t glyphSize, const size_t glyphTileSize, GlyphInfo *glyphInfo); @@ -273,6 +286,7 @@ public: // Client view interface virtual void ProcessDisplays(); virtual void UpdateView(); + virtual void FlushView(); }; #pragma mark - diff --git a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm index 4ad525f20..d0c2e9733 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm +++ b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm @@ -199,13 +199,14 @@ [texDisplayLoad32Desc setResourceOptions:MTLResourceStorageModeManaged]; [texDisplayLoad32Desc setStorageMode:MTLStorageModeManaged]; [texDisplayLoad32Desc setCpuCacheMode:MTLCPUCacheModeWriteCombined]; - [texDisplayLoad32Desc setUsage:MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite]; + [texDisplayLoad32Desc setUsage:MTLTextureUsageShaderRead]; texDisplayFetch32NativeMain = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; texDisplayFetch32NativeTouch = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; texDisplayFetch32CustomMain = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; texDisplayFetch32CustomTouch = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; + [texDisplayLoad32Desc setUsage:MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite]; texDisplayPostprocessNativeMain = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; texDisplayPostprocessCustomMain = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; texDisplayPostprocessNativeTouch = [[device newTextureWithDescriptor:texDisplayLoad32Desc] retain]; @@ -750,7 +751,6 @@ } sharedData = nil; - availableResources = dispatch_semaphore_create(3); _cdv = new MacMetalDisplayView(); _cdv->SetFrontendLayer(self); @@ -760,6 +760,7 @@ [colorAttachment0Desc setLoadAction:MTLLoadActionClear]; [colorAttachment0Desc setStoreAction:MTLStoreActionStore]; [colorAttachment0Desc setClearColor:MTLClearColorMake(0.0, 0.0, 0.0, 1.0)]; + [colorAttachment0Desc setTexture:nil]; pixelScalePipeline = nil; displayOutputPipeline = nil; @@ -795,6 +796,9 @@ needsScreenVerticesUpdate = YES; needsHUDVerticesUpdate = YES; + pthread_mutex_init(&_mutexDisplayTextureUpdate, NULL); + pthread_mutex_init(&_mutexBufferUpdate, NULL); + return self; } @@ -821,6 +825,7 @@ [self setTexDisplayPixelScaleMain:nil]; [self setTexDisplayPixelScaleTouch:nil]; + [[self colorAttachment0Desc] setTexture:nil]; [self setPixelScalePipeline:nil]; [self setDisplayOutputPipeline:nil]; [self setTexHUDCharMap:nil]; @@ -828,7 +833,8 @@ [self setSharedData:nil]; delete _cdv; - dispatch_release(availableResources); + pthread_mutex_destroy(&_mutexDisplayTextureUpdate); + pthread_mutex_destroy(&_mutexBufferUpdate); [super dealloc]; } @@ -1250,6 +1256,8 @@ const bool useDeposterize = _cdv->GetSourceDeposterize(); const NDSDisplayID selectedDisplaySource[2] = { _cdv->GetSelectedDisplaySourceForDisplay(NDSDisplayID_Main), _cdv->GetSelectedDisplaySourceForDisplay(NDSDisplayID_Touch) }; + pthread_mutex_lock(&_mutexDisplayTextureUpdate); + if (selectedDisplaySource[NDSDisplayID_Main] == NDSDisplayID_Main) { _texDisplayOutput[NDSDisplayID_Main] = [sharedData texDisplaySrcTargetMain]; @@ -1447,15 +1455,17 @@ (float)[_texDisplayOutput[NDSDisplayID_Touch] width], (float)[_texDisplayOutput[NDSDisplayID_Touch] height], (float *)[_displayTexCoordBuffer contents]); [_displayTexCoordBuffer didModifyRange:NSMakeRange(0, sizeof(float) * (4 * 8))]; + + pthread_mutex_unlock(&_mutexDisplayTextureUpdate); } -- (void) renderToDrawable +- (void) updateRenderBuffers { const NDSDisplayInfo &displayInfo = _cdv->GetEmuDisplayInfo(); // Set up the view properties. - BOOL didChangeViewProperties = NO; - BOOL needEncodeViewport = NO; + bool didChangeViewProperties = false; + bool needEncodeViewport = false; MTLViewport newViewport; newViewport.originX = 0.0; @@ -1465,14 +1475,16 @@ newViewport.znear = 0.0; newViewport.zfar = 1.0; + pthread_mutex_lock(&_mutexBufferUpdate); + if ([self needsViewportUpdate]) { - needEncodeViewport = YES; + needEncodeViewport = true; DisplayViewShaderProperties *viewProps = (DisplayViewShaderProperties *)[_cdvPropertiesBuffer contents]; viewProps->width = _cdv->GetViewProperties().clientWidth; viewProps->height = _cdv->GetViewProperties().clientHeight; - didChangeViewProperties = YES; + didChangeViewProperties = true; [self setNeedsViewportUpdate:NO]; } @@ -1483,7 +1495,7 @@ viewProps->rotation = _cdv->GetViewProperties().rotation; viewProps->viewScale = _cdv->GetViewProperties().viewScale; viewProps->lowerHUDMipMapLevel = ( ((float)HUD_TEXTBOX_BASE_SCALE * _cdv->GetHUDObjectScale() / _cdv->GetScaleFactor()) >= (2.0/3.0) ) ? 0 : 1; - didChangeViewProperties = YES; + didChangeViewProperties = true; [self setNeedsRotationScaleUpdate:NO]; } @@ -1494,7 +1506,7 @@ } // Set up the display properties. - BOOL willDrawDisplays = (displayInfo.pixelBytes != 0); + bool willDrawDisplays = (displayInfo.pixelBytes != 0); if (willDrawDisplays && [self needsScreenVerticesUpdate]) { _cdv->SetScreenVertices((float *)[_displayVtxPositionBuffer contents]); @@ -1506,7 +1518,7 @@ // Set up the HUD properties. size_t hudLength = _cdv->GetHUDString().length(); size_t hudTouchLineLength = 0; - BOOL willDrawHUD = _cdv->GetHUDVisibility() && ([self texHUDCharMap] != nil); + bool willDrawHUD = _cdv->GetHUDVisibility() && ([self texHUDCharMap] != nil); if (_cdv->GetHUDShowInput()) { @@ -1558,165 +1570,168 @@ _cdv->ClearHUDNeedsUpdate(); } - // Now that everything is set up, request a layer drawable and draw everything. - dispatch_semaphore_wait(availableResources, DISPATCH_TIME_FOREVER); + _needEncodeViewport = needEncodeViewport; + _newViewport = newViewport; + _willDrawDisplays = willDrawDisplays; + _willDrawHUD = willDrawHUD; + _willDrawHUDInput = _cdv->GetHUDShowInput(); + _hudStringLength = _cdv->GetHUDString().length(); + _hudTouchLineLength = hudTouchLineLength; - id drawable = [self nextDrawable]; - if (drawable == nil) + pthread_mutex_unlock(&_mutexBufferUpdate); +} + +- (void) renderAndFlushDrawable +{ + @autoreleasepool { - puts("MacMetalDisplayView: No drawable object was available!\n"); - dispatch_semaphore_signal(availableResources); - return; - } - - id texture = [drawable texture]; - if (texture == nil) - { - dispatch_semaphore_signal(availableResources); - return; - } - - [[self colorAttachment0Desc] setTexture:texture]; - - id cb = [[sharedData commandQueue] commandBufferWithUnretainedReferences]; - id ce = [cb renderCommandEncoderWithDescriptor:_outputRenderPassDesc]; - - if (needEncodeViewport) - { - [ce setViewport:newViewport]; - } - - // Draw the NDS displays. - if (willDrawDisplays) - { - [ce setRenderPipelineState:[self displayOutputPipeline]]; - [ce setVertexBuffer:_displayVtxPositionBuffer offset:0 atIndex:0]; - [ce setVertexBuffer:_displayTexCoordBuffer offset:0 atIndex:1]; - [ce setVertexBuffer:_cdvPropertiesBuffer offset:0 atIndex:2]; + pthread_mutex_lock(&_mutexDisplayTextureUpdate); + pthread_mutex_lock(&_mutexBufferUpdate); + + // Now that everything is set up, go ahead and draw everything. + id layerDrawable = [self nextDrawable]; + [colorAttachment0Desc setTexture:[layerDrawable texture]]; + id cb = [[sharedData commandQueue] commandBufferWithUnretainedReferences]; + id ce = [cb renderCommandEncoderWithDescriptor:_outputRenderPassDesc]; - switch (_cdv->GetViewProperties().mode) + if (_needEncodeViewport) { - case ClientDisplayMode_Main: - { - if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Main)) - { - [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Main] atIndex:0]; - [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; - } - break; - } - - case ClientDisplayMode_Touch: - { - if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Touch)) - { - [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Touch] atIndex:0]; - [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:4 vertexCount:4]; - } - break; - } - - case ClientDisplayMode_Dual: - { - const NDSDisplayID majorDisplayID = (_cdv->GetViewProperties().order == ClientDisplayOrder_MainFirst) ? NDSDisplayID_Main : NDSDisplayID_Touch; - const size_t majorDisplayVtx = (_cdv->GetViewProperties().order == ClientDisplayOrder_MainFirst) ? 8 : 12; - - switch (_cdv->GetViewProperties().layout) - { - case ClientDisplayLayout_Hybrid_2_1: - case ClientDisplayLayout_Hybrid_16_9: - case ClientDisplayLayout_Hybrid_16_10: - { - if (_cdv->IsSelectedDisplayEnabled(majorDisplayID)) - { - [ce setFragmentTexture:_texDisplayOutput[majorDisplayID] atIndex:0]; - [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:majorDisplayVtx vertexCount:4]; - } - break; - } - - default: - break; - } - - if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Main)) - { - [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Main] atIndex:0]; - [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; - } - - if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Touch)) - { - [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Touch] atIndex:0]; - [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:4 vertexCount:4]; - } - } - - default: - break; + [ce setViewport:_newViewport]; } - } - - // Draw the HUD. - if (willDrawHUD) - { - uint8_t isScreenOverlay = 0; - [ce setRenderPipelineState:[sharedData hudPipeline]]; - [ce setVertexBuffer:_hudVtxPositionBuffer offset:0 atIndex:0]; - [ce setVertexBuffer:_hudVtxColorBuffer offset:0 atIndex:1]; - [ce setVertexBuffer:_hudTexCoordBuffer offset:0 atIndex:2]; - [ce setVertexBuffer:_cdvPropertiesBuffer offset:0 atIndex:3]; - [ce setFragmentTexture:[self texHUDCharMap] atIndex:0]; - - // First, draw the inputs. - if (_cdv->GetHUDShowInput()) + // Draw the NDS displays. + if (_willDrawDisplays) { - isScreenOverlay = 1; + [ce setRenderPipelineState:[self displayOutputPipeline]]; + [ce setVertexBuffer:_displayVtxPositionBuffer offset:0 atIndex:0]; + [ce setVertexBuffer:_displayTexCoordBuffer offset:0 atIndex:1]; + [ce setVertexBuffer:_cdvPropertiesBuffer offset:0 atIndex:2]; + + switch (_cdv->GetViewProperties().mode) + { + case ClientDisplayMode_Main: + { + if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Main)) + { + [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Main] atIndex:0]; + [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + } + break; + } + + case ClientDisplayMode_Touch: + { + if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Touch)) + { + [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Touch] atIndex:0]; + [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:4 vertexCount:4]; + } + break; + } + + case ClientDisplayMode_Dual: + { + const NDSDisplayID majorDisplayID = (_cdv->GetViewProperties().order == ClientDisplayOrder_MainFirst) ? NDSDisplayID_Main : NDSDisplayID_Touch; + const size_t majorDisplayVtx = (_cdv->GetViewProperties().order == ClientDisplayOrder_MainFirst) ? 8 : 12; + + switch (_cdv->GetViewProperties().layout) + { + case ClientDisplayLayout_Hybrid_2_1: + case ClientDisplayLayout_Hybrid_16_9: + case ClientDisplayLayout_Hybrid_16_10: + { + if (_cdv->IsSelectedDisplayEnabled(majorDisplayID)) + { + [ce setFragmentTexture:_texDisplayOutput[majorDisplayID] atIndex:0]; + [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:majorDisplayVtx vertexCount:4]; + } + break; + } + + default: + break; + } + + if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Main)) + { + [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Main] atIndex:0]; + [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + } + + if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Touch)) + { + [ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Touch] atIndex:0]; + [ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:4 vertexCount:4]; + } + } + + default: + break; + } + } + + // Draw the HUD. + if (_willDrawHUD) + { + uint8_t isScreenOverlay = 0; + + [ce setRenderPipelineState:[sharedData hudPipeline]]; + [ce setVertexBuffer:_hudVtxPositionBuffer offset:0 atIndex:0]; + [ce setVertexBuffer:_hudVtxColorBuffer offset:0 atIndex:1]; + [ce setVertexBuffer:_hudTexCoordBuffer offset:0 atIndex:2]; + [ce setVertexBuffer:_cdvPropertiesBuffer offset:0 atIndex:3]; + [ce setFragmentTexture:[self texHUDCharMap] atIndex:0]; + + // First, draw the inputs. + if (_willDrawHUDInput) + { + isScreenOverlay = 1; + [ce setVertexBytes:&isScreenOverlay length:sizeof(uint8_t) atIndex:4]; + [ce setFragmentSamplerState:[sharedData samplerHUDBox] atIndex:0]; + [ce drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:_hudTouchLineLength * 6 + indexType:MTLIndexTypeUInt16 + indexBuffer:[sharedData hudIndexBuffer] + indexBufferOffset:(_hudStringLength + HUD_INPUT_ELEMENT_LENGTH) * 6 * sizeof(uint16_t)]; + + isScreenOverlay = 0; + [ce setVertexBytes:&isScreenOverlay length:sizeof(uint8_t) atIndex:4]; + [ce setFragmentSamplerState:[sharedData samplerHUDText] atIndex:0]; + [ce drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:HUD_INPUT_ELEMENT_LENGTH * 6 + indexType:MTLIndexTypeUInt16 + indexBuffer:[sharedData hudIndexBuffer] + indexBufferOffset:_hudStringLength * 6 * sizeof(uint16_t)]; + } + + // Next, draw the backing text box. [ce setVertexBytes:&isScreenOverlay length:sizeof(uint8_t) atIndex:4]; [ce setFragmentSamplerState:[sharedData samplerHUDBox] atIndex:0]; [ce drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:hudTouchLineLength * 6 + indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:[sharedData hudIndexBuffer] - indexBufferOffset:(_cdv->GetHUDString().length() + HUD_INPUT_ELEMENT_LENGTH) * 6 * sizeof(uint16_t)]; + indexBufferOffset:0]; - isScreenOverlay = 0; - [ce setVertexBytes:&isScreenOverlay length:sizeof(uint8_t) atIndex:4]; + // Finally, draw each character inside the box. [ce setFragmentSamplerState:[sharedData samplerHUDText] atIndex:0]; [ce drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:HUD_INPUT_ELEMENT_LENGTH * 6 + indexCount:(_hudStringLength - 1) * 6 indexType:MTLIndexTypeUInt16 indexBuffer:[sharedData hudIndexBuffer] - indexBufferOffset:_cdv->GetHUDString().length() * 6 * sizeof(uint16_t)]; + indexBufferOffset:6 * sizeof(uint16_t)]; } - // Next, draw the backing text box. - [ce setVertexBytes:&isScreenOverlay length:sizeof(uint8_t) atIndex:4]; - [ce setFragmentSamplerState:[sharedData samplerHUDBox] atIndex:0]; - [ce drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:6 - indexType:MTLIndexTypeUInt16 - indexBuffer:[sharedData hudIndexBuffer] - indexBufferOffset:0]; + [ce endEncoding]; - // Finally, draw each character inside the box. - [ce setFragmentSamplerState:[sharedData samplerHUDText] atIndex:0]; - [ce drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:(_cdv->GetHUDString().length() - 1) * 6 - indexType:MTLIndexTypeUInt16 - indexBuffer:[sharedData hudIndexBuffer] - indexBufferOffset:6 * sizeof(uint16_t)]; + [cb presentDrawable:layerDrawable]; + [cb addCompletedHandler:^(id block) { + pthread_mutex_unlock(&_mutexBufferUpdate); + pthread_mutex_unlock(&_mutexDisplayTextureUpdate); + }]; + + [cb commit]; } - - [ce endEncoding]; - - [cb presentDrawable:drawable]; - [cb addCompletedHandler:^(id block) { - dispatch_semaphore_signal(availableResources); - }]; - - [cb commit]; } @end @@ -1828,6 +1843,7 @@ MacMetalDisplayView::MacMetalDisplayView() _canFilterOnGPU = true; _filtersPreferGPU = true; _willFilterOnGPU = true; + _spinlockViewNeedsFlush = OS_SPINLOCK_INIT; _mutexProcessPtr = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(_mutexProcessPtr, NULL); @@ -1839,6 +1855,26 @@ MacMetalDisplayView::~MacMetalDisplayView() free(this->_mutexProcessPtr); } +bool MacMetalDisplayView::GetViewNeedsFlush() +{ + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + const bool viewNeedsFlush = this->_viewNeedsFlush; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); + + return viewNeedsFlush; +} + +void MacMetalDisplayView::SetAllowViewFlushes(bool allowFlushes) +{ + CGDirectDisplayID displayID = (CGDirectDisplayID)this->GetDisplayViewID(); + MacClientSharedObject *sharedData = this->GetSharedData(); + + if (![sharedData isDisplayLinkRunningUsingID:displayID]) + { + [sharedData displayLinkStartUsingID:displayID]; + } +} + void MacMetalDisplayView::_UpdateNormalSize() { [(DisplayViewMetalLayer *)this->GetFrontendLayer() setNeedsScreenVerticesUpdate:YES]; @@ -1935,13 +1971,29 @@ void MacMetalDisplayView::ProcessDisplays() void MacMetalDisplayView::UpdateView() { - if (this->_allowViewUpdates) + if (!this->_allowViewUpdates || (this->GetNSView() == nil)) { - @autoreleasepool - { - [(DisplayViewMetalLayer *)this->GetFrontendLayer() renderToDrawable]; - } + return; } + + // For every update, ensure that the CVDisplayLink is started so that the update + // will eventually get flushed. + this->SetAllowViewFlushes(true); + + [(DisplayViewMetalLayer *)this->GetFrontendLayer() updateRenderBuffers]; + + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + this->_viewNeedsFlush = true; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); +} + +void MacMetalDisplayView::FlushView() +{ + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + this->_viewNeedsFlush = false; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); + + [(DisplayViewMetalLayer *)this->GetFrontendLayer() renderAndFlushDrawable]; } #pragma mark - diff --git a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h index 764513fa0..3b6e22f86 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h +++ b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h @@ -20,6 +20,7 @@ #import #import +#include #import "DisplayViewCALayer.h" #import "../cocoa_GPU.h" @@ -67,7 +68,8 @@ protected: NSOpenGLPixelFormat *_nsPixelFormat; CGLContextObj _context; CGLPixelFormatObj _pixelFormat; - bool _willRenderToCALayer; + + OSSpinLock _spinlockViewNeedsFlush; public: void operator delete(void *ptr); @@ -80,8 +82,8 @@ public: CGLPixelFormatObj GetPixelFormat() const; CGLContextObj GetContext() const; - bool GetRenderToCALayer() const; - void SetRenderToCALayer(const bool renderToLayer); + virtual bool GetViewNeedsFlush(); + virtual void SetAllowViewFlushes(bool allowFlushes); virtual void LoadHUDFont(); @@ -97,6 +99,7 @@ public: virtual void LoadDisplays(); virtual void ProcessDisplays(); virtual void UpdateView(); + virtual void FlushView(); virtual void FinishFrameAtIndex(const u8 bufferIndex); virtual void LockDisplayTextures(); virtual void UnlockDisplayTextures(); diff --git a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm index d586336ac..4561f5330 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm +++ b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm @@ -277,8 +277,8 @@ MacOGLDisplayView::MacOGLDisplayView() _nsContext = nil; _context = nil; - _willRenderToCALayer = false; _allowViewUpdates = false; + _spinlockViewNeedsFlush = OS_SPINLOCK_INIT; } void MacOGLDisplayView::Init() @@ -328,14 +328,24 @@ CGLContextObj MacOGLDisplayView::GetContext() const return this->_context; } -bool MacOGLDisplayView::GetRenderToCALayer() const +bool MacOGLDisplayView::GetViewNeedsFlush() { - return this->_willRenderToCALayer; + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + const bool viewNeedsFlush = this->_viewNeedsFlush; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); + + return viewNeedsFlush; } -void MacOGLDisplayView::SetRenderToCALayer(const bool renderToLayer) +void MacOGLDisplayView::SetAllowViewFlushes(bool allowFlushes) { - this->_willRenderToCALayer = renderToLayer; + CGDirectDisplayID displayID = (CGDirectDisplayID)this->GetDisplayViewID(); + MacClientSharedObject *sharedData = this->GetSharedData(); + + if (![sharedData isDisplayLinkRunningUsingID:displayID]) + { + [sharedData displayLinkStartUsingID:displayID]; + } } void MacOGLDisplayView::LoadHUDFont() @@ -408,25 +418,40 @@ void MacOGLDisplayView::ProcessDisplays() void MacOGLDisplayView::UpdateView() { - if (!this->_allowViewUpdates) + if (!this->_allowViewUpdates || (this->GetNSView() == nil)) { return; } - if (this->_willRenderToCALayer) + if (this->GetRenderToCALayer()) { - this->CALayerDisplay(); + [[this->GetNSView() layer] setNeedsDisplay]; } else { - CGLLockContext(this->_context); - CGLSetCurrentContext(this->_context); - this->RenderViewOGL(); - CGLFlushDrawable(this->_context); - CGLUnlockContext(this->_context); + // For every update, ensure that the CVDisplayLink is started so that the update + // will eventually get flushed. + this->SetAllowViewFlushes(true); + + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + this->_viewNeedsFlush = true; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); } } +void MacOGLDisplayView::FlushView() +{ + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + this->_viewNeedsFlush = false; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); + + CGLLockContext(this->_context); + CGLSetCurrentContext(this->_context); + this->RenderViewOGL(); + CGLFlushDrawable(this->_context); + CGLUnlockContext(this->_context); +} + void MacOGLDisplayView::FinishFrameAtIndex(const u8 bufferIndex) { CGLLockContext(this->_context); diff --git a/desmume/src/frontend/cocoa/userinterface/appDelegate.mm b/desmume/src/frontend/cocoa/userinterface/appDelegate.mm index 6289ff660..cc5f0ec19 100644 --- a/desmume/src/frontend/cocoa/userinterface/appDelegate.mm +++ b/desmume/src/frontend/cocoa/userinterface/appDelegate.mm @@ -33,6 +33,7 @@ #import "cocoa_firmware.h" #import "cocoa_globals.h" #import "cocoa_input.h" +#import "cocoa_output.h" #import "cocoa_rom.h" #import "cocoa_util.h"