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.
This commit is contained in:
rogerman 2017-09-16 00:13:52 -07:00
parent 759a039e25
commit 4957d7be5b
17 changed files with 788 additions and 303 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 = "<group>"; };
ABADF1171DEA4C1200A142B1 /* Database.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Database.cpp; sourceTree = "<group>"; };
ABADF11A1DEA4CF700A142B1 /* features_cpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = features_cpu.c; sourceTree = "<group>"; };
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 = "<group>"; };
ABAF0A401A96E67200B95B75 /* RomInfoPanel.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RomInfoPanel.mm; sourceTree = "<group>"; };
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 */,

View File

@ -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 = "<group>"; };
AB43528517D5BA95007417C8 /* fsnitro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsnitro.h; sourceTree = "<group>"; };
AB43528617D5BA95007417C8 /* fsnitro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fsnitro.cpp; sourceTree = "<group>"; };
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 = "<group>"; };
AB4C4C2B16F55C64002E07CD /* cpu_detect_x86_gcc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpu_detect_x86_gcc.cpp; sourceTree = "<group>"; };
AB4C4C2C16F55C64002E07CD /* FIFOSampleBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIFOSampleBuffer.cpp; sourceTree = "<group>"; };
@ -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 */,

View File

@ -16,8 +16,10 @@
*/
#import <Foundation/Foundation.h>
#import <CoreVideo/CoreVideo.h>
#include <pthread.h>
#include <libkern/OSAtomic.h>
#include <map>
#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<CGDirectDisplayID, CVDisplayLinkRef> DisplayLinksActiveMap;
typedef std::map<CGDirectDisplayID, int64_t> 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();

View File

@ -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<CGDirectDisplayID> 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<NSString *, id> *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;

View File

@ -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;

View File

@ -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);

View File

@ -21,6 +21,10 @@
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#import "InputManager.h"
@class CocoaDSDisplayVideo;
@class MacClientSharedObject;
class ClientDisplay3DView;
@protocol DisplayViewCALayer <NSObject>
@ -30,17 +34,68 @@ class ClientDisplay3DView;
@end
@protocol CocoaDisplayViewProtocol <InputHIDManagerTarget, NSObject>
@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<DisplayViewCALayer> *_frontendLayer;
bool _willRenderToCALayer;
MacClientSharedObject *_sharedData;
public:
DisplayViewCALayerInterface();
NSView* GetNSView() const;
void SetNSView(NSView *theView);
CALayer<DisplayViewCALayer>* GetFrontendLayer() const;
void SetFrontendLayer(CALayer<DisplayViewCALayer> *layer);
void CALayerDisplay();
bool GetRenderToCALayer() const;
void SetRenderToCALayer(const bool renderToLayer);
MacClientSharedObject* GetSharedData();
void SetSharedData(MacClientSharedObject *sharedObject);
};
#endif // _DISPLAYVIEWCALAYER_H

View File

@ -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<DisplayViewCALayer>* 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;
}

View File

@ -17,11 +17,9 @@
#import <Cocoa/Cocoa.h>
#import <OpenGL/OpenGL.h>
#include <libkern/OSAtomic.h>
#include <map>
#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 <InputHIDManagerTarget>
@interface DisplayView : NSView<CocoaDisplayViewProtocol>
{
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<CocoaDisplayViewProtocol> *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<CocoaDisplayViewProtocol> *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;

View File

@ -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<NSScreen *, DisplayWindowController *> _screenMap; //
[masterWindow display];
}
- (void) updateDisplayID
{
NSScreen *screen = [[self window] screen];
NSDictionary<NSString *, id> *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<NSScreen *, DisplayWindowController *> _screenMap; //
- (void)windowDidLoad
{
NSRect newViewFrameRect = NSMakeRect(0.0f, (CGFloat)_statusBarHeight, (CGFloat)_localViewProps.clientWidth, (CGFloat)_localViewProps.clientHeight);
DisplayView *newView = [[[DisplayView alloc] initWithFrame:newViewFrameRect] autorelease];
NSView<CocoaDisplayViewProtocol> *newView = (NSView<CocoaDisplayViewProtocol> *)[[[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<NSScreen *, DisplayWindowController *> _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<NSScreen *, DisplayWindowController *> _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<NSScreen *, DisplayWindowController *> _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<DisplayViewCALayer>)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<NSScreen *, DisplayWindowController *> _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<NSScreen *, DisplayWindowController *> _screenMap; //
localLayer = [[DisplayViewMetalLayer alloc] init];
[(DisplayViewMetalLayer *)localLayer setSharedData:(MetalDisplayViewSharedData *)macSharedData];
MacMetalDisplayView *cdv = (MacMetalDisplayView *)[(id<DisplayViewCALayer>)localLayer clientDisplay3DView];
cdv->SetFetchObject([cdsGPU fetchObject]);
cdv->Init();
MacMetalDisplayView *macMTLCDV = (MacMetalDisplayView *)[(id<DisplayViewCALayer>)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<NSScreen *, DisplayWindowController *> _screenMap; //
MacOGLDisplayView *macOGLCDV = (MacOGLDisplayView *)[(id<DisplayViewCALayer>)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<NSScreen *, DisplayWindowController *> _screenMap; //
#endif
localOGLContext = macOGLCDV->GetNSContext();
[localOGLContext retain];
macOGLCDV->SetRenderToCALayer(false);
}
}
ClientDisplay3DView *cdv = [(id<DisplayViewCALayer>)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<NSScreen *, DisplayWindowController *> _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<DisplayViewCALayer>)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<NSScreen *, DisplayWindowController *> _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<NSScreen *, DisplayWindowController *> _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)];
}

View File

@ -20,6 +20,7 @@
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#include <libkern/OSAtomic.h>
#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 -

View File

@ -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<CAMetalDrawable> 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;
}
pthread_mutex_lock(&_mutexDisplayTextureUpdate);
pthread_mutex_lock(&_mutexBufferUpdate);
id<MTLTexture> texture = [drawable texture];
if (texture == nil)
{
dispatch_semaphore_signal(availableResources);
return;
}
// Now that everything is set up, go ahead and draw everything.
id<CAMetalDrawable> layerDrawable = [self nextDrawable];
[colorAttachment0Desc setTexture:[layerDrawable texture]];
id<MTLCommandBuffer> cb = [[sharedData commandQueue] commandBufferWithUnretainedReferences];
id<MTLRenderCommandEncoder> ce = [cb renderCommandEncoderWithDescriptor:_outputRenderPassDesc];
[[self colorAttachment0Desc] setTexture:texture];
id<MTLCommandBuffer> cb = [[sharedData commandQueue] commandBufferWithUnretainedReferences];
id<MTLRenderCommandEncoder> 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];
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;
}
[ce setViewport:_newViewport];
}
case ClientDisplayMode_Touch:
{
if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Touch))
{
[ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Touch] atIndex:0];
[ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:4 vertexCount:4];
}
break;
}
// 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];
case ClientDisplayMode_Dual:
switch (_cdv->GetViewProperties().mode)
{
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 ClientDisplayMode_Main:
{
case ClientDisplayLayout_Hybrid_2_1:
case ClientDisplayLayout_Hybrid_16_9:
case ClientDisplayLayout_Hybrid_16_10:
if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Main))
{
if (_cdv->IsSelectedDisplayEnabled(majorDisplayID))
[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:
{
[ce setFragmentTexture:_texDisplayOutput[majorDisplayID] atIndex:0];
[ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:majorDisplayVtx vertexCount:4];
if (_cdv->IsSelectedDisplayEnabled(majorDisplayID))
{
[ce setFragmentTexture:_texDisplayOutput[majorDisplayID] atIndex:0];
[ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:majorDisplayVtx vertexCount:4];
}
break;
}
break;
default:
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];
}
}
if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Main))
{
[ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Main] atIndex:0];
[ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
}
default:
break;
}
}
if (_cdv->IsSelectedDisplayEnabled(NDSDisplayID_Touch))
{
[ce setFragmentTexture:_texDisplayOutput[NDSDisplayID_Touch] atIndex:0];
[ce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:4 vertexCount:4];
}
// 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)];
}
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 (_cdv->GetHUDShowInput())
{
isScreenOverlay = 1;
// 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<MTLCommandBuffer> block) {
pthread_mutex_unlock(&_mutexBufferUpdate);
pthread_mutex_unlock(&_mutexDisplayTextureUpdate);
}];
[cb commit];
}
[ce endEncoding];
[cb presentDrawable:drawable];
[cb addCompletedHandler:^(id<MTLCommandBuffer> 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 -

View File

@ -20,6 +20,7 @@
#import <Cocoa/Cocoa.h>
#import <OpenGL/OpenGL.h>
#include <libkern/OSAtomic.h>
#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();

View File

@ -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);

View File

@ -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"