nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: Filename : OVR_Win32_HMDDevice.cpp nuclear@1: Content : Win32 Interface to HMD - detects HMD display nuclear@1: Created : September 21, 2012 nuclear@1: Authors : Michael Antonov nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: *************************************************************************************/ nuclear@1: nuclear@1: #include "OVR_Win32_HMDDevice.h" nuclear@1: nuclear@1: #include "OVR_Win32_DeviceManager.h" nuclear@1: nuclear@1: #include nuclear@1: nuclear@1: namespace OVR { namespace Win32 { nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, nuclear@1: const String& deviceId, const String& displayDeviceName) nuclear@1: : DeviceCreateDesc(factory, Device_HMD), nuclear@1: DeviceId(deviceId), DisplayDeviceName(displayDeviceName), nuclear@1: DesktopX(0), DesktopY(0), Contents(0), nuclear@1: HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0) nuclear@1: { nuclear@1: } nuclear@1: HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) nuclear@1: : DeviceCreateDesc(other.pFactory, Device_HMD), nuclear@1: DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), nuclear@1: DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), nuclear@1: HResolution(other.HResolution), VResolution(other.VResolution), nuclear@1: HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, nuclear@1: DeviceCreateDesc** pcandidate) const nuclear@1: { nuclear@1: if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) nuclear@1: return Match_None; nuclear@1: nuclear@1: // There are several reasons we can come in here: nuclear@1: // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc nuclear@1: // - Require exact device DeviceId/DeviceName match nuclear@1: // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc nuclear@1: // - This DeviceId is empty; becomes candidate nuclear@1: // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc nuclear@1: // - This other.DeviceId is empty; becomes candidate nuclear@1: nuclear@1: const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; nuclear@1: nuclear@1: if ((DeviceId == s2.DeviceId) && nuclear@1: (DisplayDeviceName == s2.DisplayDeviceName)) nuclear@1: { nuclear@1: // Non-null DeviceId may match while size is different if screen size was overwritten nuclear@1: // by SensorDisplayInfo in prior iteration. nuclear@1: if (!DeviceId.IsEmpty() || nuclear@1: ((HScreenSize == s2.HScreenSize) && nuclear@1: (VScreenSize == s2.VScreenSize)) ) nuclear@1: { nuclear@1: *pcandidate = 0; nuclear@1: return Match_Found; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // DisplayInfo takes precedence, although we try to match it first. nuclear@1: if ((HResolution == s2.HResolution) && nuclear@1: (VResolution == s2.VResolution) && nuclear@1: (HScreenSize == s2.HScreenSize) && nuclear@1: (VScreenSize == s2.VScreenSize)) nuclear@1: { nuclear@1: if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: nuclear@1: *pcandidate = 0; nuclear@1: return Match_Found; nuclear@1: } nuclear@1: nuclear@1: // SensorDisplayInfo may override resolution settings, so store as candidate. nuclear@1: if (s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: // OTHER HMD Monitor desc may initialize DeviceName/Id nuclear@1: else if (DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: nuclear@1: return Match_None; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, nuclear@1: bool* newDeviceFlag) nuclear@1: { nuclear@1: // This candidate was the the "best fit" to apply sensor DisplayInfo to. nuclear@1: OVR_ASSERT(other.Type == Device_HMD); nuclear@1: nuclear@1: const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; nuclear@1: nuclear@1: // Force screen size on resolution from SensorDisplayInfo. nuclear@1: // We do this because USB detection is more reliable as compared to HDMI EDID, nuclear@1: // which may be corrupted by splitter reporting wrong monitor nuclear@1: if (s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: HScreenSize = s2.HScreenSize; nuclear@1: VScreenSize = s2.VScreenSize; nuclear@1: Contents |= Contents_Screen; nuclear@1: nuclear@1: if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) nuclear@1: { nuclear@1: memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); nuclear@1: Contents |= Contents_Distortion; nuclear@1: } nuclear@1: DeviceId = s2.DeviceId; nuclear@1: DisplayDeviceName = s2.DisplayDeviceName; nuclear@1: DesktopX = s2.DesktopX; nuclear@1: DesktopY = s2.DesktopY; nuclear@1: if (newDeviceFlag) *newDeviceFlag = true; nuclear@1: } nuclear@1: else if (DeviceId.IsEmpty()) nuclear@1: { nuclear@1: DeviceId = s2.DeviceId; nuclear@1: DisplayDeviceName = s2.DisplayDeviceName; nuclear@1: DesktopX = s2.DesktopX; nuclear@1: DesktopY = s2.DesktopY; nuclear@1: nuclear@1: // ScreenSize and Resolution are NOT assigned here, since they may have nuclear@1: // come from a sensor DisplayInfo (which has precedence over HDMI). nuclear@1: nuclear@1: if (newDeviceFlag) *newDeviceFlag = true; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (newDeviceFlag) *newDeviceFlag = false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::MatchDevice(const String& path) nuclear@1: { nuclear@1: return DeviceId.CompareNoCase(path) == 0; nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: nuclear@1: const wchar_t* FormatDisplayStateFlags(wchar_t* buff, int length, DWORD flags) nuclear@1: { nuclear@1: buff[0] = 0; nuclear@1: if (flags & DISPLAY_DEVICE_ACTIVE) nuclear@1: wcscat_s(buff, length, L"Active "); nuclear@1: if (flags & DISPLAY_DEVICE_MIRRORING_DRIVER) nuclear@1: wcscat_s(buff, length, L"Mirroring_Driver "); nuclear@1: if (flags & DISPLAY_DEVICE_MODESPRUNED) nuclear@1: wcscat_s(buff, length, L"ModesPruned "); nuclear@1: if (flags & DISPLAY_DEVICE_PRIMARY_DEVICE) nuclear@1: wcscat_s(buff, length, L"Primary "); nuclear@1: if (flags & DISPLAY_DEVICE_REMOVABLE) nuclear@1: wcscat_s(buff, length, L"Removable "); nuclear@1: if (flags & DISPLAY_DEVICE_VGA_COMPATIBLE) nuclear@1: wcscat_s(buff, length, L"VGA_Compatible "); nuclear@1: return buff; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // Callback for monitor enumeration to store all the monitor handles nuclear@1: nuclear@1: // Used to capture all the active monitor handles nuclear@1: struct MonitorSet nuclear@1: { nuclear@1: enum { MaxMonitors = 8 }; nuclear@1: HMONITOR Monitors[MaxMonitors]; nuclear@1: int MonitorCount; nuclear@1: }; nuclear@1: nuclear@1: BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) nuclear@1: { nuclear@1: MonitorSet* monitorSet = (MonitorSet*)dwData; nuclear@1: if (monitorSet->MonitorCount > MonitorSet::MaxMonitors) nuclear@1: return FALSE; nuclear@1: nuclear@1: monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor; nuclear@1: monitorSet->MonitorCount++; nuclear@1: return TRUE; nuclear@1: }; nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** HMDDeviceFactory nuclear@1: nuclear@1: HMDDeviceFactory HMDDeviceFactory::Instance; nuclear@1: nuclear@1: void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) nuclear@1: { nuclear@1: MonitorSet monitors; nuclear@1: monitors.MonitorCount = 0; nuclear@1: // Get all the monitor handles nuclear@1: EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors); nuclear@1: nuclear@1: bool foundHMD = false; nuclear@1: nuclear@1: // DeviceManager* manager = getManager(); nuclear@1: DISPLAY_DEVICE dd, ddm; nuclear@1: UINT i, j; nuclear@1: nuclear@1: for (i = 0; nuclear@1: (ZeroMemory(&dd, sizeof(dd)), dd.cb = sizeof(dd), nuclear@1: EnumDisplayDevices(0, i, &dd, 0)) != 0; i++) nuclear@1: { nuclear@1: nuclear@1: /* nuclear@1: wchar_t buff[500], flagsBuff[200]; nuclear@1: nuclear@1: swprintf_s(buff, 500, L"\nDEV: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n", nuclear@1: dd.DeviceName, dd.DeviceString, nuclear@1: dd.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, dd.StateFlags), nuclear@1: dd.DeviceID, dd.DeviceKey); nuclear@1: ::OutputDebugString(buff); nuclear@1: */ nuclear@1: nuclear@1: for (j = 0; nuclear@1: (ZeroMemory(&ddm, sizeof(ddm)), ddm.cb = sizeof(ddm), nuclear@1: EnumDisplayDevices(dd.DeviceName, j, &ddm, 0)) != 0; j++) nuclear@1: { nuclear@1: /* nuclear@1: wchar_t mbuff[500]; nuclear@1: swprintf_s(mbuff, 500, L"MON: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n", nuclear@1: ddm.DeviceName, ddm.DeviceString, nuclear@1: ddm.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, ddm.StateFlags), nuclear@1: ddm.DeviceID, ddm.DeviceKey); nuclear@1: ::OutputDebugString(mbuff); nuclear@1: */ nuclear@1: nuclear@1: // Our monitor hardware has string "RTD2205" in it nuclear@1: // Nate's device "CVT0003" nuclear@1: if (wcsstr(ddm.DeviceID, L"RTD2205") || nuclear@1: wcsstr(ddm.DeviceID, L"CVT0003") || nuclear@1: wcsstr(ddm.DeviceID, L"MST0030") || nuclear@1: wcsstr(ddm.DeviceID, L"OVR00") ) // Part of Oculus EDID. nuclear@1: { nuclear@1: String deviceId(ddm.DeviceID); nuclear@1: String displayDeviceName(ddm.DeviceName); nuclear@1: nuclear@1: // The default monitor coordinates nuclear@1: int mx = 0; nuclear@1: int my = 0; nuclear@1: int mwidth = 1280; nuclear@1: int mheight = 800; nuclear@1: nuclear@1: // Find the matching MONITORINFOEX for this device so we can get the nuclear@1: // screen coordinates nuclear@1: MONITORINFOEX info; nuclear@1: for (int m=0; m < monitors.MonitorCount; m++) nuclear@1: { nuclear@1: info.cbSize = sizeof(MONITORINFOEX); nuclear@1: GetMonitorInfo(monitors.Monitors[m], &info); nuclear@1: if (_tcsstr(ddm.DeviceName, info.szDevice) == ddm.DeviceName) nuclear@1: { // If the device name starts with the monitor name nuclear@1: // then we found the matching DISPLAY_DEVICE and MONITORINFO nuclear@1: // so we can gather the monitor coordinates nuclear@1: mx = info.rcMonitor.left; nuclear@1: my = info.rcMonitor.top; nuclear@1: //mwidth = info.rcMonitor.right - info.rcMonitor.left; nuclear@1: //mheight = info.rcMonitor.bottom - info.rcMonitor.top; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: HMDDeviceCreateDesc hmdCreateDesc(this, deviceId, displayDeviceName); nuclear@1: nuclear@1: if (wcsstr(ddm.DeviceID, L"OVR0002")) nuclear@1: { nuclear@1: hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12096f, 0.06804f); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (hmdCreateDesc.Is7Inch()) nuclear@1: { nuclear@1: // Physical dimension of SLA screen. nuclear@1: hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@1: OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n", nuclear@1: deviceId.ToCStr(), displayDeviceName.ToCStr())); nuclear@1: nuclear@1: // Notify caller about detected device. This will call EnumerateAddDevice nuclear@1: // if the this is the first time device was detected. nuclear@1: visitor.Visit(hmdCreateDesc); nuclear@1: foundHMD = true; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Real HMD device is not found; however, we still may have a 'fake' HMD nuclear@1: // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo. nuclear@1: // Need to find it and set 'Enumerated' to true to avoid Removal notification. nuclear@1: if (!foundHMD) nuclear@1: { nuclear@1: Ptr hmdDevDesc = getManager()->FindDevice("", Device_HMD); nuclear@1: if (hmdDevDesc) nuclear@1: hmdDevDesc->Enumerated = true; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() nuclear@1: { nuclear@1: return new HMDDevice(this); nuclear@1: } nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::Is7Inch() const nuclear@1: { nuclear@1: return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); nuclear@1: } nuclear@1: nuclear@1: Profile* HMDDeviceCreateDesc::GetProfileAddRef() const nuclear@1: { nuclear@1: // Create device may override profile name, so get it from there is possible. nuclear@1: ProfileManager* profileManager = GetManagerImpl()->GetProfileManager(); nuclear@1: ProfileType profileType = GetProfileType(); nuclear@1: const char * profileName = pDevice ? nuclear@1: ((HMDDevice*)pDevice)->GetProfileName() : nuclear@1: profileManager->GetDefaultProfileName(profileType); nuclear@1: nuclear@1: return profileName ? nuclear@1: profileManager->LoadProfile(profileType, profileName) : nuclear@1: profileManager->GetDeviceDefaultProfile(profileType); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const nuclear@1: { nuclear@1: if ((info->InfoClassType != Device_HMD) && nuclear@1: (info->InfoClassType != Device_None)) nuclear@1: return false; nuclear@1: nuclear@1: bool is7Inch = Is7Inch(); nuclear@1: nuclear@1: OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, nuclear@1: is7Inch ? "Oculus Rift DK1" : nuclear@1: ((HResolution >= 1920) ? "Oculus Rift DK HD" : "Oculus Rift DK1-Prototype") ); nuclear@1: OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); nuclear@1: info->Type = Device_HMD; nuclear@1: info->Version = 0; nuclear@1: nuclear@1: // Display detection. nuclear@1: if (info->InfoClassType == Device_HMD) nuclear@1: { nuclear@1: HMDInfo* hmdInfo = static_cast(info); nuclear@1: nuclear@1: hmdInfo->DesktopX = DesktopX; nuclear@1: hmdInfo->DesktopY = DesktopY; nuclear@1: hmdInfo->HResolution = HResolution; nuclear@1: hmdInfo->VResolution = VResolution; nuclear@1: hmdInfo->HScreenSize = HScreenSize; nuclear@1: hmdInfo->VScreenSize = VScreenSize; nuclear@1: hmdInfo->VScreenCenter = VScreenSize * 0.5f; nuclear@1: hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. nuclear@1: hmdInfo->LensSeparationDistance = 0.0635f; nuclear@1: nuclear@1: // Obtain IPD from profile. nuclear@1: Ptr profile = *GetProfileAddRef(); nuclear@1: nuclear@1: if (profile) nuclear@1: { nuclear@1: hmdInfo->InterpupillaryDistance = profile->GetIPD(); nuclear@1: // TBD: Switch on EyeCup type. nuclear@1: } nuclear@1: nuclear@1: if (Contents & Contents_Distortion) nuclear@1: { nuclear@1: memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (is7Inch) nuclear@1: { nuclear@1: // 7" screen. nuclear@1: hmdInfo->DistortionK[0] = 1.0f; nuclear@1: hmdInfo->DistortionK[1] = 0.22f; nuclear@1: hmdInfo->DistortionK[2] = 0.24f; nuclear@1: hmdInfo->EyeToScreenDistance = 0.041f; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: hmdInfo->DistortionK[0] = 1.0f; nuclear@1: hmdInfo->DistortionK[1] = 0.18f; nuclear@1: hmdInfo->DistortionK[2] = 0.115f; nuclear@1: nuclear@1: if (HResolution == 1920) nuclear@1: hmdInfo->EyeToScreenDistance = 0.040f; nuclear@1: else nuclear@1: hmdInfo->EyeToScreenDistance = 0.0387f; nuclear@1: } nuclear@1: nuclear@1: hmdInfo->ChromaAbCorrection[0] = 0.996f; nuclear@1: hmdInfo->ChromaAbCorrection[1] = -0.004f; nuclear@1: hmdInfo->ChromaAbCorrection[2] = 1.014f; nuclear@1: hmdInfo->ChromaAbCorrection[3] = 0.0f; nuclear@1: } nuclear@1: nuclear@1: OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), nuclear@1: DisplayDeviceName.ToCStr()); nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** HMDDevice nuclear@1: nuclear@1: HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) nuclear@1: : OVR::DeviceImpl(createDesc, 0) nuclear@1: { nuclear@1: } nuclear@1: HMDDevice::~HMDDevice() nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: bool HMDDevice::Initialize(DeviceBase* parent) nuclear@1: { nuclear@1: pParent = parent; nuclear@1: nuclear@1: // Initialize user profile to default for device. nuclear@1: ProfileManager* profileManager = GetManager()->GetProfileManager(); nuclear@1: ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType()); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: void HMDDevice::Shutdown() nuclear@1: { nuclear@1: ProfileName.Clear(); nuclear@1: pCachedProfile.Clear(); nuclear@1: pParent.Clear(); nuclear@1: } nuclear@1: nuclear@1: Profile* HMDDevice::GetProfile() const nuclear@1: { nuclear@1: if (!pCachedProfile) nuclear@1: pCachedProfile = *getDesc()->GetProfileAddRef(); nuclear@1: return pCachedProfile.GetPtr(); nuclear@1: } nuclear@1: nuclear@1: const char* HMDDevice::GetProfileName() const nuclear@1: { nuclear@1: return ProfileName.ToCStr(); nuclear@1: } nuclear@1: nuclear@1: bool HMDDevice::SetProfileName(const char* name) nuclear@1: { nuclear@1: pCachedProfile.Clear(); nuclear@1: if (!name) nuclear@1: { nuclear@1: ProfileName.Clear(); nuclear@1: return 0; nuclear@1: } nuclear@1: if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name)) nuclear@1: { nuclear@1: ProfileName = name; nuclear@1: return true; nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: OVR::SensorDevice* HMDDevice::GetSensor() nuclear@1: { nuclear@1: // Just return first sensor found since we have no way to match it yet. nuclear@1: OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); nuclear@1: if (sensor) nuclear@1: sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); nuclear@1: return sensor; nuclear@1: } nuclear@1: nuclear@1: }} // namespace OVR::Win32 nuclear@1: nuclear@1: