Skip to content

Commit

Permalink
Support OS X display reconfiguration. Fix automatic GPU switching.
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-seddon committed Sep 5, 2018
1 parent 23f890a commit eb24940
Showing 1 changed file with 111 additions and 51 deletions.
162 changes: 111 additions & 51 deletions src/b2/VBlankMonitorOSX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "VBlankMonitor.h"
#include <vector>
#include "Messages.h"
#include <inttypes.h>

#include <shared/enum_def.h>
#include "VBlankMonitorOSX_private.inl"
Expand All @@ -14,47 +15,35 @@
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

LOG_TAGGED_DEFINE(VBLANK,"vblank","VBLANK",&log_printer_stderr_and_debugger)

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

class VBlankMonitorOSX:
public VBlankMonitor
{
public:
~VBlankMonitorOSX() {
CGDisplayRemoveReconfigurationCallback(&ReconfigurationCallback,this);
this->ResetDisplaysList();
}

bool Init(Handler *handler,Messages *messages) override {
CVReturn cvr;
CGError cge;

ASSERT(!m_handler);
m_handler=handler;

if(!this->InitDisplaysList(messages)) {
return false;
}

for(auto &&display:m_displays) {
cvr=CVDisplayLinkCreateWithCGDisplay(display->id,
&display->link);
if(cvr!=kCVReturnSuccess) {
messages->e.f("CVDisplayLinkCreateWithCGDisplay failed: %s\n",GetCVReturnEnumName(cvr));
return false;
}

cvr=CVDisplayLinkSetOutputCallback(display->link,
&OutputCallback,
display.get());
if(cvr!=kCVReturnSuccess) {
messages->e.f("CVDisplayLinkSetOutputCallback failed: %s\n",GetCVReturnEnumName(cvr));
return false;
}

cvr=CVDisplayLinkStart(display->link);
if(cvr!=kCVReturnSuccess) {
messages->e.f("CVDisplayLinkStart failed: %s\n",GetCVReturnEnumName(cvr));
return false;
}
cge=CGDisplayRegisterReconfigurationCallback(&ReconfigurationCallback,this);
if(cge!=kCGErrorSuccess) {
return false;
}

return true;
}

Expand Down Expand Up @@ -93,12 +82,30 @@ class VBlankMonitorOSX:
protected:
private:
struct Display {
// CGDirectDisplayID=uint32_t
CGDirectDisplayID id=0;
CGDirectDisplayID const id;
VBlankMonitorOSX *const vbm;

CVDisplayLinkRef link=nullptr;
void *data=nullptr;
VBlankMonitorOSX *vbm=nullptr;
std::atomic<bool> stop_thread{false};

Display(CGDirectDisplayID id_,VBlankMonitorOSX *vbm_):
id(id_),
vbm(vbm_)
{
}

~Display() {
CVReturn cvr=CVDisplayLinkStop(this->link);
ASSERTF(cvr==kCVReturnSuccess,"%s",GetCVReturnEnumName(cvr));
this->link=nullptr;

CVDisplayLinkRelease(this->link);
this->link=nullptr;

if(this->data) {
this->vbm->m_handler->FreeDisplayData(this->id,this->data);
}
}
};
Handler *m_handler=nullptr;
std::vector<std::unique_ptr<Display>> m_displays;
Expand All @@ -124,40 +131,79 @@ class VBlankMonitorOSX:
}

for(uint32_t i=0;i<num_cgdisplays;++i) {
auto &&display=std::make_unique<Display>();

display->id=cgdisplays[i];
display->vbm=this;

display->data=m_handler->AllocateDisplayData(display->id);
if(!display->data) {
messages->e.f("AllocateDisplayData failed\n");
return false;
if(!this->AddDisplay(cgdisplays[i])) {
// I have no idea how much help this error will be, but at least you'll know.
messages->e.f("Failed to initialise display 0x%" PRIx32 "\n",cgdisplays[i]);
}

m_displays.push_back(std::move(display));
}

return true;
}

void ResetDisplaysList() {
while(!m_displays.empty()) {
this->RemoveDisplay(m_displays[0]->id);
}
}

bool AddDisplay(CGDirectDisplayID id) {
CVReturn cvr;

for(auto &&display:m_displays) {
if(display->link) {
CVReturn cvr;

cvr=CVDisplayLinkStop(display->link);
ASSERTF(cvr==kCVReturnSuccess,"%s",GetCVReturnEnumName(cvr));

CVDisplayLinkRelease(display->link);
display->link=nullptr;
if(display->id==id) {
LOGF(VBLANK,"Add Display %" PRIx32 ": already added.\n",id);
return true;
}

m_handler->FreeDisplayData(display->id,display->data);
display->data=nullptr;
}

auto &&display=std::make_unique<Display>(id,this);

m_displays.clear();
CGRect bounds=CGDisplayBounds(id);
(void)bounds;
LOGF(VBLANK,"Add Dispay %" PRIx32 ": ",id);
LOGI(VBLANK);

LOGF(VBLANK,"(%.1f,%.1f), %.1f x %.1f\n",bounds.origin.x,bounds.origin.y,bounds.size.width,bounds.size.height);

display->data=m_handler->AllocateDisplayData(display->id);
if(!display->data) {
LOGF(VBLANK,"AllocateDisplayData failed\n");
return false;
}

cvr=CVDisplayLinkCreateWithCGDisplay(display->id,
&display->link);
if(cvr!=kCVReturnSuccess) {
LOGF(VBLANK,"CVDisplayLinkCreateWithCGDisplay failed: %s\n",GetCVReturnEnumName(cvr));
return false;
}

cvr=CVDisplayLinkSetOutputCallback(display->link,
&OutputCallback,
display.get());
if(cvr!=kCVReturnSuccess) {
LOGF(VBLANK,"CVDisplayLinkSetOutputCallback failed: %s\n",GetCVReturnEnumName(cvr));
return false;
}

cvr=CVDisplayLinkStart(display->link);
if(cvr!=kCVReturnSuccess) {
LOGF(VBLANK,"CVDisplayLinkStart failed: %s\n",GetCVReturnEnumName(cvr));
return false;
}

m_displays.push_back(std::move(display));
return true;
}

void RemoveDisplay(CGDirectDisplayID id) {
for(auto &&display_it=m_displays.begin();display_it!=m_displays.end();++display_it) {
if((*display_it)->id==id) {
LOGF(VBLANK,"Remove Display %" PRIx32 "\n",id);
m_displays.erase(display_it);
return;
}
}
}

static CVReturn OutputCallback(CVDisplayLinkRef displayLink,
Expand All @@ -177,6 +223,20 @@ class VBlankMonitorOSX:

return kCVReturnSuccess;
}

static void ReconfigurationCallback(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo)
{
auto vbm=(VBlankMonitorOSX *)userInfo;

if(flags&kCGDisplayAddFlag) {
vbm->AddDisplay(display);
} else if(flags&kCGDisplayRemoveFlag) {
vbm->RemoveDisplay(display);
}
}

};

//////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit eb24940

Please sign in to comment.