oculus1

annotate libovr/Src/osx/OVR_OSX_DeviceManager.cpp @ 29:9a973ef0e2a3

fixed the performance issue under MacOSX by replacing glutSolidTeapot (which uses glEvalMesh) with my own teapot generator.
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 27 Oct 2013 06:31:18 +0200
parents
children
rev   line source
nuclear@1 1 /************************************************************************************
nuclear@1 2
nuclear@1 3 Filename : OVR_OSX_DeviceManager.cpp
nuclear@1 4 Content : OSX specific DeviceManager implementation.
nuclear@1 5 Created : March 14, 2013
nuclear@1 6 Authors : Lee Cooper
nuclear@1 7
nuclear@1 8 Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
nuclear@1 9
nuclear@1 10 Use of this software is subject to the terms of the Oculus license
nuclear@1 11 agreement provided at the time of installation or download, or which
nuclear@1 12 otherwise accompanies this software in either electronic or hard copy form.
nuclear@1 13
nuclear@1 14 *************************************************************************************/
nuclear@1 15
nuclear@1 16 #include "OVR_OSX_DeviceManager.h"
nuclear@1 17
nuclear@1 18 // Sensor & HMD Factories
nuclear@1 19 #include "OVR_LatencyTestImpl.h"
nuclear@1 20 #include "OVR_SensorImpl.h"
nuclear@1 21 #include "OVR_OSX_HMDDevice.h"
nuclear@1 22 #include "OVR_OSX_HIDDevice.h"
nuclear@1 23
nuclear@1 24 #include "Kernel/OVR_Timer.h"
nuclear@1 25 #include "Kernel/OVR_Std.h"
nuclear@1 26 #include "Kernel/OVR_Log.h"
nuclear@1 27
nuclear@1 28 #include <IOKit/hid/IOHIDManager.h>
nuclear@1 29 #include <IOKit/hid/IOHIDKeys.h>
nuclear@1 30
nuclear@1 31
nuclear@1 32 namespace OVR { namespace OSX {
nuclear@1 33
nuclear@1 34 //-------------------------------------------------------------------------------------
nuclear@1 35 // **** OSX::DeviceManager
nuclear@1 36
nuclear@1 37 DeviceManager::DeviceManager()
nuclear@1 38 {
nuclear@1 39 }
nuclear@1 40
nuclear@1 41 DeviceManager::~DeviceManager()
nuclear@1 42 {
nuclear@1 43 OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called"));
nuclear@1 44 }
nuclear@1 45
nuclear@1 46 bool DeviceManager::Initialize(DeviceBase*)
nuclear@1 47 {
nuclear@1 48 if (!DeviceManagerImpl::Initialize(0))
nuclear@1 49 return false;
nuclear@1 50
nuclear@1 51 // Start the background thread.
nuclear@1 52 pThread = *new DeviceManagerThread();
nuclear@1 53 if (!pThread || !pThread->Start())
nuclear@1 54 return false;
nuclear@1 55
nuclear@1 56 // Wait for the thread to be fully up and running.
nuclear@1 57 pThread->StartupEvent.Wait();
nuclear@1 58
nuclear@1 59 // Do this now that we know the thread's run loop.
nuclear@1 60 HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
nuclear@1 61
nuclear@1 62 CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
nuclear@1 63
nuclear@1 64 pCreateDesc->pDevice = this;
nuclear@1 65 LogText("OVR::DeviceManager - initialized.\n");
nuclear@1 66
nuclear@1 67 return true;
nuclear@1 68 }
nuclear@1 69
nuclear@1 70 void DeviceManager::Shutdown()
nuclear@1 71 {
nuclear@1 72 LogText("OVR::DeviceManager - shutting down.\n");
nuclear@1 73
nuclear@1 74 CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
nuclear@1 75
nuclear@1 76 // Set Manager shutdown marker variable; this prevents
nuclear@1 77 // any existing DeviceHandle objects from accessing device.
nuclear@1 78 pCreateDesc->pLock->pManager = 0;
nuclear@1 79
nuclear@1 80 // Push for thread shutdown *WITH NO WAIT*.
nuclear@1 81 // This will have the following effect:
nuclear@1 82 // - Exit command will get enqueued, which will be executed later on the thread itself.
nuclear@1 83 // - Beyond this point, this DeviceManager object may be deleted by our caller.
nuclear@1 84 // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
nuclear@1 85 // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
nuclear@1 86 // after pManager is null.
nuclear@1 87 // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
nuclear@1 88 // reference to the thread object.
nuclear@1 89 pThread->Shutdown();
nuclear@1 90 pThread.Clear();
nuclear@1 91
nuclear@1 92 DeviceManagerImpl::Shutdown();
nuclear@1 93 }
nuclear@1 94
nuclear@1 95 ThreadCommandQueue* DeviceManager::GetThreadQueue()
nuclear@1 96 {
nuclear@1 97 return pThread;
nuclear@1 98 }
nuclear@1 99
nuclear@1 100 ThreadId DeviceManager::GetThreadId() const
nuclear@1 101 {
nuclear@1 102 return pThread->GetThreadId();
nuclear@1 103 }
nuclear@1 104
nuclear@1 105 bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
nuclear@1 106 {
nuclear@1 107 if ((info->InfoClassType != Device_Manager) &&
nuclear@1 108 (info->InfoClassType != Device_None))
nuclear@1 109 return false;
nuclear@1 110
nuclear@1 111 info->Type = Device_Manager;
nuclear@1 112 info->Version = 0;
nuclear@1 113 OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
nuclear@1 114 OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
nuclear@1 115 return true;
nuclear@1 116 }
nuclear@1 117
nuclear@1 118 DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
nuclear@1 119 {
nuclear@1 120 // TBD: Can this be avoided in the future, once proper device notification is in place?
nuclear@1 121 pThread->PushCall((DeviceManagerImpl*)this,
nuclear@1 122 &DeviceManager::EnumerateAllFactoryDevices, true);
nuclear@1 123
nuclear@1 124 return DeviceManagerImpl::EnumerateDevicesEx(args);
nuclear@1 125 }
nuclear@1 126
nuclear@1 127 void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display,
nuclear@1 128 CGDisplayChangeSummaryFlags flags,
nuclear@1 129 void *userInfo)
nuclear@1 130 {
nuclear@1 131 DeviceManager* manager = reinterpret_cast<DeviceManager*>(userInfo);
nuclear@1 132 OVR_UNUSED(manager);
nuclear@1 133
nuclear@1 134 if (flags & kCGDisplayAddFlag)
nuclear@1 135 {
nuclear@1 136 LogText("Display Added, id = %d\n", int(display));
nuclear@1 137 manager->EnumerateDevices<HMDDevice>();
nuclear@1 138 }
nuclear@1 139 else if (flags & kCGDisplayRemoveFlag)
nuclear@1 140 {
nuclear@1 141 LogText("Display Removed, id = %d\n", int(display));
nuclear@1 142 manager->EnumerateDevices<HMDDevice>();
nuclear@1 143 }
nuclear@1 144 }
nuclear@1 145
nuclear@1 146 //-------------------------------------------------------------------------------------
nuclear@1 147 // ***** DeviceManager Thread
nuclear@1 148
nuclear@1 149 DeviceManagerThread::DeviceManagerThread()
nuclear@1 150 : Thread(ThreadStackSize)
nuclear@1 151 {
nuclear@1 152 }
nuclear@1 153
nuclear@1 154 DeviceManagerThread::~DeviceManagerThread()
nuclear@1 155 {
nuclear@1 156 }
nuclear@1 157
nuclear@1 158 int DeviceManagerThread::Run()
nuclear@1 159 {
nuclear@1 160
nuclear@1 161 SetThreadName("OVR::DeviceManagerThread");
nuclear@1 162 LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId());
nuclear@1 163
nuclear@1 164 // Store out the run loop ref.
nuclear@1 165 RunLoop = CFRunLoopGetCurrent();
nuclear@1 166
nuclear@1 167 // Create a 'source' to enable us to signal the run loop to process the command queue.
nuclear@1 168 CFRunLoopSourceContext sourceContext;
nuclear@1 169 memset(&sourceContext, 0, sizeof(sourceContext));
nuclear@1 170 sourceContext.version = 0;
nuclear@1 171 sourceContext.info = this;
nuclear@1 172 sourceContext.perform = &staticCommandQueueSourceCallback;
nuclear@1 173
nuclear@1 174 CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext);
nuclear@1 175
nuclear@1 176 CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
nuclear@1 177
nuclear@1 178
nuclear@1 179 // Signal to the parent thread that initialization has finished.
nuclear@1 180 StartupEvent.SetEvent();
nuclear@1 181
nuclear@1 182
nuclear@1 183 ThreadCommand::PopBuffer command;
nuclear@1 184
nuclear@1 185 while(!IsExiting())
nuclear@1 186 {
nuclear@1 187 // PopCommand will reset event on empty queue.
nuclear@1 188 if (PopCommand(&command))
nuclear@1 189 {
nuclear@1 190 command.Execute();
nuclear@1 191 }
nuclear@1 192 else
nuclear@1 193 {
nuclear@1 194 SInt32 exitReason = 0;
nuclear@1 195 do {
nuclear@1 196
nuclear@1 197 UInt32 waitMs = INT_MAX;
nuclear@1 198
nuclear@1 199 // If devices have time-dependent logic registered, get the longest wait
nuclear@1 200 // allowed based on current ticks.
nuclear@1 201 if (!TicksNotifiers.IsEmpty())
nuclear@1 202 {
nuclear@1 203 UInt64 ticksMks = Timer::GetTicks();
nuclear@1 204 UInt32 waitAllowed;
nuclear@1 205
nuclear@1 206 for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
nuclear@1 207 {
nuclear@1 208 waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
nuclear@1 209 if (waitAllowed < waitMs)
nuclear@1 210 waitMs = waitAllowed;
nuclear@1 211 }
nuclear@1 212 }
nuclear@1 213
nuclear@1 214 // Enter blocking run loop. We may continue until we timeout in which
nuclear@1 215 // case it's time to service the ticks. Or if commands arrive in the command
nuclear@1 216 // queue then the source callback will call 'CFRunLoopStop' causing this
nuclear@1 217 // to return.
nuclear@1 218 CFTimeInterval blockInterval = 0.001 * (double) waitMs;
nuclear@1 219 exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false);
nuclear@1 220
nuclear@1 221 if (exitReason == kCFRunLoopRunFinished)
nuclear@1 222 {
nuclear@1 223 // Maybe this will occur during shutdown?
nuclear@1 224 break;
nuclear@1 225 }
nuclear@1 226 else if (exitReason == kCFRunLoopRunStopped )
nuclear@1 227 {
nuclear@1 228 // Commands need processing or we're being shutdown.
nuclear@1 229 break;
nuclear@1 230 }
nuclear@1 231 else if (exitReason == kCFRunLoopRunTimedOut)
nuclear@1 232 {
nuclear@1 233 // Timed out so that we can service our ticks callbacks.
nuclear@1 234 continue;
nuclear@1 235 }
nuclear@1 236 else if (exitReason == kCFRunLoopRunHandledSource)
nuclear@1 237 {
nuclear@1 238 // Should never occur since the last param when we call
nuclear@1 239 // 'CFRunLoopRunInMode' is false.
nuclear@1 240 OVR_ASSERT(false);
nuclear@1 241 break;
nuclear@1 242 }
nuclear@1 243 else
nuclear@1 244 {
nuclear@1 245 OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code"));
nuclear@1 246 break;
nuclear@1 247 }
nuclear@1 248 }
nuclear@1 249 while(true);
nuclear@1 250 }
nuclear@1 251 }
nuclear@1 252
nuclear@1 253
nuclear@1 254 CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
nuclear@1 255 CFRelease(CommandQueueSource);
nuclear@1 256
nuclear@1 257 LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId());
nuclear@1 258
nuclear@1 259 return 0;
nuclear@1 260 }
nuclear@1 261
nuclear@1 262 void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext)
nuclear@1 263 {
nuclear@1 264 DeviceManagerThread* pThread = (DeviceManagerThread*) pContext;
nuclear@1 265 pThread->commandQueueSourceCallback();
nuclear@1 266 }
nuclear@1 267
nuclear@1 268 void DeviceManagerThread::commandQueueSourceCallback()
nuclear@1 269 {
nuclear@1 270 CFRunLoopStop(RunLoop);
nuclear@1 271 }
nuclear@1 272
nuclear@1 273 bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
nuclear@1 274 {
nuclear@1 275 TicksNotifiers.PushBack(notify);
nuclear@1 276 return true;
nuclear@1 277 }
nuclear@1 278
nuclear@1 279 bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
nuclear@1 280 {
nuclear@1 281 for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
nuclear@1 282 {
nuclear@1 283 if (TicksNotifiers[i] == notify)
nuclear@1 284 {
nuclear@1 285 TicksNotifiers.RemoveAt(i);
nuclear@1 286 return true;
nuclear@1 287 }
nuclear@1 288 }
nuclear@1 289 return false;
nuclear@1 290 }
nuclear@1 291
nuclear@1 292 void DeviceManagerThread::Shutdown()
nuclear@1 293 {
nuclear@1 294 // Push for thread shutdown *WITH NO WAIT*.
nuclear@1 295 // This will have the following effect:
nuclear@1 296 // - Exit command will get enqueued, which will be executed later on the thread itself.
nuclear@1 297 // - Beyond this point, this DeviceManager object may be deleted by our caller.
nuclear@1 298 // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
nuclear@1 299 // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
nuclear@1 300 // after pManager is null.
nuclear@1 301 // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
nuclear@1 302 // reference to the thread object.
nuclear@1 303 PushExitCommand(false);
nuclear@1 304
nuclear@1 305 // make sure CFRunLoopRunInMode is woken up
nuclear@1 306 CFRunLoopSourceSignal(CommandQueueSource);
nuclear@1 307 CFRunLoopWakeUp(RunLoop);
nuclear@1 308 }
nuclear@1 309
nuclear@1 310 } // namespace OSX
nuclear@1 311
nuclear@1 312
nuclear@1 313 //-------------------------------------------------------------------------------------
nuclear@1 314 // ***** Creation
nuclear@1 315
nuclear@1 316 // Creates a new DeviceManager and initializes OVR.
nuclear@1 317 DeviceManager* DeviceManager::Create()
nuclear@1 318 {
nuclear@1 319
nuclear@1 320 if (!System::IsInitialized())
nuclear@1 321 {
nuclear@1 322 // Use custom message, since Log is not yet installed.
nuclear@1 323 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
nuclear@1 324 LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
nuclear@1 325 return 0;
nuclear@1 326 }
nuclear@1 327
nuclear@1 328 Ptr<OSX::DeviceManager> manager = *new OSX::DeviceManager;
nuclear@1 329
nuclear@1 330 if (manager)
nuclear@1 331 {
nuclear@1 332 if (manager->Initialize(0))
nuclear@1 333 {
nuclear@1 334 manager->AddFactory(&LatencyTestDeviceFactory::Instance);
nuclear@1 335 manager->AddFactory(&SensorDeviceFactory::Instance);
nuclear@1 336 manager->AddFactory(&OSX::HMDDeviceFactory::Instance);
nuclear@1 337
nuclear@1 338 manager->AddRef();
nuclear@1 339 }
nuclear@1 340 else
nuclear@1 341 {
nuclear@1 342 manager.Clear();
nuclear@1 343 }
nuclear@1 344 }
nuclear@1 345
nuclear@1 346 return manager.GetPtr();
nuclear@1 347 }
nuclear@1 348
nuclear@1 349 } // namespace OVR