void StimulusDisplay::stateSystemCallback(const Datum &data, MWorksTime time) { unique_lock lock(display_lock); int newState = data.getInteger(); if ((IDLE == newState) && displayLinksRunning) { // If another thread is waiting for a display refresh, allow it to complete before stopping // the display link while (waitingForRefresh) { refreshCond.wait(lock); } displayLinksRunning = false; currentOutputTimeUS = -1; // We need to release the lock before calling CVDisplayLinkStop, because // StimulusDisplay::displayLinkCallback could be blocked waiting for the lock, and // CVDisplayLinkStop won't return until displayLinkCallback exits, leading to deadlock. lock.unlock(); // NOTE: As of OS X 10.11, stopping the display links from a non-main thread causes issues dispatch_sync(dispatch_get_main_queue(), ^{ for (auto dl : displayLinks) { if (kCVReturnSuccess != CVDisplayLinkStop(dl)) { merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to stop updates on display %d", CVDisplayLinkGetCurrentCGDisplay(dl)); } } }); mprintf(M_DISPLAY_MESSAGE_DOMAIN, "Display updates stopped"); } else if ((RUNNING == newState) && !displayLinksRunning) {
DisplayRefreshMonitorMac::~DisplayRefreshMonitorMac() { if (m_displayLink) { CVDisplayLinkStop(m_displayLink); CVDisplayLinkRelease(m_displayLink); m_displayLink = nullptr; } cancelCallOnMainThread(DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread, this); }
void StimulusDisplay::stateSystemCallback(const Datum &data, MWorksTime time) { unique_lock lock(display_lock); int newState = data.getInteger(); if (IDLE == newState) { if (CVDisplayLinkIsRunning(displayLink)) { // If another thread is waiting for a display refresh, allow it to complete before stopping // the display link while (waitingForRefresh) { refreshCond.wait(lock); } // We need to release the lock before calling CVDisplayLinkStop, because // StimulusDisplay::displayLinkCallback could be blocked waiting for the lock, and // CVDisplayLinkStop won't return until displayLinkCallback exits, leading to deadlock. lock.unlock(); if (kCVReturnSuccess != CVDisplayLinkStop(displayLink)) { merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to stop display updates"); } else { currentOutputTimeUS = -1; mprintf(M_DISPLAY_MESSAGE_DOMAIN, "Display updates stopped"); } } } else if (RUNNING == newState) { if (!CVDisplayLinkIsRunning(displayLink)) { lastFrameTime = 0; if (kCVReturnSuccess != CVDisplayLinkStart(displayLink)) { merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to start display updates"); } else { // Wait for a refresh to complete, so we know that getCurrentOutputTimeUS() will return a valid time ensureRefresh(lock); mprintf(M_DISPLAY_MESSAGE_DOMAIN, "Display updates started (main = %d, current = %d)", CGMainDisplayID(), CVDisplayLinkGetCurrentCGDisplay(displayLink)); } } } }
void PsychOSCloseWindow(PsychWindowRecordType *windowRecord) { CGDirectDisplayID cgDisplayID; // Disable rendering context: CGLSetCurrentContext(NULL); // Destroy pixelformat object: CGLDestroyPixelFormat(windowRecord->targetSpecific.pixelFormatObject); // Destroy rendering context: CGLReleaseContext(windowRecord->targetSpecific.contextObject); if (windowRecord->targetSpecific.glusercontextObject) CGLReleaseContext(windowRecord->targetSpecific.glusercontextObject); if (windowRecord->targetSpecific.glswapcontextObject) CGLReleaseContext(windowRecord->targetSpecific.glswapcontextObject); // Last reference to this screen? In that case we have to shutdown the fallback // vbl timestamping and vblank counting facilities for this screen: if (screenRefCount[windowRecord->screenNumber] == 1) { // Last one on this screen will be gone in a second. // Shutdown and release CVDisplayLink for this windows screen, if any: if (cvDisplayLink[windowRecord->screenNumber]) { if (PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: Releasing CVDisplayLink for screen %i.\n", windowRecord->screenNumber); if (CVDisplayLinkIsRunning(cvDisplayLink[windowRecord->screenNumber])) CVDisplayLinkStop(cvDisplayLink[windowRecord->screenNumber]); PsychYieldIntervalSeconds(0.1); CVDisplayLinkRelease(cvDisplayLink[windowRecord->screenNumber]); cvDisplayLink[windowRecord->screenNumber] = NULL; PsychYieldIntervalSeconds(0.1); // Teardown shared data structure and mutex: PsychDestroyMutex(&(cvDisplayLinkData[windowRecord->screenNumber].mutex)); } } // Release reference of this window to its screen: screenRefCount[windowRecord->screenNumber]--; // Destroy Cocoa onscreen window, if any: if (windowRecord->targetSpecific.windowHandle) PsychCocoaDisposeWindow(windowRecord); windowRecord->targetSpecific.windowHandle = NULL; return; }
static CVReturn p1_display_video_clock_callback( CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { P1DisplayVideoClock *dvclock = (P1DisplayVideoClock *) displayLinkContext; P1VideoClock *vclock = (P1VideoClock *) displayLinkContext; P1Object *obj = (P1Object *) displayLinkContext; CVReturn ret; p1_object_lock(obj); if (obj->state.current == P1_STATE_STOPPING) { ret = CVDisplayLinkStop(dvclock->display_link); if (ret != kCVReturnSuccess) { p1_log(obj, P1_LOG_ERROR, "Failed to stop display link: Core Video error %d", ret); obj->state.flags |= P1_FLAG_ERROR; } p1_display_video_clock_kill_session(dvclock); obj->state.current = P1_STATE_IDLE; p1_object_notify(obj); goto end; } if (obj->state.current == P1_STATE_STARTING) { // Get the display refresh period. double period = CVDisplayLinkGetActualOutputVideoRefreshPeriod(dvclock->display_link); if (period == 0.0) goto end; // Set the frame rate based on this and the divisor. vclock->fps_num = (uint32_t) round(1.0 / period); vclock->fps_den = dvclock->divisor; // Report running. obj->state.current = P1_STATE_RUNNING; p1_object_notify(obj); } if (obj->state.current == P1_STATE_RUNNING) { // Skip tick based on divisor. if (dvclock->skip_counter >= dvclock->divisor) dvclock->skip_counter = 0; if (dvclock->skip_counter++ != 0) goto end; p1_object_unlock(obj); p1_video_clock_tick(vclock, inNow->hostTime); return kCVReturnSuccess; } end: p1_object_unlock(obj); return kCVReturnSuccess; }