diff options
Diffstat (limited to 'LibOVR/Src/Displays/OVR_Display.cpp')
-rwxr-xr-x[-rw-r--r--] | LibOVR/Src/Displays/OVR_Display.cpp | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/LibOVR/Src/Displays/OVR_Display.cpp b/LibOVR/Src/Displays/OVR_Display.cpp index 55eb8a9..7c74eee 100644..100755 --- a/LibOVR/Src/Displays/OVR_Display.cpp +++ b/LibOVR/Src/Displays/OVR_Display.cpp @@ -27,10 +27,262 @@ limitations under the License. #include "OVR_Display.h" + namespace OVR { // Place platform-independent code here +static bool DirectDisplayInitialized = false; + +bool Display::GetDirectDisplayInitialized() +{ + return DirectDisplayInitialized; +} + +void Display::SetDirectDisplayInitialized(bool initialized) +{ + DirectDisplayInitialized = initialized; +} + + +//----------------------------------------------------------------------------- +// EDID Parsing + +// FIXME: This can be done much more compactly without the bitfields. +#pragma pack(push, 1) + +#if defined(_MSC_VER) +#pragma warning(disable: 4201) // Nameless struct/union +#endif + +// All of our EDIDs use the Detailed timing descriptors, and not the +// older Standard timing info in the EDID. Conforming EDID v1.3+ displays +// always put their preferred resolution, refresh, and timing info into the +// first Detailed timing descriptor. + +#ifndef byte_t +typedef unsigned char byte_t; +#endif + +//static const uint32_t EDIDv13Size = 128; // Standard EDID v1.3 is 128 bytes. +static const uint32_t FirstDetailedTimingOffset = 54; // Detailed timing table starts here. +static const uint32_t DetailedTimingDescriptorCount = 4; +static const byte_t MonitorSerialNumberType = 0xFF; +static const byte_t MonitorNameType = 0xFC; + +// Expected signature of EDID +static const byte_t EDIDSignature[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +struct EDID_Header +{ + byte_t Signature[8]; + + byte_t VendorIDHigh; + byte_t VendorIDLow; + + uint16_t ProductCode; + uint32_t SerialNumber; + + // We don't currently use anything farther into the header + byte_t Unused[112]; +}; + +struct EDID_Detailed_Timing_Descriptor +{ + uint16_t PixelClock; // In 10Khz units + + byte_t HActivePixelsLSB; + byte_t HBlankingPixelsLSB; + + union + { + struct + { + byte_t HBlankingPixelsMSB : 4; + byte_t HActivePixelsMSB : 4; + } Values; + byte_t Value_; + } HSizeMSB; + + byte_t VActivePixelsLSB; + byte_t VBlankingPixelsLSB; + union + { + struct + { + byte_t VBlankingPixelsMSB : 4; + byte_t VActivePixelsMSB : 4; + } Values; + byte_t Value_; + } VSizeMSB; + + byte_t HSyncOffsetPixelsLSB; + byte_t HSyncPulseWidthPixelsLSB; + + union + { + struct + { + byte_t VSyncPulseWidthLSB : 4; + byte_t VSyncOffsetPixelsLSB : 4; + } Values; + byte_t Value_; + } VSync; + + union + { + struct + { + byte_t VSyncPulseWidthMSB : 2; + byte_t VSyncOffsetMSB : 2; + byte_t HSyncPulseWidthMSB : 2; + byte_t HSyncOffsetMSB : 2; + } Values; + byte_t Value_; + } SyncMSB; + + byte_t Unused[6]; // We don't use anything else in the header +}; + +struct EDID_Other_Descriptor +{ + byte_t Reserved[3]; + byte_t Type; + byte_t Reserved1; + char Data[13]; +}; + +#pragma pack(pop) + + +static void StripTrailingWhitespace(char* str) +{ + // Get initial string length + int mlen = (int)strlen(str); + + // While removing trailing characters, + while (mlen > 0) + { + // If trailing character should be stripped, + char trailing = str[mlen - 1]; + if (trailing == '\n' || trailing == ' ') + { + // Strip it and reduce string length. + str[mlen - 1] = '\0'; + --mlen; + } + else + { + // Stop here. + break; + } + } +} + +bool DisplayEDID::Parse(const unsigned char* edid) +{ + const EDID_Header* header = (const EDID_Header*)edid; + + if (memcmp(header->Signature, EDIDSignature, sizeof(EDIDSignature)) != 0) + { + OVR_ASSERT(false); + return false; + } + + memset(VendorName, 0, sizeof(VendorName)); + memset(MonitorName, 0, sizeof(MonitorName)); + memset(SerialNumber, 0, sizeof(SerialNumber)); + + // Extract the 5-bit chars for the Vendor ID (PNP code) + uint16_t char1 = (header->VendorIDHigh >> 2) & 0x1F; + uint16_t char2 = ((header->VendorIDHigh & 0x2) << 3) | (header->VendorIDLow >> 5); + uint16_t char3 = header->VendorIDLow & 0x1F; + VendorName[0] = (char)char1 - 1 + 'A'; + VendorName[1] = (char)char2 - 1 + 'A'; + VendorName[2] = (char)char3 - 1 + 'A'; + VendorName[3] = '\0'; + ModelNumber = header->ProductCode; + + const EDID_Detailed_Timing_Descriptor* detailedTiming = (const EDID_Detailed_Timing_Descriptor*)&edid[FirstDetailedTimingOffset]; + + // First Detailed timing info is always preferred mode: + uint32_t hActive = (detailedTiming->HSizeMSB.Values.HActivePixelsMSB << 8) | detailedTiming->HActivePixelsLSB; + uint32_t vActive = (detailedTiming->VSizeMSB.Values.VActivePixelsMSB << 8) | detailedTiming->VActivePixelsLSB; + uint32_t hBlanking = (detailedTiming->HSizeMSB.Values.HBlankingPixelsMSB << 8) | detailedTiming->HBlankingPixelsLSB; + uint32_t vBlanking = (detailedTiming->VSizeMSB.Values.VBlankingPixelsMSB << 8) | detailedTiming->VBlankingPixelsLSB; + + // Need to scale up the values, since they're in 10Khz, and we're using integer math without fractions + uint32_t denom = 1000; + uint32_t totalPixels = (hActive + hBlanking) * (vActive + vBlanking); + uint32_t vSyncNumerator = (uint32_t)(((uint64_t)detailedTiming->PixelClock * 10000 * denom) / totalPixels); + + Width = hActive; + Height = vActive; + + RefreshNumerator = vSyncNumerator; + RefreshDenominator = denom; + + // The remaining ones can hold extra info. Look for monitor name & serial number strings. + ++detailedTiming; + for (int i = 1; i < (int)DetailedTimingDescriptorCount; ++i, ++detailedTiming) + { + if (detailedTiming->PixelClock == 0) + { + // Not a timing info, use OtherDescriptor instead + const EDID_Other_Descriptor* other = (const EDID_Other_Descriptor*)detailedTiming; + switch (other->Type) + { + case MonitorNameType: + static_assert(sizeof(MonitorName) == sizeof(other->Data) + 1, "serial number field size is off"); + memcpy(MonitorName, other->Data, sizeof(MonitorName)); + MonitorName[sizeof(MonitorName) - 1] = '\0'; + StripTrailingWhitespace(MonitorName); + break; + + case MonitorSerialNumberType: + static_assert(sizeof(SerialNumber) == sizeof(other->Data) + 1, "serial number field size is off"); + memcpy(SerialNumber, other->Data, sizeof(SerialNumber)); + SerialNumber[sizeof(SerialNumber) - 1] = '\0'; + StripTrailingWhitespace(SerialNumber); + break; + + default: + break; + } + } + } + + return true; +} + +HmdTypeEnum HmdTypeFromModelNumber(int modelNumber) +{ + HmdTypeEnum deviceTypeGuess = HmdType_Unknown; + switch (modelNumber) + { + case 3: deviceTypeGuess = HmdType_DK2; break; + case 2: deviceTypeGuess = HmdType_DKHDProto; break; + case 1: deviceTypeGuess = HmdType_DK1; break; + default: break; + } + return deviceTypeGuess; +} + +bool Display::MatchDisplay(const Display* other) +{ + // Note this is not checking the DeviceName, which corresponds to which monitor the device is. + // This allows matching to match a display that has changed how it is plugged in. + // The rotation must match, which allows us to react properly by regenerating the HMD info. + bool displayMatch = + (DisplayID == other->DisplayID) && + (EdidSerialNumber == other->EdidSerialNumber) && + (NativeResolutionInPixels == other->NativeResolutionInPixels) && + (Rotation == other->Rotation) && + (ApplicationExclusive == other->ApplicationExclusive); + + return displayMatch; +} + } // namespace OVR |