diff options
author | Brad Davis <[email protected]> | 2013-06-27 11:25:32 -0800 |
---|---|---|
committer | Brad Davis <[email protected]> | 2013-06-28 10:47:29 -0700 |
commit | ebefcc885f74461cd0e3f19b5ae3622dc6cf6dbc (patch) | |
tree | 2b16db7350fce54c2e6c1b1c4020d67419cb1164 /LibOVR/Src | |
parent | 0ade748e1845694c5cbe562fb823e56f09773e27 (diff) |
SDK 0.2.2
Diffstat (limited to 'LibOVR/Src')
83 files changed, 28805 insertions, 0 deletions
diff --git a/LibOVR/Src/Kernel/OVR_Alg.cpp b/LibOVR/Src/Kernel/OVR_Alg.cpp new file mode 100644 index 0000000..ea82f2a --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Alg.cpp @@ -0,0 +1,46 @@ +/************************************************************************************
+
+Filename : OVR_Alg.cpp
+Content : Static lookup tables for Alg functions
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Types.h"
+
+namespace OVR { namespace Alg {
+
+//------------------------------------------------------------------------
+extern const UByte UpperBitTable[256] =
+{
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+extern const UByte LowerBitTable[256] =
+{
+ 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
+};
+
+
+}} // OVE::Alg
diff --git a/LibOVR/Src/Kernel/OVR_Alg.h b/LibOVR/Src/Kernel/OVR_Alg.h new file mode 100644 index 0000000..841e8f0 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Alg.h @@ -0,0 +1,953 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Alg.h
+Content : Simple general purpose algorithms: Sort, Binary Search, etc.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Alg_h
+#define OVR_Alg_h
+
+#include "OVR_Types.h"
+#include <string.h>
+
+namespace OVR { namespace Alg {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Operator extensions
+
+template <typename T> OVR_FORCE_INLINE void Swap(T &a, T &b)
+{ T temp(a); a = b; b = temp; }
+
+
+// ***** min/max are not implemented in Visual Studio 6 standard STL
+
+template <typename T> OVR_FORCE_INLINE const T Min(const T a, const T b)
+{ return (a < b) ? a : b; }
+
+template <typename T> OVR_FORCE_INLINE const T Max(const T a, const T b)
+{ return (b < a) ? a : b; }
+
+template <typename T> OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal)
+{ return Max<T>(minVal, Min<T>(v, maxVal)); }
+
+template <typename T> OVR_FORCE_INLINE int Chop(T f)
+{ return (int)f; }
+
+template <typename T> OVR_FORCE_INLINE T Lerp(T a, T b, T f)
+{ return (b - a) * f + a; }
+
+
+// These functions stand to fix a stupid VC++ warning (with /Wp64 on):
+// "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data"
+// Use these functions instead of gmin/gmax if the argument has size
+// of the pointer to avoid the warning. Though, functionally they are
+// absolutelly the same as regular gmin/gmax.
+template <typename T> OVR_FORCE_INLINE const T PMin(const T a, const T b)
+{
+ OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt));
+ return (a < b) ? a : b;
+}
+template <typename T> OVR_FORCE_INLINE const T PMax(const T a, const T b)
+{
+ OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt));
+ return (b < a) ? a : b;
+}
+
+
+template <typename T> OVR_FORCE_INLINE const T Abs(const T v)
+{ return (v>=0) ? v : -v; }
+
+
+//-----------------------------------------------------------------------------------
+// ***** OperatorLess
+//
+template<class T> struct OperatorLess
+{
+ static bool Compare(const T& a, const T& b)
+ {
+ return a < b;
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The comparison predicate must be specified.
+template<class Array, class Less>
+void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less)
+{
+ enum
+ {
+ Threshold = 9
+ };
+
+ if(end - start < 2) return;
+
+ SPInt stack[80];
+ SPInt* top = stack;
+ SPInt base = (SPInt)start;
+ SPInt limit = (SPInt)end;
+
+ for(;;)
+ {
+ SPInt len = limit - base;
+ SPInt i, j, pivot;
+
+ if(len > Threshold)
+ {
+ // we use base + len/2 as the pivot
+ pivot = base + len / 2;
+ Swap(arr[base], arr[pivot]);
+
+ i = base + 1;
+ j = limit - 1;
+
+ // now ensure that *i <= *base <= *j
+ if(less(arr[j], arr[i])) Swap(arr[j], arr[i]);
+ if(less(arr[base], arr[i])) Swap(arr[base], arr[i]);
+ if(less(arr[j], arr[base])) Swap(arr[j], arr[base]);
+
+ for(;;)
+ {
+ do i++; while( less(arr[i], arr[base]) );
+ do j--; while( less(arr[base], arr[j]) );
+
+ if( i > j )
+ {
+ break;
+ }
+
+ Swap(arr[i], arr[j]);
+ }
+
+ Swap(arr[base], arr[j]);
+
+ // now, push the largest sub-array
+ if(j - base > limit - i)
+ {
+ top[0] = base;
+ top[1] = j;
+ base = i;
+ }
+ else
+ {
+ top[0] = i;
+ top[1] = limit;
+ limit = j;
+ }
+ top += 2;
+ }
+ else
+ {
+ // the sub-array is small, perform insertion sort
+ j = base;
+ i = j + 1;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j == base)
+ {
+ break;
+ }
+ }
+ }
+ if(top > stack)
+ {
+ top -= 2;
+ base = top[0];
+ limit = top[1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The data type must have a defined "<" operator.
+template<class Array>
+void QuickSortSliced(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ QuickSortSliced(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+// Same as corresponding G_QuickSortSliced but with checking array limits to avoid
+// crash in the case of wrong comparator functor.
+template<class Array, class Less>
+bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less)
+{
+ enum
+ {
+ Threshold = 9
+ };
+
+ if(end - start < 2) return true;
+
+ SPInt stack[80];
+ SPInt* top = stack;
+ SPInt base = (SPInt)start;
+ SPInt limit = (SPInt)end;
+
+ for(;;)
+ {
+ SPInt len = limit - base;
+ SPInt i, j, pivot;
+
+ if(len > Threshold)
+ {
+ // we use base + len/2 as the pivot
+ pivot = base + len / 2;
+ Swap(arr[base], arr[pivot]);
+
+ i = base + 1;
+ j = limit - 1;
+
+ // now ensure that *i <= *base <= *j
+ if(less(arr[j], arr[i])) Swap(arr[j], arr[i]);
+ if(less(arr[base], arr[i])) Swap(arr[base], arr[i]);
+ if(less(arr[j], arr[base])) Swap(arr[j], arr[base]);
+
+ for(;;)
+ {
+ do
+ {
+ i++;
+ if (i >= limit)
+ return false;
+ } while( less(arr[i], arr[base]) );
+ do
+ {
+ j--;
+ if (j < 0)
+ return false;
+ } while( less(arr[base], arr[j]) );
+
+ if( i > j )
+ {
+ break;
+ }
+
+ Swap(arr[i], arr[j]);
+ }
+
+ Swap(arr[base], arr[j]);
+
+ // now, push the largest sub-array
+ if(j - base > limit - i)
+ {
+ top[0] = base;
+ top[1] = j;
+ base = i;
+ }
+ else
+ {
+ top[0] = i;
+ top[1] = limit;
+ limit = j;
+ }
+ top += 2;
+ }
+ else
+ {
+ // the sub-array is small, perform insertion sort
+ j = base;
+ i = j + 1;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j == base)
+ {
+ break;
+ }
+ }
+ }
+ if(top > stack)
+ {
+ top -= 2;
+ base = top[0];
+ limit = top[1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+template<class Array>
+bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ return QuickSortSlicedSafe(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The comparison predicate must be specified.
+template<class Array, class Less>
+void QuickSort(Array& arr, Less less)
+{
+ QuickSortSliced(arr, 0, arr.GetSize(), less);
+}
+
+// checks for boundaries
+template<class Array, class Less>
+bool QuickSortSafe(Array& arr, Less less)
+{
+ return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The data type must have a defined "<" operator.
+template<class Array>
+void QuickSort(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+template<class Array>
+bool QuickSortSafe(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The comparison predicate must be specified.
+// Unlike Quick Sort, the Insertion Sort works much slower in average,
+// but may be much faster on almost sorted arrays. Besides, it guarantees
+// that the elements will not be swapped if not necessary. For example,
+// an array with all equal elements will remain "untouched", while
+// Quick Sort will considerably shuffle the elements in this case.
+template<class Array, class Less>
+void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less)
+{
+ UPInt j = start;
+ UPInt i = j + 1;
+ UPInt limit = end;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j <= start)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The data type must have a defined "<" operator.
+template<class Array>
+void InsertionSortSliced(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ InsertionSortSliced(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The comparison predicate must be specified.
+
+template<class Array, class Less>
+void InsertionSort(Array& arr, Less less)
+{
+ InsertionSortSliced(arr, 0, arr.GetSize(), less);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The data type must have a defined "<" operator.
+template<class Array>
+void InsertionSort(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSliced
+//
+template<class Array, class Value, class Less>
+UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less)
+{
+ SPInt first = (SPInt)start;
+ SPInt len = (SPInt)(end - start);
+ SPInt half;
+ SPInt middle;
+
+ while(len > 0)
+ {
+ half = len >> 1;
+ middle = first + half;
+ if(less(arr[middle], val))
+ {
+ first = middle + 1;
+ len = len - half - 1;
+ }
+ else
+ {
+ len = half;
+ }
+ }
+ return (UPInt)first;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSliced
+//
+template<class Array, class Value>
+UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val)
+{
+ return LowerBoundSliced(arr, start, end, val, OperatorLess<Value>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSized
+//
+template<class Array, class Value>
+UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val)
+{
+ return LowerBoundSliced(arr, 0, size, val, OperatorLess<Value>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBound
+//
+template<class Array, class Value, class Less>
+UPInt LowerBound(const Array& arr, const Value& val, Less less)
+{
+ return LowerBoundSliced(arr, 0, arr.GetSize(), val, less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBound
+//
+template<class Array, class Value>
+UPInt LowerBound(const Array& arr, const Value& val)
+{
+ return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess<Value>::Compare);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSliced
+//
+template<class Array, class Value, class Less>
+UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less)
+{
+ SPInt first = (SPInt)start;
+ SPInt len = (SPInt)(end - start);
+ SPInt half;
+ SPInt middle;
+
+ while(len > 0)
+ {
+ half = len >> 1;
+ middle = first + half;
+ if(less(val, arr[middle]))
+ {
+ len = half;
+ }
+ else
+ {
+ first = middle + 1;
+ len = len - half - 1;
+ }
+ }
+ return (UPInt)first;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSliced
+//
+template<class Array, class Value>
+UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val)
+{
+ return UpperBoundSliced(arr, start, end, val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSized
+//
+template<class Array, class Value>
+UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val)
+{
+ return UpperBoundSliced(arr, 0, size, val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBound
+//
+template<class Array, class Value, class Less>
+UPInt UpperBound(const Array& arr, const Value& val, Less less)
+{
+ return UpperBoundSliced(arr, 0, arr.GetSize(), val, less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBound
+//
+template<class Array, class Value>
+UPInt UpperBound(const Array& arr, const Value& val)
+{
+ return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** ReverseArray
+//
+template<class Array> void ReverseArray(Array& arr)
+{
+ SPInt from = 0;
+ SPInt to = arr.GetSize() - 1;
+ while(from < to)
+ {
+ Swap(arr[from], arr[to]);
+ ++from;
+ --to;
+ }
+}
+
+
+// ***** AppendArray
+//
+template<class CDst, class CSrc>
+void AppendArray(CDst& dst, const CSrc& src)
+{
+ UPInt i;
+ for(i = 0; i < src.GetSize(); i++)
+ dst.PushBack(src[i]);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayAdaptor
+//
+// A simple adapter that provides the GetSize() method and overloads
+// operator []. Used to wrap plain arrays in QuickSort and such.
+template<class T> class ArrayAdaptor
+{
+public:
+ typedef T ValueType;
+ ArrayAdaptor() : Data(0), Size(0) {}
+ ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {}
+ UPInt GetSize() const { return Size; }
+ const T& operator [] (UPInt i) const { return Data[i]; }
+ T& operator [] (UPInt i) { return Data[i]; }
+private:
+ T* Data;
+ UPInt Size;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** GConstArrayAdaptor
+//
+// A simple const adapter that provides the GetSize() method and overloads
+// operator []. Used to wrap plain arrays in LowerBound and such.
+template<class T> class ConstArrayAdaptor
+{
+public:
+ typedef T ValueType;
+ ConstArrayAdaptor() : Data(0), Size(0) {}
+ ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {}
+ UPInt GetSize() const { return Size; }
+ const T& operator [] (UPInt i) const { return Data[i]; }
+private:
+ const T* Data;
+ UPInt Size;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+extern const UByte UpperBitTable[256];
+extern const UByte LowerBitTable[256];
+
+
+
+//-----------------------------------------------------------------------------------
+inline UByte UpperBit(UPInt val)
+{
+#ifndef OVR_64BIT_POINTERS
+
+ if (val & 0xFFFF0000)
+ {
+ return (val & 0xFF000000) ?
+ UpperBitTable[(val >> 24) ] + 24:
+ UpperBitTable[(val >> 16) & 0xFF] + 16;
+ }
+ return (val & 0xFF00) ?
+ UpperBitTable[(val >> 8) & 0xFF] + 8:
+ UpperBitTable[(val ) & 0xFF];
+
+#else
+
+ if (val & 0xFFFFFFFF00000000)
+ {
+ if (val & 0xFFFF000000000000)
+ {
+ return (val & 0xFF00000000000000) ?
+ UpperBitTable[(val >> 56) ] + 56:
+ UpperBitTable[(val >> 48) & 0xFF] + 48;
+ }
+ return (val & 0xFF0000000000) ?
+ UpperBitTable[(val >> 40) & 0xFF] + 40:
+ UpperBitTable[(val >> 32) & 0xFF] + 32;
+ }
+ else
+ {
+ if (val & 0xFFFF0000)
+ {
+ return (val & 0xFF000000) ?
+ UpperBitTable[(val >> 24) ] + 24:
+ UpperBitTable[(val >> 16) & 0xFF] + 16;
+ }
+ return (val & 0xFF00) ?
+ UpperBitTable[(val >> 8) & 0xFF] + 8:
+ UpperBitTable[(val ) & 0xFF];
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------------
+inline UByte LowerBit(UPInt val)
+{
+#ifndef OVR_64BIT_POINTERS
+
+ if (val & 0xFFFF)
+ {
+ return (val & 0xFF) ?
+ LowerBitTable[ val & 0xFF]:
+ LowerBitTable[(val >> 8) & 0xFF] + 8;
+ }
+ return (val & 0xFF0000) ?
+ LowerBitTable[(val >> 16) & 0xFF] + 16:
+ LowerBitTable[(val >> 24) & 0xFF] + 24;
+
+#else
+
+ if (val & 0xFFFFFFFF)
+ {
+ if (val & 0xFFFF)
+ {
+ return (val & 0xFF) ?
+ LowerBitTable[ val & 0xFF]:
+ LowerBitTable[(val >> 8) & 0xFF] + 8;
+ }
+ return (val & 0xFF0000) ?
+ LowerBitTable[(val >> 16) & 0xFF] + 16:
+ LowerBitTable[(val >> 24) & 0xFF] + 24;
+ }
+ else
+ {
+ if (val & 0xFFFF00000000)
+ {
+ return (val & 0xFF00000000) ?
+ LowerBitTable[(val >> 32) & 0xFF] + 32:
+ LowerBitTable[(val >> 40) & 0xFF] + 40;
+ }
+ return (val & 0xFF000000000000) ?
+ LowerBitTable[(val >> 48) & 0xFF] + 48:
+ LowerBitTable[(val >> 56) & 0xFF] + 56;
+ }
+
+#endif
+}
+
+
+
+// ******* Special (optimized) memory routines
+// Note: null (bad) pointer is not tested
+class MemUtil
+{
+public:
+
+ // Memory compare
+ static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); }
+ static int Cmp16(const void* p1, const void* p2, UPInt int16Count);
+ static int Cmp32(const void* p1, const void* p2, UPInt int32Count);
+ static int Cmp64(const void* p1, const void* p2, UPInt int64Count);
+};
+
+// ** Inline Implementation
+
+inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count)
+{
+ SInt16* pa = (SInt16*)p1;
+ SInt16* pb = (SInt16*)p2;
+ unsigned ic = 0;
+ if (int16Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int16Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count)
+{
+ SInt32* pa = (SInt32*)p1;
+ SInt32* pb = (SInt32*)p2;
+ unsigned ic = 0;
+ if (int32Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int32Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count)
+{
+ SInt64* pa = (SInt64*)p1;
+ SInt64* pb = (SInt64*)p2;
+ unsigned ic = 0;
+ if (int64Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int64Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+
+// ** End Inline Implementation
+
+
+//-----------------------------------------------------------------------------------
+// ******* Byte Order Conversions
+namespace ByteUtil {
+
+ // *** Swap Byte Order
+
+ // Swap the byte order of a byte array
+ inline void SwapOrder(void* pv, int size)
+ {
+ UByte* pb = (UByte*)pv;
+ UByte temp;
+ for (int i = 0; i < size>>1; i++)
+ {
+ temp = pb[size-1-i];
+ pb[size-1-i] = pb[i];
+ pb[i] = temp;
+ }
+ }
+
+ // Swap the byte order of primitive types
+ inline UByte SwapOrder(UByte v) { return v; }
+ inline SByte SwapOrder(SByte v) { return v; }
+ inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); }
+ inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); }
+ inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); }
+ inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); }
+ inline UInt64 SwapOrder(UInt64 v)
+ {
+ return (v>>56) |
+ ((v&UInt64(0x00FF000000000000))>>40) |
+ ((v&UInt64(0x0000FF0000000000))>>24) |
+ ((v&UInt64(0x000000FF00000000))>>8) |
+ ((v&UInt64(0x00000000FF000000))<<8) |
+ ((v&UInt64(0x0000000000FF0000))<<24) |
+ ((v&UInt64(0x000000000000FF00))<<40) |
+ (v<<56);
+ }
+ inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); }
+ inline float SwapOrder(float p)
+ {
+ union {
+ float p;
+ UInt32 v;
+ } u;
+ u.p = p;
+ u.v = SwapOrder(u.v);
+ return u.p;
+ }
+
+ inline double SwapOrder(double p)
+ {
+ union {
+ double p;
+ UInt64 v;
+ } u;
+ u.p = p;
+ u.v = SwapOrder(u.v);
+ return u.p;
+ }
+
+ // *** Byte-order conversion
+
+#if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN)
+ // Little Endian to System (LE)
+ inline UByte LEToSystem(UByte v) { return v; }
+ inline SByte LEToSystem(SByte v) { return v; }
+ inline UInt16 LEToSystem(UInt16 v) { return v; }
+ inline SInt16 LEToSystem(SInt16 v) { return v; }
+ inline UInt32 LEToSystem(UInt32 v) { return v; }
+ inline SInt32 LEToSystem(SInt32 v) { return v; }
+ inline UInt64 LEToSystem(UInt64 v) { return v; }
+ inline SInt64 LEToSystem(SInt64 v) { return v; }
+ inline float LEToSystem(float v) { return v; }
+ inline double LEToSystem(double v) { return v; }
+
+ // Big Endian to System (LE)
+ inline UByte BEToSystem(UByte v) { return SwapOrder(v); }
+ inline SByte BEToSystem(SByte v) { return SwapOrder(v); }
+ inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); }
+ inline float BEToSystem(float v) { return SwapOrder(v); }
+ inline double BEToSystem(double v) { return SwapOrder(v); }
+
+ // System (LE) to Little Endian
+ inline UByte SystemToLE(UByte v) { return v; }
+ inline SByte SystemToLE(SByte v) { return v; }
+ inline UInt16 SystemToLE(UInt16 v) { return v; }
+ inline SInt16 SystemToLE(SInt16 v) { return v; }
+ inline UInt32 SystemToLE(UInt32 v) { return v; }
+ inline SInt32 SystemToLE(SInt32 v) { return v; }
+ inline UInt64 SystemToLE(UInt64 v) { return v; }
+ inline SInt64 SystemToLE(SInt64 v) { return v; }
+ inline float SystemToLE(float v) { return v; }
+ inline double SystemToLE(double v) { return v; }
+
+ // System (LE) to Big Endian
+ inline UByte SystemToBE(UByte v) { return SwapOrder(v); }
+ inline SByte SystemToBE(SByte v) { return SwapOrder(v); }
+ inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); }
+ inline float SystemToBE(float v) { return SwapOrder(v); }
+ inline double SystemToBE(double v) { return SwapOrder(v); }
+
+#elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN)
+ // Little Endian to System (BE)
+ inline UByte LEToSystem(UByte v) { return SwapOrder(v); }
+ inline SByte LEToSystem(SByte v) { return SwapOrder(v); }
+ inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); }
+ inline float LEToSystem(float v) { return SwapOrder(v); }
+ inline double LEToSystem(double v) { return SwapOrder(v); }
+
+ // Big Endian to System (BE)
+ inline UByte BEToSystem(UByte v) { return v; }
+ inline SByte BEToSystem(SByte v) { return v; }
+ inline UInt16 BEToSystem(UInt16 v) { return v; }
+ inline SInt16 BEToSystem(SInt16 v) { return v; }
+ inline UInt32 BEToSystem(UInt32 v) { return v; }
+ inline SInt32 BEToSystem(SInt32 v) { return v; }
+ inline UInt64 BEToSystem(UInt64 v) { return v; }
+ inline SInt64 BEToSystem(SInt64 v) { return v; }
+ inline float BEToSystem(float v) { return v; }
+ inline double BEToSystem(double v) { return v; }
+
+ // System (BE) to Little Endian
+ inline UByte SystemToLE(UByte v) { return SwapOrder(v); }
+ inline SByte SystemToLE(SByte v) { return SwapOrder(v); }
+ inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); }
+ inline float SystemToLE(float v) { return SwapOrder(v); }
+ inline double SystemToLE(double v) { return SwapOrder(v); }
+
+ // System (BE) to Big Endian
+ inline UByte SystemToBE(UByte v) { return v; }
+ inline SByte SystemToBE(SByte v) { return v; }
+ inline UInt16 SystemToBE(UInt16 v) { return v; }
+ inline SInt16 SystemToBE(SInt16 v) { return v; }
+ inline UInt32 SystemToBE(UInt32 v) { return v; }
+ inline SInt32 SystemToBE(SInt32 v) { return v; }
+ inline UInt64 SystemToBE(UInt64 v) { return v; }
+ inline SInt64 SystemToBE(SInt64 v) { return v; }
+ inline float SystemToBE(float v) { return v; }
+ inline double SystemToBE(double v) { return v; }
+
+#else
+ #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN"
+#endif
+
+} // namespace ByteUtil
+
+
+
+}} // OVR::Alg
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Allocator.cpp b/LibOVR/Src/Kernel/OVR_Allocator.cpp new file mode 100644 index 0000000..1f17ffe --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Allocator.cpp @@ -0,0 +1,84 @@ +/************************************************************************************
+
+Filename : OVR_Allocator.cpp
+Content : Installable memory allocator implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Allocator.h"
+#ifdef OVR_OS_MAC
+ #include <stdlib.h>
+#else
+ #include <malloc.h>
+#endif
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Allocator
+
+Allocator* Allocator::pInstance = 0;
+
+// Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding.
+void* Allocator::AllocAligned(UPInt size, UPInt align)
+{
+ OVR_ASSERT((align & (align-1)) == 0);
+ align = (align > sizeof(UPInt)) ? align : sizeof(UPInt);
+ UPInt p = (UPInt)Alloc(size+align);
+ UPInt aligned = 0;
+ if (p)
+ {
+ aligned = (UPInt(p) + align-1) & ~(align-1);
+ if (aligned == p)
+ aligned += align;
+ *(((UPInt*)aligned)-1) = aligned-p;
+ }
+ return (void*)aligned;
+}
+
+void Allocator::FreeAligned(void* p)
+{
+ UPInt src = UPInt(p) - *(((UPInt*)p)-1);
+ Free((void*)src);
+}
+
+
+//------------------------------------------------------------------------
+// ***** Default Allocator
+
+// This allocator is created and used if no other allocator is installed.
+// Default allocator delegates to system malloc.
+
+void* DefaultAllocator::Alloc(UPInt size)
+{
+ return malloc(size);
+}
+void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line)
+{
+#if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC)
+ return _malloc_dbg(size, _NORMAL_BLOCK, file, line);
+#else
+ OVR_UNUSED2(file, line);
+ return malloc(size);
+#endif
+}
+
+void* DefaultAllocator::Realloc(void* p, UPInt newSize)
+{
+ return realloc(p, newSize);
+}
+void DefaultAllocator::Free(void *p)
+{
+ return free(p);
+}
+
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Allocator.h b/LibOVR/Src/Kernel/OVR_Allocator.h new file mode 100644 index 0000000..88cbb42 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Allocator.h @@ -0,0 +1,336 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Allocator.h
+Content : Installable memory allocator
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Allocator_h
+#define OVR_Allocator_h
+
+#include "OVR_Types.h"
+
+//-----------------------------------------------------------------------------------
+
+// ***** Disable template-unfriendly MS VC++ warnings
+#if defined(OVR_CC_MSVC)
+// Pragma to prevent long name warnings in in VC++
+#pragma warning(disable : 4503)
+#pragma warning(disable : 4786)
+// In MSVC 7.1, warning about placement new POD default initializer
+#pragma warning(disable : 4345)
+#endif
+
+// Un-define new so that placement constructors work
+#undef new
+
+
+//-----------------------------------------------------------------------------------
+// ***** Placement new overrides
+
+// Calls constructor on own memory created with "new(ptr) type"
+#ifndef __PLACEMENT_NEW_INLINE
+#define __PLACEMENT_NEW_INLINE
+
+# if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU)
+# include <new>
+# else
+ // Useful on MSVC
+ OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; }
+ OVR_FORCE_INLINE void operator delete (void *, void *) { }
+# endif
+
+#endif // __PLACEMENT_NEW_INLINE
+
+
+
+//------------------------------------------------------------------------
+// ***** Macros to redefine class new/delete operators
+
+// Types specifically declared to allow disambiguation of address in
+// class member operator new.
+
+#define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \
+ void* operator new(UPInt sz) \
+ { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \
+ void* operator new(UPInt sz, const char* file, int line) \
+ { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \
+ void operator delete(void *p) \
+ { check_delete(class_name, p); OVR_FREE(p); } \
+ void operator delete(void *p, const char*, int) \
+ { check_delete(class_name, p); OVR_FREE(p); }
+
+#define OVR_MEMORY_DEFINE_PLACEMENT_NEW \
+ void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \
+ void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); }
+
+
+#define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p)
+
+// Redefined all delete/new operators in a class without custom memory initialization
+#define OVR_MEMORY_REDEFINE_NEW(class_name) \
+ OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE)
+
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Construct / Destruct
+
+// Construct/Destruct functions are useful when new is redefined, as they can
+// be called instead of placement new constructors.
+
+
+template <class T>
+OVR_FORCE_INLINE T* Construct(void *p)
+{
+ return ::new(p) T;
+}
+
+template <class T>
+OVR_FORCE_INLINE T* Construct(void *p, const T& source)
+{
+ return ::new(p) T(source);
+}
+
+// Same as above, but allows for a different type of constructor.
+template <class T, class S>
+OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source)
+{
+ return ::new(p) T(source);
+}
+
+template <class T, class S1, class S2>
+OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2)
+{
+ return ::new(p) T(src1, src2);
+}
+
+template <class T>
+OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count)
+{
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ {
+ Construct<T>(pdata);
+ }
+}
+
+template <class T>
+OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source)
+{
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ {
+ Construct<T>(pdata, source);
+ }
+}
+
+template <class T>
+OVR_FORCE_INLINE void Destruct(T *pobj)
+{
+ pobj->~T();
+ OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning.
+}
+
+template <class T>
+OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count)
+{
+ for (UPInt i=0; i<count; ++i, ++pobj)
+ pobj->~T();
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Allocator
+
+// Allocator defines a memory allocation interface that developers can override
+// to to provide memory for OVR; an instance of this class is typically created on
+// application startup and passed into System or OVR::System constructor.
+//
+//
+// Users implementing this interface must provide three functions: Alloc, Free,
+// and Realloc. Implementations of these functions must honor the requested alignment.
+// Although arbitrary alignment requests are possible, requested alignment will
+// typically be small, such as 16 bytes or less.
+
+class Allocator
+{
+ friend class System;
+public:
+
+ // *** Standard Alignment Alloc/Free
+
+ // Allocate memory of specified size with default alignment.
+ // Alloc of size==0 will allocate a tiny block & return a valid pointer;
+ // this makes it suitable for new operator.
+ virtual void* Alloc(UPInt size) = 0;
+ // Same as Alloc, but provides an option of passing debug data.
+ virtual void* AllocDebug(UPInt size, const char* file, unsigned line)
+ { OVR_UNUSED2(file, line); return Alloc(size); }
+
+ // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to
+ // new memory block, which may be the same as original pointer. Will return 0 if reallocation
+ // failed, in which case previous memory is still valid.
+ // Realloc to decrease size will never fail.
+ // Realloc of pointer == 0 is equivalent to Alloc
+ // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free().
+ virtual void* Realloc(void* p, UPInt newSize) = 0;
+
+ // Frees memory allocated by Alloc/Realloc.
+ // Free of null pointer is valid and will do nothing.
+ virtual void Free(void *p) = 0;
+
+
+ // *** Standard Alignment Alloc/Free
+
+ // Allocate memory of specified alignment.
+ // Memory allocated with AllocAligned MUST be freed with FreeAligned.
+ // Default implementation will delegate to Alloc/Free after doing rounding.
+ virtual void* AllocAligned(UPInt size, UPInt align);
+ // Frees memory allocated with AllocAligned.
+ virtual void FreeAligned(void* p);
+
+ // Returns the pointer to the current globally installed Allocator instance.
+ // This pointer is used for most of the memory allocations.
+ static Allocator* GetInstance() { return pInstance; }
+
+
+protected:
+ // onSystemShutdown is called on the allocator during System::Shutdown.
+ // At this point, all allocations should've been freed.
+ virtual void onSystemShutdown() { }
+
+public:
+ static void setInstance(Allocator* palloc)
+ {
+ OVR_ASSERT((pInstance == 0) || (palloc == 0));
+ pInstance = palloc;
+ }
+
+private:
+
+ static Allocator* pInstance;
+};
+
+
+
+//------------------------------------------------------------------------
+// ***** Allocator_SingletonSupport
+
+// Allocator_SingletonSupport is a Allocator wrapper class that implements
+// the InitSystemSingleton static function, used to create a global singleton
+// used for the OVR::System default argument initialization.
+//
+// End users implementing custom Allocator interface don't need to make use of this base
+// class; they can just create an instance of their own class on stack and pass it to System.
+
+template<class D>
+class Allocator_SingletonSupport : public Allocator
+{
+ struct AllocContainer
+ {
+ UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)];
+ bool Initialized;
+ AllocContainer() : Initialized(0) { }
+ };
+
+ AllocContainer* pContainer;
+
+public:
+ Allocator_SingletonSupport() : pContainer(0) { }
+
+ // Creates a singleton instance of this Allocator class used
+ // on OVR_DEFAULT_ALLOCATOR during System initialization.
+ static D* InitSystemSingleton()
+ {
+ static AllocContainer Container;
+ OVR_ASSERT(Container.Initialized == false);
+
+ Allocator_SingletonSupport<D> *presult = Construct<D>((void*)Container.Data);
+ presult->pContainer = &Container;
+ Container.Initialized = true;
+ return (D*)presult;
+ }
+
+protected:
+ virtual void onSystemShutdown()
+ {
+ Allocator::onSystemShutdown();
+ if (pContainer)
+ {
+ pContainer->Initialized = false;
+ Destruct((D*)this);
+ pContainer = 0;
+ }
+ }
+};
+
+//------------------------------------------------------------------------
+// ***** Default Allocator
+
+// This allocator is created and used if no other allocator is installed.
+// Default allocator delegates to system malloc.
+
+class DefaultAllocator : public Allocator_SingletonSupport<DefaultAllocator>
+{
+public:
+ virtual void* Alloc(UPInt size);
+ virtual void* AllocDebug(UPInt size, const char* file, unsigned line);
+ virtual void* Realloc(void* p, UPInt newSize);
+ virtual void Free(void *p);
+};
+
+
+//------------------------------------------------------------------------
+// ***** Memory Allocation Macros
+
+// These macros should be used for global allocation. In the future, these
+// macros will allows allocation to be extended with debug file/line information
+// if necessary.
+
+#define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s))
+#define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p))
+#define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a))
+#define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p))
+
+#ifdef OVR_BUILD_DEBUG
+#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__)
+#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l)
+#else
+#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s))
+#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s))
+#endif
+
+//------------------------------------------------------------------------
+
+// Base class that overrides the new and delete operators.
+// Deriving from this class, even as a multiple base, incurs no space overhead.
+class NewOverrideBase
+{
+public:
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW(NewOverrideBase)
+};
+
+
+} // OVR
+
+
+// Redefine operator 'new' if necessary.
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+
+#endif // OVR_Memory
diff --git a/LibOVR/Src/Kernel/OVR_Array.h b/LibOVR/Src/Kernel/OVR_Array.h new file mode 100644 index 0000000..905d58f --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Array.h @@ -0,0 +1,793 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Array.h
+Content : Template implementation for Array
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Array_h
+#define OVR_Array_h
+
+#include "OVR_ContainerAllocator.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDefaultPolicy
+//
+// Default resize behavior. No minimal capacity, Granularity=4,
+// Shrinking as needed. ArrayConstPolicy actually is the same as
+// ArrayDefaultPolicy, but parametrized with constants.
+// This struct is used only in order to reduce the template "matroska".
+struct ArrayDefaultPolicy
+{
+ ArrayDefaultPolicy() : Capacity(0) {}
+ ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {}
+
+ UPInt GetMinCapacity() const { return 0; }
+ UPInt GetGranularity() const { return 4; }
+ bool NeverShrinking() const { return 0; }
+
+ UPInt GetCapacity() const { return Capacity; }
+ void SetCapacity(UPInt capacity) { Capacity = capacity; }
+private:
+ UPInt Capacity;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayConstPolicy
+//
+// Statically parametrized resizing behavior:
+// MinCapacity, Granularity, and Shrinking flag.
+template<int MinCapacity=0, int Granularity=4, bool NeverShrink=false>
+struct ArrayConstPolicy
+{
+ typedef ArrayConstPolicy<MinCapacity, Granularity, NeverShrink> SelfType;
+
+ ArrayConstPolicy() : Capacity(0) {}
+ ArrayConstPolicy(const SelfType&) : Capacity(0) {}
+
+ UPInt GetMinCapacity() const { return MinCapacity; }
+ UPInt GetGranularity() const { return Granularity; }
+ bool NeverShrinking() const { return NeverShrink; }
+
+ UPInt GetCapacity() const { return Capacity; }
+ void SetCapacity(UPInt capacity) { Capacity = capacity; }
+private:
+ UPInt Capacity;
+};
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDataBase
+//
+// Basic operations with array data: Reserve, Resize, Free, ArrayPolicy.
+// For internal use only: ArrayData,ArrayDataCC and others.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayDataBase
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> SelfType;
+
+ ArrayDataBase()
+ : Data(0), Size(0), Policy() {}
+
+ ArrayDataBase(const SizePolicy& p)
+ : Data(0), Size(0), Policy(p) {}
+
+ ~ArrayDataBase()
+ {
+ Allocator::DestructArray(Data, Size);
+ Allocator::Free(Data);
+ }
+
+ UPInt GetCapacity() const
+ {
+ return Policy.GetCapacity();
+ }
+
+ void ClearAndRelease()
+ {
+ Allocator::DestructArray(Data, Size);
+ Allocator::Free(Data);
+ Data = 0;
+ Size = 0;
+ Policy.SetCapacity(0);
+ }
+
+ void Reserve(UPInt newCapacity)
+ {
+ if (Policy.NeverShrinking() && newCapacity < GetCapacity())
+ return;
+
+ if (newCapacity < Policy.GetMinCapacity())
+ newCapacity = Policy.GetMinCapacity();
+
+ // Resize the buffer.
+ if (newCapacity == 0)
+ {
+ if (Data)
+ {
+ Allocator::Free(Data);
+ Data = 0;
+ }
+ Policy.SetCapacity(0);
+ }
+ else
+ {
+ UPInt gran = Policy.GetGranularity();
+ newCapacity = (newCapacity + gran - 1) / gran * gran;
+ if (Data)
+ {
+ if (Allocator::IsMovable())
+ {
+ Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity);
+ }
+ else
+ {
+ T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity);
+ UPInt i, s;
+ s = (Size < newCapacity) ? Size : newCapacity;
+ for (i = 0; i < s; ++i)
+ {
+ Allocator::Construct(&newData[i], Data[i]);
+ Allocator::Destruct(&Data[i]);
+ }
+ for (i = s; i < Size; ++i)
+ {
+ Allocator::Destruct(&Data[i]);
+ }
+ Allocator::Free(Data);
+ Data = newData;
+ }
+ }
+ else
+ {
+ Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity);
+ //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this?
+ }
+ Policy.SetCapacity(newCapacity);
+ // OVR_ASSERT(Data); // need to throw (or something) on alloc failure!
+ }
+ }
+
+ // This version of Resize DOES NOT construct the elements.
+ // It's done to optimize PushBack, which uses a copy constructor
+ // instead of the default constructor and assignment
+ void ResizeNoConstruct(UPInt newSize)
+ {
+ UPInt oldSize = Size;
+
+ if (newSize < oldSize)
+ {
+ Allocator::DestructArray(Data + newSize, oldSize - newSize);
+ if (newSize < (Policy.GetCapacity() >> 1))
+ {
+ Reserve(newSize);
+ }
+ }
+ else if(newSize >= Policy.GetCapacity())
+ {
+ Reserve(newSize + (newSize >> 2));
+ }
+ //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable
+ // array may use this array and may traverse it during Reserve (in the case, if
+ // collection occurs because of heap limit exceeded).
+ Size = newSize;
+ }
+
+ ValueType* Data;
+ UPInt Size;
+ SizePolicy Policy;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayData
+//
+// General purpose array data.
+// For internal use only in Array, ArrayLH, ArrayPOD and so on.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayData : ArrayDataBase<T, Allocator, SizePolicy>
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> BaseType;
+ typedef ArrayData <T, Allocator, SizePolicy> SelfType;
+
+ ArrayData()
+ : BaseType() { }
+
+ ArrayData(int size)
+ : BaseType() { Resize(size); }
+
+ ArrayData(const SelfType& a)
+ : BaseType(a.Policy) { Append(a.Data, a.Size); }
+
+
+ void Resize(UPInt newSize)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(newSize);
+ if(newSize > oldSize)
+ Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize);
+ }
+
+ void PushBack(const ValueType& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::Construct(this->Data + this->Size - 1, val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::ConstructAlt(this->Data + this->Size - 1, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ if (count)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(this->Size + count);
+ Allocator::ConstructArray(this->Data + oldSize, count, other);
+ }
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDataCC
+//
+// A modification of ArrayData that always copy-constructs new elements
+// using a specified DefaultValue. For internal use only in ArrayCC.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayDataCC : ArrayDataBase<T, Allocator, SizePolicy>
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> BaseType;
+ typedef ArrayDataCC <T, Allocator, SizePolicy> SelfType;
+
+ ArrayDataCC(const ValueType& defval)
+ : BaseType(), DefaultValue(defval) { }
+
+ ArrayDataCC(const ValueType& defval, int size)
+ : BaseType(), DefaultValue(defval) { Resize(size); }
+
+ ArrayDataCC(const SelfType& a)
+ : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); }
+
+
+ void Resize(UPInt newSize)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(newSize);
+ if(newSize > oldSize)
+ Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue);
+ }
+
+ void PushBack(const ValueType& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::Construct(this->Data + this->Size - 1, val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::ConstructAlt(this->Data + this->Size - 1, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ if (count)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(this->Size + count);
+ Allocator::ConstructArray(this->Data + oldSize, count, other);
+ }
+ }
+
+ ValueType DefaultValue;
+};
+
+
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayBase
+//
+// Resizable array. The behavior can be POD (suffix _POD) and
+// Movable (no suffix) depending on the allocator policy.
+// In case of _POD the constructors and destructors are not called.
+//
+// Arrays can't handle non-movable objects! Don't put anything in here
+// that can't be moved around by bitwise copy.
+//
+// The addresses of elements are not persistent! Don't keep the address
+// of an element; the array contents will move around as it gets resized.
+template<class ArrayData>
+class ArrayBase
+{
+public:
+ typedef typename ArrayData::ValueType ValueType;
+ typedef typename ArrayData::AllocatorType AllocatorType;
+ typedef typename ArrayData::SizePolicyType SizePolicyType;
+ typedef ArrayBase<ArrayData> SelfType;
+
+
+#undef new
+ OVR_MEMORY_REDEFINE_NEW(ArrayBase)
+// Redefine operator 'new' if necessary.
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+
+ ArrayBase()
+ : Data() {}
+ ArrayBase(int size)
+ : Data(size) {}
+ ArrayBase(const SelfType& a)
+ : Data(a.Data) {}
+
+ ArrayBase(const ValueType& defval)
+ : Data(defval) {}
+ ArrayBase(const ValueType& defval, int size)
+ : Data(defval, size) {}
+
+ SizePolicyType* GetSizePolicy() const { return Data.Policy; }
+ void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; }
+
+ bool NeverShrinking()const { return Data.Policy.NeverShrinking(); }
+ UPInt GetSize() const { return Data.Size; }
+ bool IsEmpty() const { return Data.Size == 0; }
+ UPInt GetCapacity() const { return Data.GetCapacity(); }
+ UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); }
+
+ void ClearAndRelease() { Data.ClearAndRelease(); }
+ void Clear() { Data.Resize(0); }
+ void Resize(UPInt newSize) { Data.Resize(newSize); }
+
+ // Reserve can only increase the capacity
+ void Reserve(UPInt newCapacity)
+ {
+ if (newCapacity > Data.GetCapacity())
+ Data.Reserve(newCapacity);
+ }
+
+ // Basic access.
+ ValueType& At(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+ const ValueType& At(UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ ValueType ValueAt(UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ // Basic access.
+ ValueType& operator [] (UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+ const ValueType& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ // Raw pointer to the data. Use with caution!
+ const ValueType* GetDataPtr() const { return Data.Data; }
+ ValueType* GetDataPtr() { return Data.Data; }
+
+ // Insert the given element at the end of the array.
+ void PushBack(const ValueType& val)
+ {
+ // DO NOT pass elements of your own vector into
+ // push_back()! Since we're using references,
+ // resize() may munge the element storage!
+ // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]);
+ Data.PushBack(val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ Data.PushBackAlt(val);
+ }
+
+ // Remove the last element.
+ void PopBack(UPInt count = 1)
+ {
+ OVR_ASSERT(Data.Size >= count);
+ Data.Resize(Data.Size - count);
+ }
+
+ ValueType& PushDefault()
+ {
+ Data.PushBack(ValueType());
+ return Back();
+ }
+
+ ValueType Pop()
+ {
+ ValueType t = Back();
+ PopBack();
+ return t;
+ }
+
+
+ // Access the first element.
+ ValueType& Front() { return At(0); }
+ const ValueType& Front() const { return At(0); }
+
+ // Access the last element.
+ ValueType& Back() { return At(Data.Size - 1); }
+ const ValueType& Back() const { return At(Data.Size - 1); }
+
+ // Array copy. Copies the contents of a into this array.
+ const SelfType& operator = (const SelfType& a)
+ {
+ Resize(a.GetSize());
+ for (UPInt i = 0; i < Data.Size; i++) {
+ *(Data.Data + i) = a[i];
+ }
+ return *this;
+ }
+
+ // Removing multiple elements from the array.
+ void RemoveMultipleAt(UPInt index, UPInt num)
+ {
+ OVR_ASSERT(index + num <= Data.Size);
+ if (Data.Size == num)
+ {
+ Clear();
+ }
+ else
+ {
+ AllocatorType::DestructArray(Data.Data + index, num);
+ AllocatorType::CopyArrayForward(
+ Data.Data + index,
+ Data.Data + index + num,
+ Data.Size - num - index);
+ Data.Size -= num;
+ }
+ }
+
+ // Removing an element from the array is an expensive operation!
+ // It compacts only after removing the last element.
+ void RemoveAt(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ if (Data.Size == 1)
+ {
+ Clear();
+ }
+ else
+ {
+ AllocatorType::Destruct(Data.Data + index);
+ AllocatorType::CopyArrayForward(
+ Data.Data + index,
+ Data.Data + index + 1,
+ Data.Size - 1 - index);
+ --Data.Size;
+ }
+ }
+
+ // Insert the given object at the given index shifting all the elements up.
+ void InsertAt(UPInt index, const ValueType& val = ValueType())
+ {
+ OVR_ASSERT(index <= Data.Size);
+
+ Data.Resize(Data.Size + 1);
+ if (index < Data.Size - 1)
+ {
+ AllocatorType::CopyArrayBackward(
+ Data.Data + index + 1,
+ Data.Data + index,
+ Data.Size - 1 - index);
+ }
+ AllocatorType::Construct(Data.Data + index, val);
+ }
+
+ // Insert the given object at the given index shifting all the elements up.
+ void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType())
+ {
+ OVR_ASSERT(index <= Data.Size);
+
+ Data.Resize(Data.Size + num);
+ if (index < Data.Size - num)
+ {
+ AllocatorType::CopyArrayBackward(
+ Data.Data + index + num,
+ Data.Data + index,
+ Data.Size - num - index);
+ }
+ for (UPInt i = 0; i < num; ++i)
+ AllocatorType::Construct(Data.Data + index + i, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const SelfType& other)
+ {
+ Append(other.Data.Data, other.GetSize());
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ Data.Append(other, count);
+ }
+
+ class Iterator
+ {
+ SelfType* pArray;
+ SPInt CurIndex;
+
+ public:
+ Iterator() : pArray(0), CurIndex(-1) {}
+ Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {}
+
+ bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; }
+ bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; }
+
+ Iterator& operator++()
+ {
+ if (pArray)
+ {
+ if (CurIndex < (SPInt)pArray->GetSize())
+ ++CurIndex;
+ }
+ return *this;
+ }
+ Iterator operator++(int)
+ {
+ Iterator it(*this);
+ operator++();
+ return it;
+ }
+ Iterator& operator--()
+ {
+ if (pArray)
+ {
+ if (CurIndex >= 0)
+ --CurIndex;
+ }
+ return *this;
+ }
+ Iterator operator--(int)
+ {
+ Iterator it(*this);
+ operator--();
+ return it;
+ }
+ Iterator operator+(int delta) const
+ {
+ return Iterator(pArray, CurIndex + delta);
+ }
+ Iterator operator-(int delta) const
+ {
+ return Iterator(pArray, CurIndex - delta);
+ }
+ SPInt operator-(const Iterator& right) const
+ {
+ OVR_ASSERT(pArray == right.pArray);
+ return CurIndex - right.CurIndex;
+ }
+ ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; }
+ ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+ ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+
+ bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); }
+
+ void Remove()
+ {
+ if (!IsFinished())
+ pArray->RemoveAt(CurIndex);
+ }
+
+ SPInt GetIndex() const { return CurIndex; }
+ };
+
+ Iterator Begin() { return Iterator(this); }
+ Iterator End() { return Iterator(this, (SPInt)GetSize()); }
+ Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); }
+
+ class ConstIterator
+ {
+ const SelfType* pArray;
+ SPInt CurIndex;
+
+ public:
+ ConstIterator() : pArray(0), CurIndex(-1) {}
+ ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {}
+
+ bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; }
+ bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; }
+
+ ConstIterator& operator++()
+ {
+ if (pArray)
+ {
+ if (CurIndex < (int)pArray->GetSize())
+ ++CurIndex;
+ }
+ return *this;
+ }
+ ConstIterator operator++(int)
+ {
+ ConstIterator it(*this);
+ operator++();
+ return it;
+ }
+ ConstIterator& operator--()
+ {
+ if (pArray)
+ {
+ if (CurIndex >= 0)
+ --CurIndex;
+ }
+ return *this;
+ }
+ ConstIterator operator--(int)
+ {
+ ConstIterator it(*this);
+ operator--();
+ return it;
+ }
+ ConstIterator operator+(int delta) const
+ {
+ return ConstIterator(pArray, CurIndex + delta);
+ }
+ ConstIterator operator-(int delta) const
+ {
+ return ConstIterator(pArray, CurIndex - delta);
+ }
+ SPInt operator-(const ConstIterator& right) const
+ {
+ OVR_ASSERT(pArray == right.pArray);
+ return CurIndex - right.CurIndex;
+ }
+ const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; }
+ const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+ const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+
+ bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); }
+
+ SPInt GetIndex() const { return CurIndex; }
+ };
+ ConstIterator Begin() const { return ConstIterator(this); }
+ ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); }
+ ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); }
+
+protected:
+ ArrayData Data;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Array
+//
+// General purpose array for movable objects that require explicit
+// construction/destruction.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class Array : public ArrayBase<ArrayData<T, ContainerAllocator<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef Array<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator<T>, SizePolicy> > BaseType;
+
+ Array() : BaseType() {}
+ Array(int size) : BaseType(size) {}
+ Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ Array(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+// ***** ArrayPOD
+//
+// General purpose array for movable objects that DOES NOT require
+// construction/destruction. Constructors and destructors are not called!
+// Global heap is in use.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayPOD : public ArrayBase<ArrayData<T, ContainerAllocator_POD<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator_POD<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayPOD<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator_POD<T>, SizePolicy> > BaseType;
+
+ ArrayPOD() : BaseType() {}
+ ArrayPOD(int size) : BaseType(size) {}
+ ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ ArrayPOD(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+
+// ***** ArrayCPP
+//
+// General purpose, fully C++ compliant array. Can be used with non-movable data.
+// Global heap is in use.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayCPP : public ArrayBase<ArrayData<T, ContainerAllocator_CPP<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator_CPP<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayCPP<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator_CPP<T>, SizePolicy> > BaseType;
+
+ ArrayCPP() : BaseType() {}
+ ArrayCPP(int size) : BaseType(size) {}
+ ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ ArrayCPP(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+
+// ***** ArrayCC
+//
+// A modification of the array that uses the given default value to
+// construct the elements. The constructors and destructors are
+// properly called, the objects must be movable.
+
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayCC : public ArrayBase<ArrayDataCC<T, ContainerAllocator<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayCC<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayDataCC<T, ContainerAllocator<T>, SizePolicy> > BaseType;
+
+ ArrayCC(const ValueType& defval) : BaseType(defval) {}
+ ArrayCC(const ValueType& defval, int size) : BaseType(defval, size) {}
+ ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); }
+ ArrayCC(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Atomic.cpp b/LibOVR/Src/Kernel/OVR_Atomic.cpp new file mode 100644 index 0000000..a2f9230 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Atomic.cpp @@ -0,0 +1,82 @@ +/************************************************************************************
+
+Filename : OVR_Atomic.cpp
+Content : Contains atomic operations and inline fastest locking
+ functionality. Will contain #ifdefs for OS efficiency.
+ Have non-thread-safe implementation if not available.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Atomic.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+// Include Windows 8-Metro compatible Synchronization API
+#if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
+#include <synchapi.h>
+#endif
+
+
+namespace OVR {
+
+// ***** Windows Lock implementation
+
+#if defined(OVR_OS_WIN32)
+
+// ***** Standard Win32 Lock implementation
+
+// Constructors
+Lock::Lock(unsigned spinCount)
+{
+#if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
+ // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility
+ InitializeCriticalSectionEx(&cs, spinCount,
+ OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO));
+#else
+ // Spin count init critical section function prototype for Window NT
+ typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount)
+ (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
+
+
+ // Try to load function dynamically so that we don't require NT
+ // On Windows NT we will use InitializeCriticalSectionAndSpinCount
+ static bool initTried = 0;
+ static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0;
+
+ if (!initTried)
+ {
+ HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll"));
+ pInitFn = (Function_InitializeCriticalSectionAndSpinCount)
+ ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount");
+ initTried = true;
+ }
+
+ // Initialize the critical section
+ if (pInitFn)
+ pInitFn(&cs, spinCount);
+ else
+ ::InitializeCriticalSection(&cs);
+#endif
+
+}
+
+
+Lock::~Lock()
+{
+ DeleteCriticalSection(&cs);
+}
+
+
+#endif
+
+} // OVR
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Kernel/OVR_Atomic.h b/LibOVR/Src/Kernel/OVR_Atomic.h new file mode 100644 index 0000000..089125b --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Atomic.h @@ -0,0 +1,859 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Atomic.h
+Content : Contains atomic operations and inline fastest locking
+ functionality. Will contain #ifdefs for OS efficiency.
+ Have non-thread-safe implementaion if not available.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_Atomic_h
+#define OVR_Atomic_h
+
+#include "OVR_Types.h"
+
+// Include System thread functionality.
+#if defined(OVR_OS_WIN32)
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+
+namespace OVR {
+
+
+// ****** Declared classes
+
+// If there is NO thread support we implement AtomicOps and
+// Lock objects as no-ops. The other classes are not defined.
+template<class C> class AtomicOps;
+template<class T> class AtomicInt;
+template<class T> class AtomicPtr;
+
+class Lock;
+
+
+//-----------------------------------------------------------------------------------
+// ***** AtomicOps
+
+// Atomic operations are provided by the AtomicOps templates class,
+// implemented through system-specific AtomicOpsRaw specializations.
+// It provides several fundamental operations such as Exchange, ExchangeAdd
+// CompareAndSet, and Store_Release. Each function includes several memory
+// synchronization versions, important for multiprocessing CPUs with weak
+// memory consistency. The following memory fencing strategies are supported:
+//
+// - NoSync. No memory synchronization is done for atomic op.
+// - Release. All other memory writes are completed before atomic op
+// writes its results.
+// - Acquire. Further memory reads are forced to wait until atomic op
+// executes, guaranteeing that the right values will be seen.
+// - Sync. A combination of Release and Acquire.
+
+
+// *** AtomicOpsRaw
+
+// AtomicOpsRaw is a specialized template that provides atomic operations
+// used by AtomicOps. This class has two fundamental qualities: (1) it
+// defines a type T of correct size, and (2) provides operations that work
+// atomically, such as Exchange_Sync and CompareAndSet_Release.
+
+// AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw.
+// The primary thing is does is define sync class objects, whose destructor and
+// constructor provide places to insert appropriate synchronization calls, on
+// systems where such calls are necessary. So far, the breakdown is as follows:
+//
+// - X86 systems don't need custom syncs, since their exchange/atomic
+// instructions are implicitly synchronized.
+// - PowerPC requires lwsync/isync instructions that can use this mechanism.
+// - If some other systems require a mechanism where syncing type is associated
+// with a particular instruction, the default implementation (which implements
+// all Sync, Acquire, and Release modes in terms of NoSync and fence) may not
+// work. Ii that case it will need to be #ifdef-ed conditionally.
+
+struct AtomicOpsRawBase
+{
+#if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE)
+ // Need to have empty constructor to avoid class 'unused' variable warning.
+ struct FullSync { inline FullSync() { } };
+ struct AcquireSync { inline AcquireSync() { } };
+ struct ReleaseSync { inline ReleaseSync() { } };
+
+#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
+ struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
+
+#elif defined(OVR_CPU_MIPS)
+ struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
+
+#elif defined(OVR_CPU_ARM)
+ struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } };
+
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4)
+ // __sync functions are already full sync
+ struct FullSync { inline FullSync() { } };
+ struct AcquireSync { inline AcquireSync() { } };
+ struct ReleaseSync { inline ReleaseSync() { } };
+#endif
+};
+
+
+// 4-Byte raw data atomic op implementation class.
+struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase
+{
+#if !defined(OVR_ENABLE_THREADS)
+
+ // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
+ typedef UInt32 T;
+
+ // *** Thread - Safe Atomic Versions.
+
+#elif defined(OVR_OS_WIN32)
+
+ // Use special defined for VC6, where volatile is not used and
+ // InterlockedCompareExchange is declared incorrectly.
+ typedef LONG T;
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300)
+ typedef T* InterlockTPtr;
+ typedef LPVOID ET;
+ typedef ET* InterlockETPtr;
+#else
+ typedef volatile T* InterlockTPtr;
+ typedef T ET;
+ typedef InterlockTPtr InterlockETPtr;
+#endif
+ inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); }
+ inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); }
+ inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; }
+
+#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
+ typedef UInt32 T;
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "stwcx. %[j],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "stwcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "cmpw 0,%[r],%[cmp]\n\t"
+ "mfcr %[r]\n\t"
+ "bne- 2f\n\t"
+ "stwcx. %[val],0,%[i]\n\t"
+ "bne- 1b\n\t"
+ "2:\n"
+ : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory");
+
+ return (ret & 0x20000000) ? 1 : 0;
+ }
+
+#elif defined(OVR_CPU_MIPS)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "ll %[r],0(%[i])\n\t"
+ "sc %[j],0(%[i])\n\t"
+ "beq %[j],$0,1b\n\t"
+ "nop \n"
+ : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "ll %[r],0(%[i])\n\t"
+ "addu %[j],%[r],%[j]\n\t"
+ "sc %[j],0(%[i])\n\t"
+ "beq %[j],$0,1b\n\t"
+ "nop \n"
+ : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "move %[r],$0\n\t"
+ "ll %[o],0(%[i])\n\t"
+ "bne %[o],%[c],2f\n\t"
+ "move %[r],%[v]\n\t"
+ "sc %[r],0(%[i])\n\t"
+ "beq %[r],$0,1b\n\t"
+ "nop \n\t"
+ "2:\n"
+ : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value)
+ : "cc", "memory");
+
+ return ret;
+ }
+
+#elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (__strex(j, i) == 0)
+ return r;
+ }
+ }
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (__strex(r + j, i) == 0)
+ return r;
+ }
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (r != c)
+ return 0;
+ if (__strex(value, i) == 0)
+ return 1;
+ }
+ }
+
+#elif defined(OVR_CPU_ARM)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "ldrex %[r],[%[i]]\n\t"
+ "strex %[t],%[j],[%[i]]\n\t"
+ "cmp %[t],#0\n\t"
+ "bne 1b\n\t"
+ : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret, dummy, test;
+
+ asm volatile("1:\n\t"
+ "ldrex %[r],[%[i]]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "strex %[t],%[o],[%[i]]\n\t"
+ "cmp %[t],#0\n\t"
+ "bne 1b\n\t"
+ : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret = 1, dummy, test;
+
+ asm volatile("1:\n\t"
+ "ldrex %[o],[%[i]]\n\t"
+ "cmp %[o],%[c]\n\t"
+ "bne 2f\n\t"
+ "strex %[r],%[v],[%[i]]\n\t"
+ "cmp %[r],#0\n\t"
+ "bne 1b\n\t"
+ "2:\n"
+ : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value)
+ : "cc", "memory");
+
+ return !ret;
+ }
+
+#elif defined(OVR_CPU_X86)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ asm volatile("xchgl %1,%[i]\n"
+ : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory");
+
+ return j;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ asm volatile("lock; xaddl %1,%[i]\n"
+ : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory");
+
+ return j;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret;
+
+ asm volatile("lock; cmpxchgl %[v],%[i]\n"
+ : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory");
+
+ return (ret == c);
+ }
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
+
+ typedef UInt32 T;
+
+ static inline T Exchange_NoSync(volatile T *i, T j)
+ {
+ T v;
+ do {
+ v = *i;
+ } while (!__sync_bool_compare_and_swap(i, v, j));
+ return v;
+ }
+
+ static inline T ExchangeAdd_NoSync(volatile T *i, T j)
+ {
+ return __sync_fetch_and_add(i, j);
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
+ {
+ return __sync_bool_compare_and_swap(i, c, value);
+ }
+
+#endif // OS
+};
+
+
+// 8-Byte raw data data atomic op implementation class.
+// Currently implementation is provided only on systems with 64-bit pointers.
+struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase
+{
+#if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS)
+
+ // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
+ typedef UInt64 T;
+
+ // *** Thread - Safe OS specific versions.
+#elif defined(OVR_OS_WIN32)
+
+ // This is only for 64-bit systems.
+ typedef LONG64 T;
+ typedef volatile T* InterlockTPtr;
+ inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); }
+ inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); }
+ inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; }
+
+#elif defined(OVR_CPU_PPC64)
+
+ typedef UInt64 T;
+
+ static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j)
+ {
+ UInt64 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "mr %[o],%[j]\n\t"
+ "stdcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
+
+ return ret;
+ }
+
+ static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j)
+ {
+ UInt64 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "stdcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value)
+ {
+ UInt64 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "cmpw 0,%[r],%[cmp]\n\t"
+ "mfcr %[r]\n\t"
+ "bne- 2f\n\t"
+ "stdcx. %[val],0,%[i]\n\t"
+ "bne- 1b\n\t"
+ "2:\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc");
+
+ return (ret & 0x20000000) ? 1 : 0;
+ }
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
+
+ typedef UInt64 T;
+
+ static inline T Exchange_NoSync(volatile T *i, T j)
+ {
+ T v;
+ do {
+ v = *i;
+ } while (!__sync_bool_compare_and_swap(i, v, j));
+ return v;
+ }
+
+ static inline T ExchangeAdd_NoSync(volatile T *i, T j)
+ {
+ return __sync_fetch_and_add(i, j);
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
+ {
+ return __sync_bool_compare_and_swap(i, c, value);
+ }
+
+#endif // OS
+};
+
+
+// Default implementation for AtomicOpsRaw; provides implementation of mem-fenced
+// atomic operations where fencing is done with a sync object wrapped around a NoSync
+// operation implemented in the base class. If such implementation is not possible
+// on a given platform, #ifdefs can be used to disable it and then op functions can be
+// implemented individually in the appropriate AtomicOpsRaw<size> class.
+
+template<class O>
+struct AtomicOpsRaw_DefImpl : public O
+{
+ typedef typename O::T O_T;
+ typedef typename O::FullSync O_FullSync;
+ typedef typename O::AcquireSync O_AcquireSync;
+ typedef typename O::ReleaseSync O_ReleaseSync;
+
+ // If there is no thread support, provide the default implementation. In this case,
+ // the base class (0) must still provide the T declaration.
+#ifndef OVR_ENABLE_THREADS
+
+ // Atomic exchange of val with argument. Returns old val.
+ inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; }
+ // Adds a new val to argument; returns its old val.
+ inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; }
+ // Compares the argument data with 'c' val.
+ // If succeeded, stores val int '*p' and returns true; otherwise returns false.
+ inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; }
+
+#endif
+
+ // If NoSync wrapped implementation may not be possible, it this block should be
+ // replaced with per-function implementation in O.
+ // "AtomicOpsRaw_DefImpl<O>::" prefix in calls below.
+ inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+ inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+ inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+
+ // Loads and stores with memory fence. These have only the relevant versions.
+#ifdef OVR_CPU_X86
+ // On X86, Store_Release is implemented as exchange. Note that we can also
+ // consider 'sfence' in the future, although it is not as compatible with older CPUs.
+ inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); }
+#else
+ inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; }
+#endif
+ inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; }
+};
+
+
+template<int size>
+struct AtomicOpsRaw : public AtomicOpsRawBase { };
+
+template<>
+struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>
+{
+ // Ensure that assigned type size is correct.
+ AtomicOpsRaw()
+ { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>::T) == 4); }
+};
+template<>
+struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>
+{
+ AtomicOpsRaw()
+ { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>::T) == 8); }
+};
+
+
+// *** AtomicOps - implementation of atomic Ops for specified class
+
+// Implements atomic ops on a class, provided that the object is either
+// 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations
+// available). Relies on AtomicOpsRaw for much of implementation.
+
+template<class C>
+class AtomicOps
+{
+ typedef AtomicOpsRaw<sizeof(C)> Ops;
+ typedef typename Ops::T T;
+ typedef volatile typename Ops::T* PT;
+ // We cast through unions to (1) avoid pointer size compiler warnings
+ // and (2) ensure that there are no problems with strict pointer aliasing.
+ union C2T_union { C c; T t; };
+
+public:
+ // General purpose implementation for standard syncs.
+ inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; }
+ inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; }
+ inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; }
+ inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; }
+ inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); }
+ // Loads and stores with memory fence. These have only the relevant versions.
+ inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); }
+ inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; }
+};
+
+
+
+// Atomic value base class - implements operations shared for integers and pointers.
+template<class T>
+class AtomicValueBase
+{
+protected:
+ typedef AtomicOps<T> Ops;
+public:
+
+ volatile T Value;
+
+ inline AtomicValueBase() { }
+ explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); }
+
+ // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire
+ // here, since most algorithms do not require atomic loads. Needs some research.
+ inline operator T() const { return Value; }
+
+ // *** Standard Atomic inlines
+ inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); }
+ inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); }
+ inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); }
+ inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); }
+ inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); }
+ inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); }
+ inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); }
+ inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); }
+ // Load & Store.
+ inline void Store_Release(T val) { Ops::Store_Release(&Value, val); }
+ inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); }
+};
+
+
+// ***** AtomicPtr - Atomic pointer template
+
+// This pointer class supports atomic assignments with release,
+// increment / decrement operations, and conditional compare + set.
+
+template<class T>
+class AtomicPtr : public AtomicValueBase<T*>
+{
+ typedef typename AtomicValueBase<T*>::Ops Ops;
+
+public:
+ // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor.
+ inline AtomicPtr() : AtomicValueBase<T*>() { this->Value = 0; }
+ explicit inline AtomicPtr(T* val) : AtomicValueBase<T*>(val) { }
+
+ // Pointer access.
+ inline T* operator -> () const { return this->Load_Acquire(); }
+
+ // It looks like it is convenient to have Load_Acquire characteristics
+ // for this, since that is convenient for algorithms such as linked
+ // list traversals that can be added to bu another thread.
+ inline operator T* () const { return this->Load_Acquire(); }
+
+
+ // *** Standard Atomic inlines (applicable to pointers)
+
+ // ExhangeAdd considers pointer size for pointers.
+ template<class I>
+ inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); }
+
+ // *** Atomic Operators
+
+ inline T* operator = (T* val) { this->Store_Release(val); return val; }
+
+ template<class I>
+ inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; }
+ template<class I>
+ inline T* operator -= (I val) { return operator += (-val); }
+
+ inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; }
+ inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; }
+ inline T* operator ++ (int) { return ExchangeAdd_Sync(1); }
+ inline T* operator -- (int) { return ExchangeAdd_Sync(-1); }
+};
+
+
+// ***** AtomicInt - Atomic integer template
+
+// Implements an atomic integer type; the exact type to use is provided
+// as an argument. Supports atomic Acquire / Release semantics, atomic
+// arithmetic operations, and atomic conditional compare + set.
+
+template<class T>
+class AtomicInt : public AtomicValueBase<T>
+{
+ typedef typename AtomicValueBase<T>::Ops Ops;
+
+public:
+ inline AtomicInt() : AtomicValueBase<T>() { }
+ explicit inline AtomicInt(T val) : AtomicValueBase<T>(val) { }
+
+
+ // *** Standard Atomic inlines (applicable to int)
+ inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); }
+ inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); }
+ inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); }
+ inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); }
+ // These increments could be more efficient because they don't return a value.
+ inline void Increment_Sync() { ExchangeAdd_Sync((T)1); }
+ inline void Increment_Release() { ExchangeAdd_Release((T)1); }
+ inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); }
+ inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); }
+
+ // *** Atomic Operators
+
+ inline T operator = (T val) { this->Store_Release(val); return val; }
+ inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; }
+ inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; }
+
+ inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; }
+ inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; }
+ inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); }
+ inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); }
+
+ // More complex atomic operations. Leave it to compiler whether to optimize them or not.
+ T operator &= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp & arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator |= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp | arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator ^= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp ^ arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator *= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp * arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator /= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp / arg;
+ } while(!CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator >>= (unsigned bits)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp >> bits;
+ } while(!CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator <<= (unsigned bits)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp << bits;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Lock
+
+// Lock is a simplest and most efficient mutual-exclusion lock class.
+// Unlike Mutex, it cannot be waited on.
+
+class Lock
+{
+ // NOTE: Locks are not allocatable and they themselves should not allocate
+ // memory by standard means. This is the case because StandardAllocator
+ // relies on this class.
+ // Make 'delete' private. Don't do this for 'new' since it can be redefined.
+ void operator delete(void*) {}
+
+
+ // *** Lock implementation for various platforms.
+
+#if !defined(OVR_ENABLE_THREADS)
+
+public:
+ // With no thread support, lock does nothing.
+ inline Lock() { }
+ inline Lock(unsigned) { }
+ inline ~Lock() { }
+ inline void DoLock() { }
+ inline void Unlock() { }
+
+ // Windows.
+#elif defined(OVR_OS_WIN32)
+
+ CRITICAL_SECTION cs;
+public:
+ Lock(unsigned spinCount = 0);
+ ~Lock();
+ // Locking functions.
+ inline void DoLock() { ::EnterCriticalSection(&cs); }
+ inline void Unlock() { ::LeaveCriticalSection(&cs); }
+
+#else
+ pthread_mutex_t mutex;
+
+public:
+ static pthread_mutexattr_t RecursiveAttr;
+ static bool RecursiveAttrInit;
+
+ Lock (unsigned dummy = 0)
+ {
+ if (!RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&RecursiveAttr);
+ pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ RecursiveAttrInit = 1;
+ }
+ pthread_mutex_init(&mutex,&RecursiveAttr);
+ }
+ ~Lock () { pthread_mutex_destroy(&mutex); }
+ inline void DoLock() { pthread_mutex_lock(&mutex); }
+ inline void Unlock() { pthread_mutex_unlock(&mutex); }
+
+#endif // OVR_ENABLE_THREDS
+
+
+public:
+ // Locker class, used for automatic locking
+ class Locker
+ {
+ public:
+ Lock *pLock;
+ inline Locker(Lock *plock)
+ { pLock = plock; pLock->DoLock(); }
+ inline ~Locker()
+ { pLock->Unlock(); }
+ };
+};
+
+
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Color.h b/LibOVR/Src/Kernel/OVR_Color.h new file mode 100644 index 0000000..7980d57 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Color.h @@ -0,0 +1,55 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Color.h
+Content : Contains color struct.
+Created : February 7, 2013
+Notes :
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_Color_h
+#define OVR_Color_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+struct Color
+{
+ UByte R,G,B,A;
+
+ Color() {}
+
+ // Constructs color by channel. Alpha is set to 0xFF (fully visible)
+ // if not specified.
+ Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF)
+ : R(r), G(g), B(b), A(a) { }
+
+ // 0xAARRGGBB - Common HTML color Hex layout
+ Color(unsigned c)
+ : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)),
+ B((unsigned char)c), A((unsigned char)(c>>24)) { }
+
+ bool operator==(const Color& b) const
+ {
+ return R == b.R && G == b.G && B == b.B && A == b.A;
+ }
+
+ void GetRGBA(float *r, float *g, float *b, float* a) const
+ {
+ *r = R / 255.0f;
+ *g = G / 255.0f;
+ *b = B / 255.0f;
+ *a = A / 255.0f;
+ }
+};
+
+}
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_ContainerAllocator.h b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h new file mode 100644 index 0000000..d2adf49 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h @@ -0,0 +1,256 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_ContainerAllocator.h
+Content : Template allocators and constructors for containers.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_ContainerAllocator_h
+#define OVR_ContainerAllocator_h
+
+#include "OVR_Allocator.h"
+#include <string.h>
+
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Container Allocator
+
+// ContainerAllocator serves as a template argument for allocations done by
+// containers, such as Array and Hash; replacing it could allow allocator
+// substitution in containers.
+
+class ContainerAllocatorBase
+{
+public:
+ static void* Alloc(UPInt size) { return OVR_ALLOC(size); }
+ static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); }
+ static void Free(void *p) { OVR_FREE(p); }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Constructors, Destructors, Copiers
+
+// Plain Old Data - movable, no special constructors/destructor.
+template<class T>
+class ConstructorPOD
+{
+public:
+ static void Construct(void *) {}
+ static void Construct(void *p, const T& source)
+ {
+ *(T*)p = source;
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void *p, const S& source)
+ {
+ *(T*)p = source;
+ }
+
+ static void ConstructArray(void*, UPInt) {}
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ *(T*)pdata = source;
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ memcpy(p, psource, sizeof(T) * count);
+ }
+
+ static void Destruct(T*) {}
+ static void DestructArray(T*, UPInt) {}
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static bool IsMovable() { return true; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ConstructorMov
+//
+// Correct C++ construction and destruction for movable objects
+template<class T>
+class ConstructorMov
+{
+public:
+ static void Construct(void* p)
+ {
+ OVR::Construct<T>(p);
+ }
+
+ static void Construct(void* p, const T& source)
+ {
+ OVR::Construct<T>(p, source);
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void* p, const S& source)
+ {
+ OVR::ConstructAlt<T,S>(p, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, *psource++);
+ }
+
+ static void Destruct(T* p)
+ {
+ p->~T();
+ OVR_UNUSED(p); // Suppress silly MSVC warning
+ }
+
+ static void DestructArray(T* p, UPInt count)
+ {
+ p += count - 1;
+ for (UPInt i=0; i<count; ++i, --p)
+ p->~T();
+ }
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static bool IsMovable() { return true; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ConstructorCPP
+//
+// Correct C++ construction and destruction for movable objects
+template<class T>
+class ConstructorCPP
+{
+public:
+ static void Construct(void* p)
+ {
+ OVR::Construct<T>(p);
+ }
+
+ static void Construct(void* p, const T& source)
+ {
+ OVR::Construct<T>(p, source);
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void* p, const S& source)
+ {
+ OVR::ConstructAlt<T,S>(p, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, *psource++);
+ }
+
+ static void Destruct(T* p)
+ {
+ p->~T();
+ OVR_UNUSED(p); // Suppress silly MSVC warning
+ }
+
+ static void DestructArray(T* p, UPInt count)
+ {
+ p += count - 1;
+ for (UPInt i=0; i<count; ++i, --p)
+ p->~T();
+ }
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ for(UPInt i = 0; i < count; ++i)
+ dst[i] = src[i];
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ for(UPInt i = count; i; --i)
+ dst[i-1] = src[i-1];
+ }
+
+ static bool IsMovable() { return false; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Container Allocator with movement policy
+//
+// Simple wraps as specialized allocators
+template<class T> struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD<T> {};
+template<class T> struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov<T> {};
+template<class T> struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP<T> {};
+
+
+} // OVR
+
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_File.cpp b/LibOVR/Src/Kernel/OVR_File.cpp new file mode 100644 index 0000000..7620e37 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_File.cpp @@ -0,0 +1,571 @@ +/**************************************************************************
+
+Filename : OVR_File.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+
+#include "OVR_File.h"
+
+namespace OVR {
+
+// Buffered file adds buffering to an existing file
+// FILEBUFFER_SIZE defines the size of internal buffer, while
+// FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer
+#define FILEBUFFER_SIZE (8192-8)
+#define FILEBUFFER_TOLERANCE 4096
+
+// ** Constructor/Destructor
+
+// Hidden constructor
+// Not supposed to be used
+BufferedFile::BufferedFile() : DelegatedFile(0)
+{
+ pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
+ BufferMode = NoBuffer;
+ FilePos = 0;
+ Pos = 0;
+ DataSize = 0;
+}
+
+// Takes another file as source
+BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile)
+{
+ pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
+ BufferMode = NoBuffer;
+ FilePos = pfile->LTell();
+ Pos = 0;
+ DataSize = 0;
+}
+
+
+// Destructor
+BufferedFile::~BufferedFile()
+{
+ // Flush in case there's data
+ if (pFile)
+ FlushBuffer();
+ // Get rid of buffer
+ if (pBuffer)
+ OVR_FREE(pBuffer);
+}
+
+/*
+bool BufferedFile::VCopy(const Object &source)
+{
+ if (!DelegatedFile::VCopy(source))
+ return 0;
+
+ // Data members
+ BufferedFile *psource = (BufferedFile*)&source;
+
+ // Buffer & the mode it's in
+ pBuffer = psource->pBuffer;
+ BufferMode = psource->BufferMode;
+ Pos = psource->Pos;
+ DataSize = psource->DataSize;
+ return 1;
+}
+*/
+
+// Initializes buffering to a certain mode
+bool BufferedFile::SetBufferMode(BufferModeType mode)
+{
+ if (!pBuffer)
+ return false;
+ if (mode == BufferMode)
+ return true;
+
+ FlushBuffer();
+
+ // Can't set write mode if we can't write
+ if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) )
+ return 0;
+
+ // And SetMode
+ BufferMode = mode;
+ Pos = 0;
+ DataSize = 0;
+ return 1;
+}
+
+// Flushes buffer
+void BufferedFile::FlushBuffer()
+{
+ switch(BufferMode)
+ {
+ case WriteBuffer:
+ // Write data in buffer
+ FilePos += pFile->Write(pBuffer,Pos);
+ Pos = 0;
+ break;
+
+ case ReadBuffer:
+ // Seek back & reset buffer data
+ if ((DataSize-Pos)>0)
+ FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur);
+ DataSize = 0;
+ Pos = 0;
+ break;
+ default:
+ // not handled!
+ break;
+ }
+}
+
+// Reloads data for ReadBuffer
+void BufferedFile::LoadBuffer()
+{
+ if (BufferMode == ReadBuffer)
+ {
+ // We should only reload once all of pre-loaded buffer is consumed.
+ OVR_ASSERT(Pos == DataSize);
+
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE);
+ DataSize = sz<0 ? 0 : (unsigned)sz;
+ Pos = 0;
+ FilePos += DataSize;
+ }
+}
+
+
+// ** Overridden functions
+
+// We override all the functions that can possibly
+// require buffer mode switch, flush, or extra calculations
+
+// Tell() requires buffer adjustment
+int BufferedFile::Tell()
+{
+ if (BufferMode == ReadBuffer)
+ return int (FilePos - DataSize + Pos);
+
+ int pos = pFile->Tell();
+ // Adjust position based on buffer mode & data
+ if (pos!=-1)
+ {
+ OVR_ASSERT(BufferMode != ReadBuffer);
+ if (BufferMode == WriteBuffer)
+ pos += Pos;
+ }
+ return pos;
+}
+
+SInt64 BufferedFile::LTell()
+{
+ if (BufferMode == ReadBuffer)
+ return FilePos - DataSize + Pos;
+
+ SInt64 pos = pFile->LTell();
+ if (pos!=-1)
+ {
+ OVR_ASSERT(BufferMode != ReadBuffer);
+ if (BufferMode == WriteBuffer)
+ pos += Pos;
+ }
+ return pos;
+}
+
+int BufferedFile::GetLength()
+{
+ int len = pFile->GetLength();
+ // If writing through buffer, file length may actually be bigger
+ if ((len!=-1) && (BufferMode==WriteBuffer))
+ {
+ int currPos = pFile->Tell() + Pos;
+ if (currPos>len)
+ len = currPos;
+ }
+ return len;
+}
+SInt64 BufferedFile::LGetLength()
+{
+ SInt64 len = pFile->LGetLength();
+ // If writing through buffer, file length may actually be bigger
+ if ((len!=-1) && (BufferMode==WriteBuffer))
+ {
+ SInt64 currPos = pFile->LTell() + Pos;
+ if (currPos>len)
+ len = currPos;
+ }
+ return len;
+}
+
+/*
+bool BufferedFile::Stat(FileStats *pfs)
+{
+ // Have to fix up length is stat
+ if (pFile->Stat(pfs))
+ {
+ if (BufferMode==WriteBuffer)
+ {
+ SInt64 currPos = pFile->LTell() + Pos;
+ if (currPos > pfs->Size)
+ {
+ pfs->Size = currPos;
+ // ??
+ pfs->Blocks = (pfs->Size+511) >> 9;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+*/
+
+int BufferedFile::Write(const UByte *psourceBuffer, int numBytes)
+{
+ if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer))
+ {
+ // If not data space in buffer, flush
+ if ((FILEBUFFER_SIZE-(int)Pos)<numBytes)
+ {
+ FlushBuffer();
+ // If bigger then tolerance, just write directly
+ if (numBytes>FILEBUFFER_TOLERANCE)
+ {
+ int sz = pFile->Write(psourceBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+ }
+ }
+
+ // Enough space in buffer.. so copy to it
+ memcpy(pBuffer+Pos, psourceBuffer, numBytes);
+ Pos += numBytes;
+ return numBytes;
+ }
+ int sz = pFile->Write(psourceBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+}
+
+int BufferedFile::Read(UByte *pdestBuffer, int numBytes)
+{
+ if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer))
+ {
+ // Data in buffer... copy it
+ if ((int)(DataSize-Pos) >= numBytes)
+ {
+ memcpy(pdestBuffer, pBuffer+Pos, numBytes);
+ Pos += numBytes;
+ return numBytes;
+ }
+
+ // Not enough data in buffer, copy buffer
+ int readBytes = DataSize-Pos;
+ memcpy(pdestBuffer, pBuffer+Pos, readBytes);
+ numBytes -= readBytes;
+ pdestBuffer += readBytes;
+ Pos = DataSize;
+
+ // Don't reload buffer if more then tolerance
+ // (No major advantage, and we don't want to write a loop)
+ if (numBytes>FILEBUFFER_TOLERANCE)
+ {
+ numBytes = pFile->Read(pdestBuffer,numBytes);
+ if (numBytes > 0)
+ {
+ FilePos += numBytes;
+ Pos = DataSize = 0;
+ }
+ return readBytes + ((numBytes==-1) ? 0 : numBytes);
+ }
+
+ // Reload the buffer
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ LoadBuffer();
+ if ((int)(DataSize-Pos) < numBytes)
+ numBytes = (int)DataSize-Pos;
+
+ memcpy(pdestBuffer, pBuffer+Pos, numBytes);
+ Pos += numBytes;
+ return numBytes + readBytes;
+
+ /*
+ // Alternative Read implementation. The one above is probably better
+ // due to FILEBUFFER_TOLERANCE.
+ int total = 0;
+
+ do {
+ int bufferBytes = (int)(DataSize-Pos);
+ int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes;
+
+ memcpy(pdestBuffer, pBuffer+Pos, copyBytes);
+ numBytes -= copyBytes;
+ pdestBuffer += copyBytes;
+ Pos += copyBytes;
+ total += copyBytes;
+
+ if (numBytes == 0)
+ break;
+ LoadBuffer();
+
+ } while (DataSize > 0);
+
+ return total;
+ */
+ }
+ int sz = pFile->Read(pdestBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+}
+
+
+int BufferedFile::SkipBytes(int numBytes)
+{
+ int skippedBytes = 0;
+
+ // Special case for skipping a little data in read buffer
+ if (BufferMode==ReadBuffer)
+ {
+ skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos);
+ Pos += skippedBytes;
+ numBytes -= skippedBytes;
+ }
+
+ if (numBytes)
+ {
+ numBytes = pFile->SkipBytes(numBytes);
+ // Make sure we return the actual number skipped, or error
+ if (numBytes!=-1)
+ {
+ skippedBytes += numBytes;
+ FilePos += numBytes;
+ Pos = DataSize = 0;
+ }
+ else if (skippedBytes <= 0)
+ skippedBytes = -1;
+ }
+ return skippedBytes;
+}
+
+int BufferedFile::BytesAvailable()
+{
+ int available = pFile->BytesAvailable();
+ // Adjust available size based on buffers
+ switch(BufferMode)
+ {
+ case ReadBuffer:
+ available += DataSize-Pos;
+ break;
+ case WriteBuffer:
+ available -= Pos;
+ if (available<0)
+ available= 0;
+ break;
+ default:
+ break;
+ }
+ return available;
+}
+
+bool BufferedFile::Flush()
+{
+ FlushBuffer();
+ return pFile->Flush();
+}
+
+// Seeking could be optimized better..
+int BufferedFile::Seek(int offset, int origin)
+{
+ if (BufferMode == ReadBuffer)
+ {
+ if (origin == Seek_Cur)
+ {
+ // Seek can fall either before or after Pos in the buffer,
+ // but it must be within bounds.
+ if (((unsigned(offset) + Pos)) <= DataSize)
+ {
+ Pos += offset;
+ return int (FilePos - DataSize + Pos);
+ }
+
+ // Lightweight buffer "Flush". We do this to avoid an extra seek
+ // back operation which would take place if we called FlushBuffer directly.
+ origin = Seek_Set;
+ OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0);
+ offset = (int)(FilePos - DataSize + Pos) + offset;
+ Pos = DataSize = 0;
+ }
+ else if (origin == Seek_Set)
+ {
+ if (((unsigned)offset - (FilePos-DataSize)) <= DataSize)
+ {
+ OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0);
+ Pos = (unsigned)offset - (unsigned)(FilePos-DataSize);
+ return offset;
+ }
+ Pos = DataSize = 0;
+ }
+ else
+ {
+ FlushBuffer();
+ }
+ }
+ else
+ {
+ FlushBuffer();
+ }
+
+ /*
+ // Old Seek Logic
+ if (origin == Seek_Cur && offset + Pos < DataSize)
+ {
+ //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset));
+ Pos += offset;
+ OVR_ASSERT(int (Pos) >= 0);
+ return int (FilePos - DataSize + Pos);
+ }
+ else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos)
+ {
+ Pos = unsigned(offset - FilePos + DataSize);
+ OVR_ASSERT(int (Pos) >= 0);
+ return int (FilePos - DataSize + Pos);
+ }
+
+ FlushBuffer();
+ */
+
+
+ FilePos = pFile->Seek(offset,origin);
+ return int (FilePos);
+}
+
+SInt64 BufferedFile::LSeek(SInt64 offset, int origin)
+{
+ if (BufferMode == ReadBuffer)
+ {
+ if (origin == Seek_Cur)
+ {
+ // Seek can fall either before or after Pos in the buffer,
+ // but it must be within bounds.
+ if (((unsigned(offset) + Pos)) <= DataSize)
+ {
+ Pos += (unsigned)offset;
+ return SInt64(FilePos - DataSize + Pos);
+ }
+
+ // Lightweight buffer "Flush". We do this to avoid an extra seek
+ // back operation which would take place if we called FlushBuffer directly.
+ origin = Seek_Set;
+ offset = (SInt64)(FilePos - DataSize + Pos) + offset;
+ Pos = DataSize = 0;
+ }
+ else if (origin == Seek_Set)
+ {
+ if (((UInt64)offset - (FilePos-DataSize)) <= DataSize)
+ {
+ Pos = (unsigned)((UInt64)offset - (FilePos-DataSize));
+ return offset;
+ }
+ Pos = DataSize = 0;
+ }
+ else
+ {
+ FlushBuffer();
+ }
+ }
+ else
+ {
+ FlushBuffer();
+ }
+
+/*
+ OVR_ASSERT(BufferMode != NoBuffer);
+
+ if (origin == Seek_Cur && offset + Pos < DataSize)
+ {
+ Pos += int (offset);
+ return FilePos - DataSize + Pos;
+ }
+ else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos))
+ {
+ Pos = unsigned(offset - FilePos + DataSize);
+ return FilePos - DataSize + Pos;
+ }
+
+ FlushBuffer();
+ */
+
+ FilePos = pFile->LSeek(offset,origin);
+ return FilePos;
+}
+
+int BufferedFile::CopyFromStream(File *pstream, int byteSize)
+{
+ // We can't rely on overridden Write()
+ // because delegation doesn't override virtual pointers
+ // So, just re-implement
+ UByte buff[0x4000];
+ int count = 0;
+ int szRequest, szRead, szWritten;
+
+ while(byteSize)
+ {
+ szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize;
+
+ szRead = pstream->Read(buff,szRequest);
+ szWritten = 0;
+ if (szRead > 0)
+ szWritten = Write(buff,szRead);
+
+ count +=szWritten;
+ byteSize-=szWritten;
+ if (szWritten < szRequest)
+ break;
+ }
+ return count;
+}
+
+// Closing files
+bool BufferedFile::Close()
+{
+ switch(BufferMode)
+ {
+ case WriteBuffer:
+ FlushBuffer();
+ break;
+ case ReadBuffer:
+ // No need to seek back on close
+ BufferMode = NoBuffer;
+ break;
+ default:
+ break;
+ }
+ return pFile->Close();
+}
+
+
+// ***** Global path helpers
+
+// Find trailing short filename in a path.
+const char* OVR_CDECL GetShortFilename(const char* purl)
+{
+ UPInt len = OVR_strlen(purl);
+ for (UPInt i=len; i>0; i--)
+ if (purl[i]=='\\' || purl[i]=='/')
+ return purl+i+1;
+ return purl;
+}
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_File.h b/LibOVR/Src/Kernel/OVR_File.h new file mode 100644 index 0000000..262f5cf --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_File.h @@ -0,0 +1,518 @@ +/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_File.h
+Content : Header for all internal file management - functions and structures
+ to be inherited by OS specific subclasses.
+Created : September 19, 2012
+Notes :
+
+Notes : errno may not be preserved across use of BaseFile member functions
+ : Directories cannot be deleted while files opened from them are in use
+ (For the GetFullName function)
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_File_h
+#define OVR_File_h
+
+#include "OVR_RefCount.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+#include <stdio.h>
+#include "OVR_String.h"
+
+namespace OVR {
+
+// ***** Declared classes
+class FileConstants;
+class File;
+class DelegatedFile;
+class BufferedFile;
+
+
+// ***** Flags for File & Directory accesses
+
+class FileConstants
+{
+public:
+
+ // *** File open flags
+ enum OpenFlags
+ {
+ Open_Read = 1,
+ Open_Write = 2,
+ Open_ReadWrite = 3,
+
+ // Opens file and truncates it to zero length
+ // - file must have write permission
+ // - when used with Create, it opens an existing
+ // file and empties it or creates a new file
+ Open_Truncate = 4,
+
+ // Creates and opens new file
+ // - does not erase contents if file already
+ // exists unless combined with Truncate
+ Open_Create = 8,
+
+ // Returns an error value if the file already exists
+ Open_CreateOnly = 24,
+
+ // Open file with buffering
+ Open_Buffered = 32
+ };
+
+ // *** File Mode flags
+ enum Modes
+ {
+ Mode_Read = 0444,
+ Mode_Write = 0222,
+ Mode_Execute = 0111,
+
+ Mode_ReadWrite = 0666
+ };
+
+ // *** Seek operations
+ enum SeekOps
+ {
+ Seek_Set = 0,
+ Seek_Cur = 1,
+ Seek_End = 2
+ };
+
+ // *** Errors
+ enum Errors
+ {
+ Error_FileNotFound = 0x1001,
+ Error_Access = 0x1002,
+ Error_IOError = 0x1003,
+ Error_DiskFull = 0x1004
+ };
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** File Class
+
+// The pure virtual base random-access file
+// This is a base class to all files
+
+class File : public RefCountBase<File>, public FileConstants
+{
+public:
+ File() { }
+ // ** Location Information
+
+ // Returns a file name path relative to the 'reference' directory
+ // This is often a path that was used to create a file
+ // (this is not a global path, global path can be obtained with help of directory)
+ virtual const char* GetFilePath() = 0;
+
+
+ // ** File Information
+
+ // Return 1 if file's usable (open)
+ virtual bool IsValid() = 0;
+ // Return 1 if file's writable, otherwise 0
+ virtual bool IsWritable() = 0;
+
+ // Return position
+ virtual int Tell() = 0;
+ virtual SInt64 LTell() = 0;
+
+ // File size
+ virtual int GetLength() = 0;
+ virtual SInt64 LGetLength() = 0;
+
+ // Returns file stats
+ // 0 for failure
+ //virtual bool Stat(FileStats *pfs) = 0;
+
+ // Return errno-based error code
+ // Useful if any other function failed
+ virtual int GetErrorCode() = 0;
+
+
+ // ** Stream implementation & I/O
+
+ // Blocking write, will write in the given number of bytes to the stream
+ // Returns : -1 for error
+ // Otherwise number of bytes read
+ virtual int Write(const UByte *pbufer, int numBytes) = 0;
+ // Blocking read, will read in the given number of bytes or less from the stream
+ // Returns : -1 for error
+ // Otherwise number of bytes read,
+ // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed
+ virtual int Read(UByte *pbufer, int numBytes) = 0;
+
+ // Skips (ignores) a given # of bytes
+ // Same return values as Read
+ virtual int SkipBytes(int numBytes) = 0;
+
+ // Returns the number of bytes available to read from a stream without blocking
+ // For a file, this should generally be number of bytes to the end
+ virtual int BytesAvailable() = 0;
+
+ // Causes any implementation's buffered data to be delivered to destination
+ // Return 0 for error
+ virtual bool Flush() = 0;
+
+
+ // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking
+ inline bool IsEOF() { return !BytesAvailable(); }
+
+
+ // Seeking
+ // Returns new position, -1 for error
+ virtual int Seek(int offset, int origin=Seek_Set) = 0;
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0;
+ // Seek simplification
+ int SeekToBegin() {return Seek(0); }
+ int SeekToEnd() {return Seek(0,Seek_End); }
+ int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); }
+
+
+ // Appends other file data from a stream
+ // Return -1 for error, else # of bytes written
+ virtual int CopyFromStream(File *pstream, int byteSize) = 0;
+
+ // Closes the file
+ // After close, file cannot be accessed
+ virtual bool Close() = 0;
+
+
+ // ***** Inlines for convenient primitive type serialization
+
+ // Read/Write helpers
+private:
+ UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; }
+ UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; }
+ UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; }
+ UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; }
+ void PWrite64(UInt64 v) { Write((UByte*)&v, 8); }
+ void PWrite32(UInt32 v) { Write((UByte*)&v, 4); }
+ void PWrite16(UInt16 v) { Write((UByte*)&v, 2); }
+ void PWrite8(UByte v) { Write((UByte*)&v, 1); }
+
+public:
+
+ // Writing primitive types - Little Endian
+ inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); }
+ inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); }
+ // Writing primitive types - Big Endian
+ inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); }
+ inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); }
+
+ // Reading primitive types - Little Endian
+ inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); }
+ inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); }
+ inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); }
+ inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); }
+ inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); }
+ inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); }
+ inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); }
+ inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); }
+ // Reading primitive types - Big Endian
+ inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); }
+ inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); }
+ inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); }
+ inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); }
+ inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); }
+ inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); }
+ inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); }
+ inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); }
+};
+
+
+// *** Delegated File
+
+class DelegatedFile : public File
+{
+protected:
+ // Delegating file pointer
+ Ptr<File> pFile;
+
+ // Hidden default constructor
+ DelegatedFile() : pFile(0) { }
+ DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); }
+public:
+ // Constructors
+ DelegatedFile(File *pfile) : pFile(pfile) { }
+
+ // ** Location Information
+ virtual const char* GetFilePath() { return pFile->GetFilePath(); }
+
+ // ** File Information
+ virtual bool IsValid() { return pFile && pFile->IsValid(); }
+ virtual bool IsWritable() { return pFile->IsWritable(); }
+// virtual bool IsRecoverable() { return pFile->IsRecoverable(); }
+
+ virtual int Tell() { return pFile->Tell(); }
+ virtual SInt64 LTell() { return pFile->LTell(); }
+
+ virtual int GetLength() { return pFile->GetLength(); }
+ virtual SInt64 LGetLength() { return pFile->LGetLength(); }
+
+ //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); }
+
+ virtual int GetErrorCode() { return pFile->GetErrorCode(); }
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); }
+ virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); }
+
+ virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); }
+
+ virtual int BytesAvailable() { return pFile->BytesAvailable(); }
+
+ virtual bool Flush() { return pFile->Flush(); }
+
+ // Seeking
+ virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); }
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); }
+
+ virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); }
+
+ // Closing the file
+ virtual bool Close() { return pFile->Close(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Buffered File
+
+// This file class adds buffering to an existing file
+// Buffered file never fails by itself; if there's not
+// enough memory for buffer, no buffer's used
+
+class BufferedFile : public DelegatedFile
+{
+protected:
+ enum BufferModeType
+ {
+ NoBuffer,
+ ReadBuffer,
+ WriteBuffer
+ };
+
+ // Buffer & the mode it's in
+ UByte* pBuffer;
+ BufferModeType BufferMode;
+ // Position in buffer
+ unsigned Pos;
+ // Data in buffer if reading
+ unsigned DataSize;
+ // Underlying file position
+ UInt64 FilePos;
+
+ // Initializes buffering to a certain mode
+ bool SetBufferMode(BufferModeType mode);
+ // Flushes buffer
+ // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position
+ void FlushBuffer();
+ // Loads data into ReadBuffer
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ void LoadBuffer();
+
+ // Hidden constructor
+ BufferedFile();
+ inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); }
+public:
+
+ // Constructor
+ // - takes another file as source
+ BufferedFile(File *pfile);
+ ~BufferedFile();
+
+
+ // ** Overridden functions
+
+ // We override all the functions that can possibly
+ // require buffer mode switch, flush, or extra calculations
+ virtual int Tell();
+ virtual SInt64 LTell();
+
+ virtual int GetLength();
+ virtual SInt64 LGetLength();
+
+// virtual bool Stat(GFileStats *pfs);
+
+ virtual int Write(const UByte *pbufer, int numBytes);
+ virtual int Read(UByte *pbufer, int numBytes);
+
+ virtual int SkipBytes(int numBytes);
+
+ virtual int BytesAvailable();
+
+ virtual bool Flush();
+
+ virtual int Seek(int offset, int origin=Seek_Set);
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set);
+
+ virtual int CopyFromStream(File *pstream, int byteSize);
+
+ virtual bool Close();
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Memory File
+
+class MemoryFile : public File
+{
+public:
+
+ const char* GetFilePath() { return FilePath.ToCStr(); }
+
+ bool IsValid() { return Valid; }
+ bool IsWritable() { return false; }
+
+ bool Flush() { return true; }
+ int GetErrorCode() { return 0; }
+
+ int Tell() { return FileIndex; }
+ SInt64 LTell() { return (SInt64) FileIndex; }
+
+ int GetLength() { return FileSize; }
+ SInt64 LGetLength() { return (SInt64) FileSize; }
+
+ bool Close()
+ {
+ Valid = false;
+ return false;
+ }
+
+ int CopyFromStream(File *pstream, int byteSize)
+ { OVR_UNUSED2(pstream, byteSize);
+ return 0;
+ }
+
+ int Write(const UByte *pbuffer, int numBytes)
+ { OVR_UNUSED2(pbuffer, numBytes);
+ return 0;
+ }
+
+ int Read(UByte *pbufer, int numBytes)
+ {
+ if (FileIndex + numBytes > FileSize)
+ {
+ numBytes = FileSize - FileIndex;
+ }
+
+ if (numBytes > 0)
+ {
+ ::memcpy (pbufer, &FileData [FileIndex], numBytes);
+
+ FileIndex += numBytes;
+ }
+
+ return numBytes;
+ }
+
+ int SkipBytes(int numBytes)
+ {
+ if (FileIndex + numBytes > FileSize)
+ {
+ numBytes = FileSize - FileIndex;
+ }
+
+ FileIndex += numBytes;
+
+ return numBytes;
+ }
+
+ int BytesAvailable()
+ {
+ return (FileSize - FileIndex);
+ }
+
+ int Seek(int offset, int origin = Seek_Set)
+ {
+ switch (origin)
+ {
+ case Seek_Set : FileIndex = offset; break;
+ case Seek_Cur : FileIndex += offset; break;
+ case Seek_End : FileIndex = FileSize - offset; break;
+ }
+
+ return FileIndex;
+ }
+
+ SInt64 LSeek(SInt64 offset, int origin = Seek_Set)
+ {
+ return (SInt64) Seek((int) offset, origin);
+ }
+
+public:
+
+ MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize)
+ : FilePath(fileName)
+ {
+ FileData = pBuffer;
+ FileSize = buffSize;
+ FileIndex = 0;
+ Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false;
+ }
+
+ // pfileName should be encoded as UTF-8 to support international file names.
+ MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize)
+ : FilePath(pfileName)
+ {
+ FileData = pBuffer;
+ FileSize = buffSize;
+ FileIndex = 0;
+ Valid = (pfileName && pBuffer && buffSize > 0) ? true : false;
+ }
+private:
+
+ String FilePath;
+ const UByte *FileData;
+ int FileSize;
+ int FileIndex;
+ bool Valid;
+};
+
+
+// ***** Global path helpers
+
+// Find trailing short filename in a path.
+const char* OVR_CDECL GetShortFilename(const char* purl);
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_FileFILE.cpp b/LibOVR/Src/Kernel/OVR_FileFILE.cpp new file mode 100644 index 0000000..4ee32b6 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_FileFILE.cpp @@ -0,0 +1,583 @@ +/**************************************************************************
+
+Filename : OVR_FileFILE.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+#include "OVR_Types.h"
+#include "OVR_Log.h"
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+#ifndef OVR_OS_WINCE
+#include <sys/stat.h>
+#endif
+
+#include "OVR_SysFile.h"
+
+#ifndef OVR_OS_WINCE
+#include <errno.h>
+#endif
+
+namespace OVR {
+
+// ***** File interface
+
+// ***** FILEFile - C streams file
+
+static int SFerror ()
+{
+ if (errno == ENOENT)
+ return FileConstants::Error_FileNotFound;
+ else if (errno == EACCES || errno == EPERM)
+ return FileConstants::Error_Access;
+ else if (errno == ENOSPC)
+ return FileConstants::Error_DiskFull;
+ else
+ return FileConstants::Error_IOError;
+};
+
+#ifdef OVR_OS_WIN32
+#include "windows.h"
+// A simple helper class to disable/enable system error mode, if necessary
+// Disabling happens conditionally only if a drive name is involved
+class SysErrorModeDisabler
+{
+ BOOL Disabled;
+ UINT OldMode;
+public:
+ SysErrorModeDisabler(const char* pfileName)
+ {
+ if (pfileName && (pfileName[0]!=0) && pfileName[1]==':')
+ {
+ Disabled = 1;
+ OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS);
+ }
+ else
+ Disabled = 0;
+ }
+
+ ~SysErrorModeDisabler()
+ {
+ if (Disabled) ::SetErrorMode(OldMode);
+ }
+};
+#else
+class SysErrorModeDisabler
+{
+public:
+ SysErrorModeDisabler(const char* pfileName) { }
+};
+#endif // OVR_OS_WIN32
+
+
+// This macro enables verification of I/O results after seeks against a pre-loaded
+// full file buffer copy. This is generally not necessary, but can been used to debug
+// memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory
+// under FMOD with XP64 (32-bit) and Realtek HA Audio driver.
+//#define GFILE_VERIFY_SEEK_ERRORS
+
+
+// This is the simplest possible file implementation, it wraps around the descriptor
+// This file is delegated to by SysFile.
+
+class FILEFile : public File
+{
+protected:
+
+ // Allocated filename
+ String FileName;
+
+ // File handle & open mode
+ bool Opened;
+ FILE* fs;
+ int OpenFlags;
+ // Error code for last request
+ int ErrorCode;
+
+ int LastOp;
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ UByte* pFileTestBuffer;
+ unsigned FileTestLength;
+ unsigned TestPos; // File pointer position during tests.
+#endif
+
+public:
+
+ FILEFile()
+ {
+ Opened = 0; FileName = "";
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ pFileTestBuffer =0;
+ FileTestLength =0;
+ TestPos =0;
+#endif
+ }
+ // Initialize file by opening it
+ FILEFile(const String& fileName, int flags, int Mode);
+ // The 'pfileName' should be encoded as UTF-8 to support international file names.
+ FILEFile(const char* pfileName, int flags, int Mode);
+
+ ~FILEFile()
+ {
+ if (Opened)
+ Close();
+ }
+
+ virtual const char* GetFilePath();
+
+ // ** File Information
+ virtual bool IsValid();
+ virtual bool IsWritable();
+
+ // Return position / file size
+ virtual int Tell();
+ virtual SInt64 LTell();
+ virtual int GetLength();
+ virtual SInt64 LGetLength();
+
+// virtual bool Stat(FileStats *pfs);
+ virtual int GetErrorCode();
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes);
+ virtual int Read(UByte *pbuffer, int numBytes);
+ virtual int SkipBytes(int numBytes);
+ virtual int BytesAvailable();
+ virtual bool Flush();
+ virtual int Seek(int offset, int origin);
+ virtual SInt64 LSeek(SInt64 offset, int origin);
+
+ virtual int CopyFromStream(File *pStream, int byteSize);
+ virtual bool Close();
+private:
+ void init();
+};
+
+
+// Initialize file by opening it
+FILEFile::FILEFile(const String& fileName, int flags, int mode)
+ : FileName(fileName), OpenFlags(flags)
+{
+ OVR_UNUSED(mode);
+ init();
+}
+
+// The 'pfileName' should be encoded as UTF-8 to support international file names.
+FILEFile::FILEFile(const char* pfileName, int flags, int mode)
+ : FileName(pfileName), OpenFlags(flags)
+{
+ OVR_UNUSED(mode);
+ init();
+}
+
+void FILEFile::init()
+{
+ // Open mode for file's open
+ const char *omode = "rb";
+
+ if (OpenFlags & Open_Truncate)
+ {
+ if (OpenFlags & Open_Read)
+ omode = "w+b";
+ else
+ omode = "wb";
+ }
+ else if (OpenFlags & Open_Create)
+ {
+ if (OpenFlags & Open_Read)
+ omode = "a+b";
+ else
+ omode = "ab";
+ }
+ else if (OpenFlags & Open_Write)
+ omode = "r+b";
+
+#ifdef OVR_OS_WIN32
+ SysErrorModeDisabler disabler(FileName.ToCStr());
+#endif
+
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ wchar_t womode[16];
+ wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t));
+ UTF8Util::DecodeString(pwFileName, FileName.ToCStr());
+ OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0]));
+ UTF8Util::DecodeString(womode, omode);
+ _wfopen_s(&fs, pwFileName, womode);
+ OVR_FREE(pwFileName);
+#else
+ fs = fopen(FileName.ToCStr(), omode);
+#endif
+ if (fs)
+ rewind (fs);
+ Opened = (fs != NULL);
+ // Set error code
+ if (!Opened)
+ ErrorCode = SFerror();
+ else
+ {
+ // If we are testing file seek correctness, pre-load the entire file so
+ // that we can do comparison tests later.
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ TestPos = 0;
+ fseek(fs, 0, SEEK_END);
+ FileTestLength = ftell(fs);
+ fseek(fs, 0, SEEK_SET);
+ pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength);
+ if (pFileTestBuffer)
+ {
+ OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength));
+ Seek(0, Seek_Set);
+ }
+#endif
+
+ ErrorCode = 0;
+ }
+ LastOp = 0;
+}
+
+
+const char* FILEFile::GetFilePath()
+{
+ return FileName.ToCStr();
+}
+
+
+// ** File Information
+bool FILEFile::IsValid()
+{
+ return Opened;
+}
+bool FILEFile::IsWritable()
+{
+ return IsValid() && (OpenFlags&Open_Write);
+}
+/*
+bool FILEFile::IsRecoverable()
+{
+ return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC);
+}
+*/
+
+// Return position / file size
+int FILEFile::Tell()
+{
+ int pos = (int)ftell (fs);
+ if (pos < 0)
+ ErrorCode = SFerror();
+ return pos;
+}
+
+SInt64 FILEFile::LTell()
+{
+ SInt64 pos = ftell(fs);
+ if (pos < 0)
+ ErrorCode = SFerror();
+ return pos;
+}
+
+int FILEFile::GetLength()
+{
+ int pos = Tell();
+ if (pos >= 0)
+ {
+ Seek (0, Seek_End);
+ int size = Tell();
+ Seek (pos, Seek_Set);
+ return size;
+ }
+ return -1;
+}
+SInt64 FILEFile::LGetLength()
+{
+ SInt64 pos = LTell();
+ if (pos >= 0)
+ {
+ LSeek (0, Seek_End);
+ SInt64 size = LTell();
+ LSeek (pos, Seek_Set);
+ return size;
+ }
+ return -1;
+}
+
+int FILEFile::GetErrorCode()
+{
+ return ErrorCode;
+}
+
+// ** Stream implementation & I/O
+int FILEFile::Write(const UByte *pbuffer, int numBytes)
+{
+ if (LastOp && LastOp != Open_Write)
+ fflush(fs);
+ LastOp = Open_Write;
+ int written = (int) fwrite(pbuffer, 1, numBytes, fs);
+ if (written < numBytes)
+ ErrorCode = SFerror();
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (written > 0)
+ TestPos += written;
+#endif
+
+ return written;
+}
+
+int FILEFile::Read(UByte *pbuffer, int numBytes)
+{
+ if (LastOp && LastOp != Open_Read)
+ fflush(fs);
+ LastOp = Open_Read;
+ int read = (int) fread(pbuffer, 1, numBytes, fs);
+ if (read < numBytes)
+ ErrorCode = SFerror();
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (read > 0)
+ {
+ // Read-in data must match our pre-loaded buffer data!
+ UByte* pcompareBuffer = pFileTestBuffer + TestPos;
+ for (int i=0; i< read; i++)
+ {
+ OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]);
+ }
+
+ //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read));
+ TestPos += read;
+ OVR_ASSERT(ftell(fs) == (int)TestPos);
+ }
+#endif
+
+ return read;
+}
+
+// Seeks ahead to skip bytes
+int FILEFile::SkipBytes(int numBytes)
+{
+ SInt64 pos = LTell();
+ SInt64 newPos = LSeek(numBytes, Seek_Cur);
+
+ // Return -1 for major error
+ if ((pos==-1) || (newPos==-1))
+ {
+ return -1;
+ }
+ //ErrorCode = ((NewPos-Pos)<numBytes) ? errno : 0;
+
+ return int (newPos-(int)pos);
+}
+
+// Return # of bytes till EOF
+int FILEFile::BytesAvailable()
+{
+ SInt64 pos = LTell();
+ SInt64 endPos = LGetLength();
+
+ // Return -1 for major error
+ if ((pos==-1) || (endPos==-1))
+ {
+ ErrorCode = SFerror();
+ return 0;
+ }
+ else
+ ErrorCode = 0;
+
+ return int (endPos-(int)pos);
+}
+
+// Flush file contents
+bool FILEFile::Flush()
+{
+ return !fflush(fs);
+}
+
+int FILEFile::Seek(int offset, int origin)
+{
+ int newOrigin = 0;
+ switch(origin)
+ {
+ case Seek_Set: newOrigin = SEEK_SET; break;
+ case Seek_Cur: newOrigin = SEEK_CUR; break;
+ case Seek_End: newOrigin = SEEK_END; break;
+ }
+
+ if (newOrigin == SEEK_SET && offset == Tell())
+ return Tell();
+
+ if (fseek (fs, offset, newOrigin))
+ {
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ OVR_ASSERT(0);
+#endif
+ return -1;
+ }
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ // Track file position after seeks for read verification later.
+ switch(origin)
+ {
+ case Seek_Set: TestPos = offset; break;
+ case Seek_Cur: TestPos += offset; break;
+ case Seek_End: TestPos = FileTestLength + offset; break;
+ }
+ OVR_ASSERT((int)TestPos == Tell());
+#endif
+
+ return (int)Tell();
+}
+
+SInt64 FILEFile::LSeek(SInt64 offset, int origin)
+{
+ return Seek((int)offset,origin);
+}
+
+int FILEFile::CopyFromStream(File *pstream, int byteSize)
+{
+ UByte buff[0x4000];
+ int count = 0;
+ int szRequest, szRead, szWritten;
+
+ while (byteSize)
+ {
+ szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize;
+
+ szRead = pstream->Read(buff, szRequest);
+ szWritten = 0;
+ if (szRead > 0)
+ szWritten = Write(buff, szRead);
+
+ count += szWritten;
+ byteSize -= szWritten;
+ if (szWritten < szRequest)
+ break;
+ }
+ return count;
+}
+
+
+bool FILEFile::Close()
+{
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (pFileTestBuffer)
+ {
+ OVR_FREE(pFileTestBuffer);
+ pFileTestBuffer = 0;
+ FileTestLength = 0;
+ }
+#endif
+
+ bool closeRet = !fclose(fs);
+
+ if (!closeRet)
+ {
+ ErrorCode = SFerror();
+ return 0;
+ }
+ else
+ {
+ Opened = 0;
+ fs = 0;
+ ErrorCode = 0;
+ }
+
+ // Handle safe truncate
+ /*
+ if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC)
+ {
+ // Delete original file (if it existed)
+ DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName);
+ if (oldAttributes!=0xFFFFFFFF)
+ if (!FileUtilWin32::DeleteFile(FileName))
+ {
+ // Try to remove the readonly attribute
+ FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) );
+ // And delete the file again
+ if (!FileUtilWin32::DeleteFile(FileName))
+ return 0;
+ }
+
+ // Rename temp file to real filename
+ if (!FileUtilWin32::MoveFile(TempName, FileName))
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ }
+ */
+ return 1;
+}
+
+/*
+bool FILEFile::CloseCancel()
+{
+ bool closeRet = (bool)::CloseHandle(fd);
+
+ if (!closeRet)
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ else
+ {
+ Opened = 0;
+ fd = INVALID_HANDLE_VALUE;
+ ErrorCode = 0;
+ }
+
+ // Handle safe truncate (delete tmp file, leave original unchanged)
+ if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC)
+ if (!FileUtilWin32::DeleteFile(TempName))
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ return 1;
+}
+*/
+
+File *FileFILEOpen(const String& path, int flags, int mode)
+{
+ return new FILEFile(path, flags, mode);
+}
+
+// Helper function: obtain file information time.
+bool SysFile::GetFileStat(FileStat* pfileStat, const String& path)
+{
+#if defined(OVR_OS_WIN32)
+ // 64-bit implementation on Windows.
+ struct __stat64 fileStat;
+ // Stat returns 0 for success.
+ wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t));
+ UTF8Util::DecodeString(pwpath, path.ToCStr());
+
+ int ret = _wstat64(pwpath, &fileStat);
+ OVR_FREE(pwpath);
+ if (ret) return false;
+#else
+ struct stat fileStat;
+ // Stat returns 0 for success.
+ if (stat(path, &fileStat) != 0)
+ return false;
+#endif
+ pfileStat->AccessTime = fileStat.st_atime;
+ pfileStat->ModifyTime = fileStat.st_mtime;
+ pfileStat->FileSize = fileStat.st_size;
+ return true;
+}
+
+} // Scaleform
diff --git a/LibOVR/Src/Kernel/OVR_Hash.h b/LibOVR/Src/Kernel/OVR_Hash.h new file mode 100644 index 0000000..6667ac2 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Hash.h @@ -0,0 +1,1291 @@ +/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Hash.h
+Content : Template hash-table/set implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Hash_h
+#define OVR_Hash_h
+
+#include "OVR_ContainerAllocator.h"
+#include "OVR_Alg.h"
+
+// 'new' operator is redefined/used in this file.
+#undef new
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Hash Table Implementation
+
+// HastSet and Hash.
+//
+// Hash table, linear probing, internal chaining. One interesting/nice thing
+// about this implementation is that the table itself is a flat chunk of memory
+// containing no pointers, only relative indices. If the key and value types
+// of the Hash contain no pointers, then the Hash can be serialized using raw IO.
+//
+// Never shrinks, unless you explicitly Clear() it. Expands on
+// demand, though. For best results, if you know roughly how big your
+// table will be, default it to that size when you create it.
+//
+// Key usability feature:
+//
+// 1. Allows node hash values to either be cached or not.
+//
+// 2. Allows for alternative keys with methods such as GetAlt(). Handy
+// if you need to search nodes by their components; no need to create
+// temporary nodes.
+//
+
+
+// *** Hash functors:
+//
+// IdentityHash - use when the key is already a good hash
+// HFixedSizeHash - general hash based on object's in-memory representation.
+
+
+// Hash is just the input value; can use this for integer-indexed hash tables.
+template<class C>
+class IdentityHash
+{
+public:
+ UPInt operator()(const C& data) const
+ { return (UPInt) data; }
+};
+
+// Computes a hash of an object's representation.
+template<class C>
+class FixedSizeHash
+{
+public:
+ // Alternative: "sdbm" hash function, suggested at same web page
+ // above, http::/www.cs.yorku.ca/~oz/hash.html
+ // This is somewhat slower then Bernstein, but it works way better than the above
+ // hash function for hashing large numbers of 32-bit ints.
+ static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381)
+ {
+ const UByte* data = (const UByte*) data_in;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = (h << 16) + (h << 6) - h + (UPInt)data[size];
+ }
+ return h;
+ }
+
+ UPInt operator()(const C& data) const
+ {
+ unsigned char* p = (unsigned char*) &data;
+ int size = sizeof(C);
+
+ return SDBM_Hash(p, size);
+ }
+};
+
+
+
+// *** HashsetEntry Entry types.
+
+// Compact hash table Entry type that re-computes hash keys during hash traversal.
+// Good to use if the hash function is cheap or the hash value is already cached in C.
+template<class C, class HashF>
+class HashsetEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ C Value;
+
+ HashsetEntry()
+ : NextInChain(-2) { }
+ HashsetEntry(const HashsetEntry& e)
+ : NextInChain(e.NextInChain), Value(e.Value) { }
+ HashsetEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+
+ // Cached hash value access - can be optimized bu storing hash locally.
+ // Mask value only needs to be used if SetCachedHash is not implemented.
+ UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; }
+ void SetCachedHash(UPInt) {}
+
+ void Clear()
+ {
+ Value.~C(); // placement delete
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+// Hash table Entry type that caches the Entry hash value for nodes, so that it
+// does not need to be re-computed during access.
+template<class C, class HashF>
+class HashsetCachedEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ UPInt HashValue;
+ C Value;
+
+ HashsetCachedEntry()
+ : NextInChain(-2) { }
+ HashsetCachedEntry(const HashsetCachedEntry& e)
+ : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
+ HashsetCachedEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+
+ // Cached hash value access - can be optimized bu storing hash locally.
+ // Mask value only needs to be used if SetCachedHash is not implemented.
+ UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
+ void SetCachedHash(UPInt hashValue) { HashValue = hashValue; }
+
+ void Clear()
+ {
+ Value.~C();
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// *** HashSet implementation - relies on either cached or regular entries.
+//
+// Use: Entry = HashsetCachedEntry<C, HashF> if hashes are expensive to
+// compute and thus need caching in entries.
+// Entry = HashsetEntry<C, HashF> if hashes are already externally cached.
+//
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C>,
+ class Entry = HashsetCachedEntry<C, HashF> >
+class HashSetBase
+{
+ enum { HashMinSize = 8 };
+
+public:
+ OVR_MEMORY_REDEFINE_NEW(HashSetBase)
+
+ typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> SelfType;
+
+ HashSetBase() : pTable(NULL) { }
+ HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); }
+ HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); }
+
+ ~HashSetBase()
+ {
+ if (pTable)
+ {
+ // Delete the entries.
+ for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (!e->IsEmpty())
+ e->Free();
+ }
+
+ Allocator::Free(pTable);
+ pTable = NULL;
+ }
+ }
+
+
+ void Assign(const SelfType& src)
+ {
+ Clear();
+ if (src.IsEmpty() == false)
+ {
+ SetCapacity(src.GetSize());
+
+ for (ConstIterator it = src.Begin(); it != src.End(); ++it)
+ {
+ Add(*it);
+ }
+ }
+ }
+
+
+ // Remove all entries from the HashSet table.
+ void Clear()
+ {
+ if (pTable)
+ {
+ // Delete the entries.
+ for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (!e->IsEmpty())
+ e->Clear();
+ }
+
+ Allocator::Free(pTable);
+ pTable = NULL;
+ }
+ }
+
+ // Returns true if the HashSet is empty.
+ bool IsEmpty() const
+ {
+ return pTable == NULL || pTable->EntryCount == 0;
+ }
+
+
+ // Set a new or existing value under the key, to the value.
+ // Pass a different class of 'key' so that assignment reference object
+ // can be passed instead of the actual object.
+ template<class CRef>
+ void Set(const CRef& key)
+ {
+ UPInt hashValue = HashF()(key);
+ SPInt index = (SPInt)-1;
+
+ if (pTable != NULL)
+ index = findIndexCore(key, hashValue & pTable->SizeMask);
+
+ if (index >= 0)
+ {
+ E(index).Value = key;
+ }
+ else
+ {
+ // Entry under key doesn't exist.
+ add(key, hashValue);
+ }
+ }
+
+ template<class CRef>
+ inline void Add(const CRef& key)
+ {
+ UPInt hashValue = HashF()(key);
+ add(key, hashValue);
+ }
+
+ // Remove by alternative key.
+ template<class K>
+ void RemoveAlt(const K& key)
+ {
+ if (pTable == NULL)
+ return;
+
+ UPInt hashValue = AltHashF()(key);
+ SPInt index = hashValue & pTable->SizeMask;
+
+ Entry* e = &E(index);
+
+ // If empty node or occupied by collider, we have nothing to remove.
+ if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index))
+ return;
+
+ // Save index
+ SPInt naturalIndex = index;
+ SPInt prevIndex = -1;
+
+ while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key))
+ {
+ // Keep looking through the chain.
+ prevIndex = index;
+ index = e->NextInChain;
+ if (index == -1)
+ return; // End of chain, item not found
+ e = &E(index);
+ }
+
+ // Found it - our item is at index
+ if (naturalIndex == index)
+ {
+ // If we have a follower, move it to us
+ if (!e->IsEndOfChain())
+ {
+ Entry* enext = &E(e->NextInChain);
+ e->Clear();
+ new (e) Entry(*enext);
+ // Point us to the follower's cell that will be cleared
+ e = enext;
+ }
+ }
+ else
+ {
+ // We are not at natural index, so deal with the prev items next index
+ E(prevIndex).NextInChain = e->NextInChain;
+ }
+
+ // Clear us, of the follower cell that was moved.
+ e->Clear();
+ pTable->EntryCount --;
+ // Should we check the size to condense hash? ...
+ }
+
+ // Remove by main key.
+ template<class CRef>
+ void Remove(const CRef& key)
+ {
+ RemoveAlt(key);
+ }
+
+ // Retrieve the pointer to a value under the given key.
+ // - If there's no value under the key, then return NULL.
+ // - If there is a value, return the pointer.
+ template<class K>
+ C* Get(const K& key)
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ const C* Get(const K& key) const
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ // Alternative key versions of Get. Used by Hash.
+ template<class K>
+ const C* GetAlt(const K& key) const
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ C* GetAlt(const K& key)
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ bool GetAlt(const K& key, C* pval) const
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ {
+ if (pval)
+ *pval = E(index).Value;
+ return true;
+ }
+ return false;
+ }
+
+
+ UPInt GetSize() const
+ {
+ return pTable == NULL ? 0 : (UPInt)pTable->EntryCount;
+ }
+
+
+ // Resize the HashSet table to fit one more Entry. Often this
+ // doesn't involve any action.
+ void CheckExpand()
+ {
+ if (pTable == NULL)
+ {
+ // Initial creation of table. Make a minimum-sized table.
+ setRawCapacity(HashMinSize);
+ }
+ else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4)
+ {
+ // pTable is more than 5/4 ths full. Expand.
+ setRawCapacity((pTable->SizeMask + 1) * 2);
+ }
+ }
+
+ // Hint the bucket count to >= n.
+ void Resize(UPInt n)
+ {
+ // Not really sure what this means in relation to
+ // STLport's hash_map... they say they "increase the
+ // bucket count to at least n" -- but does that mean
+ // their real capacity after Resize(n) is more like
+ // n*2 (since they do linked-list chaining within
+ // buckets?).
+ SetCapacity(n);
+ }
+
+ // Size the HashSet so that it can comfortably contain the given
+ // number of elements. If the HashSet already contains more
+ // elements than newSize, then this may be a no-op.
+ void SetCapacity(UPInt newSize)
+ {
+ UPInt newRawSize = (newSize * 5) / 4;
+ if (newRawSize <= GetSize())
+ return;
+ setRawCapacity(newRawSize);
+ }
+
+ // Disable inappropriate 'operator ->' warning on MSVC6.
+#ifdef OVR_CC_MSVC
+#if (OVR_CC_MSVC < 1300)
+# pragma warning(disable : 4284)
+#endif
+#endif
+
+ // Iterator API, like STL.
+ struct ConstIterator
+ {
+ const C& operator * () const
+ {
+ OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask);
+ return pHash->E(Index).Value;
+ }
+
+ const C* operator -> () const
+ {
+ OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask);
+ return &pHash->E(Index).Value;
+ }
+
+ void operator ++ ()
+ {
+ // Find next non-empty Entry.
+ if (Index <= (SPInt)pHash->pTable->SizeMask)
+ {
+ Index++;
+ while ((UPInt)Index <= pHash->pTable->SizeMask &&
+ pHash->E(Index).IsEmpty())
+ {
+ Index++;
+ }
+ }
+ }
+
+ bool operator == (const ConstIterator& it) const
+ {
+ if (IsEnd() && it.IsEnd())
+ {
+ return true;
+ }
+ else
+ {
+ return (pHash == it.pHash) && (Index == it.Index);
+ }
+ }
+
+ bool operator != (const ConstIterator& it) const
+ {
+ return ! (*this == it);
+ }
+
+
+ bool IsEnd() const
+ {
+ return (pHash == NULL) ||
+ (pHash->pTable == NULL) ||
+ (Index > (SPInt)pHash->pTable->SizeMask);
+ }
+
+ ConstIterator()
+ : pHash(NULL), Index(0)
+ { }
+
+ public:
+ // Constructor was intentionally made public to allow create
+ // iterator with arbitrary index.
+ ConstIterator(const SelfType* h, SPInt index)
+ : pHash(h), Index(index)
+ { }
+
+ const SelfType* GetContainer() const
+ {
+ return pHash;
+ }
+ SPInt GetIndex() const
+ {
+ return Index;
+ }
+
+ protected:
+ friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
+
+ const SelfType* pHash;
+ SPInt Index;
+ };
+
+ friend struct ConstIterator;
+
+
+ // Non-const Iterator; Get most of it from ConstIterator.
+ struct Iterator : public ConstIterator
+ {
+ // Allow non-const access to entries.
+ C& operator*() const
+ {
+ OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask);
+ return const_cast<SelfType*>(ConstIterator::pHash)->E(ConstIterator::Index).Value;
+ }
+
+ C* operator->() const
+ {
+ return &(operator*());
+ }
+
+ Iterator()
+ : ConstIterator(NULL, 0)
+ { }
+
+ // Removes current element from Hash
+ void Remove()
+ {
+ RemoveAlt(operator*());
+ }
+
+ template <class K>
+ void RemoveAlt(const K& key)
+ {
+ SelfType* phash = const_cast<SelfType*>(ConstIterator::pHash);
+ //Entry* ee = &phash->E(ConstIterator::Index);
+ //const C& key = ee->Value;
+
+ UPInt hashValue = AltHashF()(key);
+ SPInt index = hashValue & phash->pTable->SizeMask;
+
+ Entry* e = &phash->E(index);
+
+ // If empty node or occupied by collider, we have nothing to remove.
+ if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index))
+ return;
+
+ // Save index
+ SPInt naturalIndex = index;
+ SPInt prevIndex = -1;
+
+ while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key))
+ {
+ // Keep looking through the chain.
+ prevIndex = index;
+ index = e->NextInChain;
+ if (index == -1)
+ return; // End of chain, item not found
+ e = &phash->E(index);
+ }
+
+ if (index == (SPInt)ConstIterator::Index)
+ {
+ // Found it - our item is at index
+ if (naturalIndex == index)
+ {
+ // If we have a follower, move it to us
+ if (!e->IsEndOfChain())
+ {
+ Entry* enext = &phash->E(e->NextInChain);
+ e->Clear();
+ new (e) Entry(*enext);
+ // Point us to the follower's cell that will be cleared
+ e = enext;
+ --ConstIterator::Index;
+ }
+ }
+ else
+ {
+ // We are not at natural index, so deal with the prev items next index
+ phash->E(prevIndex).NextInChain = e->NextInChain;
+ }
+
+ // Clear us, of the follower cell that was moved.
+ e->Clear();
+ phash->pTable->EntryCount --;
+ }
+ else
+ OVR_ASSERT(0); //?
+ }
+
+ private:
+ friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
+
+ Iterator(SelfType* h, SPInt i0)
+ : ConstIterator(h, i0)
+ { }
+ };
+
+ friend struct Iterator;
+
+ Iterator Begin()
+ {
+ if (pTable == 0)
+ return Iterator(NULL, 0);
+
+ // Scan till we hit the First valid Entry.
+ UPInt i0 = 0;
+ while (i0 <= pTable->SizeMask && E(i0).IsEmpty())
+ {
+ i0++;
+ }
+ return Iterator(this, i0);
+ }
+ Iterator End() { return Iterator(NULL, 0); }
+
+ ConstIterator Begin() const { return const_cast<SelfType*>(this)->Begin(); }
+ ConstIterator End() const { return const_cast<SelfType*>(this)->End(); }
+
+ template<class K>
+ Iterator Find(const K& key)
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return Iterator(this, index);
+ return Iterator(NULL, 0);
+ }
+
+ template<class K>
+ Iterator FindAlt(const K& key)
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return Iterator(this, index);
+ return Iterator(NULL, 0);
+ }
+
+ template<class K>
+ ConstIterator Find(const K& key) const { return const_cast<SelfType*>(this)->Find(key); }
+
+ template<class K>
+ ConstIterator FindAlt(const K& key) const { return const_cast<SelfType*>(this)->FindAlt(key); }
+
+private:
+ // Find the index of the matching Entry. If no match, then return -1.
+ template<class K>
+ SPInt findIndex(const K& key) const
+ {
+ if (pTable == NULL)
+ return -1;
+ UPInt hashValue = HashF()(key) & pTable->SizeMask;
+ return findIndexCore(key, hashValue);
+ }
+
+ template<class K>
+ SPInt findIndexAlt(const K& key) const
+ {
+ if (pTable == NULL)
+ return -1;
+ UPInt hashValue = AltHashF()(key) & pTable->SizeMask;
+ return findIndexCore(key, hashValue);
+ }
+
+ // Find the index of the matching Entry. If no match, then return -1.
+ template<class K>
+ SPInt findIndexCore(const K& key, UPInt hashValue) const
+ {
+ // Table must exist.
+ OVR_ASSERT(pTable != 0);
+ // Hash key must be 'and-ed' by the caller.
+ OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0);
+
+ UPInt index = hashValue;
+ const Entry* e = &E(index);
+
+ // If empty or occupied by a collider, not found.
+ if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index))
+ return -1;
+
+ while(1)
+ {
+ OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue);
+
+ if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key)
+ {
+ // Found it.
+ return index;
+ }
+ // Values can not be equal at this point.
+ // That would mean that the hash key for the same value differs.
+ OVR_ASSERT(!(e->Value == key));
+
+ // Keep looking through the chain.
+ index = e->NextInChain;
+ if (index == (UPInt)-1)
+ break; // end of chain
+
+ e = &E(index);
+ OVR_ASSERT(!e->IsEmpty());
+ }
+ return -1;
+ }
+
+
+ // Add a new value to the HashSet table, under the specified key.
+ template<class CRef>
+ void add(const CRef& key, UPInt hashValue)
+ {
+ CheckExpand();
+ hashValue &= pTable->SizeMask;
+
+ pTable->EntryCount++;
+
+ SPInt index = hashValue;
+ Entry* naturalEntry = &(E(index));
+
+ if (naturalEntry->IsEmpty())
+ {
+ // Put the new Entry in.
+ new (naturalEntry) Entry(key, -1);
+ }
+ else
+ {
+ // Find a blank spot.
+ SPInt blankIndex = index;
+ do {
+ blankIndex = (blankIndex + 1) & pTable->SizeMask;
+ } while(!E(blankIndex).IsEmpty());
+
+ Entry* blankEntry = &E(blankIndex);
+
+ if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index)
+ {
+ // Collision. Link into this chain.
+
+ // Move existing list head.
+ new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor
+
+ // Put the new info in the natural Entry.
+ naturalEntry->Value = key;
+ naturalEntry->NextInChain = blankIndex;
+ }
+ else
+ {
+ // Existing Entry does not naturally
+ // belong in this slot. Existing
+ // Entry must be moved.
+
+ // Find natural location of collided element (i.e. root of chain)
+ SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask);
+ OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask);
+ for (;;)
+ {
+ Entry* e = &E(collidedIndex);
+ if (e->NextInChain == index)
+ {
+ // Here's where we need to splice.
+ new (blankEntry) Entry(*naturalEntry);
+ e->NextInChain = blankIndex;
+ break;
+ }
+ collidedIndex = e->NextInChain;
+ OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask);
+ }
+
+ // Put the new data in the natural Entry.
+ naturalEntry->Value = key;
+ naturalEntry->NextInChain = -1;
+ }
+ }
+
+ // Record hash value: has effect only if cached node is used.
+ naturalEntry->SetCachedHash(hashValue);
+ }
+
+ // Index access helpers.
+ Entry& E(UPInt index)
+ {
+ // Must have pTable and access needs to be within bounds.
+ OVR_ASSERT(index <= pTable->SizeMask);
+ return *(((Entry*) (pTable + 1)) + index);
+ }
+ const Entry& E(UPInt index) const
+ {
+ OVR_ASSERT(index <= pTable->SizeMask);
+ return *(((Entry*) (pTable + 1)) + index);
+ }
+
+
+ // Resize the HashSet table to the given size (Rehash the
+ // contents of the current table). The arg is the number of
+ // HashSet table entries, not the number of elements we should
+ // actually contain (which will be less than this).
+ void setRawCapacity(UPInt newSize)
+ {
+ if (newSize == 0)
+ {
+ // Special case.
+ Clear();
+ return;
+ }
+
+ // Minimum size; don't incur rehashing cost when expanding
+ // very small tables. Not that we perform this check before
+ // 'log2f' call to avoid fp exception with newSize == 1.
+ if (newSize < HashMinSize)
+ newSize = HashMinSize;
+ else
+ {
+ // Force newSize to be a power of two.
+ int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1);
+ OVR_ASSERT((UPInt(1) << bits) >= newSize);
+ newSize = UPInt(1) << bits;
+ }
+
+ SelfType newHash;
+ newHash.pTable = (TableType*)
+ Allocator::Alloc(
+ sizeof(TableType) + sizeof(Entry) * newSize);
+ // Need to do something on alloc failure!
+ OVR_ASSERT(newHash.pTable);
+
+ newHash.pTable->EntryCount = 0;
+ newHash.pTable->SizeMask = newSize - 1;
+ UPInt i, n;
+
+ // Mark all entries as empty.
+ for (i = 0; i < newSize; i++)
+ newHash.E(i).NextInChain = -2;
+
+ // Copy stuff to newHash
+ if (pTable)
+ {
+ for (i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (e->IsEmpty() == false)
+ {
+ // Insert old Entry into new HashSet.
+ newHash.Add(e->Value);
+ // placement delete of old element
+ e->Clear();
+ }
+ }
+
+ // Delete our old data buffer.
+ Allocator::Free(pTable);
+ }
+
+ // Steal newHash's data.
+ pTable = newHash.pTable;
+ newHash.pTable = NULL;
+ }
+
+ struct TableType
+ {
+ UPInt EntryCount;
+ UPInt SizeMask;
+ // Entry array follows this structure
+ // in memory.
+ };
+ TableType* pTable;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C>,
+ class Entry = HashsetCachedEntry<C, HashF> >
+class HashSet : public HashSetBase<C, HashF, AltHashF, Allocator, Entry>
+{
+public:
+ typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> BaseType;
+ typedef HashSet<C, HashF, AltHashF, Allocator, Entry> SelfType;
+
+ HashSet() { }
+ HashSet(int sizeHint) : BaseType(sizeHint) { }
+ HashSet(const SelfType& src) : BaseType(src) { }
+ ~HashSet() { }
+
+ void operator = (const SelfType& src) { BaseType::Assign(src); }
+
+ // Set a new or existing value under the key, to the value.
+ // Pass a different class of 'key' so that assignment reference object
+ // can be passed instead of the actual object.
+ template<class CRef>
+ void Set(const CRef& key)
+ {
+ BaseType::Set(key);
+ }
+
+ template<class CRef>
+ inline void Add(const CRef& key)
+ {
+ BaseType::Add(key);
+ }
+
+ // Hint the bucket count to >= n.
+ void Resize(UPInt n)
+ {
+ BaseType::SetCapacity(n);
+ }
+
+ // Size the HashSet so that it can comfortably contain the given
+ // number of elements. If the HashSet already contains more
+ // elements than newSize, then this may be a no-op.
+ void SetCapacity(UPInt newSize)
+ {
+ BaseType::SetCapacity(newSize);
+ }
+
+};
+
+// HashSet with uncached hash code; declared for convenience.
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C> >
+class HashSetUncached : public HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> >
+{
+public:
+
+ typedef HashSetUncached<C, HashF, AltHashF, Allocator> SelfType;
+ typedef HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> > BaseType;
+
+ // Delegated constructors.
+ HashSetUncached() { }
+ HashSetUncached(int sizeHint) : BaseType(sizeHint) { }
+ HashSetUncached(const SelfType& src) : BaseType(src) { }
+ ~HashSetUncached() { }
+
+ void operator = (const SelfType& src)
+ {
+ BaseType::operator = (src);
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Hash hash table implementation
+
+// Node for Hash - necessary so that Hash can delegate its implementation
+// to HashSet.
+template<class C, class U, class HashF>
+struct HashNode
+{
+ typedef HashNode<C, U, HashF> SelfType;
+ typedef C FirstType;
+ typedef U SecondType;
+
+ C First;
+ U Second;
+
+ // NodeRef is used to allow passing of elements into HashSet
+ // without using a temporary object.
+ struct NodeRef
+ {
+ const C* pFirst;
+ const U* pSecond;
+
+ NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { }
+ NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { }
+
+ // Enable computation of ghash_node_hashf.
+ inline UPInt GetHash() const { return HashF()(*pFirst); }
+ // Necessary conversion to allow HashNode::operator == to work.
+ operator const C& () const { return *pFirst; }
+ };
+
+ // Note: No default constructor is necessary.
+ HashNode(const HashNode& src) : First(src.First), Second(src.Second) { }
+ HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { }
+ void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; }
+
+ template<class K>
+ bool operator == (const K& src) const { return (First == src); }
+
+ template<class K>
+ static UPInt CalcHash(const K& data) { return HashF()(data); }
+ inline UPInt GetHash() const { return HashF()(First); }
+
+ // Hash functors used with this node. A separate functor is used for alternative
+ // key lookup so that it does not need to access the '.First' element.
+ struct NodeHashF
+ {
+ template<class K>
+ UPInt operator()(const K& data) const { return data.GetHash(); }
+ };
+ struct NodeAltHashF
+ {
+ template<class K>
+ UPInt operator()(const K& data) const { return HashNode<C,U,HashF>::CalcHash(data); }
+ };
+};
+
+
+
+// **** Extra hashset_entry types to allow NodeRef construction.
+
+// The big difference between the below types and the ones used in hash_set is that
+// these allow initializing the node with 'typename C::NodeRef& keyRef', which
+// is critical to avoid temporary node allocation on stack when using placement new.
+
+// Compact hash table Entry type that re-computes hash keys during hash traversal.
+// Good to use if the hash function is cheap or the hash value is already cached in C.
+template<class C, class HashF>
+class HashsetNodeEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ C Value;
+
+ HashsetNodeEntry()
+ : NextInChain(-2) { }
+ HashsetNodeEntry(const HashsetNodeEntry& e)
+ : NextInChain(e.NextInChain), Value(e.Value) { }
+ HashsetNodeEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+ HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next)
+ : NextInChain(next), Value(keyRef) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+ UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; }
+ void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); }
+
+ void Clear()
+ {
+ Value.~C(); // placement delete
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+// Hash table Entry type that caches the Entry hash value for nodes, so that it
+// does not need to be re-computed during access.
+template<class C, class HashF>
+class HashsetCachedNodeEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ UPInt HashValue;
+ C Value;
+
+ HashsetCachedNodeEntry()
+ : NextInChain(-2) { }
+ HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e)
+ : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
+ HashsetCachedNodeEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+ HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next)
+ : NextInChain(next), Value(keyRef) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+ UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
+ void SetCachedHash(UPInt hashValue) { HashValue = hashValue; }
+
+ void Clear()
+ {
+ Value.~C();
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+template<class C, class U,
+ class HashF = FixedSizeHash<C>,
+ class Allocator = ContainerAllocator<C>,
+ class HashNode = OVR::HashNode<C,U,HashF>,
+ class Entry = HashsetCachedNodeEntry<HashNode, typename HashNode::NodeHashF>,
+ class Container = HashSet<HashNode, typename HashNode::NodeHashF,
+ typename HashNode::NodeAltHashF, Allocator,
+ Entry> >
+class Hash
+{
+public:
+ OVR_MEMORY_REDEFINE_NEW(Hash)
+
+ // Types used for hash_set.
+ typedef U ValueType;
+ typedef Hash<C, U, HashF, Allocator, HashNode, Entry, Container> SelfType;
+
+ // Actual hash table itself, implemented as hash_set.
+ Container mHash;
+
+public:
+ Hash() { }
+ Hash(int sizeHint) : mHash(sizeHint) { }
+ Hash(const SelfType& src) : mHash(src.mHash) { }
+ ~Hash() { }
+
+ void operator = (const SelfType& src) { mHash = src.mHash; }
+
+ // Remove all entries from the Hash table.
+ inline void Clear() { mHash.Clear(); }
+ // Returns true if the Hash is empty.
+ inline bool IsEmpty() const { return mHash.IsEmpty(); }
+
+ // Access (set).
+ inline void Set(const C& key, const U& value)
+ {
+ typename HashNode::NodeRef e(key, value);
+ mHash.Set(e);
+ }
+ inline void Add(const C& key, const U& value)
+ {
+ typename HashNode::NodeRef e(key, value);
+ mHash.Add(e);
+ }
+
+ // Removes an element by clearing its Entry.
+ inline void Remove(const C& key)
+ {
+ mHash.RemoveAlt(key);
+ }
+ template<class K>
+ inline void RemoveAlt(const K& key)
+ {
+ mHash.RemoveAlt(key);
+ }
+
+ // Retrieve the value under the given key.
+ // - If there's no value under the key, then return false and leave *pvalue alone.
+ // - If there is a value, return true, and Set *Pvalue to the Entry's value.
+ // - If value == NULL, return true or false according to the presence of the key.
+ bool Get(const C& key, U* pvalue) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ if (p)
+ {
+ if (pvalue)
+ *pvalue = p->Second;
+ return true;
+ }
+ return false;
+ }
+
+ template<class K>
+ bool GetAlt(const K& key, U* pvalue) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ if (p)
+ {
+ if (pvalue)
+ *pvalue = p->Second;
+ return true;
+ }
+ return false;
+ }
+
+ // Retrieve the pointer to a value under the given key.
+ // - If there's no value under the key, then return NULL.
+ // - If there is a value, return the pointer.
+ inline U* Get(const C& key)
+ {
+ HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+ inline const U* Get(const C& key) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+
+ template<class K>
+ inline U* GetAlt(const K& key)
+ {
+ HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+ template<class K>
+ inline const U* GetAlt(const K& key) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+
+ // Sizing methods - delegate to Hash.
+ inline UPInt GetSize() const { return mHash.GetSize(); }
+ inline void Resize(UPInt n) { mHash.Resize(n); }
+ inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); }
+
+ // Iterator API, like STL.
+ typedef typename Container::ConstIterator ConstIterator;
+ typedef typename Container::Iterator Iterator;
+
+ inline Iterator Begin() { return mHash.Begin(); }
+ inline Iterator End() { return mHash.End(); }
+ inline ConstIterator Begin() const { return mHash.Begin(); }
+ inline ConstIterator End() const { return mHash.End(); }
+
+ Iterator Find(const C& key) { return mHash.FindAlt(key); }
+ ConstIterator Find(const C& key) const { return mHash.FindAlt(key); }
+
+ template<class K>
+ Iterator FindAlt(const K& key) { return mHash.FindAlt(key); }
+ template<class K>
+ ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); }
+};
+
+
+
+// Hash with uncached hash code; declared for convenience.
+template<class C, class U, class HashF = FixedSizeHash<C>, class Allocator = ContainerAllocator<C> >
+class HashUncached
+ : public Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
+ HashsetNodeEntry<HashNode<C,U,HashF>, typename HashNode<C,U,HashF>::NodeHashF> >
+{
+public:
+ typedef HashUncached<C, U, HashF, Allocator> SelfType;
+ typedef Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
+ HashsetNodeEntry<HashNode<C,U,HashF>,
+ typename HashNode<C,U,HashF>::NodeHashF> > BaseType;
+
+ // Delegated constructors.
+ HashUncached() { }
+ HashUncached(int sizeHint) : BaseType(sizeHint) { }
+ HashUncached(const SelfType& src) : BaseType(src) { }
+ ~HashUncached() { }
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+};
+
+
+
+// And identity hash in which keys serve as hash value. Can be uncached,
+// since hash computation is assumed cheap.
+template<class C, class U, class Allocator = ContainerAllocator<C>, class HashF = IdentityHash<C> >
+class HashIdentity
+ : public HashUncached<C, U, HashF, Allocator>
+{
+public:
+ typedef HashIdentity<C, U, Allocator, HashF> SelfType;
+ typedef HashUncached<C, U, HashF, Allocator> BaseType;
+
+ // Delegated constructors.
+ HashIdentity() { }
+ HashIdentity(int sizeHint) : BaseType(sizeHint) { }
+ HashIdentity(const SelfType& src) : BaseType(src) { }
+ ~HashIdentity() { }
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+};
+
+
+} // OVR
+
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_KeyCodes.h b/LibOVR/Src/Kernel/OVR_KeyCodes.h new file mode 100644 index 0000000..647688a --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_KeyCodes.h @@ -0,0 +1,240 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_KeyCodes.h
+Content : Common keyboard constants
+Created : September 19, 2012
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_KeyCodes_h
+#define OVR_KeyCodes_h
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** KeyCode
+
+// KeyCode enumeration defines platform-independent keyboard key constants.
+// Note that Key_A through Key_Z are mapped to capital ascii constants.
+
+enum KeyCode
+{
+ // Key_None indicates that no key was specified.
+ Key_None = 0,
+
+ // A through Z and numbers 0 through 9.
+ Key_A = 65,
+ Key_B,
+ Key_C,
+ Key_D,
+ Key_E,
+ Key_F,
+ Key_G,
+ Key_H,
+ Key_I,
+ Key_J,
+ Key_K,
+ Key_L,
+ Key_M,
+ Key_N,
+ Key_O,
+ Key_P,
+ Key_Q,
+ Key_R,
+ Key_S,
+ Key_T,
+ Key_U,
+ Key_V,
+ Key_W,
+ Key_X,
+ Key_Y,
+ Key_Z,
+ Key_Num0 = 48,
+ Key_Num1,
+ Key_Num2,
+ Key_Num3,
+ Key_Num4,
+ Key_Num5,
+ Key_Num6,
+ Key_Num7,
+ Key_Num8,
+ Key_Num9,
+
+ // Numeric keypad.
+ Key_KP_0 = 0xa0,
+ Key_KP_1,
+ Key_KP_2,
+ Key_KP_3,
+ Key_KP_4,
+ Key_KP_5,
+ Key_KP_6,
+ Key_KP_7,
+ Key_KP_8,
+ Key_KP_9,
+ Key_KP_Multiply,
+ Key_KP_Add,
+ Key_KP_Enter,
+ Key_KP_Subtract,
+ Key_KP_Decimal,
+ Key_KP_Divide,
+
+ // Function keys.
+ Key_F1 = 0xb0,
+ Key_F2,
+ Key_F3,
+ Key_F4,
+ Key_F5,
+ Key_F6,
+ Key_F7,
+ Key_F8,
+ Key_F9,
+ Key_F10,
+ Key_F11,
+ Key_F12,
+ Key_F13,
+ Key_F14,
+ Key_F15,
+
+ // Other keys.
+ Key_Backspace = 8,
+ Key_Tab,
+ Key_Clear = 12,
+ Key_Return,
+ Key_Shift = 16,
+ Key_Control,
+ Key_Alt,
+ Key_Pause,
+ Key_CapsLock = 20, // Toggle
+ Key_Escape = 27,
+ Key_Space = 32,
+ Key_Quote = 39,
+ Key_PageUp = 0xc0,
+ Key_PageDown,
+ Key_End,
+ Key_Home,
+ Key_Left,
+ Key_Up,
+ Key_Right,
+ Key_Down,
+ Key_Insert,
+ Key_Delete,
+ Key_Help,
+
+ Key_Comma = 44,
+ Key_Minus,
+ Key_Slash = 47,
+ Key_Period,
+ Key_NumLock = 144, // Toggle
+ Key_ScrollLock = 145, // Toggle
+
+ Key_Semicolon = 59,
+ Key_Equal = 61,
+ Key_Bar = 192,
+ Key_BracketLeft = 91,
+ Key_Backslash,
+ Key_BracketRight,
+
+ Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard
+ Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard.
+ Key_ICO_HELP = 0xE3, // Help key on ICO
+ Key_ICO_00 = 0xE4, // 00 key on ICO
+
+ Key_Meta,
+
+ // Total number of keys.
+ Key_CodeCount
+};
+
+
+//-----------------------------------------------------------------------------------
+
+class KeyModifiers
+{
+public:
+ enum
+ {
+ Key_ShiftPressed = 0x01,
+ Key_CtrlPressed = 0x02,
+ Key_AltPressed = 0x04,
+ Key_MetaPressed = 0x08,
+ Key_CapsToggled = 0x10,
+ Key_NumToggled = 0x20,
+ Key_ScrollToggled = 0x40,
+
+ Initialized_Bit = 0x80,
+ Initialized_Mask = 0xFF
+ };
+ unsigned char States;
+
+ KeyModifiers() : States(0) { }
+ KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { }
+
+ void Reset() { States = 0; }
+
+ bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; }
+ bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; }
+ bool IsAltPressed() const { return (States & Key_AltPressed) != 0; }
+ bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; }
+ bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; }
+ bool IsNumToggled() const { return (States & Key_NumToggled) != 0; }
+ bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; }
+
+ void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; }
+ void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; }
+ void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; }
+ void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; }
+ void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; }
+ void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; }
+ void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; }
+
+ bool IsInitialized() const { return (States & Initialized_Mask) != 0; }
+};
+
+
+//-----------------------------------------------------------------------------------
+
+/*
+enum PadKeyCode
+{
+ Pad_None, // Indicates absence of key code.
+ Pad_Back,
+ Pad_Start,
+ Pad_A,
+ Pad_B,
+ Pad_X,
+ Pad_Y,
+ Pad_R1, // RightShoulder;
+ Pad_L1, // LeftShoulder;
+ Pad_R2, // RightTrigger;
+ Pad_L2, // LeftTrigger;
+ Pad_Up,
+ Pad_Down,
+ Pad_Right,
+ Pad_Left,
+ Pad_Plus,
+ Pad_Minus,
+ Pad_1,
+ Pad_2,
+ Pad_H,
+ Pad_C,
+ Pad_Z,
+ Pad_O,
+ Pad_T,
+ Pad_S,
+ Pad_Select,
+ Pad_Home,
+ Pad_RT, // RightThumb;
+ Pad_LT // LeftThumb;
+};
+*/
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_List.h b/LibOVR/Src/Kernel/OVR_List.h new file mode 100644 index 0000000..0292f51 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_List.h @@ -0,0 +1,325 @@ +/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_List.h
+Content : Template implementation for doubly-connected linked List
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_List_h
+#define OVR_List_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** ListNode
+//
+// Base class for the elements of the intrusive linked list.
+// To store elements in the List do:
+//
+// struct MyData : ListNode<MyData>
+// {
+// . . .
+// };
+
+template<class T>
+struct ListNode
+{
+ union {
+ T* pPrev;
+ void* pVoidPrev;
+ };
+ union {
+ T* pNext;
+ void* pVoidNext;
+ };
+
+ void RemoveNode()
+ {
+ pPrev->pNext = pNext;
+ pNext->pPrev = pPrev;
+ }
+
+ // Removes us from the list and inserts pnew there instead.
+ void ReplaceNodeWith(T* pnew)
+ {
+ pPrev->pNext = pnew;
+ pNext->pPrev = pnew;
+ pnew->pPrev = pPrev;
+ pnew->pNext = pNext;
+ }
+
+ // Inserts the argument linked list node after us in the list.
+ void InsertNodeAfter(T* p)
+ {
+ p->pPrev = pNext->pPrev; // this
+ p->pNext = pNext;
+ pNext->pPrev = p;
+ pNext = p;
+ }
+ // Inserts the argument linked list node before us in the list.
+ void InsertNodeBefore(T* p)
+ {
+ p->pNext = pNext->pPrev; // this
+ p->pPrev = pPrev;
+ pPrev->pNext = p;
+ pPrev = p;
+ }
+
+ void Alloc_MoveTo(ListNode<T>* pdest)
+ {
+ pdest->pNext = pNext;
+ pdest->pPrev = pPrev;
+ pPrev->pNext = (T*)pdest;
+ pNext->pPrev = (T*)pdest;
+ }
+};
+
+
+//------------------------------------------------------------------------
+// ***** List
+//
+// Doubly linked intrusive list.
+// The data type must be derived from ListNode.
+//
+// Adding: PushFront(), PushBack().
+// Removing: Remove() - the element must be in the list!
+// Moving: BringToFront(), SendToBack() - the element must be in the list!
+//
+// Iterating:
+// MyData* data = MyList.GetFirst();
+// while (!MyList.IsNull(data))
+// {
+// . . .
+// data = MyList.GetNext(data);
+// }
+//
+// Removing:
+// MyData* data = MyList.GetFirst();
+// while (!MyList.IsNull(data))
+// {
+// MyData* next = MyList.GetNext(data);
+// if (ToBeRemoved(data))
+// MyList.Remove(data);
+// data = next;
+// }
+//
+
+// List<> represents a doubly-linked list if T, where each T must derive
+// from ListNode<B>. B specifies the base class that was directly
+// derived from ListNode, and is only necessary if there is an intermediate
+// inheritance chain.
+
+template<class T, class B = T> class List
+{
+public:
+ typedef T ValueType;
+
+ List()
+ {
+ Root.pNext = Root.pPrev = (ValueType*)&Root;
+ }
+
+ void Clear()
+ {
+ Root.pNext = Root.pPrev = (ValueType*)&Root;
+ }
+
+ const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; }
+ const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; }
+ ValueType* GetFirst() { return (ValueType*)Root.pNext; }
+ ValueType* GetLast () { return (ValueType*)Root.pPrev; }
+
+ // Determine if list is empty (i.e.) points to itself.
+ // Go through void* access to avoid issues with strict-aliasing optimizing out the
+ // access after RemoveNode(), etc.
+ bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; }
+ bool IsFirst(const ValueType* p) const { return p == Root.pNext; }
+ bool IsLast (const ValueType* p) const { return p == Root.pPrev; }
+ bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; }
+
+ inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; }
+ inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; }
+ inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; }
+ inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; }
+
+ void PushFront(ValueType* p)
+ {
+ p->pNext = Root.pNext;
+ p->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = p;
+ Root.pNext = p;
+ }
+
+ void PushBack(ValueType* p)
+ {
+ p->pPrev = Root.pPrev;
+ p->pNext = (ValueType*)&Root;
+ Root.pPrev->pNext = p;
+ Root.pPrev = p;
+ }
+
+ static void Remove(ValueType* p)
+ {
+ p->pPrev->pNext = p->pNext;
+ p->pNext->pPrev = p->pPrev;
+ }
+
+ void BringToFront(ValueType* p)
+ {
+ Remove(p);
+ PushFront(p);
+ }
+
+ void SendToBack(ValueType* p)
+ {
+ Remove(p);
+ PushBack(p);
+ }
+
+ // Appends the contents of the argument list to the front of this list;
+ // items are removed from the argument list.
+ void PushListToFront(List<T>& src)
+ {
+ if (!src.IsEmpty())
+ {
+ ValueType* pfirst = src.GetFirst();
+ ValueType* plast = src.GetLast();
+ src.Clear();
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+ void PushListToBack(List<T>& src)
+ {
+ if (!src.IsEmpty())
+ {
+ ValueType* pfirst = src.GetFirst();
+ ValueType* plast = src.GetLast();
+ src.Clear();
+ plast->pNext = (ValueType*)&Root;
+ pfirst->pPrev = Root.pPrev;
+ Root.pPrev->pNext = pfirst;
+ Root.pPrev = plast;
+ }
+ }
+
+ // Removes all source list items after (and including) the 'pfirst' node from the
+ // source list and adds them to out list.
+ void PushFollowingListItemsToFront(List<T>& src, ValueType *pfirst)
+ {
+ if (pfirst != &src.Root)
+ {
+ ValueType *plast = src.Root.pPrev;
+
+ // Remove list remainder from source.
+ pfirst->pPrev->pNext = (ValueType*)&src.Root;
+ src.Root.pPrev = pfirst->pPrev;
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+ // Removes all source list items up to but NOT including the 'pend' node from the
+ // source list and adds them to out list.
+ void PushPrecedingListItemsToFront(List<T>& src, ValueType *ptail)
+ {
+ if (src.GetFirst() != ptail)
+ {
+ ValueType *pfirst = src.Root.pNext;
+ ValueType *plast = ptail->pPrev;
+
+ // Remove list remainder from source.
+ ptail->pPrev = (ValueType*)&src.Root;
+ src.Root.pNext = ptail;
+
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+
+ // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend',
+ // and adds them to out list. Note that source items MUST already be in the list.
+ void PushListItemsToFront(ValueType *pfirst, ValueType *pend)
+ {
+ if (pfirst != pend)
+ {
+ ValueType *plast = pend->pPrev;
+
+ // Remove list remainder from source.
+ pfirst->pPrev->pNext = pend;
+ pend->pPrev = pfirst->pPrev;
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+
+ void Alloc_MoveTo(List<T>* pdest)
+ {
+ if (IsEmpty())
+ pdest->Clear();
+ else
+ {
+ pdest->Root.pNext = Root.pNext;
+ pdest->Root.pPrev = Root.pPrev;
+
+ Root.pNext->pPrev = (ValueType*)&pdest->Root;
+ Root.pPrev->pNext = (ValueType*)&pdest->Root;
+ }
+ }
+
+
+private:
+ // Copying is prohibited
+ List(const List<T>&);
+ const List<T>& operator = (const List<T>&);
+
+ ListNode<B> Root;
+};
+
+
+//------------------------------------------------------------------------
+// ***** FreeListElements
+//
+// Remove all elements in the list and free them in the allocator
+
+template<class List, class Allocator>
+void FreeListElements(List& list, Allocator& allocator)
+{
+ typename List::ValueType* self = list.GetFirst();
+ while(!list.IsNull(self))
+ {
+ typename List::ValueType* next = list.GetNext(self);
+ allocator.Free(self);
+ self = next;
+ }
+ list.Clear();
+}
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Log.cpp b/LibOVR/Src/Kernel/OVR_Log.cpp new file mode 100644 index 0000000..baede98 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Log.cpp @@ -0,0 +1,173 @@ +/************************************************************************************
+
+Filename : OVR_Log.cpp
+Content : Logging support
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Log.h"
+#include "OVR_Std.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#if defined(OVR_OS_WIN32)
+#include <windows.h>
+#elif defined(OVR_OS_ANDROID)
+#include <android/log.h>
+#endif
+
+namespace OVR {
+
+// Global Log pointer.
+Log* volatile OVR_GlobalLog = 0;
+
+//-----------------------------------------------------------------------------------
+// ***** Log Implementation
+
+Log::~Log()
+{
+ // Clear out global log
+ if (this == OVR_GlobalLog)
+ {
+ // TBD: perhaps we should ASSERT if this happens before system shutdown?
+ OVR_GlobalLog = 0;
+ }
+}
+
+void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList)
+{
+ if ((messageType & LoggingMask) == 0)
+ return;
+#ifndef OVR_BUILD_DEBUG
+ if (IsDebugMessage(messageType))
+ return;
+#endif
+
+ char buffer[MaxLogBufferMessageSize];
+ FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList);
+ DefaultLogOutput(buffer, IsDebugMessage(messageType));
+}
+
+void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...)
+{
+ va_list argList;
+ va_start(argList, pfmt);
+ LogMessageVarg(messageType, pfmt, argList);
+ va_end(argList);
+}
+
+
+void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType,
+ const char* fmt, va_list argList)
+{
+ bool addNewline = true;
+
+ switch(messageType)
+ {
+ case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break;
+ case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break;
+ case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break;
+ case Log_Text: buffer[0] = 0; addNewline = false; break;
+ case Log_DebugText: buffer[0] = 0; addNewline = false; break;
+ default:
+ buffer[0] = 0;
+ addNewline = false;
+ break;
+ }
+
+ UPInt prefixLength = OVR_strlen(buffer);
+ char *buffer2 = buffer + prefixLength;
+ OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList);
+
+ if (addNewline)
+ OVR_strcat(buffer, bufferSize, "\n");
+}
+
+
+void Log::DefaultLogOutput(const char* formattedText, bool debug)
+{
+
+#if defined(OVR_OS_WIN32)
+ // Under Win32, output regular messages to console if it exists; debug window otherwise.
+ static DWORD dummyMode;
+ static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) &&
+ (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode));
+
+ if (!hasConsole || debug)
+ {
+ ::OutputDebugStringA(formattedText);
+ }
+ else
+ {
+ fputs(formattedText, stdout);
+ }
+
+#elif defined(OVR_OS_ANDROID)
+ __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText);
+
+#else
+ fputs(formattedText, stdout);
+
+#endif
+
+ // Just in case.
+ OVR_UNUSED2(formattedText, debug);
+}
+
+
+//static
+void Log::SetGlobalLog(Log *log)
+{
+ OVR_GlobalLog = log;
+}
+//static
+Log* Log::GetGlobalLog()
+{
+// No global log by default?
+// if (!OVR_GlobalLog)
+// OVR_GlobalLog = GetDefaultLog();
+ return OVR_GlobalLog;
+}
+
+//static
+Log* Log::GetDefaultLog()
+{
+ // Create default log pointer statically so that it can be used
+ // even during startup.
+ static Log defaultLog;
+ return &defaultLog;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Global Logging functions
+
+#define OVR_LOG_FUNCTION_IMPL(Name) \
+ void Log##Name(const char* fmt, ...) \
+ { \
+ if (OVR_GlobalLog) \
+ { \
+ va_list argList; va_start(argList, fmt); \
+ OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \
+ va_end(argList); \
+ } \
+ }
+
+OVR_LOG_FUNCTION_IMPL(Text)
+OVR_LOG_FUNCTION_IMPL(Error)
+
+#ifdef OVR_BUILD_DEBUG
+OVR_LOG_FUNCTION_IMPL(DebugText)
+OVR_LOG_FUNCTION_IMPL(Debug)
+OVR_LOG_FUNCTION_IMPL(Assert)
+#endif
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Log.h b/LibOVR/Src/Kernel/OVR_Log.h new file mode 100644 index 0000000..acc298d --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Log.h @@ -0,0 +1,193 @@ +/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_Log.h
+Content : Logging support
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Log_h
+#define OVR_Log_h
+
+#include "OVR_Types.h"
+#include <stdarg.h>
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Logging Constants
+
+// LogMaskConstants defined bit mask constants that describe what log messages
+// should be displayed.
+enum LogMaskConstants
+{
+ LogMask_Regular = 0x100,
+ LogMask_Debug = 0x200,
+ LogMask_None = 0,
+ LogMask_All = LogMask_Regular|LogMask_Debug
+};
+
+
+// LogMessageType describes the type of the log message, controls when it is
+// displayed and what prefix/suffix is given to it. Messages are subdivided into
+// regular and debug logging types. Debug logging is only generated in debug builds.
+//
+// Log_Text - General output text displayed without prefix or new-line.
+// Used in OVR libraries for general log flow messages
+// such as "Device Initialized".
+//
+// Log_Error - Error message output with "Error: %s\n", intended for
+// application/sample-level use only, in cases where an expected
+// operation failed. OVR libraries should not use this internally,
+// reporting status codes instead.
+//
+// Log_DebugText - Message without prefix or new lines; output in Debug build only.
+//
+// Log_Debug - Debug-build only message, formatted with "Debug: %s\n".
+// Intended to comment on incorrect API usage that doesn't lead
+// to crashes but can be avoided with proper use.
+// There is no Debug Error on purpose, since real errors should
+// be handled by API user.
+//
+// Log_Assert - Debug-build only message, formatted with "Assert: %s\n".
+// Intended for severe unrecoverable conditions in library
+// source code. Generated though OVR_ASSERT_MSG(c, "Text").
+
+enum LogMessageType
+{
+ // General Logging
+ Log_Text = LogMask_Regular | 0,
+ Log_Error = LogMask_Regular | 1, // "Error: %s\n".
+
+ // Debug-only messages (not generated in release build)
+ Log_DebugText = LogMask_Debug | 0,
+ Log_Debug = LogMask_Debug | 1, // "Debug: %s\n".
+ Log_Assert = LogMask_Debug | 2, // "Assert: %s\n".
+};
+
+
+// LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types
+#ifdef __GNUC__
+# define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b)))
+#else
+# define OVR_LOG_VAARG_ATTRIBUTE(a,b)
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Log
+
+// Log defines a base class interface that can be implemented to catch both
+// debug and runtime messages.
+// Debug logging can be overridden by calling Log::SetGlobalLog.
+
+class Log
+{
+ friend class System;
+public:
+ Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { }
+ virtual ~Log();
+
+ // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated.
+ enum { MaxLogBufferMessageSize = 2048 };
+
+ unsigned GetLoggingMask() const { return LoggingMask; }
+ void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; }
+
+ // This virtual function receives all the messages,
+ // developers should override this function in order to do custom logging
+ virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList);
+
+ // Call the logging function with specific message type, with no type filtering.
+ void LogMessage(LogMessageType messageType,
+ const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4);
+
+
+ // Helper used by LogMessageVarg to format the log message, writing the resulting
+ // string into buffer. It formats text based on fmt and appends prefix/new line
+ // based on LogMessageType.
+ static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType,
+ const char* fmt, va_list argList);
+
+ // Default log output implementation used by by LogMessageVarg.
+ // Debug flag may be used to re-direct output on some platforms, but doesn't
+ // necessarily disable it in release builds; that is the job of the called.
+ static void DefaultLogOutput(const char* textBuffer, bool debug);
+
+ // Determines if the specified message type is for debugging only.
+ static bool IsDebugMessage(LogMessageType messageType)
+ {
+ return (messageType & LogMask_Debug) != 0;
+ }
+
+ // *** Global APIs
+
+ // Global Log registration APIs.
+ // - Global log is used for OVR_DEBUG messages. Set global log to null (0)
+ // to disable all logging.
+ static void SetGlobalLog(Log *log);
+ static Log* GetGlobalLog();
+
+ // Returns default log singleton instance.
+ static Log* GetDefaultLog();
+
+ // Applies logMask to the default log and returns a pointer to it.
+ // By default, only Debug logging is enabled, so to avoid SDK generating console
+ // messages in user app (those are always disabled in release build,
+ // even if the flag is set). This function is useful in System constructor.
+ static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug)
+ {
+ Log* log = GetDefaultLog();
+ log->SetLoggingMask(logMask);
+ return log;
+ }
+
+private:
+ // Logging mask described by LogMaskConstants.
+ unsigned LoggingMask;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Global Logging Functions and Debug Macros
+
+// These functions will output text to global log with semantics described by
+// their LogMessageType.
+void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+
+#ifdef OVR_BUILD_DEBUG
+
+ // Debug build only logging.
+ void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+ void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+ void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+
+ // Macro to do debug logging, printf-style.
+ // An extra set of set of parenthesis must be used around arguments,
+ // as in: OVR_LOG_DEBUG(("Value %d", 2)).
+ #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0)
+ #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0)
+
+ #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0)
+
+#else
+
+ // If not in debug build, macros do nothing.
+ #define OVR_DEBUG_LOG(args) ((void)0)
+ #define OVR_DEBUG_LOG_TEXT(args) ((void)0)
+ #define OVR_ASSERT_LOG(c, args) ((void)0)
+
+#endif
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Math.cpp b/LibOVR/Src/Kernel/OVR_Math.cpp new file mode 100644 index 0000000..eaf23fe --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Math.cpp @@ -0,0 +1,153 @@ +/************************************************************************************
+
+Filename : OVR_Math.h
+Content : Implementation of 3D primitives such as vectors, matrices.
+Created : September 4, 2012
+Authors : Andrew Reisse, Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Math.h"
+
+#include <float.h>
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// ***** Math
+
+
+// Single-precision Math constants class.
+const float Math<float>::Pi = 3.1415926f;
+const float Math<float>::TwoPi = 3.1415926f * 2;
+const float Math<float>::PiOver2 = 3.1415926f / 2.0f;
+const float Math<float>::PiOver4 = 3.1415926f / 4.0f;
+const float Math<float>::E = 2.7182818f;
+
+const float Math<float>::MaxValue = FLT_MAX;
+const float Math<float>::MinPositiveValue = FLT_MIN;
+
+const float Math<float>::RadToDegreeFactor = 360.0f / Math<float>::TwoPi;
+const float Math<float>::DegreeToRadFactor = Math<float>::TwoPi / 360.0f;
+
+const float Math<float>::Tolerance = 0.00001f;
+const float Math<float>::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems
+
+
+// Double-precision Math constants class.
+const double Math<double>::Pi = 3.14159265358979;
+const double Math<double>::TwoPi = 3.14159265358979 * 2;
+const double Math<double>::PiOver2 = 3.14159265358979 / 2.0;
+const double Math<double>::PiOver4 = 3.14159265358979 / 4.0;
+const double Math<double>::E = 2.71828182845905;
+
+const double Math<double>::MaxValue = DBL_MAX;
+const double Math<double>::MinPositiveValue = DBL_MIN;
+
+const double Math<double>::RadToDegreeFactor = 360.0 / Math<double>::TwoPi;
+const double Math<double>::DegreeToRadFactor = Math<double>::TwoPi / 360.0;
+
+const double Math<double>::Tolerance = 0.00001;
+const double Math<double>::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix4f
+
+
+Matrix4f Matrix4f::LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up)
+{
+ Vector3f z = (eye - at).Normalized(); // Forward
+ Vector3f x = up.Cross(z).Normalized(); // Right
+ Vector3f y = z.Cross(x);
+
+ Matrix4f m(x.x, x.y, x.z, -(x * eye),
+ y.x, y.y, y.z, -(y * eye),
+ z.x, z.y, z.z, -(z * eye),
+ 0, 0, 0, 1 );
+ return m;
+}
+
+Matrix4f Matrix4f::LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up)
+{
+ Vector3f z = (at - eye).Normalized(); // Forward
+ Vector3f x = up.Cross(z).Normalized(); // Right
+ Vector3f y = z.Cross(x);
+
+ Matrix4f m(x.x, x.y, x.z, -(x * eye),
+ y.x, y.y, y.z, -(y * eye),
+ z.x, z.y, z.z, -(z * eye),
+ 0, 0, 0, 1 );
+ return m;
+}
+
+
+Matrix4f Matrix4f::PerspectiveLH(float yfov, float aspect, float znear, float zfar)
+{
+ Matrix4f m;
+ float tanHalfFov = tan(yfov * 0.5f);
+
+ m.M[0][0] = 1.0f / (aspect * tanHalfFov);
+ m.M[1][1] = 1.0f / tanHalfFov;
+ m.M[2][2] = zfar / (zfar - znear);
+ m.M[3][2] = 1.0f;
+ m.M[2][3] = (zfar * znear) / (znear - zfar);
+ m.M[3][3] = 0.0f;
+
+ // Note: Post-projection matrix result assumes Left-Handed coordinate system,
+ // with Y up, X right and Z forward. This supports positive z-buffer values.
+ return m;
+}
+
+
+Matrix4f Matrix4f::PerspectiveRH(float yfov, float aspect, float znear, float zfar)
+{
+ Matrix4f m;
+ float tanHalfFov = tan(yfov * 0.5f);
+
+ m.M[0][0] = 1.0f / (aspect * tanHalfFov);
+ m.M[1][1] = 1.0f / tanHalfFov;
+ m.M[2][2] = zfar / (znear - zfar);
+ // m.M[2][2] = zfar / (zfar - znear);
+ m.M[3][2] = -1.0f;
+ m.M[2][3] = (zfar * znear) / (znear - zfar);
+ m.M[3][3] = 0.0f;
+
+ // Note: Post-projection matrix result assumes Left-Handed coordinate system,
+ // with Y up, X right and Z forward. This supports positive z-buffer values.
+ // This is the case even for RHS cooridnate input.
+ return m;
+}
+
+
+/*
+OffCenterLH
+
+2*zn/(r-l) 0 0 0
+0 2*zn/(t-b) 0 0
+(l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1
+0 0 zn*zf/(zn-zf) 0
+
+*/
+
+
+Matrix4f Matrix4f::Ortho2D(float w, float h)
+{
+ Matrix4f m;
+ m.M[0][0] = 2.0f/w;
+ m.M[1][1] = -2.0f/h;
+ m.M[0][3] = -1.0;
+ m.M[1][3] = 1.0;
+ m.M[2][2] = 0;
+ return m;
+}
+
+}
diff --git a/LibOVR/Src/Kernel/OVR_Math.h b/LibOVR/Src/Kernel/OVR_Math.h new file mode 100644 index 0000000..8c5a7ba --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Math.h @@ -0,0 +1,1070 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Math.h
+Content : Implementation of 3D primitives such as vectors, matrices.
+Created : September 4, 2012
+Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Math_h
+#define OVR_Math_h
+
+#include <assert.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "OVR_Types.h"
+#include "OVR_RefCount.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// Constants for 3D world/axis definitions.
+
+// Definitions of axes for coordinate and rotation conversions.
+enum Axis
+{
+ Axis_X = 0, Axis_Y = 1, Axis_Z = 2
+};
+
+// RotateDirection describes the rotation direction around an axis, interpreted as follows:
+// CW - Clockwise while looking "down" from positive axis towards the origin.
+// CCW - Counter-clockwise while looking from the positive axis towards the origin,
+// which is in the negative axis direction.
+// CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate
+// system defines Y up, X right, and Z back (pointing out from the screen). In this
+// system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane.
+enum RotateDirection
+{
+ Rotate_CCW = 1,
+ Rotate_CW = -1
+};
+
+enum HandedSystem
+{
+ Handed_R = 1, Handed_L = -1
+};
+
+// AxisDirection describes which way the axis points. Used by WorldAxes.
+enum AxisDirection
+{
+ Axis_Up = 2,
+ Axis_Down = -2,
+ Axis_Right = 1,
+ Axis_Left = -1,
+ Axis_In = 3,
+ Axis_Out = -3
+};
+
+struct WorldAxes
+{
+ AxisDirection XAxis, YAxis, ZAxis;
+
+ WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z)
+ : XAxis(x), YAxis(y), ZAxis(z)
+ { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));}
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Math
+
+// Math class contains constants and functions. This class is a template specialized
+// per type, with Math<float> and Math<double> being distinct.
+template<class Type>
+class Math
+{
+};
+
+// Single-precision Math constants class.
+template<>
+class Math<float>
+{
+public:
+ static const float Pi;
+ static const float TwoPi;
+ static const float PiOver2;
+ static const float PiOver4;
+ static const float E;
+
+ static const float MaxValue; // Largest positive float Value
+ static const float MinPositiveValue; // Smallest possible positive value
+
+ static const float RadToDegreeFactor;
+ static const float DegreeToRadFactor;
+
+ static const float Tolerance; // 0.00001f;
+ static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems
+};
+
+// Double-precision Math constants class.
+template<>
+class Math<double>
+{
+public:
+ static const double Pi;
+ static const double TwoPi;
+ static const double PiOver2;
+ static const double PiOver4;
+ static const double E;
+
+ static const double MaxValue; // Largest positive double Value
+ static const double MinPositiveValue; // Smallest possible positive value
+
+ static const double RadToDegreeFactor;
+ static const double DegreeToRadFactor;
+
+ static const double Tolerance; // 0.00001f;
+ static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems
+};
+
+typedef Math<float> Mathf;
+typedef Math<double> Mathd;
+
+// Conversion functions between degrees and radians
+template<class FT>
+FT RadToDegree(FT rads) { return rads * Math<FT>::RadToDegreeFactor; }
+template<class FT>
+FT DegreeToRad(FT rads) { return rads * Math<FT>::DegreeToRadFactor; }
+
+template<class T>
+class Quat;
+
+//-------------------------------------------------------------------------------------
+// ***** Vector2f - 2D Vector2f
+
+// Vector2f represents a 2-dimensional vector or point in space,
+// consisting of coordinates x and y,
+
+template<class T>
+class Vector2
+{
+public:
+ T x, y;
+
+ Vector2() : x(0), y(0) { }
+ Vector2(T x_, T y_) : x(x_), y(y_) { }
+ explicit Vector2(T s) : x(s), y(s) { }
+
+ bool operator== (const Vector2& b) const { return x == b.x && y == b.y; }
+ bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; }
+
+ Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); }
+ Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; }
+ Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); }
+ Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; }
+ Vector2 operator- () const { return Vector2(-x, -y); }
+
+ // Scalar multiplication/division scales vector.
+ Vector2 operator* (T s) const { return Vector2(x*s, y*s); }
+ Vector2& operator*= (T s) { x *= s; y *= s; return *this; }
+
+ Vector2 operator/ (T s) const { T rcp = T(1)/s;
+ return Vector2(x*rcp, y*rcp); }
+ Vector2& operator/= (T s) { T rcp = T(1)/s;
+ x *= rcp; y *= rcp;
+ return *this; }
+
+ // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
+ bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance)
+ {
+ return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance);
+ }
+
+ // Dot product overload.
+ // Used to calculate angle q between two vectors among other things,
+ // as (A dot B) = |a||b|cos(q).
+ T operator* (const Vector2& b) const { return x*b.x + y*b.y; }
+
+ // Returns the angle from this vector to b, in radians.
+ T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); }
+
+ // Return Length of the vector squared.
+ T LengthSq() const { return (x * x + y * y); }
+ // Return vector length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Returns distance between two points represented by vectors.
+ T Distance(Vector2& b) const { return (*this - b).Length(); }
+
+ // Determine if this a unit vector.
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+ // Normalize, convention vector length to 1.
+ void Normalize() { *this /= Length(); }
+ // Returns normalized (unit) version of the vector without modifying itself.
+ Vector2 Normalized() const { return *this / Length(); }
+
+ // Linearly interpolates from this vector to another.
+ // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
+ Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; }
+
+ // Projects this vector onto the argument; in other words,
+ // A.Project(B) returns projection of vector A onto B.
+ Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); }
+};
+
+
+typedef Vector2<float> Vector2f;
+typedef Vector2<double> Vector2d;
+
+//-------------------------------------------------------------------------------------
+// ***** Vector3f - 3D Vector3f
+
+// Vector3f represents a 3-dimensional vector or point in space,
+// consisting of coordinates x, y and z.
+
+template<class T>
+class Vector3
+{
+public:
+ T x, y, z;
+
+ Vector3() : x(0), y(0), z(0) { }
+ Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { }
+ explicit Vector3(T s) : x(s), y(s), z(s) { }
+
+ bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; }
+ bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; }
+
+ Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); }
+ Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; }
+ Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); }
+ Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; }
+ Vector3 operator- () const { return Vector3(-x, -y, -z); }
+
+ // Scalar multiplication/division scales vector.
+ Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); }
+ Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; }
+
+ Vector3 operator/ (T s) const { T rcp = T(1)/s;
+ return Vector3(x*rcp, y*rcp, z*rcp); }
+ Vector3& operator/= (T s) { T rcp = T(1)/s;
+ x *= rcp; y *= rcp; z *= rcp;
+ return *this; }
+
+ // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
+ bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance)
+ {
+ return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance);
+ }
+
+ // Dot product overload.
+ // Used to calculate angle q between two vectors among other things,
+ // as (A dot B) = |a||b|cos(q).
+ T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; }
+
+ // Compute cross product, which generates a normal vector.
+ // Direction vector can be determined by right-hand rule: Pointing index finder in
+ // direction a and middle finger in direction b, thumb will point in a.Cross(b).
+ Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y,
+ z*b.x - x*b.z,
+ x*b.y - y*b.x); }
+
+ // Returns the angle from this vector to b, in radians.
+ T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); }
+
+ // Return Length of the vector squared.
+ T LengthSq() const { return (x * x + y * y + z * z); }
+ // Return vector length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Returns distance between two points represented by vectors.
+ T Distance(Vector3& b) const { return (*this - b).Length(); }
+
+ // Determine if this a unit vector.
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+ // Normalize, convention vector length to 1.
+ void Normalize() { *this /= Length(); }
+ // Returns normalized (unit) version of the vector without modifying itself.
+ Vector3 Normalized() const { return *this / Length(); }
+
+ // Linearly interpolates from this vector to another.
+ // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
+ Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; }
+
+ // Projects this vector onto the argument; in other words,
+ // A.Project(B) returns projection of vector A onto B.
+ Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); }
+};
+
+
+typedef Vector3<float> Vector3f;
+typedef Vector3<double> Vector3d;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix4f
+
+// Matrix4f is a 4x4 matrix used for 3d transformations and projections.
+// Translation stored in the last column.
+// The matrix is stored in row-major order in memory, meaning that values
+// of the first row are stored before the next one.
+//
+// The arrangement of the matrix is chosen to be in Right-Handed
+// coordinate system and counterclockwise rotations when looking down
+// the axis
+//
+// Transformation Order:
+// - Transformations are applied from right to left, so the expression
+// M1 * M2 * M3 * V means that the vector V is transformed by M3 first,
+// followed by M2 and M1.
+//
+// Coordinate system: Right Handed
+//
+// Rotations: Counterclockwise when looking down the axis. All angles are in radians.
+//
+// | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector.
+// | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector.
+// | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector.
+// | 30 31 32 33 |
+//
+// The basis vectors are first three columns.
+
+class Matrix4f
+{
+ static Matrix4f IdentityValue;
+
+public:
+ float M[4][4];
+
+ enum NoInitType { NoInit };
+
+ // Construct with no memory initialization.
+ Matrix4f(NoInitType) { }
+
+ // By default, we construct identity matrix.
+ Matrix4f()
+ {
+ SetIdentity();
+ }
+
+ Matrix4f(float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34;
+ M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44;
+ }
+
+ Matrix4f(float m11, float m12, float m13,
+ float m21, float m22, float m23,
+ float m31, float m32, float m33)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0;
+ M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1;
+ }
+
+ static const Matrix4f& Identity() { return IdentityValue; }
+
+ void SetIdentity()
+ {
+ M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1;
+ M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0;
+ M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0;
+ M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0;
+ }
+
+ // Multiplies two matrices into destination with minimum copying.
+ static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b)
+ {
+ OVR_ASSERT((d != &a) && (d != &b));
+ int i = 0;
+ do {
+ d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0];
+ d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1];
+ d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2];
+ d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3];
+ } while((++i) < 4);
+
+ return *d;
+ }
+
+ Matrix4f operator* (const Matrix4f& b) const
+ {
+ Matrix4f result(Matrix4f::NoInit);
+ Multiply(&result, *this, b);
+ return result;
+ }
+
+ Matrix4f& operator*= (const Matrix4f& b)
+ {
+ return Multiply(this, Matrix4f(*this), b);
+ }
+
+ Matrix4f operator* (float s) const
+ {
+ return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s,
+ M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s,
+ M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s,
+ M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s);
+ }
+
+ Matrix4f& operator*= (float s)
+ {
+ M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s;
+ M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s;
+ M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s;
+ M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s;
+ return *this;
+ }
+
+ Vector3f Transform(const Vector3f& v) const
+ {
+ return Vector3f(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3],
+ M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3],
+ M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]);
+ }
+
+ Matrix4f Transposed() const
+ {
+ return Matrix4f(M[0][0], M[1][0], M[2][0], M[3][0],
+ M[0][1], M[1][1], M[2][1], M[3][1],
+ M[0][2], M[1][2], M[2][2], M[3][2],
+ M[0][3], M[1][3], M[2][3], M[3][3]);
+ }
+
+ void Transpose()
+ {
+ *this = Transposed();
+ }
+
+
+ float SubDet (const int* rows, const int* cols) const
+ {
+ return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]])
+ - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]])
+ + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]);
+ }
+
+ float Cofactor(int I, int J) const
+ {
+ const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}};
+ return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]);
+ }
+
+ float Determinant() const
+ {
+ return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3);
+ }
+
+ Matrix4f Adjugated() const
+ {
+ return Matrix4f(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0),
+ Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1),
+ Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2),
+ Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3));
+ }
+
+ Matrix4f Inverted() const
+ {
+ float det = Determinant();
+ assert(det != 0);
+ return Adjugated() * (1.0f/det);
+ }
+
+ void Invert()
+ {
+ *this = Inverted();
+ }
+
+ //AnnaSteve:
+ // a,b,c, are the YawPitchRoll angles to be returned
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A3
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D, HandedSystem S>
+ void ToEulerAngles(float *a, float *b, float *c)
+ {
+ OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3));
+
+ float psign = -1.0f;
+ if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation
+ psign = 1.0f;
+
+ float pm = psign*M[A1][A3];
+ if (pm < -1.0f + Math<float>::SingularityRadius)
+ { // South pole singularity
+ *a = 0.0f;
+ *b = -S*D*Math<float>::PiOver2;
+ *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
+ }
+ else if (pm > 1.0 - Math<float>::SingularityRadius)
+ { // North pole singularity
+ *a = 0.0f;
+ *b = S*D*Math<float>::PiOver2;
+ *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
+ }
+ else
+ { // Normal case (nonsingular)
+ *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] );
+ *b = S*D*asin(pm);
+ *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] );
+ }
+
+ return;
+ }
+
+ //AnnaSteve:
+ // a,b,c, are the YawPitchRoll angles to be returned
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A1
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, RotateDirection D, HandedSystem S>
+ void ToEulerAnglesABA(float *a, float *b, float *c)
+ {
+ OVR_COMPILER_ASSERT(A1 != A2);
+
+ // Determine the axis that was not supplied
+ int m = 3 - A1 - A2;
+
+ float psign = -1.0f;
+ if ((A1 + 1) % 3 == A2) // Determine whether even permutation
+ psign = 1.0f;
+
+ float c2 = M[A1][A1];
+ if (c2 < -1.0 + Math<float>::SingularityRadius)
+ { // South pole singularity
+ *a = 0.0f;
+ *b = S*D*Math<float>::Pi;
+ *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
+ }
+ else if (c2 > 1.0 - Math<float>::SingularityRadius)
+ { // North pole singularity
+ *a = 0.0f;
+ *b = 0.0f;
+ *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
+ }
+ else
+ { // Normal case (nonsingular)
+ *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]);
+ *b = S*D*acos(c2);
+ *c = S*D*atan2( M[A1][A2],psign*M[A1][m]);
+ }
+ return;
+ }
+
+ // Creates a matrix that converts the vertices from one coordinate system
+ // to another.
+ //
+ static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from)
+ {
+ // Holds axis values from the 'to' structure
+ int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis };
+
+ // The inverse of the toArray
+ int inv[4];
+ inv[0] = inv[abs(to.XAxis)] = 0;
+ inv[abs(to.YAxis)] = 1;
+ inv[abs(to.ZAxis)] = 2;
+
+ Matrix4f m(0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0);
+
+ // Only three values in the matrix need to be changed to 1 or -1.
+ m.M[inv[abs(from.XAxis)]][0] = float(from.XAxis/toArray[inv[abs(from.XAxis)]]);
+ m.M[inv[abs(from.YAxis)]][1] = float(from.YAxis/toArray[inv[abs(from.YAxis)]]);
+ m.M[inv[abs(from.ZAxis)]][2] = float(from.ZAxis/toArray[inv[abs(from.ZAxis)]]);
+ return m;
+ }
+
+
+
+ static Matrix4f Translation(const Vector3f& v)
+ {
+ Matrix4f t;
+ t.M[0][3] = v.x;
+ t.M[1][3] = v.y;
+ t.M[2][3] = v.z;
+ return t;
+ }
+
+ static Matrix4f Translation(float x, float y, float z = 0.0f)
+ {
+ Matrix4f t;
+ t.M[0][3] = x;
+ t.M[1][3] = y;
+ t.M[2][3] = z;
+ return t;
+ }
+
+ static Matrix4f Scaling(const Vector3f& v)
+ {
+ Matrix4f t;
+ t.M[0][0] = v.x;
+ t.M[1][1] = v.y;
+ t.M[2][2] = v.z;
+ return t;
+ }
+
+ static Matrix4f Scaling(float x, float y, float z)
+ {
+ Matrix4f t;
+ t.M[0][0] = x;
+ t.M[1][1] = y;
+ t.M[2][2] = z;
+ return t;
+ }
+
+ static Matrix4f Scaling(float s)
+ {
+ Matrix4f t;
+ t.M[0][0] = s;
+ t.M[1][1] = s;
+ t.M[2][2] = s;
+ return t;
+ }
+
+
+
+ //AnnaSteve : Just for quick testing. Not for final API. Need to remove case.
+ static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s)
+ {
+ float sina = s * d *sin(angle);
+ float cosa = cos(angle);
+
+ switch(A)
+ {
+ case Axis_X:
+ return Matrix4f(1, 0, 0,
+ 0, cosa, -sina,
+ 0, sina, cosa);
+ case Axis_Y:
+ return Matrix4f(cosa, 0, sina,
+ 0, 1, 0,
+ -sina, 0, cosa);
+ case Axis_Z:
+ return Matrix4f(cosa, -sina, 0,
+ sina, cosa, 0,
+ 0, 0, 1);
+ }
+ }
+
+
+ // Creates a rotation matrix rotating around the X axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4f RotationX(float angle)
+ {
+ float sina = sin(angle);
+ float cosa = cos(angle);
+ return Matrix4f(1, 0, 0,
+ 0, cosa, -sina,
+ 0, sina, cosa);
+ }
+
+ // Creates a rotation matrix rotating around the Y axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4f RotationY(float angle)
+ {
+ float sina = sin(angle);
+ float cosa = cos(angle);
+ return Matrix4f(cosa, 0, sina,
+ 0, 1, 0,
+ -sina, 0, cosa);
+ }
+
+ // Creates a rotation matrix rotating around the Z axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4f RotationZ(float angle)
+ {
+ float sina = sin(angle);
+ float cosa = cos(angle);
+ return Matrix4f(cosa, -sina, 0,
+ sina, cosa, 0,
+ 0, 0, 1);
+ }
+
+
+ // LookAtRH creates a View transformation matrix for right-handed coordinate system.
+ // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up'
+ // specifying the up vector. The resulting matrix should be used with PerspectiveRH
+ // projection.
+ static Matrix4f LookAtRH(const Vector3f& eye, const Vector3f& at, const Vector3f& up);
+
+ // LookAtLH creates a View transformation matrix for left-handed coordinate system.
+ // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up'
+ // specifying the up vector.
+ static Matrix4f LookAtLH(const Vector3f& eye, const Vector3f& at, const Vector3f& up);
+
+
+ // PerspectiveRH creates a right-handed perspective projection matrix that can be
+ // used with the Oculus sample renderer.
+ // yfov - Specifies vertical field of view in radians.
+ // aspect - Screen aspect ration, which is usually width/height for square pixels.
+ // Note that xfov = yfov * aspect.
+ // znear - Absolute value of near Z clipping clipping range.
+ // zfar - Absolute value of far Z clipping clipping range (larger then near).
+ // Even though RHS usually looks in the direction of negative Z, positive values
+ // are expected for znear and zfar.
+ static Matrix4f PerspectiveRH(float yfov, float aspect, float znear, float zfar);
+
+
+ // PerspectiveRH creates a left-handed perspective projection matrix that can be
+ // used with the Oculus sample renderer.
+ // yfov - Specifies vertical field of view in radians.
+ // aspect - Screen aspect ration, which is usually width/height for square pixels.
+ // Note that xfov = yfov * aspect.
+ // znear - Absolute value of near Z clipping clipping range.
+ // zfar - Absolute value of far Z clipping clipping range (larger then near).
+ static Matrix4f PerspectiveLH(float yfov, float aspect, float znear, float zfar);
+
+
+ static Matrix4f Ortho2D(float w, float h);
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Quat
+
+// Quatf represents a quaternion class used for rotations.
+//
+// Quaternion multiplications are done in right-to-left order, to match the
+// behavior of matrices.
+
+
+template<class T>
+class Quat
+{
+public:
+ // w + Xi + Yj + Zk
+ T x, y, z, w;
+
+ Quat() : x(0), y(0), z(0), w(1) {}
+ Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {}
+
+
+ // Constructs rotation quaternion around the axis.
+ Quat(const Vector3<T>& axis, T angle)
+ {
+ Vector3<T> unitAxis = axis.Normalized();
+ T sinHalfAngle = sin(angle * T(0.5));
+
+ w = cos(angle * T(0.5));
+ x = unitAxis.x * sinHalfAngle;
+ y = unitAxis.y * sinHalfAngle;
+ z = unitAxis.z * sinHalfAngle;
+ }
+
+ //AnnaSteve:
+ void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s)
+ {
+ T sinHalfAngle = s * d *sin(angle * (T)0.5);
+ T v[3];
+ v[0] = v[1] = v[2] = (T)0;
+ v[A] = sinHalfAngle;
+ //return Quat(v[0], v[1], v[2], cos(angle * (T)0.5));
+ w = cos(angle * (T)0.5);
+ x = v[0];
+ y = v[1];
+ z = v[2];
+ }
+
+
+ void GetAxisAngle(Vector3<T>* axis, T* angle) const
+ {
+ if (LengthSq() > Math<T>::Tolerance * Math<T>::Tolerance)
+ {
+ *axis = Vector3<T>(x, y, z).Normalized();
+ *angle = 2 * acos(w);
+ }
+ else
+ {
+ *axis = Vector3<T>(1, 0, 0);
+ *angle= 0;
+ }
+ }
+
+ bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; }
+ bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; }
+
+ Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); }
+ Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; }
+ Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); }
+ Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; }
+
+ Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); }
+ Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; }
+ Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); }
+ Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; }
+
+ // Get Imaginary part vector
+ Vector3<T> Imag() const { return Vector3<T>(x,y,z); }
+
+ // Get quaternion length.
+ T Length() const { return sqrt(x * x + y * y + z * z + w * w); }
+ // Get quaternion length squared.
+ T LengthSq() const { return (x * x + y * y + z * z + w * w); }
+ // Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure)
+ T Distance(const Quat& q) const
+ {
+ T d1 = (*this - q).Length();
+ T d2 = (*this + q).Length(); // Antipoldal point check
+ return (d1 < d2) ? d1 : d2;
+ }
+ T DistanceSq(const Quat& q) const
+ {
+ T d1 = (*this - q).LengthSq();
+ T d2 = (*this + q).LengthSq(); // Antipoldal point check
+ return (d1 < d2) ? d1 : d2;
+ }
+
+ // Normalize
+ bool IsNormalized() const { return fabs(LengthSq() - 1) < Math<T>::Tolerance; }
+ void Normalize() { *this /= Length(); }
+ Quat Normalized() const { return *this / Length(); }
+
+ // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized.
+ Quat Conj() const { return Quat(-x, -y, -z, w); }
+
+ // AnnaSteve fixed: order of quaternion multiplication
+ // Quaternion multiplication. Combines quaternion rotations, performing the one on the
+ // right hand side first.
+ Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y,
+ w * b.y - x * b.z + y * b.w + z * b.x,
+ w * b.z + x * b.y - y * b.x + z * b.w,
+ w * b.w - x * b.x - y * b.y - z * b.z); }
+
+ //
+ // this^p normalized; same as rotating by this p times.
+ Quat PowNormalized(T p) const
+ {
+ Vector3<T> v;
+ T a;
+ GetAxisAngle(&v, &a);
+ return Quat(v, a * p);
+ }
+
+ // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise,
+ // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1.
+ Vector3<T> Rotate(const Vector3<T>& v) const
+ {
+ return ((*this * Quat<T>(v.x, v.y, v.z, 0)) * Inverted()).Imag();
+ }
+
+
+ // Inversed quaternion rotates in the opposite direction.
+ Quat Inverted() const
+ {
+ return Quat(-x, -y, -z, w);
+ }
+
+ // Sets this quaternion to the one rotates in the opposite direction.
+ void Invert() const
+ {
+ *this = Quat(-x, -y, -z, w);
+ }
+
+ // Converting quaternion to matrix.
+ operator Matrix4f() const
+ {
+ T ww = w*w;
+ T xx = x*x;
+ T yy = y*y;
+ T zz = z*z;
+
+ return Matrix4f(float(ww + xx - yy - zz), float(T(2) * (x*y - w*z)), float(T(2) * (x*z + w*y)),
+ float(T(2) * (x*y + w*z)), float(ww - xx + yy - zz), float(T(2) * (y*z - w*x)),
+ float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) );
+ }
+
+
+ // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of
+ // axis rotations and the specified coordinate system. Right-handed coordinate system
+ // is the default, with CCW rotations while looking in the negative axis direction.
+ // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A3
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D, HandedSystem S>
+ void GetEulerAngles(T *a, T *b, T *c)
+ {
+ OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3));
+
+ T Q[3] = { x, y, z }; //Quaternion components x,y,z
+
+ T ww = w*w;
+ T Q11 = Q[A1]*Q[A1];
+ T Q22 = Q[A2]*Q[A2];
+ T Q33 = Q[A3]*Q[A3];
+
+ T psign = T(-1.0);
+ // Determine whether even permutation
+ if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3))
+ psign = T(1.0);
+
+ T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]);
+
+ if (s2 < (T)-1.0 + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = T(0.0);
+ *b = -S*D*Math<T>::PiOver2;
+ *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]),
+ ww + Q22 - Q11 - Q33 );
+ }
+ else if (s2 > (T)1.0 - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = (T)0.0;
+ *b = S*D*Math<T>::PiOver2;
+ *c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]),
+ ww + Q22 - Q11 - Q33);
+ }
+ else
+ {
+ *a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]),
+ ww + Q33 - Q11 - Q22);
+ *b = S*D*asin(s2);
+ *c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]),
+ ww + Q11 - Q22 - Q33);
+ }
+ return;
+ }
+
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D>
+ void GetEulerAngles(T *a, T *b, T *c)
+ { GetEulerAngles<A1, A2, A3, D, Handed_R>(a, b, c); }
+
+ template <Axis A1, Axis A2, Axis A3>
+ void GetEulerAngles(T *a, T *b, T *c)
+ { GetEulerAngles<A1, A2, A3, Rotate_CCW, Handed_R>(a, b, c); }
+
+
+ // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of
+ // axis rotations and the specified coordinate system. Right-handed coordinate system
+ // is the default, with CCW rotations while looking in the negative axis direction.
+ // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A1
+ // Rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, RotateDirection D, HandedSystem S>
+ void GetEulerAnglesABA(T *a, T *b, T *c)
+ {
+ OVR_COMPILER_ASSERT(A1 != A2);
+
+ T Q[3] = {x, y, z}; // Quaternion components
+
+ // Determine the missing axis that was not supplied
+ int m = 3 - A1 - A2;
+
+ T ww = w*w;
+ T Q11 = Q[A1]*Q[A1];
+ T Q22 = Q[A2]*Q[A2];
+ T Qmm = Q[m]*Q[m];
+
+ T psign = T(-1.0);
+ if ((A1 + 1) % 3 == A2) // Determine whether even permutation
+ {
+ psign = (T)1.0;
+ }
+
+ T c2 = ww + Q11 - Q22 - Qmm;
+ if (c2 < (T)-1.0 + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = (T)0.0;
+ *b = S*D*Math<T>::Pi;
+ *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]),
+ ww + Q22 - Q11 - Qmm);
+ }
+ else if (c2 > (T)1.0 - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = (T)0.0;
+ *b = (T)0.0;
+ *c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]),
+ ww + Q22 - Q11 - Qmm);
+ }
+ else
+ {
+ *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2],
+ w*Q[A2] -psign*Q[A1]*Q[m]);
+ *b = S*D*acos(c2);
+ *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2],
+ w*Q[A2] + psign*Q[A1]*Q[m]);
+ }
+ return;
+ }
+};
+
+
+typedef Quat<float> Quatf;
+typedef Quat<double> Quatd;
+
+//-------------------------------------------------------------------------------------
+// ***** Plane
+
+// Consists of a normal vector and distance from the origin where the plane is located.
+
+template<class T>
+class Plane : public RefCountBase<Plane<T> >
+{
+public:
+ Vector3<T> N;
+ T D;
+
+ Plane() : D(0) {}
+
+ // Normals must already be normalized
+ Plane(const Vector3<T>& n, T d) : N(n), D(d) {}
+ Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {}
+
+ // construct from a point on the plane and the normal
+ Plane(const Vector3<T>& p, const Vector3<T>& n) : N(n), D(-(p * n)) {}
+
+ // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane).
+ T TestSide(const Vector3<T>& p) const
+ {
+ return (N * p) + D;
+ }
+
+ Plane<T> Flipped() const
+ {
+ return Plane(-N, -D);
+ }
+
+ void Flip()
+ {
+ N = -N;
+ D = -D;
+ }
+
+ bool operator==(const Plane<T>& rhs) const
+ {
+ return (this->D == rhs.D && this->N == rhs.N);
+ }
+};
+
+typedef Plane<float> Planef;
+
+}
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_RefCount.cpp b/LibOVR/Src/Kernel/OVR_RefCount.cpp new file mode 100644 index 0000000..a1171fa --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_RefCount.cpp @@ -0,0 +1,100 @@ +/************************************************************************************
+
+Filename : OVR_RefCount.cpp
+Content : Reference counting implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_RefCount.h"
+#include "OVR_Atomic.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+#ifdef OVR_CC_ARM
+void* ReturnArg0(void* p)
+{
+ return p;
+}
+#endif
+
+// ***** Reference Count Base implementation
+
+RefCountImplCore::~RefCountImplCore()
+{
+ // RefCount can be either 1 or 0 here.
+ // 0 if Release() was properly called.
+ // 1 if the object was declared on stack or as an aggregate.
+ OVR_ASSERT(RefCount <= 1);
+}
+
+#ifdef OVR_BUILD_DEBUG
+void RefCountImplCore::reportInvalidDelete(void *pmem)
+{
+ OVR_DEBUG_LOG(
+ ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem));
+ OVR_ASSERT(0);
+}
+#endif
+
+RefCountNTSImplCore::~RefCountNTSImplCore()
+{
+ // RefCount can be either 1 or 0 here.
+ // 0 if Release() was properly called.
+ // 1 if the object was declared on stack or as an aggregate.
+ OVR_ASSERT(RefCount <= 1);
+}
+
+#ifdef OVR_BUILD_DEBUG
+void RefCountNTSImplCore::reportInvalidDelete(void *pmem)
+{
+ OVR_DEBUG_LOG(
+ ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem));
+ OVR_ASSERT(0);
+}
+#endif
+
+
+// *** Thread-Safe RefCountImpl
+
+void RefCountImpl::AddRef()
+{
+ AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, 1);
+}
+void RefCountImpl::Release()
+{
+ if ((AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ delete this;
+}
+
+// *** Thread-Safe RefCountVImpl w/virtual AddRef/Release
+
+void RefCountVImpl::AddRef()
+{
+ AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, 1);
+}
+void RefCountVImpl::Release()
+{
+ if ((AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ delete this;
+}
+
+// *** NON-Thread-Safe RefCountImpl
+
+void RefCountNTSImpl::Release() const
+{
+ RefCount--;
+ if (RefCount == 0)
+ delete this;
+}
+
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_RefCount.h b/LibOVR/Src/Kernel/OVR_RefCount.h new file mode 100644 index 0000000..8622050 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_RefCount.h @@ -0,0 +1,522 @@ +/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_RefCount.h
+Content : Reference counting implementation headers
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_RefCount_h
+#define OVR_RefCount_h
+
+#include "OVR_Types.h"
+#include "OVR_Allocator.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Reference Counting
+
+// There are three types of reference counting base classes:
+//
+// RefCountBase - Provides thread-safe reference counting (Default).
+// RefCountBaseNTS - Non Thread Safe version of reference counting.
+
+
+// ***** Declared classes
+
+template<class C>
+class RefCountBase;
+template<class C>
+class RefCountBaseNTS;
+
+class RefCountImpl;
+class RefCountNTSImpl;
+
+
+//-----------------------------------------------------------------------------------
+// ***** Implementation For Reference Counting
+
+// RefCountImplCore holds RefCount value and defines a few utility
+// functions shared by all implementations.
+
+class RefCountImplCore
+{
+protected:
+ volatile int RefCount;
+
+public:
+ // RefCountImpl constructor always initializes RefCount to 1 by default.
+ OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { }
+
+ // Need virtual destructor
+ // This: 1. Makes sure the right destructor's called.
+ // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem()
+ virtual ~RefCountImplCore();
+
+ // Debug method only.
+ int GetRefCount() const { return RefCount; }
+
+ // This logic is used to detect invalid 'delete' calls of reference counted
+ // objects. Direct delete calls are not allowed on them unless they come in
+ // internally from Release.
+#ifdef OVR_BUILD_DEBUG
+ static void OVR_CDECL reportInvalidDelete(void *pmem);
+ inline static void checkInvalidDelete(RefCountImplCore *pmem)
+ {
+ if (pmem->RefCount != 0)
+ reportInvalidDelete(pmem);
+ }
+#else
+ inline static void checkInvalidDelete(RefCountImplCore *) { }
+#endif
+
+ // Base class ref-count content should not be copied.
+ void operator = (const RefCountImplCore &) { }
+};
+
+class RefCountNTSImplCore
+{
+protected:
+ mutable int RefCount;
+
+public:
+ // RefCountImpl constructor always initializes RefCount to 1 by default.
+ OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { }
+
+ // Need virtual destructor
+ // This: 1. Makes sure the right destructor's called.
+ // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem()
+ virtual ~RefCountNTSImplCore();
+
+ // Debug method only.
+ int GetRefCount() const { return RefCount; }
+
+ // This logic is used to detect invalid 'delete' calls of reference counted
+ // objects. Direct delete calls are not allowed on them unless they come in
+ // internally from Release.
+#ifdef OVR_BUILD_DEBUG
+ static void OVR_CDECL reportInvalidDelete(void *pmem);
+ OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem)
+ {
+ if (pmem->RefCount != 0)
+ reportInvalidDelete(pmem);
+ }
+#else
+ OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { }
+#endif
+
+ // Base class ref-count content should not be copied.
+ void operator = (const RefCountNTSImplCore &) { }
+};
+
+
+
+// RefCountImpl provides Thread-Safe implementation of reference counting, so
+// it should be used by default in most places.
+
+class RefCountImpl : public RefCountImplCore
+{
+public:
+ // Thread-Safe Ref-Count Implementation.
+ void AddRef();
+ void Release();
+};
+
+// RefCountVImpl provides Thread-Safe implementation of reference counting, plus,
+// virtual AddRef and Release.
+
+class RefCountVImpl : public RefCountImplCore
+{
+public:
+ // Thread-Safe Ref-Count Implementation.
+ virtual void AddRef();
+ virtual void Release();
+};
+
+
+// RefCountImplNTS provides Non-Thread-Safe implementation of reference counting,
+// which is slightly more efficient since it doesn't use atomics.
+
+class RefCountNTSImpl : public RefCountNTSImplCore
+{
+public:
+ OVR_FORCE_INLINE void AddRef() const { RefCount++; }
+ void Release() const;
+};
+
+
+
+// RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking
+// to the reference counting implementation. Base must be one of the RefCountImpl classes.
+
+template<class Base>
+class RefCountBaseStatImpl : public Base
+{
+public:
+ RefCountBaseStatImpl() { }
+
+ // *** Override New and Delete
+
+ // DOM-IGNORE-BEGIN
+ // Undef new temporarily if it is being redefined
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+#ifdef OVR_BUILD_DEBUG
+ // Custom check used to detect incorrect calls of 'delete' on ref-counted objects.
+ #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \
+ do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0)
+#else
+ #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p)
+#endif
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE)
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+ // OVR_BUILD_DEFINE_NEW
+ // DOM-IGNORE-END
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// *** End user RefCountBase<> classes
+
+
+// RefCountBase is a base class for classes that require thread-safe reference
+// counting; it also overrides the new and delete operators to use MemoryHeap.
+//
+// Reference counted objects start out with RefCount value of 1. Further lifetime
+// management is done through the AddRef() and Release() methods, typically
+// hidden by Ptr<>.
+
+template<class C>
+class RefCountBase : public RefCountBaseStatImpl<RefCountImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl<RefCountImpl>() { }
+};
+
+// RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release
+
+template<class C>
+class RefCountBaseV : public RefCountBaseStatImpl<RefCountVImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl<RefCountVImpl>() { }
+};
+
+
+// RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference
+// counting; it also overrides the new and delete operators to use MemoryHeap.
+// This class should only be used if all pointers to it are known to be assigned,
+// destroyed and manipulated within one thread.
+//
+// Reference counted objects start out with RefCount value of 1. Further lifetime
+// management is done through the AddRef() and Release() methods, typically
+// hidden by Ptr<>.
+
+template<class C>
+class RefCountBaseNTS : public RefCountBaseStatImpl<RefCountNTSImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl<RefCountNTSImpl>() { }
+};
+
+//-----------------------------------------------------------------------------------
+// ***** Pickable template pointer
+enum PickType { PickValue };
+
+template <typename T>
+class Pickable
+{
+public:
+ Pickable() : pV(NULL) {}
+ explicit Pickable(T* p) : pV(p) {}
+ Pickable(T* p, PickType) : pV(p)
+ {
+ OVR_ASSERT(pV);
+ if (pV)
+ pV->AddRef();
+ }
+ template <typename OT>
+ Pickable(const Pickable<OT>& other) : pV(other.GetPtr()) {}
+
+public:
+ Pickable& operator =(const Pickable& other)
+ {
+ OVR_ASSERT(pV == NULL);
+ pV = other.pV;
+ // Extra check.
+ //other.pV = NULL;
+ return *this;
+ }
+
+public:
+ T* GetPtr() const { return pV; }
+ T* operator->() const
+ {
+ return pV;
+ }
+ T& operator*() const
+ {
+ OVR_ASSERT(pV);
+ return *pV;
+ }
+
+private:
+ T* pV;
+};
+
+template <typename T>
+OVR_FORCE_INLINE
+Pickable<T> MakePickable(T* p)
+{
+ return Pickable<T>(p);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Ref-Counted template pointer
+
+// Automatically AddRefs and Releases interfaces
+
+void* ReturnArg0(void* p);
+
+template<class C>
+class Ptr
+{
+#ifdef OVR_CC_ARM
+ static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); }
+#endif
+
+protected:
+ C *pObject;
+
+public:
+
+ // Constructors
+ OVR_FORCE_INLINE Ptr() : pObject(0)
+ { }
+#ifdef OVR_CC_ARM
+ OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj))
+#else
+ OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj)
+#endif
+ { }
+ OVR_FORCE_INLINE Ptr(Pickable<C> v) : pObject(v.GetPtr())
+ {
+ // No AddRef() on purpose.
+ }
+ OVR_FORCE_INLINE Ptr(Ptr<C>& other, PickType) : pObject(other.pObject)
+ {
+ other.pObject = NULL;
+ // No AddRef() on purpose.
+ }
+ OVR_FORCE_INLINE Ptr(C *pobj)
+ {
+ if (pobj) pobj->AddRef();
+ pObject = pobj;
+ }
+ OVR_FORCE_INLINE Ptr(const Ptr<C> &src)
+ {
+ if (src.pObject) src.pObject->AddRef();
+ pObject = src.pObject;
+ }
+
+ template<class R>
+ OVR_FORCE_INLINE Ptr(Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ pObject = src;
+ }
+ template<class R>
+ OVR_FORCE_INLINE Ptr(Pickable<R> v) : pObject(v.GetPtr())
+ {
+ // No AddRef() on purpose.
+ }
+
+ // Destructor
+ OVR_FORCE_INLINE ~Ptr()
+ {
+ if (pObject) pObject->Release();
+ }
+
+ // Compares
+ OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; }
+ OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; }
+
+ OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; }
+ OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; }
+
+
+ OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; }
+
+ // Assignment
+ template<class R>
+ OVR_FORCE_INLINE const Ptr<C>& operator = (const Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+ // Specialization
+ OVR_FORCE_INLINE const Ptr<C>& operator = (const Ptr<C> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+
+ OVR_FORCE_INLINE const Ptr<C>& operator = (C *psrc)
+ {
+ if (psrc) psrc->AddRef();
+ if (pObject) pObject->Release();
+ pObject = psrc;
+ return *this;
+ }
+ OVR_FORCE_INLINE const Ptr<C>& operator = (C &src)
+ {
+ if (pObject) pObject->Release();
+ pObject = &src;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& operator = (Pickable<C> src)
+ {
+ return Pick(src);
+ }
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& operator = (Pickable<R> src)
+ {
+ return Pick(src);
+ }
+
+ // Set Assignment
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(const Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+ // Specialization
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(const Ptr<C> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(C *psrc)
+ {
+ if (psrc) psrc->AddRef();
+ if (pObject) pObject->Release();
+ pObject = psrc;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(C &src)
+ {
+ if (pObject) pObject->Release();
+ pObject = &src;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(Pickable<C> src)
+ {
+ return Pick(src);
+ }
+
+ // Nulls ref-counted pointer without decrement
+ OVR_FORCE_INLINE void NullWithoutRelease()
+ {
+ pObject = 0;
+ }
+
+ // Clears the pointer to the object
+ OVR_FORCE_INLINE void Clear()
+ {
+ if (pObject) pObject->Release();
+ pObject = 0;
+ }
+
+ // Obtain pointer reference directly, for D3D interfaces
+ OVR_FORCE_INLINE C*& GetRawRef() { return pObject; }
+
+ // Access Operators
+ OVR_FORCE_INLINE C* GetPtr() const { return pObject; }
+ OVR_FORCE_INLINE C& operator * () const { return *pObject; }
+ OVR_FORCE_INLINE C* operator -> () const { return pObject; }
+ // Conversion
+ OVR_FORCE_INLINE operator C* () const { return pObject; }
+
+ // Pickers.
+
+ // Pick a value.
+ OVR_FORCE_INLINE Ptr<C>& Pick(Ptr<C>& other)
+ {
+ if (&other != this)
+ {
+ if (pObject) pObject->Release();
+ pObject = other.pObject;
+ other.pObject = 0;
+ }
+
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& Pick(Pickable<C> v)
+ {
+ if (v.GetPtr() != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = v.GetPtr();
+ }
+
+ return *this;
+ }
+
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& Pick(Pickable<R> v)
+ {
+ if (v.GetPtr() != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = v.GetPtr();
+ }
+
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& Pick(C* p)
+ {
+ if (p != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = p;
+ }
+
+ return *this;
+ }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Std.cpp b/LibOVR/Src/Kernel/OVR_Std.cpp new file mode 100644 index 0000000..088f9e9 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Std.cpp @@ -0,0 +1,1025 @@ +/************************************************************************************
+
+Filename : OVR_Std.cpp
+Content : Standard C function implementation
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+// localeconv() call in OVR_strtod()
+#include <locale.h>
+
+namespace OVR {
+
+// Source for functions not available on all platforms is included here.
+
+// Case insensitive compare implemented in platform-specific way.
+int OVR_CDECL OVR_stricmp(const char* a, const char* b)
+{
+#if defined(OVR_OS_WIN32)
+ #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_stricmp(a, b);
+ #else
+ return ::stricmp(a, b);
+ #endif
+
+#else
+ return strcasecmp(a, b);
+#endif
+}
+
+int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_strnicmp(a, b, count);
+#else
+ return ::strnicmp(a, b, count);
+#endif
+
+#else
+ return strncasecmp(a, b, count);
+#endif
+}
+
+wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcscpy_s(dest, destsize, src);
+ return dest;
+#elif defined(OVR_OS_WIN32)
+ OVR_UNUSED(destsize);
+ wcscpy(dest, src);
+ return dest;
+#else
+ UPInt l = OVR_wcslen(src) + 1; // incl term null
+ l = (l < destsize) ? l : destsize;
+ memcpy(dest, src, l * sizeof(wchar_t));
+ return dest;
+#endif
+}
+
+wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcsncpy_s(dest, destsize, src, count);
+ return dest;
+#else
+ UPInt srclen = OVR_wcslen(src);
+ UPInt l = Alg::Min(srclen, count);
+ l = (l < destsize) ? l : destsize;
+ memcpy(dest, src, l * sizeof(wchar_t));
+ if (count > srclen)
+ {
+ UPInt remLen = Alg::Min(destsize - l, (count - srclen));
+ memset(&dest[l], 0, sizeof(wchar_t)*remLen);
+ }
+ else if (l < destsize)
+ dest[l] = 0;
+ return dest;
+#endif
+}
+
+
+wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcscat_s(dest, destsize, src);
+ return dest;
+#elif defined(OVR_OS_WIN32)
+ OVR_UNUSED(destsize);
+ wcscat(dest, src);
+ return dest;
+#else
+ UPInt dstlen = OVR_wcslen(dest); // do not incl term null
+ UPInt srclen = OVR_wcslen(src) + 1; // incl term null
+ UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen;
+ memcpy(dest + dstlen, src, copylen * sizeof(wchar_t));
+ return dest;
+#endif
+}
+
+UPInt OVR_CDECL OVR_wcslen(const wchar_t* str)
+{
+#if defined(OVR_OS_WIN32)
+ return wcslen(str);
+#else
+ UPInt i = 0;
+ while(str[i] != '\0')
+ ++i;
+ return i;
+#endif
+}
+
+int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX)
+ return wcscmp(a, b);
+#else
+ // not supported, use custom implementation
+ const wchar_t *pa = a, *pb = b;
+ while (*pa && *pb)
+ {
+ wchar_t ca = *pa;
+ wchar_t cb = *pb;
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return 1;
+ pa++;
+ pb++;
+ }
+ if (*pa)
+ return 1;
+ else if (*pb)
+ return -1;
+ else
+ return 0;
+#endif
+}
+
+int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_wcsicmp(a, b);
+#else
+ return ::wcsicmp(a, b);
+#endif
+#elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE)
+ // not supported, use custom implementation
+ const wchar_t *pa = a, *pb = b;
+ while (*pa && *pb)
+ {
+ wchar_t ca = OVR_towlower(*pa);
+ wchar_t cb = OVR_towlower(*pb);
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return 1;
+ pa++;
+ pb++;
+ }
+ if (*pa)
+ return 1;
+ else if (*pb)
+ return -1;
+ else
+ return 0;
+#else
+ return wcscasecmp(a, b);
+#endif
+}
+
+// This function is not inline because of dependency on <locale.h>
+double OVR_CDECL OVR_strtod(const char* string, char** tailptr)
+{
+#if !defined(OVR_OS_ANDROID)
+ const char s = *localeconv()->decimal_point;
+
+ if (s != '.')
+ {
+ char buffer[347 + 1];
+
+ OVR_strcpy(buffer, sizeof(buffer), string);
+
+ for (char* c = buffer; *c != '\0'; ++c)
+ {
+ if (*c == '.')
+ {
+ *c = s;
+ break;
+ }
+ }
+
+ return strtod(buffer, tailptr);
+ }
+#endif
+
+ return strtod(string, tailptr);
+}
+
+
+#ifndef OVR_NO_WCTYPE
+
+//// Use this class to generate Unicode bitsets. For example:
+////
+//// UnicodeBitSet bitSet;
+//// for(unsigned i = 0; i < 65536; ++i)
+//// {
+//// if (iswalpha(i))
+//// bitSet.Set(i);
+//// }
+//// bitSet.Dump();
+////
+////---------------------------------------------------------------
+//class UnicodeBitSet
+//{
+//public:
+// UnicodeBitSet()
+// {
+// memset(Offsets, 0, sizeof(Offsets));
+// memset(Bits, 0, sizeof(Bits));
+// }
+//
+// void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); }
+//
+// void Dump()
+// {
+// unsigned i, j;
+// unsigned offsetCount = 0;
+// for(i = 0; i < 256; ++i)
+// {
+// if (isNull(i)) Offsets[i] = 0;
+// else
+// if (isFull(i)) Offsets[i] = 1;
+// else Offsets[i] = UInt16(offsetCount++ * 16 + 256);
+// }
+// for(i = 0; i < 16; ++i)
+// {
+// for(j = 0; j < 16; ++j)
+// {
+// printf("%5u,", Offsets[i*16+j]);
+// }
+// printf("\n");
+// }
+// for(i = 0; i < 256; ++i)
+// {
+// if (Offsets[i] > 255)
+// {
+// for(j = 0; j < 16; j++)
+// {
+// printf("%5u,", Bits[i][j]);
+// }
+// printf("\n");
+// }
+// }
+// }
+//
+//private:
+// bool isNull(unsigned n) const
+// {
+// const UInt16* p = Bits[n];
+// for(unsigned i = 0; i < 16; ++i)
+// if (p[i] != 0) return false;
+// return true;
+// }
+//
+// bool isFull(unsigned n) const
+// {
+// const UInt16* p = Bits[n];
+// for(unsigned i = 0; i < 16; ++i)
+// if (p[i] != 0xFFFF) return false;
+// return true;
+// }
+//
+// UInt16 Offsets[256];
+// UInt16 Bits[256][16];
+//};
+
+
+const UInt16 UnicodeAlnumBits[] = {
+ 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+ 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+ 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720,
+ 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832,
+ 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407,
+65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+ 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15,
+65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+ 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7,
+ 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191,
+ 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3,
+34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12,
+65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+ 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0,
+65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127,
+65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0,
+ 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+ 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047,
+65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+ 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+ 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+
+const UInt16 UnicodeAlphaBits[] = {
+ 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+ 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+ 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720,
+ 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832,
+ 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407,
+65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+ 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15,
+65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+ 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7,
+ 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168,
+ 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3,
+34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0,
+40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0,
+57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0,
+57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12,
+65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0,
+ 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0,
+65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127,
+65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31,
+65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0,
+ 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+ 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047,
+65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+ 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+ 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+
+const UInt16 UnicodeDigitBits[] = {
+ 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384,
+ 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464,
+ 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0,
+ 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0,
+ 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const UInt16 UnicodeSpaceBits[] = {
+ 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const UInt16 UnicodeXDigitBits[] = {
+ 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272,
+ 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+// Uncomment if necessary
+//const UInt16 UnicodeCntrlBits[] = {
+// 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0,
+// 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336,
+//65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0,
+//32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584};
+//
+//const UInt16 UnicodeGraphBits[] = {
+// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736,
+// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848,
+// 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535,
+//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15,
+//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31,
+// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191,
+//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3,
+//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28,
+//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0,
+//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175,
+//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0,
+// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0,
+//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095,
+//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+//
+//const UInt16 UnicodePrintBits[] = {
+// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736,
+// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848,
+// 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535,
+//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15,
+//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31,
+// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191,
+//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3,
+//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28,
+//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0,
+//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175,
+//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0,
+// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0,
+//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095,
+//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959,
+//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+//
+//const UInt16 UnicodePunctBits[] = {
+// 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384,
+// 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0,
+// 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560,
+// 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128,
+// 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24,
+// 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0,
+//16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+// 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048,
+// 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0,
+// 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048,
+// 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//
+//const UInt16 UnicodeLowerBits[] = {
+// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368,
+// 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432,
+// 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407,
+//43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565,
+//43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15,
+// 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554,
+// 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0,
+//43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682,
+// 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316,
+// 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+//50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//
+//const UInt16 UnicodeUpperBits[] = {
+// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384,
+// 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416,
+// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0,
+//21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974,
+//21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0,
+//65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277,
+// 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0,
+//21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341,
+//65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936,
+//14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+
+// MA: March 19, 2010
+// Modified ToUpper and ToLower tables to match values expected by AS3 tests.
+// ToLower modifications:
+// 304 -> 105
+// 1024 -> 1104 *
+// 1037 -> 1117 *
+// UoUpper modifications:
+// 255 -> 376
+// 305 -> 73
+// 383 -> 83
+// 1104 -> 1024 *
+// 1117 -> 1037 *
+// Entries marked with a '*' don't make complete sense based on Unicode manual, although
+// they match AS3.
+
+
+static const UInt16 UnicodeToUpperBits[] = {
+ 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368,
+ 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416,
+ 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407,
+43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3.
+43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0,
+ 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3.
+ 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0,
+43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682,
+ 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0,
+ 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0,
+ 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static const UInt16 UnicodeToLowerBits[] = {
+ 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384,
+ 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432,
+ 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0,
+21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3.
+21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0,
+65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277,
+ 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0,
+21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341,
+65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840,
+ 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0,
+ 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+struct GUnicodePairType
+{
+ UInt16 Key, Value;
+};
+
+static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key)
+{
+ return a.Key < key;
+}
+
+static const GUnicodePairType UnicodeToUpperTable[] = {
+{ 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71},
+{ 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78},
+{ 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85},
+{ 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193},
+{ 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200},
+{ 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207},
+{ 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214},
+{ 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222},
+{ 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266},
+{ 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280},
+{ 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294},
+{ 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310},
+{ 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325},
+{ 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340},
+{ 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354},
+{ 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368},
+{ 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386},
+{ 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418},
+{ 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440},
+{ 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465},
+{ 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478},
+{ 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492},
+{ 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512},
+{ 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526},
+{ 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393},
+{ 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406},
+{ 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434},
+{ 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914},
+{ 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921},
+{ 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928},
+{ 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935},
+{ 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911},
+{ 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006},
+{ 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046},
+{ 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053},
+{ 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060},
+{ 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067},
+{ 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027},
+{ 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034},
+{ 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124},
+{ 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138},
+{ 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152},
+{ 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180},
+{ 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194},
+{ 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208},
+{ 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227},
+{ 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244},
+{ 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258},
+{ 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330},
+{ 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337},
+{ 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344},
+{ 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351},
+{ 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358},
+{ 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365},
+{ 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690},
+{ 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704},
+{ 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718},
+{ 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732},
+{ 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746},
+{ 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760},
+{ 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774},
+{ 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788},
+{ 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802},
+{ 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816},
+{ 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840},
+{ 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854},
+{ 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868},
+{ 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882},
+{ 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896},
+{ 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910},
+{ 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924},
+{ 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948},
+{ 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963},
+{ 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980},
+{ 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995},
+{ 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010},
+{ 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031},
+{ 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046},
+{ 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139},
+{ 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186},
+{ 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169},
+{ 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549},
+{ 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556},
+{ 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401},
+{ 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408},
+{ 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415},
+{ 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422},
+{ 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318},
+{65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325},
+{65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332},
+{65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}};
+
+static const GUnicodePairType UnicodeToLowerTable[] = {
+{ 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103},
+{ 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110},
+{ 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117},
+{ 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225},
+{ 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232},
+{ 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239},
+{ 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246},
+{ 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254},
+{ 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269},
+{ 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283},
+{ 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297},
+{ 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314},
+{ 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328},
+{ 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343},
+{ 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357},
+{ 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371},
+{ 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595},
+{ 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396},
+{ 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617},
+{ 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419},
+{ 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650},
+{ 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454},
+{ 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470},
+{ 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485},
+{ 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501},
+{ 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519},
+{ 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533},
+{ 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973},
+{ 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950},
+{ 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957},
+{ 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965},
+{ 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995},
+{ 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105},
+{ 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112},
+{ 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072},
+{ 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079},
+{ 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086},
+{ 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093},
+{ 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100},
+{ 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127},
+{ 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141},
+{ 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169},
+{ 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183},
+{ 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197},
+{ 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211},
+{ 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233},
+{ 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247},
+{ 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263},
+{ 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379},
+{ 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386},
+{ 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393},
+{ 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400},
+{ 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407},
+{ 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414},
+{ 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310},
+{ 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317},
+{ 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324},
+{ 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331},
+{ 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338},
+{ 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687},
+{ 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701},
+{ 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715},
+{ 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729},
+{ 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743},
+{ 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757},
+{ 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771},
+{ 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785},
+{ 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799},
+{ 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813},
+{ 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827},
+{ 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851},
+{ 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865},
+{ 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879},
+{ 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893},
+{ 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907},
+{ 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921},
+{ 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938},
+{ 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953},
+{ 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970},
+{ 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985},
+{ 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000},
+{ 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019},
+{ 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036},
+{ 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049},
+{ 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054},
+{ 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056},
+{ 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563},
+{ 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570},
+{ 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425},
+{ 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432},
+{ 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439},
+{ 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446},
+{ 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348},
+{65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355},
+{65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362},
+{65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369},
+{65338,65370}, {65535, 0}};
+
+int OVR_CDECL OVR_towupper(wchar_t charCode)
+{
+ // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits.
+ if (UnicodeCharIs(UnicodeToUpperBits, charCode))
+ {
+ // To protect from memory overrun in case the character is not found
+ // we use one extra fake element in the table {65536, 0}.
+ UPInt idx = Alg::LowerBoundSliced(
+ UnicodeToUpperTable,
+ 0,
+ sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1,
+ (UInt16)charCode,
+ CmpUnicodeKey);
+ return UnicodeToUpperTable[idx].Value;
+ }
+ return charCode;
+}
+
+int OVR_CDECL OVR_towlower(wchar_t charCode)
+{
+ // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits.
+ if (UnicodeCharIs(UnicodeToLowerBits, charCode))
+ {
+ // To protect from memory overrun in case the character is not found
+ // we use one extra fake element in the table {65536, 0}.
+ UPInt idx = Alg::LowerBoundSliced(
+ UnicodeToLowerTable,
+ 0,
+ sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1,
+ (UInt16)charCode,
+ CmpUnicodeKey);
+ return UnicodeToLowerTable[idx].Value;
+ }
+ return charCode;
+}
+
+#endif //OVR_NO_WCTYPE
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Std.h b/LibOVR/Src/Kernel/OVR_Std.h new file mode 100644 index 0000000..b20a376 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Std.h @@ -0,0 +1,503 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Std.h
+Content : Standard C function interface
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Std_h
+#define OVR_Std_h
+
+#include "OVR_Types.h"
+#include <stdarg.h> // for va_list args
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+#define OVR_MSVC_SAFESTRING
+#include <errno.h>
+#endif
+
+// Wide-char funcs
+#include <wchar.h>
+#include <wctype.h>
+
+namespace OVR {
+
+#if defined(OVR_OS_WIN32)
+inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ _itoa_s(val, dest, destsize, radix);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return itoa(val, dest, radix);
+#endif
+}
+#else // OVR_OS_WIN32
+inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix)
+{
+ if (val == 0)
+ {
+ if (len > 1)
+ {
+ dest[0] = '0';
+ dest[1] = '\0';
+ }
+ return dest;
+ }
+
+ int cur = val;
+ unsigned int i = 0;
+ unsigned int sign = 0;
+
+ if (val < 0)
+ {
+ val = -val;
+ sign = 1;
+ }
+
+ while ((val != 0) && (i < (len - 1 - sign)))
+ {
+ cur = val % radix;
+ val /= radix;
+
+ if (radix == 16)
+ {
+ switch(cur)
+ {
+ case 10:
+ dest[i] = 'a';
+ break;
+ case 11:
+ dest[i] = 'b';
+ break;
+ case 12:
+ dest[i] = 'c';
+ break;
+ case 13:
+ dest[i] = 'd';
+ break;
+ case 14:
+ dest[i] = 'e';
+ break;
+ case 15:
+ dest[i] = 'f';
+ break;
+ default:
+ dest[i] = (char)('0' + cur);
+ break;
+ }
+ }
+ else
+ {
+ dest[i] = (char)('0' + cur);
+ }
+ ++i;
+ }
+
+ if (sign)
+ {
+ dest[i++] = '-';
+ }
+
+ for (unsigned int j = 0; j < i / 2; ++j)
+ {
+ char tmp = dest[j];
+ dest[j] = dest[i - 1 - j];
+ dest[i - 1 - j] = tmp;
+ }
+ dest[i] = '\0';
+
+ return dest;
+}
+
+#endif
+
+
+// String functions
+
+inline UPInt OVR_CDECL OVR_strlen(const char* str)
+{
+ return strlen(str);
+}
+
+inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strcpy_s(dest, destsize, src);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strcpy(dest, src);
+#endif
+}
+
+inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strncpy_s(dest, destsize, src, count);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strncpy(dest, src, count);
+#endif
+}
+
+inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strcat_s(dest, destsize, src);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strcat(dest, src);
+#endif
+}
+
+inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src)
+{
+ return strcmp(dest, src);
+}
+
+inline const char* OVR_CDECL OVR_strchr(const char* str, char c)
+{
+ return strchr(str, c);
+}
+
+inline char* OVR_CDECL OVR_strchr(char* str, char c)
+{
+ return strchr(str, c);
+}
+
+inline const char* OVR_strrchr(const char* str, char c)
+{
+ UPInt len = OVR_strlen(str);
+ for (UPInt i=len; i>0; i--)
+ if (str[i]==c)
+ return str+i;
+ return 0;
+}
+
+inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c)
+{
+ for (SPInt i = (SPInt)size - 1; i >= 0; i--)
+ {
+ if (str[i] == c)
+ return str + i;
+ }
+ return 0;
+}
+
+inline char* OVR_CDECL OVR_strrchr(char* str, char c)
+{
+ UPInt len = OVR_strlen(str);
+ for (UPInt i=len; i>0; i--)
+ if (str[i]==c)
+ return str+i;
+ return 0;
+}
+
+
+double OVR_CDECL OVR_strtod(const char* string, char** tailptr);
+
+inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix)
+{
+ return strtol(string, tailptr, radix);
+}
+
+inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix)
+{
+ return strtoul(string, tailptr, radix);
+}
+
+inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size)
+{
+ return strncmp(ws1, ws2, size);
+}
+
+inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _strtoui64(nptr, endptr, base);
+#else
+ return strtoull(nptr, endptr, base);
+#endif
+}
+
+inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _strtoi64(nptr, endptr, base);
+#else
+ return strtoll(nptr, endptr, base);
+#endif
+}
+
+
+inline SInt64 OVR_CDECL OVR_atoq(const char* string)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _atoi64(string);
+#else
+ return atoll(string);
+#endif
+}
+
+inline UInt64 OVR_CDECL OVR_atouq(const char* string)
+{
+ return OVR_strtouq(string, NULL, 10);
+}
+
+
+// Implemented in GStd.cpp in platform-specific manner.
+int OVR_CDECL OVR_stricmp(const char* dest, const char* src);
+int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count);
+
+inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...)
+{
+ va_list argList;
+ va_start(argList,format);
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ #if defined(OVR_MSVC_SAFESTRING)
+ ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList);
+ OVR_ASSERT(ret != -1);
+ #else
+ OVR_UNUSED(destsize);
+ ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character
+ OVR_ASSERT(ret != -1);
+ dest[destsize-1] = 0;
+ #endif
+#else
+ OVR_UNUSED(destsize);
+ ret = vsprintf(dest, format, argList);
+ OVR_ASSERT(ret < destsize);
+#endif
+ va_end(argList);
+ return ret;
+}
+
+inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList)
+{
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ #if defined(OVR_MSVC_SAFESTRING)
+ dest[0] = '\0';
+ int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList);
+ if (rv == -1)
+ {
+ dest[destsize - 1] = '\0';
+ ret = destsize - 1;
+ }
+ else
+ ret = (UPInt)rv;
+ #else
+ OVR_UNUSED(destsize);
+ int rv = _vsnprintf(dest, destsize - 1, format, argList);
+ OVR_ASSERT(rv != -1);
+ ret = (UPInt)rv;
+ dest[destsize-1] = 0;
+ #endif
+#else
+ OVR_UNUSED(destsize);
+ ret = (UPInt)vsprintf(dest, format, argList);
+ OVR_ASSERT(ret < destsize);
+#endif
+ return ret;
+}
+
+// Returns the number of characters in the formatted string.
+inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList)
+{
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ ret = (UPInt) _vscprintf(format, argList);
+#else
+ ret = (UPInt) vsnprintf(NULL, 0, format, argList);
+#endif
+ return ret;
+}
+
+
+wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src);
+wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count);
+wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src);
+UPInt OVR_CDECL OVR_wcslen(const wchar_t* str);
+int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b);
+int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b);
+
+inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_wcsicoll(a, b);
+#else
+ return ::wcsicoll(a, b);
+#endif
+#else
+ // not supported, use regular wcsicmp
+ return OVR_wcsicmp(a, b);
+#endif
+}
+
+inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX)
+ return wcscoll(a, b);
+#else
+ // not supported, use regular wcscmp
+ return OVR_wcscmp(a, b);
+#endif
+}
+
+#ifndef OVR_NO_WCTYPE
+
+inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode)
+{
+ unsigned offset = table[charCode >> 8];
+ if (offset == 0) return 0;
+ if (offset == 1) return 1;
+ return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0;
+}
+
+extern const UInt16 UnicodeAlnumBits[];
+extern const UInt16 UnicodeAlphaBits[];
+extern const UInt16 UnicodeDigitBits[];
+extern const UInt16 UnicodeSpaceBits[];
+extern const UInt16 UnicodeXDigitBits[];
+
+// Uncomment if necessary
+//extern const UInt16 UnicodeCntrlBits[];
+//extern const UInt16 UnicodeGraphBits[];
+//extern const UInt16 UnicodeLowerBits[];
+//extern const UInt16 UnicodePrintBits[];
+//extern const UInt16 UnicodePunctBits[];
+//extern const UInt16 UnicodeUpperBits[];
+
+inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); }
+inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); }
+inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); }
+inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); }
+inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); }
+
+// Uncomment if necessary
+//inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); }
+//inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); }
+//inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); }
+//inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); }
+//inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); }
+//inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); }
+
+int OVR_CDECL OVR_towupper(wchar_t charCode);
+int OVR_CDECL OVR_towlower(wchar_t charCode);
+
+#else // OVR_NO_WCTYPE
+
+inline int OVR_CDECL OVR_iswspace(wchar_t c)
+{
+ return iswspace(c);
+}
+
+inline int OVR_CDECL OVR_iswdigit(wchar_t c)
+{
+ return iswdigit(c);
+}
+
+inline int OVR_CDECL OVR_iswxdigit(wchar_t c)
+{
+ return iswxdigit(c);
+}
+
+inline int OVR_CDECL OVR_iswalpha(wchar_t c)
+{
+ return iswalpha(c);
+}
+
+inline int OVR_CDECL OVR_iswalnum(wchar_t c)
+{
+ return iswalnum(c);
+}
+
+inline wchar_t OVR_CDECL OVR_towlower(wchar_t c)
+{
+ return (wchar_t)towlower(c);
+}
+
+inline wchar_t OVR_towupper(wchar_t c)
+{
+ return (wchar_t)towupper(c);
+}
+
+#endif // OVR_NO_WCTYPE
+
+// ASCII versions of tolower and toupper. Don't use "char"
+inline int OVR_CDECL OVR_tolower(int c)
+{
+ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c;
+}
+
+inline int OVR_CDECL OVR_toupper(int c)
+{
+ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
+}
+
+
+
+inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr)
+{
+#if defined(OVR_OS_OTHER)
+ OVR_UNUSED(tailptr);
+ char buffer[64];
+ char* tp = NULL;
+ UPInt max = OVR_wcslen(string);
+ if (max > 63) max = 63;
+ unsigned char c = 0;
+ for (UPInt i=0; i < max; i++)
+ {
+ c = (unsigned char)string[i];
+ buffer[i] = ((c) < 128 ? (char)c : '!');
+ }
+ buffer[max] = 0;
+ return OVR_strtod(buffer, &tp);
+#else
+ return wcstod(string, tailptr);
+#endif
+}
+
+inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix)
+{
+#if defined(OVR_OS_OTHER)
+ OVR_UNUSED(tailptr);
+ char buffer[64];
+ char* tp = NULL;
+ UPInt max = OVR_wcslen(string);
+ if (max > 63) max = 63;
+ unsigned char c = 0;
+ for (UPInt i=0; i < max; i++)
+ {
+ c = (unsigned char)string[i];
+ buffer[i] = ((c) < 128 ? (char)c : '!');
+ }
+ buffer[max] = 0;
+ return strtol(buffer, &tp, radix);
+#else
+ return wcstol(string, tailptr, radix);
+#endif
+}
+
+} // OVR
+
+#endif // OVR_Std_h
diff --git a/LibOVR/Src/Kernel/OVR_String.cpp b/LibOVR/Src/Kernel/OVR_String.cpp new file mode 100644 index 0000000..b591ede --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String.cpp @@ -0,0 +1,752 @@ +/************************************************************************************
+
+Filename : OVR_String.cpp
+Content : String UTF8 string implementation with copy-on-write semantics
+ (thread-safe for assignment but not modification).
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_String.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef OVR_OS_QNX
+# include <strings.h>
+#endif
+
+namespace OVR {
+
+#define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift)
+
+String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} };
+
+
+String::String()
+{
+ pData = &NullData;
+ pData->AddRef();
+};
+
+String::String(const char* pdata)
+{
+ // Obtain length in bytes; it doesn't matter if _data is UTF8.
+ UPInt size = pdata ? OVR_strlen(pdata) : 0;
+ pData = AllocDataCopy1(size, 0, pdata, size);
+};
+
+String::String(const char* pdata1, const char* pdata2, const char* pdata3)
+{
+ // Obtain length in bytes; it doesn't matter if _data is UTF8.
+ UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0;
+ UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0;
+ UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0;
+
+ DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0,
+ pdata1, size1, pdata2, size2);
+ memcpy(pdataDesc->Data + size1 + size2, pdata3, size3);
+ pData = pdataDesc;
+}
+
+String::String(const char* pdata, UPInt size)
+{
+ OVR_ASSERT((size == 0) || (pdata != 0));
+ pData = AllocDataCopy1(size, 0, pdata, size);
+};
+
+
+String::String(const InitStruct& src, UPInt size)
+{
+ pData = AllocData(size, 0);
+ src.InitString(GetData()->Data, size);
+}
+
+String::String(const String& src)
+{
+ pData = src.GetData();
+ pData->AddRef();
+}
+
+String::String(const StringBuffer& src)
+{
+ pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize());
+}
+
+String::String(const wchar_t* data)
+{
+ pData = &NullData;
+ pData->AddRef();
+ // Simplified logic for wchar_t constructor.
+ if (data)
+ *this = data;
+}
+
+
+String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize)
+{
+ String::DataDesc* pdesc;
+
+ if (size == 0)
+ {
+ pdesc = &NullData;
+ pdesc->AddRef();
+ return pdesc;
+ }
+
+ pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size);
+ pdesc->Data[size] = 0;
+ pdesc->RefCount = 1;
+ pdesc->Size = size | lengthIsSize;
+ return pdesc;
+}
+
+
+String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize,
+ const char* pdata, UPInt copySize)
+{
+ String::DataDesc* pdesc = AllocData(size, lengthIsSize);
+ memcpy(pdesc->Data, pdata, copySize);
+ return pdesc;
+}
+
+String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize,
+ const char* pdata1, UPInt copySize1,
+ const char* pdata2, UPInt copySize2)
+{
+ String::DataDesc* pdesc = AllocData(size, lengthIsSize);
+ memcpy(pdesc->Data, pdata1, copySize1);
+ memcpy(pdesc->Data + copySize1, pdata2, copySize2);
+ return pdesc;
+}
+
+
+UPInt String::GetLength() const
+{
+ // Optimize length accesses for non-UTF8 character strings.
+ DataDesc* pdata = GetData();
+ UPInt length, size = pdata->GetSize();
+
+ if (pdata->LengthIsSize())
+ return size;
+
+ length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size);
+
+ if (length == size)
+ pdata->Size |= String_LengthIsSize;
+
+ return length;
+}
+
+
+//static UInt32 String_CharSearch(const char* buf, )
+
+
+UInt32 String::GetCharAt(UPInt index) const
+{
+ SPInt i = (SPInt) index;
+ DataDesc* pdata = GetData();
+ const char* buf = pdata->Data;
+ UInt32 c;
+
+ if (pdata->LengthIsSize())
+ {
+ OVR_ASSERT(index < pdata->GetSize());
+ buf += i;
+ return UTF8Util::DecodeNextChar_Advance0(&buf);
+ }
+
+ c = UTF8Util::GetCharAt(index, buf, pdata->GetSize());
+ return c;
+}
+
+UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const
+{
+ DataDesc* pdata = GetData();
+ SPInt i = (SPInt) index;
+ const char* buf = pdata->Data;
+ const char* end = buf + pdata->GetSize();
+ UInt32 c;
+
+ do
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ i--;
+
+ if (buf >= end)
+ {
+ // We've hit the end of the string; don't go further.
+ OVR_ASSERT(i == 0);
+ return c;
+ }
+ } while (i >= 0);
+
+ *offset = buf;
+
+ return c;
+}
+
+UInt32 String::GetNextChar(const char** offset) const
+{
+ return UTF8Util::DecodeNextChar(offset);
+}
+
+
+
+void String::AppendChar(UInt32 ch)
+{
+ DataDesc* pdata = GetData();
+ UPInt size = pdata->GetSize();
+ char buff[8];
+ SPInt encodeSize = 0;
+
+ // Converts ch into UTF8 string and fills it into buff.
+ UTF8Util::EncodeChar(buff, &encodeSize, ch);
+ OVR_ASSERT(encodeSize >= 0);
+
+ SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0,
+ pdata->Data, size, buff, (UPInt)encodeSize));
+ pdata->Release();
+}
+
+
+void String::AppendString(const wchar_t* pstr, SPInt len)
+{
+ if (!pstr)
+ return;
+
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+ UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len);
+
+ DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0,
+ pdata->Data, oldSize);
+ UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len);
+
+ SetData(pnewData);
+ pdata->Release();
+}
+
+
+void String::AppendString(const char* putf8str, SPInt utf8StrSz)
+{
+ if (!putf8str || !utf8StrSz)
+ return;
+ if (utf8StrSz == -1)
+ utf8StrSz = (SPInt)OVR_strlen(putf8str);
+
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+
+ SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0,
+ pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz));
+ pdata->Release();
+}
+
+void String::AssignString(const InitStruct& src, UPInt size)
+{
+ DataDesc* poldData = GetData();
+ DataDesc* pnewData = AllocData(size, 0);
+ src.InitString(pnewData->Data, size);
+ SetData(pnewData);
+ poldData->Release();
+}
+
+void String::AssignString(const char* putf8str, UPInt size)
+{
+ DataDesc* poldData = GetData();
+ SetData(AllocDataCopy1(size, 0, putf8str, size));
+ poldData->Release();
+}
+
+void String::operator = (const char* pstr)
+{
+ AssignString(pstr, pstr ? OVR_strlen(pstr) : 0);
+}
+
+void String::operator = (const wchar_t* pwstr)
+{
+ DataDesc* poldData = GetData();
+ UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0;
+
+ DataDesc* pnewData = AllocData(size, 0);
+ UTF8Util::EncodeString(pnewData->Data, pwstr);
+ SetData(pnewData);
+ poldData->Release();
+}
+
+
+void String::operator = (const String& src)
+{
+ DataDesc* psdata = src.GetData();
+ DataDesc* pdata = GetData();
+
+ SetData(psdata);
+ psdata->AddRef();
+ pdata->Release();
+}
+
+
+void String::operator = (const StringBuffer& src)
+{
+ DataDesc* polddata = GetData();
+ SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()));
+ polddata->Release();
+}
+
+void String::operator += (const String& src)
+{
+ DataDesc *pourData = GetData(),
+ *psrcData = src.GetData();
+ UPInt ourSize = pourData->GetSize(),
+ srcSize = psrcData->GetSize();
+ UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag();
+
+ SetData(AllocDataCopy2(ourSize + srcSize, lflag,
+ pourData->Data, ourSize, psrcData->Data, srcSize));
+ pourData->Release();
+}
+
+
+String String::operator + (const char* str) const
+{
+ String tmp1(*this);
+ tmp1 += (str ? str : "");
+ return tmp1;
+}
+
+String String::operator + (const String& src) const
+{
+ String tmp1(*this);
+ tmp1 += src;
+ return tmp1;
+}
+
+void String::Remove(UPInt posAt, SPInt removeLength)
+{
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+ // Length indicates the number of characters to remove.
+ UPInt length = GetLength();
+
+ // If index is past the string, nothing to remove.
+ if (posAt >= length)
+ return;
+ // Otherwise, cap removeLength to the length of the string.
+ if ((posAt + removeLength) > length)
+ removeLength = length - posAt;
+
+ // Get the byte position of the UTF8 char at position posAt.
+ SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize);
+ SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos);
+
+ SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(),
+ pdata->Data, bytePos,
+ pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize)));
+ pdata->Release();
+}
+
+
+String String::Substring(UPInt start, UPInt end) const
+{
+ UPInt length = GetLength();
+ if ((start >= length) || (start >= end))
+ return String();
+
+ DataDesc* pdata = GetData();
+
+ // If size matches, we know the exact index range.
+ if (pdata->LengthIsSize())
+ return String(pdata->Data + start, end - start);
+
+ // Get position of starting character.
+ SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize());
+ SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart);
+ return String(pdata->Data + byteStart, (UPInt)byteSize);
+}
+
+void String::Clear()
+{
+ NullData.AddRef();
+ GetData()->Release();
+ SetData(&NullData);
+}
+
+
+String String::ToUpper() const
+{
+ UInt32 c;
+ const char* psource = GetData()->Data;
+ const char* pend = psource + GetData()->GetSize();
+ String str;
+ SPInt bufferOffset = 0;
+ char buffer[512];
+
+ while(psource < pend)
+ {
+ do {
+ c = UTF8Util::DecodeNextChar_Advance0(&psource);
+ UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c)));
+ } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8)));
+
+ // Append string a piece at a time.
+ str.AppendString(buffer, bufferOffset);
+ bufferOffset = 0;
+ }
+
+ return str;
+}
+
+String String::ToLower() const
+{
+ UInt32 c;
+ const char* psource = GetData()->Data;
+ const char* pend = psource + GetData()->GetSize();
+ String str;
+ SPInt bufferOffset = 0;
+ char buffer[512];
+
+ while(psource < pend)
+ {
+ do {
+ c = UTF8Util::DecodeNextChar_Advance0(&psource);
+ UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c)));
+ } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8)));
+
+ // Append string a piece at a time.
+ str.AppendString(buffer, bufferOffset);
+ bufferOffset = 0;
+ }
+
+ return str;
+}
+
+
+
+String& String::Insert(const char* substr, UPInt posAt, SPInt strSize)
+{
+ DataDesc* poldData = GetData();
+ UPInt oldSize = poldData->GetSize();
+ UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize;
+ UPInt byteIndex = (poldData->LengthIsSize()) ?
+ posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize);
+
+ OVR_ASSERT(byteIndex <= oldSize);
+
+ DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0,
+ poldData->Data, byteIndex, substr, insertSize);
+ memcpy(pnewData->Data + byteIndex + insertSize,
+ poldData->Data + byteIndex, oldSize - byteIndex);
+ SetData(pnewData);
+ poldData->Release();
+ return *this;
+}
+
+/*
+String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len)
+{
+ for (SPInt i = 0; i < len; ++i)
+ {
+ UPInt charw = InsertCharAt(substr[i], posAt);
+ posAt += charw;
+ }
+ return *this;
+}
+*/
+
+UPInt String::InsertCharAt(UInt32 c, UPInt posAt)
+{
+ char buf[8];
+ SPInt index = 0;
+ UTF8Util::EncodeChar(buf, &index, c);
+ OVR_ASSERT(index >= 0);
+ buf[(UPInt)index] = 0;
+
+ Insert(buf, posAt, index);
+ return (UPInt)index;
+}
+
+
+int String::CompareNoCase(const char* a, const char* b)
+{
+ return OVR_stricmp(a, b);
+}
+
+int String::CompareNoCase(const char* a, const char* b, SPInt len)
+{
+ if (len)
+ {
+ SPInt f,l;
+ SPInt slen = len;
+ const char *s = b;
+ do {
+ f = (SPInt)OVR_tolower((int)(*(a++)));
+ l = (SPInt)OVR_tolower((int)(*(b++)));
+ } while (--len && f && (f == l) && *b != 0);
+
+ if (f == l && (len != 0 || *b != 0))
+ {
+ f = (SPInt)slen;
+ l = (SPInt)OVR_strlen(s);
+ return int(f - l);
+ }
+
+ return int(f - l);
+ }
+ else
+ return (0-(int)OVR_strlen(b));
+}
+
+// ***** Implement hash static functions
+
+// Hash function
+UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed)
+{
+ const UByte* pdata = (const UByte*) pdataIn;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = ((h << 5) + h) ^ (unsigned) pdata[size];
+ }
+
+ return h;
+}
+
+// Hash function, case-insensitive
+UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed)
+{
+ const UByte* pdata = (const UByte*) pdataIn;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = ((h << 5) + h) ^ OVR_tolower(pdata[size]);
+ }
+
+ // Alternative: "sdbm" hash function, suggested at same web page above.
+ // h = 0;
+ // for bytes { h = (h << 16) + (h << 6) - hash + *p; }
+ return h;
+}
+
+
+
+// ***** String Buffer used for Building Strings
+
+
+#define OVR_SBUFF_DEFAULT_GROW_SIZE 512
+// Constructors / Destructor.
+StringBuffer::StringBuffer()
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+}
+
+StringBuffer::StringBuffer(UPInt growSize)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ SetGrowSize(growSize);
+}
+
+StringBuffer::StringBuffer(const char* data)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ *this = data;
+}
+
+StringBuffer::StringBuffer(const char* data, UPInt dataSize)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(data, dataSize);
+}
+
+StringBuffer::StringBuffer(const String& src)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(src.ToCStr(), src.GetSize());
+}
+
+StringBuffer::StringBuffer(const StringBuffer& src)
+ : pData(NULL), Size(0), BufferSize(src.GetGrowSize()), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(src.ToCStr(), src.GetSize());
+ LengthIsSize = src.LengthIsSize;
+}
+
+StringBuffer::StringBuffer(const wchar_t* data)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ *this = data;
+}
+
+StringBuffer::~StringBuffer()
+{
+ if (pData)
+ OVR_FREE(pData);
+}
+void StringBuffer::SetGrowSize(UPInt growSize)
+{
+ if (growSize <= 16)
+ GrowSize = 16;
+ else
+ {
+ UByte bits = Alg::UpperBit(UInt32(growSize-1));
+ UPInt size = 1<<bits;
+ GrowSize = size == growSize ? growSize : size;
+ }
+}
+
+UPInt StringBuffer::GetLength() const
+{
+ UPInt length, size = GetSize();
+ if (LengthIsSize)
+ return size;
+
+ length = (UPInt)UTF8Util::GetLength(pData, (UPInt)GetSize());
+
+ if (length == GetSize())
+ LengthIsSize = true;
+ return length;
+}
+
+void StringBuffer::Reserve(UPInt _size)
+{
+ if (_size >= BufferSize) // >= because of trailing zero! (!AB)
+ {
+ BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1);
+ if (!pData)
+ pData = (char*)OVR_ALLOC(BufferSize);
+ else
+ pData = (char*)OVR_REALLOC(pData, BufferSize);
+ }
+}
+void StringBuffer::Resize(UPInt _size)
+{
+ Reserve(_size);
+ LengthIsSize = false;
+ Size = _size;
+ if (pData)
+ pData[Size] = 0;
+}
+
+void StringBuffer::Clear()
+{
+ Resize(0);
+ /*
+ if (pData != pEmptyNullData)
+ {
+ OVR_FREE(pHeap, pData);
+ pData = pEmptyNullData;
+ Size = BufferSize = 0;
+ LengthIsSize = false;
+ }
+ */
+}
+// Appends a character
+void StringBuffer::AppendChar(UInt32 ch)
+{
+ char buff[8];
+ UPInt origSize = GetSize();
+
+ // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes
+ // in the UTF8 string.
+ SPInt srcSize = 0;
+ UTF8Util::EncodeChar(buff, &srcSize, ch);
+ OVR_ASSERT(srcSize >= 0);
+
+ UPInt size = origSize + srcSize;
+ Resize(size);
+ memcpy(pData + origSize, buff, srcSize);
+}
+
+// Append a string
+void StringBuffer::AppendString(const wchar_t* pstr, SPInt len)
+{
+ if (!pstr)
+ return;
+
+ SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len);
+ UPInt origSize = GetSize();
+ UPInt size = srcSize + origSize;
+
+ Resize(size);
+ UTF8Util::EncodeString(pData + origSize, pstr, len);
+}
+
+void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz)
+{
+ if (!putf8str || !utf8StrSz)
+ return;
+ if (utf8StrSz == -1)
+ utf8StrSz = (SPInt)OVR_strlen(putf8str);
+
+ UPInt origSize = GetSize();
+ UPInt size = utf8StrSz + origSize;
+
+ Resize(size);
+ memcpy(pData + origSize, putf8str, utf8StrSz);
+}
+
+
+void StringBuffer::operator = (const char* pstr)
+{
+ pstr = pstr ? pstr : "";
+ UPInt size = OVR_strlen(pstr);
+ Resize(size);
+ memcpy(pData, pstr, size);
+}
+
+void StringBuffer::operator = (const wchar_t* pstr)
+{
+ pstr = pstr ? pstr : L"";
+ UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr);
+ Resize(size);
+ UTF8Util::EncodeString(pData, pstr);
+}
+
+void StringBuffer::operator = (const String& src)
+{
+ Resize(src.GetSize());
+ memcpy(pData, src.ToCStr(), src.GetSize());
+}
+
+
+// Inserts substr at posAt
+void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len)
+{
+ UPInt oldSize = Size;
+ UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len;
+ UPInt byteIndex = LengthIsSize ? posAt :
+ (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size);
+
+ OVR_ASSERT(byteIndex <= oldSize);
+ Reserve(oldSize + insertSize);
+
+ memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1);
+ memcpy (pData + byteIndex, substr, insertSize);
+ LengthIsSize = false;
+ Size = oldSize + insertSize;
+ pData[Size] = 0;
+}
+
+// Inserts character at posAt
+UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt)
+{
+ char buf[8];
+ SPInt len = 0;
+ UTF8Util::EncodeChar(buf, &len, c);
+ OVR_ASSERT(len >= 0);
+ buf[(UPInt)len] = 0;
+
+ Insert(buf, posAt, len);
+ return (UPInt)len;
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_String.h b/LibOVR/Src/Kernel/OVR_String.h new file mode 100644 index 0000000..112878c --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String.h @@ -0,0 +1,645 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_String.h
+Content : String UTF8 string implementation with copy-on-write semantics
+ (thread-safe for assignment but not modification).
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_String_h
+#define OVR_String_h
+
+#include "OVR_Types.h"
+#include "OVR_Allocator.h"
+#include "OVR_UTF8Util.h"
+#include "OVR_Atomic.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+namespace OVR {
+
+// ***** Classes
+
+class String;
+class StringBuffer;
+
+
+//-----------------------------------------------------------------------------------
+// ***** String Class
+
+// String is UTF8 based string class with copy-on-write implementation
+// for assignment.
+
+class String
+{
+protected:
+
+ enum FlagConstants
+ {
+ //Flag_GetLength = 0x7FFFFFFF,
+ // This flag is set if GetLength() == GetSize() for a string.
+ // Avoid extra scanning is Substring and indexing logic.
+ Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1)
+ };
+
+
+ // Internal structure to hold string data
+ struct DataDesc
+ {
+ // Number of bytes. Will be the same as the number of chars if the characters
+ // are ascii, may not be equal to number of chars in case string data is UTF8.
+ UPInt Size;
+ volatile SInt32 RefCount;
+ char Data[1];
+
+ void AddRef()
+ {
+ AtomicOps<SInt32>::ExchangeAdd_NoSync(&RefCount, 1);
+ }
+ // Decrement ref count. This needs to be thread-safe, since
+ // a different thread could have also decremented the ref count.
+ // For example, if u start off with a ref count = 2. Now if u
+ // decrement the ref count and check against 0 in different
+ // statements, a different thread can also decrement the ref count
+ // in between our decrement and checking against 0 and will find
+ // the ref count = 0 and delete the object. This will lead to a crash
+ // when context switches to our thread and we'll be trying to delete
+ // an already deleted object. Hence decrementing the ref count and
+ // checking against 0 needs to made an atomic operation.
+ void Release()
+ {
+ if ((AtomicOps<SInt32>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ OVR_FREE(this);
+ }
+
+ static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; }
+ UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; }
+ UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); }
+ bool LengthIsSize() const { return GetLengthFlag() != 0; }
+ };
+
+ // Heap type of the string is encoded in the lower bits.
+ enum HeapType
+ {
+ HT_Global = 0, // Heap is global.
+ HT_Local = 1, // SF::String_loc: Heap is determined based on string's address.
+ HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class.
+ HT_Mask = 3
+ };
+
+ union {
+ DataDesc* pData;
+ UPInt HeapTypeBits;
+ };
+ typedef union {
+ DataDesc* pData;
+ UPInt HeapTypeBits;
+ } DataDescUnion;
+
+ inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); }
+
+ inline DataDesc* GetData() const
+ {
+ DataDescUnion u;
+ u.pData = pData;
+ u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask);
+ return u.pData;
+ }
+
+ inline void SetData(DataDesc* pdesc)
+ {
+ HeapType ht = GetHeapType();
+ pData = pdesc;
+ OVR_ASSERT((HeapTypeBits & HT_Mask) == 0);
+ HeapTypeBits |= ht;
+ }
+
+
+ DataDesc* AllocData(UPInt size, UPInt lengthIsSize);
+ DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize,
+ const char* pdata, UPInt copySize);
+ DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize,
+ const char* pdata1, UPInt copySize1,
+ const char* pdata2, UPInt copySize2);
+
+ // Special constructor to avoid data initalization when used in derived class.
+ struct NoConstructor { };
+ String(const NoConstructor&) { }
+
+public:
+
+ // For initializing string with dynamic buffer
+ struct InitStruct
+ {
+ virtual ~InitStruct() { }
+ virtual void InitString(char* pbuffer, UPInt size) const = 0;
+ };
+
+
+ // Constructors / Destructors.
+ String();
+ String(const char* data);
+ String(const char* data1, const char* pdata2, const char* pdata3 = 0);
+ String(const char* data, UPInt buflen);
+ String(const String& src);
+ String(const StringBuffer& src);
+ String(const InitStruct& src, UPInt size);
+ explicit String(const wchar_t* data);
+
+ // Destructor (Captain Obvious guarantees!)
+ ~String()
+ {
+ GetData()->Release();
+ }
+
+ // Declaration of NullString
+ static DataDesc NullData;
+
+
+ // *** General Functions
+
+ void Clear();
+
+ // For casting to a pointer to char.
+ operator const char*() const { return GetData()->Data; }
+ // Pointer to raw buffer.
+ const char* ToCStr() const { return GetData()->Data; }
+
+ // Returns number of bytes
+ UPInt GetSize() const { return GetData()->GetSize() ; }
+ // Tells whether or not the string is empty
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // Returns number of characters
+ UPInt GetLength() const;
+
+ // Returns character at the specified index
+ UInt32 GetCharAt(UPInt index) const;
+ UInt32 GetFirstCharAt(UPInt index, const char** offset) const;
+ UInt32 GetNextChar(const char** offset) const;
+
+ // Appends a character
+ void AppendChar(UInt32 ch);
+
+ // Append a string
+ void AppendString(const wchar_t* pstr, SPInt len = -1);
+ void AppendString(const char* putf8str, SPInt utf8StrSz = -1);
+
+ // Assigned a string with dynamic data (copied through initializer).
+ void AssignString(const InitStruct& src, UPInt size);
+ // Assigns string with known size.
+ void AssignString(const char* putf8str, UPInt size);
+
+ // Resize the string to the new size
+// void Resize(UPInt _size);
+
+ // Removes the character at posAt
+ void Remove(UPInt posAt, SPInt len = 1);
+
+ // Returns a String that's a substring of this.
+ // -start is the index of the first UTF8 character you want to include.
+ // -end is the index one past the last UTF8 character you want to include.
+ String Substring(UPInt start, UPInt end) const;
+
+ // Case-conversion
+ String ToUpper() const;
+ String ToLower() const;
+
+ // Inserts substr at posAt
+ String& Insert (const char* substr, UPInt posAt, SPInt len = -1);
+
+ // Inserts character at posAt
+ UPInt InsertCharAt(UInt32 c, UPInt posAt);
+
+ // Inserts substr at posAt, which is an index of a character (not byte).
+ // Of size is specified, it is in bytes.
+// String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1);
+
+ // Get Byte index of the character at position = index
+ UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); }
+
+ // Utility: case-insensitive string compare. stricmp() & strnicmp() are not
+ // ANSI or POSIX, do not seem to appear in Linux.
+ static int OVR_STDCALL CompareNoCase(const char* a, const char* b);
+ static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len);
+
+ // Hash function, case-insensitive
+ static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381);
+
+ // Hash function, case-sensitive
+ static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381);
+
+
+ // ***** File path parsing helper functions.
+ // Implemented in OVR_String_FilePath.cpp.
+
+ // Absolute paths can star with:
+ // - protocols: 'file://', 'http://'
+ // - windows drive: 'c:\'
+ // - UNC share name: '\\share'
+ // - unix root '/'
+ static bool HasAbsolutePath(const char* path);
+ static bool HasExtension(const char* path);
+ static bool HasProtocol(const char* path);
+
+ bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); }
+ bool HasExtension() const { return HasExtension(ToCStr()); }
+ bool HasProtocol() const { return HasProtocol(ToCStr()); }
+
+ String GetProtocol() const; // Returns protocol, if any, with trailing '://'.
+ String GetPath() const; // Returns path with trailing '/'.
+ String GetFilename() const; // Returns filename, including extension.
+ String GetExtension() const; // Returns extension with a dot.
+
+ void StripProtocol(); // Strips front protocol, if any, from the string.
+ void StripExtension(); // Strips off trailing extension.
+
+
+ // Operators
+ // Assignment
+ void operator = (const char* str);
+ void operator = (const wchar_t* str);
+ void operator = (const String& src);
+ void operator = (const StringBuffer& src);
+
+ // Addition
+ void operator += (const String& src);
+ void operator += (const char* psrc) { AppendString(psrc); }
+ void operator += (const wchar_t* psrc) { AppendString(psrc); }
+ void operator += (char ch) { AppendChar(ch); }
+ String operator + (const char* str) const;
+ String operator + (const String& src) const;
+
+ // Comparison
+ bool operator == (const String& str) const
+ {
+ return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0);
+ }
+
+ bool operator != (const String& str) const
+ {
+ return !operator == (str);
+ }
+
+ bool operator == (const char* str) const
+ {
+ return OVR_strcmp(GetData()->Data, str) == 0;
+ }
+
+ bool operator != (const char* str) const
+ {
+ return !operator == (str);
+ }
+
+ bool operator < (const char* pstr) const
+ {
+ return OVR_strcmp(GetData()->Data, pstr) < 0;
+ }
+
+ bool operator < (const String& str) const
+ {
+ return *this < str.GetData()->Data;
+ }
+
+ bool operator > (const char* pstr) const
+ {
+ return OVR_strcmp(GetData()->Data, pstr) > 0;
+ }
+
+ bool operator > (const String& str) const
+ {
+ return *this > str.GetData()->Data;
+ }
+
+ int CompareNoCase(const char* pstr) const
+ {
+ return CompareNoCase(GetData()->Data, pstr);
+ }
+ int CompareNoCase(const String& str) const
+ {
+ return CompareNoCase(GetData()->Data, str.ToCStr());
+ }
+
+ // Accesses raw bytes
+ const char& operator [] (int index) const
+ {
+ OVR_ASSERT(index >= 0 && (UPInt)index < GetSize());
+ return GetData()->Data[index];
+ }
+ const char& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < GetSize());
+ return GetData()->Data[index];
+ }
+
+
+ // Case insensitive keys are used to look up insensitive string in hash tables
+ // for SWF files with version before SWF 7.
+ struct NoCaseKey
+ {
+ const String* pStr;
+ NoCaseKey(const String &str) : pStr(&str){};
+ };
+
+ bool operator == (const NoCaseKey& strKey) const
+ {
+ return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0);
+ }
+ bool operator != (const NoCaseKey& strKey) const
+ {
+ return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0);
+ }
+
+ // Hash functor used for strings.
+ struct HashFunctor
+ {
+ UPInt operator()(const String& data) const
+ {
+ UPInt size = data.GetSize();
+ return String::BernsteinHashFunction((const char*)data, size);
+ }
+ };
+ // Case-insensitive hash functor used for strings. Supports additional
+ // lookup based on NoCaseKey.
+ struct NoCaseHashFunctor
+ {
+ UPInt operator()(const String& data) const
+ {
+ UPInt size = data.GetSize();
+ return String::BernsteinHashFunctionCIS((const char*)data, size);
+ }
+ UPInt operator()(const NoCaseKey& data) const
+ {
+ UPInt size = data.pStr->GetSize();
+ return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size);
+ }
+ };
+
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** String Buffer used for Building Strings
+
+class StringBuffer
+{
+ char* pData;
+ UPInt Size;
+ UPInt BufferSize;
+ UPInt GrowSize;
+ mutable bool LengthIsSize;
+
+public:
+
+ // Constructors / Destructor.
+ StringBuffer();
+ explicit StringBuffer(UPInt growSize);
+ StringBuffer(const char* data);
+ StringBuffer(const char* data, UPInt buflen);
+ StringBuffer(const String& src);
+ StringBuffer(const StringBuffer& src);
+ explicit StringBuffer(const wchar_t* data);
+ ~StringBuffer();
+
+
+ // Modify grow size used for growing/shrinking the buffer.
+ UPInt GetGrowSize() const { return GrowSize; }
+ void SetGrowSize(UPInt growSize);
+
+
+ // *** General Functions
+ // Does not release memory, just sets Size to 0
+ void Clear();
+
+ // For casting to a pointer to char.
+ operator const char*() const { return (pData) ? pData : ""; }
+ // Pointer to raw buffer.
+ const char* ToCStr() const { return (pData) ? pData : ""; }
+
+ // Returns number of bytes.
+ UPInt GetSize() const { return Size ; }
+ // Tells whether or not the string is empty.
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // Returns number of characters
+ UPInt GetLength() const;
+
+ // Returns character at the specified index
+ UInt32 GetCharAt(UPInt index) const;
+ UInt32 GetFirstCharAt(UPInt index, const char** offset) const;
+ UInt32 GetNextChar(const char** offset) const;
+
+
+ // Resize the string to the new size
+ void Resize(UPInt _size);
+ void Reserve(UPInt _size);
+
+ // Appends a character
+ void AppendChar(UInt32 ch);
+
+ // Append a string
+ void AppendString(const wchar_t* pstr, SPInt len = -1);
+ void AppendString(const char* putf8str, SPInt utf8StrSz = -1);
+ void AppendFormat(const char* format, ...);
+
+ // Assigned a string with dynamic data (copied through initializer).
+ //void AssignString(const InitStruct& src, UPInt size);
+
+ // Inserts substr at posAt
+ void Insert (const char* substr, UPInt posAt, SPInt len = -1);
+ // Inserts character at posAt
+ UPInt InsertCharAt(UInt32 c, UPInt posAt);
+
+ // Assignment
+ void operator = (const char* str);
+ void operator = (const wchar_t* str);
+ void operator = (const String& src);
+
+ // Addition
+ void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); }
+ void operator += (const char* psrc) { AppendString(psrc); }
+ void operator += (const wchar_t* psrc) { AppendString(psrc); }
+ void operator += (char ch) { AppendChar(ch); }
+ //String operator + (const char* str) const ;
+ //String operator + (const String& src) const ;
+
+ // Accesses raw bytes
+ char& operator [] (int index)
+ {
+ OVR_ASSERT(((UPInt)index) < GetSize());
+ return pData[index];
+ }
+ char& operator [] (UPInt index)
+ {
+ OVR_ASSERT(index < GetSize());
+ return pData[index];
+ }
+
+ const char& operator [] (int index) const
+ {
+ OVR_ASSERT(((UPInt)index) < GetSize());
+ return pData[index];
+ }
+ const char& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < GetSize());
+ return pData[index];
+ }
+};
+
+
+//
+// Wrapper for string data. The data must have a guaranteed
+// lifespan throughout the usage of the wrapper. Not intended for
+// cached usage. Not thread safe.
+//
+class StringDataPtr
+{
+public:
+ StringDataPtr() : pStr(NULL), Size(0) {}
+ StringDataPtr(const StringDataPtr& p)
+ : pStr(p.pStr), Size(p.Size) {}
+ StringDataPtr(const char* pstr, UPInt sz)
+ : pStr(pstr), Size(sz) {}
+ StringDataPtr(const char* pstr)
+ : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {}
+ explicit StringDataPtr(const String& str)
+ : pStr(str.ToCStr()), Size(str.GetSize()) {}
+ template <typename T, int N>
+ StringDataPtr(const T (&v)[N])
+ : pStr(v), Size(N) {}
+
+public:
+ const char* ToCStr() const { return pStr; }
+ UPInt GetSize() const { return Size; }
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // value is a prefix of this string
+ // Character's values are not compared.
+ bool IsPrefix(const StringDataPtr& value) const
+ {
+ return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize();
+ }
+ // value is a suffix of this string
+ // Character's values are not compared.
+ bool IsSuffix(const StringDataPtr& value) const
+ {
+ return ToCStr() <= value.ToCStr() && (End()) == (value.End());
+ }
+
+ // Find first character.
+ // init_ind - initial index.
+ SPInt FindChar(char c, UPInt init_ind = 0) const
+ {
+ for (UPInt i = init_ind; i < GetSize(); ++i)
+ if (pStr[i] == c)
+ return static_cast<SPInt>(i);
+
+ return -1;
+ }
+
+ // Find last character.
+ // init_ind - initial index.
+ SPInt FindLastChar(char c, UPInt init_ind = ~0) const
+ {
+ if (init_ind == (UPInt)~0 || init_ind > GetSize())
+ init_ind = GetSize();
+ else
+ ++init_ind;
+
+ for (UPInt i = init_ind; i > 0; --i)
+ if (pStr[i - 1] == c)
+ return static_cast<SPInt>(i - 1);
+
+ return -1;
+ }
+
+ // Create new object and trim size bytes from the left.
+ StringDataPtr GetTrimLeft(UPInt size) const
+ {
+ // Limit trim size to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+
+ return StringDataPtr(ToCStr() + size, GetSize() - size);
+ }
+ // Create new object and trim size bytes from the right.
+ StringDataPtr GetTrimRight(UPInt size) const
+ {
+ // Limit trim to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+
+ return StringDataPtr(ToCStr(), GetSize() - size);
+ }
+
+ // Create new object, which contains next token.
+ // Useful for parsing.
+ StringDataPtr GetNextToken(char separator = ':') const
+ {
+ UPInt cur_pos = 0;
+ const char* cur_str = ToCStr();
+
+ for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos)
+ {
+ if (cur_str[cur_pos] == separator)
+ {
+ break;
+ }
+ }
+
+ return StringDataPtr(ToCStr(), cur_pos);
+ }
+
+ // Trim size bytes from the left.
+ StringDataPtr& TrimLeft(UPInt size)
+ {
+ // Limit trim size to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+ pStr += size;
+ Size -= size;
+
+ return *this;
+ }
+ // Trim size bytes from the right.
+ StringDataPtr& TrimRight(UPInt size)
+ {
+ // Limit trim to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+ Size -= size;
+
+ return *this;
+ }
+
+ const char* Begin() const { return ToCStr(); }
+ const char* End() const { return ToCStr() + GetSize(); }
+
+ // Hash functor used string data pointers
+ struct HashFunctor
+ {
+ UPInt operator()(const StringDataPtr& data) const
+ {
+ return String::BernsteinHashFunction(data.ToCStr(), data.GetSize());
+ }
+ };
+
+ bool operator== (const StringDataPtr& data) const
+ {
+ return (OVR_strncmp(pStr, data.pStr, data.Size) == 0);
+ }
+
+protected:
+ const char* pStr;
+ UPInt Size;
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_StringHash.h b/LibOVR/Src/Kernel/OVR_StringHash.h new file mode 100644 index 0000000..237dc7e --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_StringHash.h @@ -0,0 +1,89 @@ +/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_StringHash.h
+Content : String hash table used when optional case-insensitive
+ lookup is required.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_StringHash_h
+#define OVR_StringHash_h
+
+#include "OVR_String.h"
+#include "OVR_Hash.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// *** StringHash
+
+// This is a custom string hash table that supports case-insensitive
+// searches through special functions such as GetCaseInsensitive, etc.
+// This class is used for Flash labels, exports and other case-insensitive tables.
+
+template<class U, class Allocator = ContainerAllocator<U> >
+class StringHash : public Hash<String, U, String::NoCaseHashFunctor, Allocator>
+{
+public:
+ typedef U ValueType;
+ typedef StringHash<U, Allocator> SelfType;
+ typedef Hash<String, U, String::NoCaseHashFunctor, Allocator> BaseType;
+
+public:
+
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+
+ bool GetCaseInsensitive(const String& key, U* pvalue) const
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey, pvalue);
+ }
+ // Pointer-returning get variety.
+ const U* GetCaseInsensitive(const String& key) const
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey);
+ }
+ U* GetCaseInsensitive(const String& key)
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey);
+ }
+
+
+ typedef typename BaseType::Iterator base_iterator;
+
+ base_iterator FindCaseInsensitive(const String& key)
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::FindAlt(ikey);
+ }
+
+ // Set just uses a find and assigns value if found. The key is not modified;
+ // this behavior is identical to Flash string variable assignment.
+ void SetCaseInsensitive(const String& key, const U& value)
+ {
+ base_iterator it = FindCaseInsensitive(key);
+ if (it != BaseType::End())
+ {
+ it->Second = value;
+ }
+ else
+ {
+ BaseType::Add(key, value);
+ }
+ }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp new file mode 100644 index 0000000..a6ceb3a --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp @@ -0,0 +1,42 @@ +/************************************************************************************
+
+Filename : OVR_String_FormatUtil.cpp
+Content : String format functions.
+Created : February 27, 2013
+Notes :
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_String.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+void StringBuffer::AppendFormat(const char* format, ...)
+{
+ va_list argList;
+
+ va_start(argList, format);
+ UPInt size = OVR_vscprintf(format, argList);
+ va_end(argList);
+
+ char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1));
+
+ va_start(argList, format);
+ UPInt result = OVR_vsprintf(buffer, size+1, format, argList);
+ OVR_UNUSED1(result);
+ va_end(argList);
+ OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf"));
+
+ AppendString(buffer);
+
+ OVR_FREE(buffer);
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp new file mode 100644 index 0000000..27e79c6 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp @@ -0,0 +1,200 @@ +/************************************************************************************
+
+Filename : OVR_String_PathUtil.cpp
+Content : String filename/url helper function
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_String.h"
+#include "OVR_UTF8Util.h"
+
+namespace OVR {
+
+//--------------------------------------------------------------------
+// ***** Path-Scanner helper function
+
+// Scans file path finding filename start and extension start, fills in their addess.
+void ScanFilePath(const char* url, const char** pfilename, const char** pext)
+{
+ const char* urlStart = url;
+ const char *filename = 0;
+ const char *lastDot = 0;
+
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+
+ while (charVal != 0)
+ {
+ if ((charVal == '/') || (charVal == '\\'))
+ {
+ filename = url;
+ lastDot = 0;
+ }
+ else if (charVal == '.')
+ {
+ lastDot = url - 1;
+ }
+
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+
+ if (pfilename)
+ {
+ // It was a naked filename
+ if (urlStart && (*urlStart != '.') && *urlStart)
+ *pfilename = urlStart;
+ else
+ *pfilename = filename;
+ }
+
+ if (pext)
+ {
+ *pext = lastDot;
+ }
+}
+
+// Scans till the end of protocol. Returns first character past protocol,
+// 0 if not found.
+// - protocol: 'file://', 'http://'
+const char* ScanPathProtocol(const char* url)
+{
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+ UInt32 charVal2;
+
+ while (charVal != 0)
+ {
+ // Treat a colon followed by a slash as absolute.
+ if (charVal == ':')
+ {
+ charVal2 = UTF8Util::DecodeNextChar(&url);
+ charVal = UTF8Util::DecodeNextChar(&url);
+ if ((charVal == '/') && (charVal2 == '\\'))
+ return url;
+ }
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+ return 0;
+}
+
+
+//--------------------------------------------------------------------
+// ***** String Path API implementation
+
+bool String::HasAbsolutePath(const char* url)
+{
+ // Absolute paths can star with:
+ // - protocols: 'file://', 'http://'
+ // - windows drive: 'c:\'
+ // - UNC share name: '\\share'
+ // - unix root '/'
+
+ // On the other hand, relative paths are:
+ // - directory: 'directory/file'
+ // - this directory: './file'
+ // - parent directory: '../file'
+ //
+ // For now, we don't parse '.' or '..' out, but instead let it be concatenated
+ // to string and let the OS figure it out. This, however, is not good for file
+ // name matching in library/etc, so it should be improved.
+
+ if (!url || !*url)
+ return true; // Treat empty strings as absolute.
+
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+
+ // Fist character of '/' or '\\' means absolute url.
+ if ((charVal == '/') || (charVal == '\\'))
+ return true;
+
+ while (charVal != 0)
+ {
+ // Treat a colon followed by a slash as absolute.
+ if (charVal == ':')
+ {
+ charVal = UTF8Util::DecodeNextChar(&url);
+ // Protocol or windows drive. Absolute.
+ if ((charVal == '/') || (charVal == '\\'))
+ return true;
+ }
+ else if ((charVal == '/') || (charVal == '\\'))
+ {
+ // Not a first character (else 'if' above the loop would have caught it).
+ // Must be a relative url.
+ break;
+ }
+
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+
+ // We get here for relative paths.
+ return false;
+}
+
+
+bool String::HasExtension(const char* path)
+{
+ const char* ext = 0;
+ ScanFilePath(path, 0, &ext);
+ return ext != 0;
+}
+bool String::HasProtocol(const char* path)
+{
+ return ScanPathProtocol(path) != 0;
+}
+
+
+String String::GetPath() const
+{
+ const char* filename = 0;
+ ScanFilePath(ToCStr(), &filename, 0);
+
+ // Technically we can have extra logic somewhere for paths,
+ // such as enforcing protocol and '/' only based on flags,
+ // but we keep it simple for now.
+ return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize());
+}
+
+String String::GetProtocol() const
+{
+ const char* protocolEnd = ScanPathProtocol(ToCStr());
+ return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0);
+}
+
+String String::GetFilename() const
+{
+ const char* filename = 0;
+ ScanFilePath(ToCStr(), &filename, 0);
+ return String(filename);
+}
+String String::GetExtension() const
+{
+ const char* ext = 0;
+ ScanFilePath(ToCStr(), 0, &ext);
+ return String(ext);
+}
+
+void String::StripExtension()
+{
+ const char* ext = 0;
+ ScanFilePath(ToCStr(), 0, &ext);
+ if (ext)
+ {
+ *this = String(ToCStr(), ext-ToCStr());
+ }
+}
+
+void String::StripProtocol()
+{
+ const char* protocol = ScanPathProtocol(ToCStr());
+ if (protocol)
+ AssignString(protocol, OVR_strlen(protocol));
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_SysFile.cpp b/LibOVR/Src/Kernel/OVR_SysFile.cpp new file mode 100644 index 0000000..f72a8d2 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_SysFile.cpp @@ -0,0 +1,125 @@ +/**************************************************************************
+
+Filename : OVR_SysFile.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+Authors : Michael Antonov
+
+Copyright : Copyright 2011 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+
+#include "OVR_SysFile.h"
+
+namespace OVR {
+
+// This is - a dummy file that fails on all calls.
+
+class UnopenedFile : public File
+{
+public:
+ UnopenedFile() { }
+ ~UnopenedFile() { }
+
+ virtual const char* GetFilePath() { return 0; }
+
+ // ** File Information
+ virtual bool IsValid() { return 0; }
+ virtual bool IsWritable() { return 0; }
+
+ // Return position / file size
+ virtual int Tell() { return 0; }
+ virtual SInt64 LTell() { return 0; }
+ virtual int GetLength() { return 0; }
+ virtual SInt64 LGetLength() { return 0; }
+
+// virtual bool Stat(FileStats *pfs) { return 0; }
+ virtual int GetErrorCode() { return Error_FileNotFound; }
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); }
+ virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); }
+ virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); }
+ virtual int BytesAvailable() { return 0; }
+ virtual bool Flush() { return 0; }
+ virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); }
+ virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); }
+
+ virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); }
+ virtual bool Close() { return 0; }
+};
+
+
+
+// ***** System File
+
+// System file is created to access objects on file system directly
+// This file can refer directly to path
+
+// ** Constructor
+SysFile::SysFile() : DelegatedFile(0)
+{
+ pFile = *new UnopenedFile;
+}
+
+File* FileFILEOpen(const String& path, int flags, int mode);
+
+// Opens a file
+SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0)
+{
+ Open(path, flags, mode);
+}
+
+
+// ** Open & management
+// Will fail if file's already open
+bool SysFile::Open(const String& path, int flags, int mode)
+{
+ pFile = *FileFILEOpen(path, flags, mode);
+ if ((!pFile) || (!pFile->IsValid()))
+ {
+ pFile = *new UnopenedFile;
+ return 0;
+ }
+ //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing
+ if (flags & Open_Buffered)
+ pFile = *new BufferedFile(pFile);
+ return 1;
+}
+
+
+// ** Overrides
+
+int SysFile::GetErrorCode()
+{
+ return pFile ? pFile->GetErrorCode() : Error_FileNotFound;
+}
+
+
+// Overrides to provide re-open support
+bool SysFile::IsValid()
+{
+ return pFile && pFile->IsValid();
+}
+bool SysFile::Close()
+{
+ if (IsValid())
+ {
+ DelegatedFile::Close();
+ pFile = *new UnopenedFile;
+ return 1;
+ }
+ return 0;
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_SysFile.h b/LibOVR/Src/Kernel/OVR_SysFile.h new file mode 100644 index 0000000..a9bb0a0 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_SysFile.h @@ -0,0 +1,93 @@ +/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_SysFile.h
+Content : Header for all internal file management - functions and structures
+ to be inherited by OS specific subclasses.
+Created : September 19, 2012
+Notes :
+
+Notes : errno may not be preserved across use of GBaseFile member functions
+ : Directories cannot be deleted while files opened from them are in use
+ (For the GetFullName function)
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_SysFile_h
+#define OVR_SysFile_h
+
+#include "OVR_File.h"
+
+namespace OVR {
+
+// ***** Declared classes
+class SysFile;
+
+//-----------------------------------------------------------------------------------
+// *** File Statistics
+
+// This class contents are similar to _stat, providing
+// creation, modify and other information about the file.
+struct FileStat
+{
+ // No change or create time because they are not available on most systems
+ SInt64 ModifyTime;
+ SInt64 AccessTime;
+ SInt64 FileSize;
+
+ bool operator== (const FileStat& stat) const
+ {
+ return ( (ModifyTime == stat.ModifyTime) &&
+ (AccessTime == stat.AccessTime) &&
+ (FileSize == stat.FileSize) );
+ }
+};
+
+//-----------------------------------------------------------------------------------
+// *** System File
+
+// System file is created to access objects on file system directly
+// This file can refer directly to path.
+// System file can be open & closed several times; however, such use is not recommended
+// This class is realy a wrapper around an implementation of File interface for a
+// particular platform.
+
+class SysFile : public DelegatedFile
+{
+protected:
+ SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); }
+public:
+
+ // ** Constructor
+ SysFile();
+ // Opens a file
+ SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite);
+
+ // ** Open & management
+ bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite);
+
+ OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite)
+ { return Open(path, Open_ReadWrite|Open_Create, mode); }
+
+ // Helper function: obtain file statistics information. In GFx, this is used to detect file changes.
+ // Return 0 if function failed, most likely because the file doesn't exist.
+ static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path);
+
+ // ** Overrides
+ // Overridden to provide re-open support
+ virtual int GetErrorCode();
+
+ virtual bool IsValid();
+
+ virtual bool Close();
+};
+
+} // Scaleform
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_System.cpp b/LibOVR/Src/Kernel/OVR_System.cpp new file mode 100644 index 0000000..8eba8e2 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_System.cpp @@ -0,0 +1,70 @@ +/************************************************************************************
+
+Filename : OVR_System.cpp
+Content : General kernel initialization/cleanup, including that
+ of the memory allocator.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_System.h"
+#include "OVR_Threads.h"
+#include "OVR_Timer.h"
+
+namespace OVR {
+
+// ***** OVR::System Implementation
+
+// Initializes System core, installing allocator.
+void System::Init(Log* log, Allocator *palloc)
+{
+ if (!Allocator::GetInstance())
+ {
+ Log::SetGlobalLog(log);
+ Timer::initializeTimerSystem();
+ Allocator::setInstance(palloc);
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("System::Init failed - duplicate call."));
+ }
+}
+
+void System::Destroy()
+{
+ if (Allocator::GetInstance())
+ {
+ // Wait for all threads to finish; this must be done so that memory
+ // allocator and all destructors finalize correctly.
+#ifdef OVR_ENABLE_THREADS
+ Thread::FinishAllThreads();
+#endif
+
+ // Shutdown heap and destroy SysAlloc singleton, if any.
+ Allocator::GetInstance()->onSystemShutdown();
+ Allocator::setInstance(0);
+
+ Timer::shutdownTimerSystem();
+ Log::SetGlobalLog(Log::GetDefaultLog());
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("System::Destroy failed - System not initialized."));
+ }
+}
+
+// Returns 'true' if system was properly initialized.
+bool System::IsInitialized()
+{
+ return Allocator::GetInstance() != 0;
+}
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_System.h b/LibOVR/Src/Kernel/OVR_System.h new file mode 100644 index 0000000..e53b4c8 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_System.h @@ -0,0 +1,67 @@ +/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_System.h
+Content : General kernel initialization/cleanup, including that
+ of the memory allocator.
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_System_h
+#define OVR_System_h
+
+#include "OVR_Allocator.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+// ***** System Core Initialization class
+
+// System initialization must take place before any other OVR_Kernel objects are used;
+// this is done my calling System::Init(). Among other things, this is necessary to
+// initialize the memory allocator. Similarly, System::Destroy must be
+// called before program exist for proper cleanup. Both of these tasks can be achieved by
+// simply creating System object first, allowing its constructor/destructor do the work.
+
+// TBD: Require additional System class for Oculus Rift API?
+
+class System
+{
+public:
+
+ // System constructor expects allocator to be specified, if it is being substituted.
+ System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug),
+ Allocator* palloc = DefaultAllocator::InitSystemSingleton())
+ {
+ Init(log, palloc);
+ }
+
+ ~System()
+ {
+ Destroy();
+ }
+
+ // Returns 'true' if system was properly initialized.
+ static bool OVR_CDECL IsInitialized();
+
+ // Initializes System core. Users can override memory implementation by passing
+ // a different Allocator here.
+ static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug),
+ Allocator *palloc = DefaultAllocator::InitSystemSingleton());
+
+ // De-initializes System more, finalizing the threading system and destroying
+ // the global memory allocator.
+ static void OVR_CDECL Destroy();
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Threads.h b/LibOVR/Src/Kernel/OVR_Threads.h new file mode 100644 index 0000000..9c29fe0 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Threads.h @@ -0,0 +1,396 @@ +/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Threads.h
+Content : Contains thread-related (safe) functionality
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_Threads_h
+#define OVR_Threads_h
+
+#include "OVR_Types.h"
+#include "OVR_Atomic.h"
+#include "OVR_RefCount.h"
+#include "OVR_Array.h"
+
+// Defines the infinite wait delay timeout
+#define OVR_WAIT_INFINITE 0xFFFFFFFF
+
+// To be defined in the project configuration options
+#ifdef OVR_ENABLE_THREADS
+
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ****** Declared classes
+
+// Declared with thread support only
+class Mutex;
+class WaitCondition;
+class Event;
+// Implementation forward declarations
+class MutexImpl;
+class WaitConditionImpl;
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Mutex
+
+// Mutex class represents a system Mutex synchronization object that provides access
+// serialization between different threads, allowing one thread mutually exclusive access
+// to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition.
+
+class Mutex
+{
+ friend class WaitConditionImpl;
+ friend class MutexImpl;
+
+ MutexImpl *pImpl;
+
+public:
+ // Constructor/destructor
+ Mutex(bool recursive = 1);
+ ~Mutex();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock();
+
+ // Returns 1 if the mutes is currently locked by another thread
+ // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired.
+ bool IsLockedByAnotherThread();
+
+ // Locker class; Used for automatic locking of a mutex withing scope
+ class Locker
+ {
+ public:
+ Mutex *pMutex;
+ Locker(Mutex *pmutex)
+ { pMutex = pmutex; pMutex->DoLock(); }
+ ~Locker()
+ { pMutex->Unlock(); }
+ };
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** WaitCondition
+
+/*
+ WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor.
+ Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that
+ call Notify() or NotifyAll().
+
+ The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then
+ starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same
+ resource, this ensures that any condition checked for while the mutex was locked does not change before
+ the wait on the condition is actually initiated.
+*/
+
+class WaitCondition
+{
+ friend class WaitConditionImpl;
+ // Internal implementation structure
+ WaitConditionImpl *pImpl;
+
+public:
+ // Constructor/destructor
+ WaitCondition();
+ ~WaitCondition();
+
+ // Release mutex and wait for condition. The mutex is re-aquired after the wait.
+ // Delay is specified in milliseconds (1/1000 of a second).
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+// Event is a wait-able synchronization object similar to Windows event.
+// Event can be waited on until it's signaled by another thread calling
+// either SetEvent or PulseEvent.
+
+class Event
+{
+ // Event state, its mutex and the wait condition
+ volatile bool State;
+ volatile bool Temporary;
+ mutable Mutex StateMutex;
+ WaitCondition StateWaitCondition;
+
+ void updateState(bool newState, bool newTemp, bool mustNotify);
+
+public:
+ Event(bool setInitially = 0) : State(setInitially), Temporary(false) { }
+ ~Event() { }
+
+ // Wait on an event condition until it is set
+ // Delay is specified in milliseconds (1/1000 of a second).
+ bool Wait(unsigned delay = OVR_WAIT_INFINITE);
+
+ // Set an event, releasing objects waiting on it
+ void SetEvent()
+ { updateState(true, false, true); }
+
+ // Reset an event, un-signaling it
+ void ResetEvent()
+ { updateState(false, false, false); }
+
+ // Set and then reset an event once a waiter is released.
+ // If threads are already waiting, they will be notified and released
+ // If threads are not waiting, the event is set until the first thread comes in
+ void PulseEvent()
+ { updateState(true, true, true); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Thread class
+
+// ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and
+// Thread::GetThreadId.
+typedef void* ThreadId;
+
+
+// *** Thread flags
+
+// Indicates that the thread is has been started, i.e. Start method has been called, and threads
+// OnExit() method has not yet been called/returned.
+#define OVR_THREAD_STARTED 0x01
+// This flag is set once the thread has ran, and finished.
+#define OVR_THREAD_FINISHED 0x02
+// This flag is set temporarily if this thread was started suspended. It is used internally.
+#define OVR_THREAD_START_SUSPENDED 0x08
+// This flag is used to ask a thread to exit. Message driven threads will usually check this flag
+// and finish once it is set.
+#define OVR_THREAD_EXIT 0x10
+
+
+class Thread : public RefCountBase<Thread>
+{ // NOTE: Waitable must be the first base since it implements RefCountImpl.
+
+public:
+
+ // *** Callback functions, can be used instead of overriding Run
+
+ // Run function prototypes.
+ // Thread function and user handle passed to it, executed by the default
+ // Thread::Run implementation if not null.
+ typedef int (*ThreadFn)(Thread *pthread, void* h);
+
+ // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried
+ ThreadFn ThreadFunction;
+ // User handle passes to a thread
+ void* UserHandle;
+
+ // Thread state to start a thread with
+ enum ThreadState
+ {
+ NotRunning = 0,
+ Running = 1,
+ Suspended = 2
+ };
+
+ // Thread priority
+ enum ThreadPriority
+ {
+ CriticalPriority,
+ HighestPriority,
+ AboveNormalPriority,
+ NormalPriority,
+ BelowNormalPriority,
+ LowestPriority,
+ IdlePriority,
+ };
+
+ // Thread constructor parameters
+ struct CreateParams
+ {
+ CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024,
+ int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority)
+ : threadFunction(func), userHandle(hand), stackSize(ssize),
+ processor(proc), initialState(state), priority(prior) {}
+ ThreadFn threadFunction; // Thread function
+ void* userHandle; // User handle passes to a thread
+ UPInt stackSize; // Thread stack size
+ int processor; // Thread hardware processor
+ ThreadState initialState; //
+ ThreadPriority priority; // Thread priority
+ };
+
+ // *** Constructors
+
+ // A default constructor always creates a thread in NotRunning state, because
+ // the derived class has not yet been initialized. The derived class can call Start explicitly.
+ // "processor" parameter specifies which hardware processor this thread will be run on.
+ // -1 means OS decides this. Implemented only on Win32
+ Thread(UPInt stackSize = 128 * 1024, int processor = -1);
+ // Constructors that initialize the thread with a pointer to function.
+ // An option to start a thread is available, but it should not be used if classes are derived from Thread.
+ // "processor" parameter specifies which hardware processor this thread will be run on.
+ // -1 means OS decides this. Implemented only on Win32
+ Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024,
+ int processor = -1, ThreadState initialState = NotRunning);
+ // Constructors that initialize the thread with a create parameters structure.
+ explicit Thread(const CreateParams& params);
+
+ // Destructor.
+ virtual ~Thread();
+
+ // Waits for all Threads to finish; should be called only from the root
+ // application thread. Once this function returns, we know that all other
+ // thread's references to Thread object have been released.
+ static void OVR_CDECL FinishAllThreads();
+
+
+ // *** Overridable Run function for thread processing
+
+ // - returning from this method will end the execution of the thread
+ // - return value is usually 0 for success
+ virtual int Run();
+ // Called after return/exit function
+ virtual void OnExit();
+
+
+ // *** Thread management
+
+ // Starts the thread if its not already running
+ // - internally sets up the threading and calls Run()
+ // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing
+ // - returns the exit code
+ virtual bool Start(ThreadState initialState = Running);
+
+ // Quits with an exit code
+ virtual void Exit(int exitCode=0);
+
+ // Suspend the thread until resumed
+ // Returns 1 for success, 0 for failure.
+ bool Suspend();
+ // Resumes currently suspended thread
+ // Returns 1 for success, 0 for failure.
+ bool Resume();
+
+ // Static function to return a pointer to the current thread
+ //static Thread* GetThread();
+
+
+ // *** Thread status query functions
+
+ bool GetExitFlag() const;
+ void SetExitFlag(bool exitFlag);
+
+ // Determines whether the thread was running and is now finished
+ bool IsFinished() const;
+ // Determines if the thread is currently suspended
+ bool IsSuspended() const;
+ // Returns current thread state
+ ThreadState GetThreadState() const;
+
+ // Returns the number of available CPUs on the system
+ static int GetCPUCount();
+
+ // Returns the thread exit code. Exit code is initialized to 0,
+ // and set to the return value if Run function after the thread is finished.
+ inline int GetExitCode() const { return ExitCode; }
+ // Returns an OS handle
+#if defined(OVR_OS_WIN32)
+ void* GetOSHandle() const { return ThreadHandle; }
+#else
+ pthread_t GetOSHandle() const { return ThreadHandle; }
+#endif
+
+#if defined(OVR_OS_WIN32)
+ ThreadId GetThreadId() const { return IdValue; }
+#else
+ ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); }
+#endif
+
+ static int GetOSPriority(ThreadPriority);
+ // *** Sleep
+
+ // Sleep secs seconds
+ static bool Sleep(unsigned secs);
+ // Sleep msecs milliseconds
+ static bool MSleep(unsigned msecs);
+
+
+ // *** Debugging functionality
+#if defined(OVR_OS_WIN32)
+ virtual void SetThreadName( const char* name );
+#else
+ virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); }
+#endif
+
+private:
+#if defined(OVR_OS_WIN32)
+ friend unsigned WINAPI Thread_Win32StartFn(void *pthread);
+
+#else
+ friend void *Thread_PthreadStartFn(void * phandle);
+
+ static int InitAttr;
+ static pthread_attr_t Attr;
+#endif
+
+protected:
+ // Thread state flags
+ AtomicInt<UInt32> ThreadFlags;
+ AtomicInt<SInt32> SuspendCount;
+ UPInt StackSize;
+
+ // Hardware processor which this thread is running on.
+ int Processor;
+ ThreadPriority Priority;
+
+#if defined(OVR_OS_WIN32)
+ void* ThreadHandle;
+ volatile ThreadId IdValue;
+
+ // System-specific cleanup function called from destructor
+ void CleanupSystemThread();
+
+#else
+ pthread_t ThreadHandle;
+#endif
+
+ // Exit code of the thread, as returned by Run.
+ int ExitCode;
+
+ // Internal run function.
+ int PRun();
+ // Finishes the thread and releases internal reference to it.
+ void FinishAndRelease();
+
+ void Init(const CreateParams& params);
+
+ // Protected copy constructor
+ Thread(const Thread &source) { OVR_UNUSED(source); }
+
+};
+
+// Returns the unique Id of a thread it is called on, intended for
+// comparison purposes.
+ThreadId GetCurrentThreadId();
+
+
+} // OVR
+
+#endif // OVR_ENABLE_THREADS
+#endif // OVR_Threads_h
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp new file mode 100644 index 0000000..c40d9d8 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp @@ -0,0 +1,795 @@ +
+#include "OVR_Threads.h"
+#include "OVR_Hash.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+#include "OVR_Timer.h"
+#include "OVR_Log.h"
+
+#include <pthread.h>
+#include <time.h>
+
+#ifdef OVR_OS_PS3
+#include <sys/sys_time.h>
+#include <sys/timer.h>
+#include <sys/synchronization.h>
+#define sleep(x) sys_timer_sleep(x)
+#define usleep(x) sys_timer_usleep(x)
+using std::timespec;
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#endif
+
+namespace OVR {
+
+// ***** Mutex implementation
+
+
+// *** Internal Mutex implementation structure
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ pthread_mutex_t SMutex;
+ bool Recursive;
+ unsigned LockCount;
+ pthread_t LockedBy;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(Mutex* pmutex, bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+ bool IsSignaled() const;
+};
+
+pthread_mutexattr_t Lock::RecursiveAttr;
+bool Lock::RecursiveAttrInit = 0;
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(Mutex* pmutex, bool recursive)
+{
+ Recursive = recursive;
+ LockCount = 0;
+
+ if (Recursive)
+ {
+ if (!Lock::RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&Lock::RecursiveAttr);
+ pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ Lock::RecursiveAttrInit = 1;
+ }
+
+ pthread_mutex_init(&SMutex, &Lock::RecursiveAttr);
+ }
+ else
+ pthread_mutex_init(&SMutex, 0);
+}
+
+MutexImpl::~MutexImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ while (pthread_mutex_lock(&SMutex));
+ LockCount++;
+ LockedBy = pthread_self();
+}
+
+bool MutexImpl::TryLock()
+{
+ if (!pthread_mutex_trylock(&SMutex))
+ {
+ LockCount++;
+ LockedBy = pthread_self();
+ return 1;
+ }
+
+ return 0;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ pthread_mutex_unlock(&SMutex);
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (pthread_self() != LockedBy)
+ return 1;
+ return 0;
+}
+
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ // NOTE: RefCount mode already thread-safe for all waitables.
+ pImpl = new MutexImpl(this, recursive);
+}
+
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+
+// ***** Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ pthread_mutex_t SMutex;
+ pthread_cond_t Condv;
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-aqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pthread_mutex_init(&SMutex, 0);
+ pthread_cond_init(&Condv, 0);
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+ pthread_cond_destroy(&Condv);
+}
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 1;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ pthread_mutex_lock(&SMutex);
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(unsigned i=0; i<lockCount; i++)
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait().
+ // The other mutex protects this gap.
+
+ if (delay == OVR_WAIT_INFINITE)
+ pthread_cond_wait(&Condv,&SMutex);
+ else
+ {
+ timespec ts;
+#ifdef OVR_OS_PS3
+ sys_time_sec_t s;
+ sys_time_nsec_t ns;
+ sys_time_get_current_time(&s, &ns);
+
+ ts.tv_sec = s + (delay / 1000);
+ ts.tv_nsec = ns + (delay % 1000) * 1000000;
+
+#else
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ ts.tv_sec = tv.tv_sec + (delay / 1000);
+ ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000;
+#endif
+ if (ts.tv_nsec > 999999999)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ int r = pthread_cond_timedwait(&Condv,&SMutex, &ts);
+ OVR_ASSERT(r == 0 || r == ETIMEDOUT);
+ if (r)
+ result = 0;
+ }
+
+ pthread_mutex_unlock(&SMutex);
+
+ // Re-aquire the mutex
+ for(unsigned i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_signal(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_broadcast(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+// ***** Current thread
+
+// Per-thread variable
+/*
+static __thread Thread* pCurrentThread = 0;
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ // NOTE: RefCount mode already thread-safe for all Waitable objects.
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ ThreadHandle = 0;
+}
+
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+
+// *** ThreadList - used to track all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ size_t operator()(const Thread* ptr)
+ {
+ return (((size_t)ptr) >> 6) ^ (size_t)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ pthread_t RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(pthread_self() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = pthread_self();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+/*
+static const char* mapsched_policy(int policy)
+{
+ switch(policy)
+ {
+ case SCHED_OTHER:
+ return "SCHED_OTHER";
+ case SCHED_RR:
+ return "SCHED_RR";
+ case SCHED_FIFO:
+ return "SCHED_FIFO";
+
+ }
+ return "UNKNOWN";
+}
+ int policy;
+ sched_param sparam;
+ pthread_getschedparam(pthread_self(), &policy, &sparam);
+ int max_prior = sched_get_priority_max(policy);
+ int min_prior = sched_get_priority_min(policy);
+ printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior);
+#include <stdio.h>
+*/
+// ***** Thread management
+
+// The actual first function called on thread start
+void* Thread_PthreadStartFn(void* phandle)
+{
+ Thread* pthread = (Thread*)phandle;
+ int result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return (void*) result;
+}
+
+int Thread::InitAttr = 0;
+pthread_attr_t Thread::Attr;
+
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+//static inline int MapToSystemPrority(Thread::ThreadPriority p)
+{
+#ifdef OVR_OS_PS3
+ switch(p)
+ {
+ case Thread::CriticalPriority: return 0;
+ case Thread::HighestPriority: return 300;
+ case Thread::AboveNormalPriority: return 600;
+ case Thread::NormalPriority: return 1000;
+ case Thread::BelowNormalPriority: return 1500;
+ case Thread::LowestPriority: return 2500;
+ case Thread::IdlePriority: return 3071;
+ } return 1000;
+#else
+ OVR_UNUSED(p);
+ return -1;
+#endif
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ if (!InitAttr)
+ {
+ pthread_attr_init(&Attr);
+ pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&Attr, 128 * 1024);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(NormalPriority);
+ pthread_attr_setschedparam(&Attr, &sparam);
+ InitAttr = 1;
+ }
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+
+ // AddRef to us until the thread is finished
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ int result;
+ if (StackSize != 128 * 1024 || Priority != NormalPriority)
+ {
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&attr, StackSize);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(Priority);
+ pthread_attr_setschedparam(&attr, &sparam);
+ result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this);
+ pthread_attr_destroy(&attr);
+ }
+ else
+ result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this);
+
+ if (result)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system"));
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread
+ // if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ pthread_exit((void *) exitCode);
+}
+
+ThreadId GetCurrentThreadId()
+{
+ return (void*)pthread_self();
+}
+
+// *** Sleep functions
+
+/* static */
+bool Thread::Sleep(unsigned secs)
+{
+ sleep(secs);
+ return 1;
+}
+/* static */
+bool Thread::MSleep(unsigned msecs)
+{
+ usleep(msecs*1000);
+ return 1;
+}
+
+/* static */
+int Thread::GetCPUCount()
+{
+ return 1;
+}
+
+
+#ifdef OVR_OS_PS3
+
+sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE };
+
+#endif
+
+}
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp b/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp new file mode 100644 index 0000000..fa3962e --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ThreadsWinAPI.cpp @@ -0,0 +1,994 @@ +/************************************************************************************
+
+Filename : OVR_ThreadsWinAPI.cpp
+Platform : WinAPI
+Content : Windows specific thread-related (safe) functionality
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Threads.h"
+#include "OVR_Hash.h"
+#include "OVR_Log.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+// For _beginthreadex / _endtheadex
+#include <process.h>
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------------
+// *** Internal Mutex implementation class
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ HANDLE hMutexOrSemaphore;
+ bool Recursive;
+ volatile unsigned LockCount;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+};
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(bool recursive)
+{
+ Recursive = recursive;
+ LockCount = 0;
+ hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL);
+}
+MutexImpl::~MutexImpl()
+{
+ CloseHandle(hMutexOrSemaphore);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0)
+ return;
+ LockCount++;
+}
+
+bool MutexImpl::TryLock()
+{
+ DWORD ret;
+ if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0)
+ return 0;
+ LockCount++;
+ return 1;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ // Release mutex
+ if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) :
+ ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0)
+ {
+ // This used to call Wait handlers if lockCount == 0.
+ }
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (!TryLock())
+ return 1;
+ Unlock(pmutex);
+ return 0;
+}
+
+/*
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+*/
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ pImpl = new MutexImpl(recursive);
+}
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Win32 Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ // Event pool entries for extra events
+ struct EventPoolEntry : public NewOverrideBase
+ {
+ HANDLE hEvent;
+ EventPoolEntry *pNext;
+ EventPoolEntry *pPrev;
+ };
+
+ Lock WaitQueueLoc;
+ // Stores free events that can be used later
+ EventPoolEntry * pFreeEventList;
+
+ // A queue of waiting objects to be signaled
+ EventPoolEntry* pQueueHead;
+ EventPoolEntry* pQueueTail;
+
+ // Allocation functions for free events
+ EventPoolEntry* GetNewEvent();
+ void ReleaseEvent(EventPoolEntry* pevent);
+
+ // Queue operations
+ void QueuePush(EventPoolEntry* pentry);
+ EventPoolEntry* QueuePop();
+ void QueueFindAndRemove(EventPoolEntry* pentry);
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-acqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pFreeEventList = 0;
+ pQueueHead =
+ pQueueTail = 0;
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ // Free all the resources
+ EventPoolEntry* p = pFreeEventList;
+ EventPoolEntry* pentry;
+
+ while(p)
+ {
+ // Move to next
+ pentry = p;
+ p = p->pNext;
+ // Delete old
+ ::CloseHandle(pentry->hEvent);
+ delete pentry;
+ }
+ // Shouldn't we also consider the queue?
+
+ // To be safe
+ pFreeEventList = 0;
+ pQueueHead =
+ pQueueTail = 0;
+}
+
+
+// Allocation functions for free events
+WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent()
+{
+ EventPoolEntry* pentry;
+
+ // If there are any free nodes, use them
+ if (pFreeEventList)
+ {
+ pentry = pFreeEventList;
+ pFreeEventList = pFreeEventList->pNext;
+ }
+ else
+ {
+ // Allocate a new node
+ pentry = new EventPoolEntry;
+ pentry->pNext = 0;
+ pentry->pPrev = 0;
+ // Non-signaled manual event
+ pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL);
+ }
+
+ return pentry;
+}
+
+void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent)
+{
+ // Mark event as non-signaled
+ ::ResetEvent(pevent->hEvent);
+ // And add it to free pool
+ pevent->pNext = pFreeEventList;
+ pevent->pPrev = 0;
+ pFreeEventList = pevent;
+}
+
+// Queue operations
+void WaitConditionImpl::QueuePush(EventPoolEntry* pentry)
+{
+ // Items already exist? Just add to tail
+ if (pQueueTail)
+ {
+ pentry->pPrev = pQueueTail;
+ pQueueTail->pNext = pentry;
+ pentry->pNext = 0;
+ pQueueTail = pentry;
+ }
+ else
+ {
+ // No items in queue
+ pentry->pNext =
+ pentry->pPrev = 0;
+ pQueueHead =
+ pQueueTail = pentry;
+ }
+}
+
+WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop()
+{
+ EventPoolEntry* pentry = pQueueHead;
+
+ // No items, null pointer
+ if (pentry)
+ {
+ // More items after this one? just grab the first item
+ if (pQueueHead->pNext)
+ {
+ pQueueHead = pentry->pNext;
+ pQueueHead->pPrev = 0;
+ }
+ else
+ {
+ // Last item left
+ pQueueTail =
+ pQueueHead = 0;
+ }
+ }
+ return pentry;
+}
+
+void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry)
+{
+ // Do an exhaustive search looking for an entry
+ EventPoolEntry* p = pQueueHead;
+
+ while(p)
+ {
+ // Entry found? Remove.
+ if (p == pentry)
+ {
+
+ // Remove the node form the list
+ // Prev link
+ if (pentry->pPrev)
+ pentry->pPrev->pNext = pentry->pNext;
+ else
+ pQueueHead = pentry->pNext;
+ // Next link
+ if (pentry->pNext)
+ pentry->pNext->pPrev = pentry->pPrev;
+ else
+ pQueueTail = pentry->pPrev;
+ // Done
+ return;
+ }
+
+ // Move to next item
+ p = p->pNext;
+ }
+}
+
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 0;
+ unsigned i;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+ EventPoolEntry* pentry;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ // Add an object to the wait queue
+ WaitQueueLoc.DoLock();
+ QueuePush(pentry = GetNewEvent());
+ WaitQueueLoc.Unlock();
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(i=0; i<lockCount; i++)
+ ::ReleaseMutex(pmutex->pImpl->hMutexOrSemaphore);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait(). However,
+ // if notify() comes in at this point in the other thread it will set our
+ // corresponding event so wait will just fall through, as expected.
+
+ // Block and wait on the event
+ DWORD waitResult = ::WaitForSingleObject(pentry->hEvent,
+ (delay == OVR_WAIT_INFINITE) ? INFINITE : delay);
+ /*
+repeat_wait:
+ DWORD waitResult =
+
+ ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE,
+ (delay == OVR_WAIT_INFINITE) ? INFINITE : delay,
+ QS_ALLINPUT);
+ */
+
+ WaitQueueLoc.DoLock();
+ switch(waitResult)
+ {
+ case WAIT_ABANDONED:
+ case WAIT_OBJECT_0:
+ result = 1;
+ // Wait was successful, therefore the event entry should already be removed
+ // So just add entry back to a free list
+ ReleaseEvent(pentry);
+ break;
+ /*
+ case WAIT_OBJECT_0 + 1:
+ // Messages in WINDOWS queue
+ {
+ MSG msg;
+ PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
+ WaitQueueLoc.Unlock();
+ goto repeat_wait;
+ }
+ break; */
+ default:
+ // Timeout, our entry should still be in a queue
+ QueueFindAndRemove(pentry);
+ ReleaseEvent(pentry);
+ }
+ WaitQueueLoc.Unlock();
+
+ // Re-aquire the mutex
+ for(i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ Lock::Locker lock(&WaitQueueLoc);
+
+ // Pop last entry & signal it
+ EventPoolEntry* pentry = QueuePop();
+ if (pentry)
+ ::SetEvent(pentry->hEvent);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ Lock::Locker lock(&WaitQueueLoc);
+
+ // Pop and signal all events
+ // NOTE : There is no need to release the events, it's the waiters job to do so
+ EventPoolEntry* pentry = QueuePop();
+ while (pentry)
+ {
+ ::SetEvent(pentry->hEvent);
+ pentry = QueuePop();
+ }
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+// Wait without a mutex
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Thread Class
+
+// Per-thread variable
+// MA: Don't use TLS for now - portability issues with DLLs, etc.
+/*
+#if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300)
+__declspec(thread) Thread* pCurrentThread = 0;
+#else
+#pragma data_seg(".tls$")
+__declspec(thread) Thread* pCurrentThread = 0;
+#pragma data_seg(".rwdata")
+#endif
+*/
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ IdValue = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ CleanupSystemThread();
+ ThreadHandle = 0;
+}
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+// *** ThreadList - used to tack all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ UPInt operator()(const Thread* ptr)
+ {
+ return (((UPInt)ptr) >> 6) ^ (UPInt)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ ThreadId RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(GetCurrentThreadId() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = GetCurrentThreadId();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+/* MA: Don't use TLS for now.
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+
+
+
+// ***** Thread management
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+{
+ switch(p)
+ {
+ case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL;
+ case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST;
+ case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL;
+ case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL;
+ case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL;
+ case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST;
+ case Thread::IdlePriority: return THREAD_PRIORITY_IDLE;
+ }
+ return THREAD_PRIORITY_NORMAL;
+}
+
+// The actual first function called on thread start
+unsigned WINAPI Thread_Win32StartFn(void * phandle)
+{
+ Thread * pthread = (Thread*)phandle;
+ if (pthread->Processor != -1)
+ {
+ DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor);
+ if (ret == 0)
+ OVR_DEBUG_LOG(("Could not set hardware processor for the thread"));
+ }
+ BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority));
+ if (ret == 0)
+ OVR_DEBUG_LOG(("Could not set thread priority"));
+ OVR_UNUSED(ret);
+
+ // Ensure that ThreadId is assigned once thread is running, in case
+ // beginthread hasn't filled it in yet.
+ pthread->IdValue = (ThreadId)::GetCurrentThreadId();
+
+ DWORD result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return (unsigned) result;
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ // Free old thread handle before creating the new one
+ CleanupSystemThread();
+
+ // AddRef to us until the thread is finished.
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+ ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize,
+ Thread_Win32StartFn, this, 0, (unsigned*)&IdValue);
+
+ // Failed? Fail the function
+ if (ThreadHandle == 0)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ // Can't suspend a thread that wasn't started
+ if (!(ThreadFlags & OVR_THREAD_STARTED))
+ return 0;
+
+ if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF)
+ {
+ SuspendCount++;
+ return 1;
+ }
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ // Can't suspend a thread that wasn't started
+ if (!(ThreadFlags & OVR_THREAD_STARTED))
+ return 0;
+
+ // Decrement count, and resume thread if it is 0
+ SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1);
+ if (oldCount >= 1)
+ {
+ if (oldCount == 1)
+ {
+ if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF)
+ return 1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread.
+ // MA: Don't use TLS for now.
+ //if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function.
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ // Call the exit function.
+ _endthreadex((unsigned)exitCode);
+}
+
+
+void Thread::CleanupSystemThread()
+{
+ if (ThreadHandle != 0)
+ {
+ ::CloseHandle(ThreadHandle);
+ ThreadHandle = 0;
+ }
+}
+
+// *** Sleep functions
+// static
+bool Thread::Sleep(unsigned secs)
+{
+ ::Sleep(secs*1000);
+ return 1;
+}
+
+// static
+bool Thread::MSleep(unsigned msecs)
+{
+ ::Sleep(msecs);
+ return 1;
+}
+
+void Thread::SetThreadName( const char* name )
+{
+#if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING)
+ // Looks ugly, but it is the recommended way to name a thread.
+ typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000
+ LPCSTR szName; // Pointer to name (in user address space)
+ DWORD dwThreadID; // Thread ID (-1 for caller thread)
+ DWORD dwFlags; // Reserved for future use; must be zero
+ } THREADNAME_INFO;
+
+ THREADNAME_INFO info;
+
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = reinterpret_cast<DWORD>(GetThreadId());
+ info.dwFlags = 0;
+
+ __try
+ {
+#ifdef _WIN64
+ RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info );
+#else
+ RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info );
+#endif
+ }
+ __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER )
+ {
+ }
+#endif // OVR_BUILD_SHIPPING
+}
+
+// static
+int Thread::GetCPUCount()
+{
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+ return (int) sysInfo.dwNumberOfProcessors;
+}
+
+// Returns the unique Id of a thread it is called on, intended for
+// comparison purposes.
+ThreadId GetCurrentThreadId()
+{
+ return (ThreadId)::GetCurrentThreadId();
+}
+
+} // OVR
+
+#endif
+
+
diff --git a/LibOVR/Src/Kernel/OVR_Timer.cpp b/LibOVR/Src/Kernel/OVR_Timer.cpp new file mode 100644 index 0000000..db25b8f --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Timer.cpp @@ -0,0 +1,156 @@ +/************************************************************************************
+
+Filename : OVR_Timer.cpp
+Content : Provides static functions for precise timing
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_Timer.h"
+
+#if defined (OVR_OS_WIN32)
+#include <windows.h>
+
+#else
+#include <sys/time.h>
+#endif
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Timer Class
+
+UInt64 Timer::GetProfileTicks()
+{
+ return (GetRawTicks() * MksPerSecond) / GetRawFrequency();
+}
+double Timer::GetProfileSeconds()
+{
+ static UInt64 StartTime = GetProfileTicks();
+ return TicksToSeconds(GetProfileTicks()-StartTime);
+}
+
+
+//------------------------------------------------------------------------
+// *** Win32 Specific Timer
+
+#if (defined (OVR_OS_WIN32))
+
+CRITICAL_SECTION WinAPI_GetTimeCS;
+volatile UInt32 WinAPI_OldTime = 0;
+volatile UInt32 WinAPI_WrapCounter = 0;
+
+
+UInt32 Timer::GetTicksMs()
+{
+ return timeGetTime();
+}
+
+UInt64 Timer::GetTicks()
+{
+ DWORD ticks = timeGetTime();
+ UInt64 result;
+
+ // On Win32 QueryPerformanceFrequency is unreliable due to SMP and
+ // performance levels, so use this logic to detect wrapping and track
+ // high bits.
+ ::EnterCriticalSection(&WinAPI_GetTimeCS);
+
+ if (WinAPI_OldTime > ticks)
+ WinAPI_WrapCounter++;
+ WinAPI_OldTime = ticks;
+
+ result = (UInt64(WinAPI_WrapCounter) << 32) | ticks;
+ ::LeaveCriticalSection(&WinAPI_GetTimeCS);
+
+ return result * MksPerMs;
+}
+
+UInt64 Timer::GetRawTicks()
+{
+ LARGE_INTEGER li;
+ QueryPerformanceCounter(&li);
+ return li.QuadPart;
+}
+
+UInt64 Timer::GetRawFrequency()
+{
+ static UInt64 perfFreq = 0;
+ if (perfFreq == 0)
+ {
+ LARGE_INTEGER freq;
+ QueryPerformanceFrequency(&freq);
+ perfFreq = freq.QuadPart;
+ }
+ return perfFreq;
+}
+
+void Timer::initializeTimerSystem()
+{
+ timeBeginPeriod(1);
+ InitializeCriticalSection(&WinAPI_GetTimeCS);
+
+}
+void Timer::shutdownTimerSystem()
+{
+ DeleteCriticalSection(&WinAPI_GetTimeCS);
+ timeEndPeriod(1);
+}
+
+#else // !OVR_OS_WIN32
+
+
+//------------------------------------------------------------------------
+// *** Standard OS Timer
+
+UInt32 Timer::GetTicksMs()
+{
+ return (UInt32)(GetProfileTicks() / 1000);
+}
+// The profile ticks implementation is just fine for a normal timer.
+UInt64 Timer::GetTicks()
+{
+ return GetProfileTicks();
+}
+
+void Timer::initializeTimerSystem()
+{
+}
+void Timer::shutdownTimerSystem()
+{
+}
+
+UInt64 Timer::GetRawTicks()
+{
+ // TODO: prefer rdtsc when available?
+
+ // Return microseconds.
+ struct timeval tv;
+ UInt64 result;
+
+ gettimeofday(&tv, 0);
+
+ result = (UInt64)tv.tv_sec * 1000000;
+ result += tv.tv_usec;
+
+ return result;
+}
+
+UInt64 Timer::GetRawFrequency()
+{
+ return MksPerSecond;
+}
+
+#endif // !OVR_OS_WIN32
+
+
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_Timer.h b/LibOVR/Src/Kernel/OVR_Timer.h new file mode 100644 index 0000000..0b71912 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Timer.h @@ -0,0 +1,100 @@ +/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_Timer.h
+Content : Provides static functions for precise timing
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Timer_h
+#define OVR_Timer_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Timer
+
+// Timer class defines a family of static functions used for application
+// timing and profiling.
+
+class Timer
+{
+public:
+ enum {
+ MsPerSecond = 1000, // Milliseconds in one second.
+ MksPerMs = 1000, // Microseconds in one millisecond.
+ MksPerSecond = MsPerSecond * MksPerMs
+ };
+
+
+ // ***** Timing APIs for Application
+ // These APIs should be used to guide animation and other program functions
+ // that require precision.
+
+ // Returns ticks in milliseconds, as a 32-bit number. May wrap around every
+ // 49.2 days. Use either time difference of two values of GetTicks to avoid
+ // wrap-around. GetTicksMs may perform better then GetTicks.
+ static UInt32 OVR_STDCALL GetTicksMs();
+
+ // GetTicks returns general-purpose high resolution application timer value,
+ // measured in microseconds (mks, or 1/1000000 of a second). The actual precision
+ // is system-specific and may be much lower, such as 1 ms.
+ static UInt64 OVR_STDCALL GetTicks();
+
+
+ // ***** Profiling APIs.
+ // These functions should be used for profiling, but may have system specific
+ // artifacts that make them less appropriate for general system use.
+ // On Win32, for example these rely on QueryPerformanceConter may have
+ // problems with thread-core switching and power modes.
+
+ // Return a hi-res timer value in mks (1/1000000 of a sec).
+ // Generally you want to call this at the start and end of an
+ // operation, and pass the difference to
+ // TicksToSeconds() to find out how long the operation took.
+ static UInt64 OVR_STDCALL GetProfileTicks();
+
+ // More convenient zero-based profile timer in seconds. First call initializes
+ // the "zero" value; future calls return the difference. Not thread safe for first call.
+ // Due to low precision of Double, may malfunction after long runtime.
+ static double OVR_STDCALL GetProfileSeconds();
+
+ // Get the raw cycle counter value, providing the maximum possible timer resolution.
+ static UInt64 OVR_STDCALL GetRawTicks();
+ static UInt64 OVR_STDCALL GetRawFrequency();
+
+
+ // ***** Tick and time unit conversion.
+
+ // Convert micro-second ticks value into seconds value.
+ static inline double TicksToSeconds(UInt64 ticks)
+ {
+ return static_cast<double>(ticks) * (1.0 / (double)MksPerSecond);
+ }
+ // Convert Raw or frequency-unit ticks to seconds based on specified frequency.
+ static inline double RawTicksToSeconds(UInt64 rawTicks, UInt64 rawFrequency)
+ {
+ return static_cast<double>(rawTicks) * rawFrequency;
+ }
+
+private:
+ friend class System;
+ // System called during program startup/shutdown.
+ static void initializeTimerSystem();
+ static void shutdownTimerSystem();
+};
+
+
+} // Scaleform::Timer
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Types.h b/LibOVR/Src/Kernel/OVR_Types.h new file mode 100644 index 0000000..40eb156 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_Types.h @@ -0,0 +1,456 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Types.h
+Content : Standard library defines and simple types
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_Types_H
+#define OVR_Types_H
+
+//-----------------------------------------------------------------------------------
+// ****** Operating System
+//
+// Type definitions exist for the following operating systems: (OVR_OS_x)
+//
+// WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP)
+// DARWIN - Darwin OS (Mac OS X)
+// LINUX - Linux
+// ANDROID - Android
+// IPHONE - iPhone
+
+#if (defined(__APPLE__) && (defined(__GNUC__) ||\
+ defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__)
+# if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED))
+# define OVR_OS_IPHONE
+# else
+# define OVR_OS_DARWIN
+# define OVR_OS_MAC
+# endif
+#elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
+# define OVR_OS_WIN32
+#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
+# define OVR_OS_WIN32
+#elif defined(__linux__) || defined(__linux)
+# define OVR_OS_LINUX
+#else
+# define OVR_OS_OTHER
+#endif
+
+#if defined(ANDROID)
+# define OVR_OS_ANDROID
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** CPU Architecture
+//
+// The following CPUs are defined: (OVR_CPU_x)
+//
+// X86 - x86 (IA-32)
+// X86_64 - x86_64 (amd64)
+// PPC - PowerPC
+// PPC64 - PowerPC64
+// MIPS - MIPS
+// OTHER - CPU for which no special support is present or needed
+
+
+#if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
+# define OVR_CPU_X86_64
+# define OVR_64BIT_POINTERS
+#elif defined(__i386__) || defined(OVR_OS_WIN32)
+# define OVR_CPU_X86
+#elif defined(__powerpc64__)
+# define OVR_CPU_PPC64
+#elif defined(__ppc__)
+# define OVR_CPU_PPC
+#elif defined(__mips__) || defined(__MIPSEL__)
+# define OVR_CPU_MIPS
+#elif defined(__arm__)
+# define OVR_CPU_ARM
+#else
+# define OVR_CPU_OTHER
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Co-Processor Architecture
+//
+// The following co-processors are defined: (OVR_CPU_x)
+//
+// SSE - Available on all modern x86 processors.
+// Altivec - Available on all modern ppc processors.
+// Neon - Available on some armv7+ processors.
+
+#if defined(__SSE__) || defined(OVR_OS_WIN32)
+# define OVR_CPU_SSE
+#endif // __SSE__
+
+#if defined( __ALTIVEC__ )
+# define OVR_CPU_ALTIVEC
+#endif // __ALTIVEC__
+
+#if defined(__ARM_NEON__)
+# define OVR_CPU_ARM_NEON
+#endif // __ARM_NEON__
+
+
+//-----------------------------------------------------------------------------------
+// ***** Compiler
+//
+// The following compilers are defined: (OVR_CC_x)
+//
+// MSVC - Microsoft Visual C/C++
+// INTEL - Intel C++ for Linux / Windows
+// GNU - GNU C++
+// ARM - ARM C/C++
+
+#if defined(__INTEL_COMPILER)
+// Intel 4.0 = 400
+// Intel 5.0 = 500
+// Intel 6.0 = 600
+// Intel 8.0 = 800
+// Intel 9.0 = 900
+# define OVR_CC_INTEL __INTEL_COMPILER
+
+#elif defined(_MSC_VER)
+// MSVC 5.0 = 1100
+// MSVC 6.0 = 1200
+// MSVC 7.0 (VC2002) = 1300
+// MSVC 7.1 (VC2003) = 1310
+// MSVC 8.0 (VC2005) = 1400
+// MSVC 9.0 (VC2008) = 1500
+// MSVC 10.0 (VC2010) = 1600
+# define OVR_CC_MSVC _MSC_VER
+
+#elif defined(__GNUC__)
+# define OVR_CC_GNU
+
+#elif defined(__CC_ARM)
+# define OVR_CC_ARM
+
+#else
+# error "Oculus does not support this Compiler"
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Compiler Warnings
+
+// Disable MSVC warnings
+#if defined(OVR_CC_MSVC)
+# pragma warning(disable : 4127) // Inconsistent dll linkage
+# pragma warning(disable : 4530) // Exception handling
+# if (OVR_CC_MSVC<1300)
+# pragma warning(disable : 4514) // Unreferenced inline function has been removed
+# pragma warning(disable : 4710) // Function not inlined
+# pragma warning(disable : 4714) // _force_inline not inlined
+# pragma warning(disable : 4786) // Debug variable name longer than 255 chars
+# endif // (OVR_CC_MSVC<1300)
+#endif // (OVR_CC_MSVC)
+
+
+
+// *** Linux Unicode - must come before Standard Includes
+
+#ifdef OVR_OS_LINUX
+// Use glibc unicode functions on linux.
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Standard Includes
+//
+#include <stddef.h>
+#include <limits.h>
+#include <float.h>
+
+
+// MSVC Based Memory Leak checking - for now
+#if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG)
+# define _CRTDBG_MAP_ALLOC
+# include <stdlib.h>
+# include <crtdbg.h>
+
+// Uncomment this to help debug memory leaks under Visual Studio in OVR apps only.
+// This shouldn't be defined in customer releases.
+# ifndef OVR_DEFINE_NEW
+# define OVR_DEFINE_NEW new(__FILE__, __LINE__)
+# define new OVR_DEFINE_NEW
+# endif
+
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Type definitions for Common Systems
+
+namespace OVR {
+
+typedef char Char;
+
+// Pointer-sized integer
+typedef size_t UPInt;
+typedef ptrdiff_t SPInt;
+
+
+#if defined(OVR_OS_WIN32)
+
+typedef char SByte; // 8 bit Integer (Byte)
+typedef unsigned char UByte;
+typedef short SInt16; // 16 bit Integer (Word)
+typedef unsigned short UInt16;
+typedef long SInt32; // 32 bit Integer
+typedef unsigned long UInt32;
+typedef __int64 SInt64; // 64 bit Integer (QWord)
+typedef unsigned __int64 UInt64;
+
+
+#elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU)
+
+typedef int SByte __attribute__((__mode__ (__QI__)));
+typedef unsigned int UByte __attribute__((__mode__ (__QI__)));
+typedef int SInt16 __attribute__((__mode__ (__HI__)));
+typedef unsigned int UInt16 __attribute__((__mode__ (__HI__)));
+typedef int SInt32 __attribute__((__mode__ (__SI__)));
+typedef unsigned int UInt32 __attribute__((__mode__ (__SI__)));
+typedef int SInt64 __attribute__((__mode__ (__DI__)));
+typedef unsigned int UInt64 __attribute__((__mode__ (__DI__)));
+
+#else
+
+#include <sys/types.h>
+typedef int8_t SByte;
+typedef uint8_t UByte;
+typedef int16_t SInt16;
+typedef uint16_t UInt16;
+typedef int32_t SInt32;
+typedef uint32_t UInt32;
+typedef int64_t SInt64;
+typedef uint64_t UInt64;
+
+#endif
+
+
+// ***** BaseTypes Namespace
+
+// BaseTypes namespace is explicitly declared to allow base types to be used
+// by customers directly without other contents of OVR namespace.
+//
+// Its is expected that GFx samples will declare 'using namespace OVR::BaseTypes'
+// to allow using these directly without polluting the target scope with other
+// OVR declarations, such as Ptr<>, String or Mutex.
+namespace BaseTypes
+{
+ using OVR::UPInt;
+ using OVR::SPInt;
+ using OVR::UByte;
+ using OVR::SByte;
+ using OVR::UInt16;
+ using OVR::SInt16;
+ using OVR::UInt32;
+ using OVR::SInt32;
+ using OVR::UInt64;
+ using OVR::SInt64;
+} // OVR::BaseTypes
+
+} // OVR
+
+
+//-----------------------------------------------------------------------------------
+// ***** Macro Definitions
+//
+// We define the following:
+//
+// OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN
+// OVR_FORCE_INLINE - Forces inline expansion of function
+// OVR_ASM - Assembly language prefix
+// OVR_STR - Prefixes string with L"" if building unicode
+//
+// OVR_STDCALL - Use stdcall calling convention (Pascal arg order)
+// OVR_CDECL - Use cdecl calling convention (C argument order)
+// OVR_FASTCALL - Use fastcall calling convention (registers)
+//
+
+// Byte order constants, OVR_BYTE_ORDER is defined to be one of these.
+#define OVR_LITTLE_ENDIAN 1
+#define OVR_BIG_ENDIAN 2
+
+
+// Force inline substitute - goes before function declaration
+#if defined(OVR_CC_MSVC)
+# define OVR_FORCE_INLINE __forceinline
+#elif defined(OVR_CC_GNU)
+# define OVR_FORCE_INLINE __attribute__((always_inline))
+#else
+# define OVR_FORCE_INLINE inline
+#endif // OVR_CC_MSVC
+
+
+#if defined(OVR_OS_WIN32)
+
+ // ***** Win32
+
+ // Byte order
+ #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN
+
+ // Calling convention - goes after function return type but before function name
+ #ifdef __cplusplus_cli
+ # define OVR_FASTCALL __stdcall
+ #else
+ # define OVR_FASTCALL __fastcall
+ #endif
+
+ #define OVR_STDCALL __stdcall
+ #define OVR_CDECL __cdecl
+
+
+ // Assembly macros
+ #if defined(OVR_CC_MSVC)
+ # define OVR_ASM _asm
+ #else
+ # define OVR_ASM asm
+ #endif // (OVR_CC_MSVC)
+
+ #ifdef UNICODE
+ # define OVR_STR(str) L##str
+ #else
+ # define OVR_STR(str) str
+ #endif // UNICODE
+
+#else
+
+ // **** Standard systems
+
+ #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \
+ (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN))
+ # define OVR_BYTE_ORDER OVR_BIG_ENDIAN
+ #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64))
+ # define OVR_BYTE_ORDER OVR_BIG_ENDIAN
+ #else
+ # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN
+ #endif
+
+ // Assembly macros
+ #define OVR_ASM __asm__
+ #define OVR_ASM_PROC(procname) OVR_ASM
+ #define OVR_ASM_END OVR_ASM
+
+ // Calling convention - goes after function return type but before function name
+ #define OVR_FASTCALL
+ #define OVR_STDCALL
+ #define OVR_CDECL
+
+#endif // defined(OVR_OS_WIN32)
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_DEBUG_BREAK, OVR_ASSERT
+//
+// If not in debug build, macros do nothing
+#ifndef OVR_BUILD_DEBUG
+
+# define OVR_DEBUG_BREAK ((void)0)
+# define OVR_ASSERT(p) ((void)0)
+
+#else
+
+// Microsoft Win32 specific debugging support
+#if defined(OVR_OS_WIN32)
+# ifdef OVR_CPU_X86
+# if defined(__cplusplus_cli)
+# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0)
+# elif defined(OVR_CC_GNU)
+# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0)
+# else
+# define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0)
+# endif
+# else
+# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0)
+# endif
+// Unix specific debugging support
+#elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
+# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0)
+#else
+# define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0)
+#endif
+
+// This will cause compiler breakpoint
+#define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0)
+
+#endif // OVR_BUILD_DEBUG
+
+
+// Compile-time assert; produces compiler error if condition is false
+#define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} }
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_UNUSED - Unused Argument handling
+
+// Macro to quiet compiler warnings about unused parameters/variables.
+#if defined(OVR_CC_GNU)
+# define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0)
+#else
+# define OVR_UNUSED(a) (a)
+#endif
+
+#define OVR_UNUSED1(a1) OVR_UNUSED(a1)
+#define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2)
+#define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3)
+#define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4)
+#define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5)
+#define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6)
+#define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7)
+#define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8)
+#define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9)
+
+
+//-----------------------------------------------------------------------------------
+// ***** Configuration Macros
+
+// SF Build type
+#ifdef OVR_BUILD_DEBUG
+# define OVR_BUILD_STRING "Debug"
+#else
+# define OVR_BUILD_STRING "Release"
+#endif
+
+
+//// Enables SF Debugging information
+//# define OVR_BUILD_DEBUG
+
+// OVR_DEBUG_STATEMENT injects a statement only in debug builds.
+// OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise.
+#ifdef OVR_BUILD_DEBUG
+#define OVR_DEBUG_STATEMENT(s) s
+#define OVR_DEBUG_SELECT(d, nd) d
+#else
+#define OVR_DEBUG_STATEMENT(s)
+#define OVR_DEBUG_SELECT(d, nd) nd
+#endif
+
+
+#define OVR_ENABLE_THREADS
+//
+// Prevents OVR from defining new within
+// type macros, so developers can override
+// new using the #define new new(...) trick
+// - used with OVR_DEFINE_NEW macro
+//# define OVR_BUILD_DEFINE_NEW
+//
+
+
+#endif // OVR_Types_h
diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.cpp b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp new file mode 100644 index 0000000..21485df --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp @@ -0,0 +1,545 @@ +/**************************************************************************
+
+Filename : OVR_UTF8Util.cpp
+Content : UTF8 Unicode character encoding/decoding support
+Created : September 19, 2012
+Notes :
+Notes : Much useful info at "UTF-8 and Unicode FAQ"
+ http://www.cl.cam.ac.uk/~mgk25/unicode.html
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_UTF8Util.h"
+
+namespace OVR { namespace UTF8Util {
+
+SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen)
+{
+ const char* p = buf;
+ SPInt length = 0;
+
+ if (buflen != -1)
+ {
+ while (p - buf < buflen)
+ {
+ // We should be able to have ASStrings with 0 in the middle.
+ UTF8Util::DecodeNextChar_Advance0(&p);
+ length++;
+ }
+ }
+ else
+ {
+ while (UTF8Util::DecodeNextChar_Advance0(&p))
+ length++;
+ }
+
+ return length;
+}
+
+UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length)
+{
+ const char* buf = putf8str;
+ UInt32 c = 0;
+
+ if (length != -1)
+ {
+ while (buf - putf8str < length)
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ if (index == 0)
+ return c;
+ index--;
+ }
+
+ return c;
+ }
+
+ do
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+
+ if (c == 0)
+ {
+ // We've hit the end of the string; don't go further.
+ OVR_ASSERT(index == 0);
+ return c;
+ }
+ } while (index >= 0);
+
+ return c;
+}
+
+SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length)
+{
+ const char* buf = putf8str;
+
+ if (length != -1)
+ {
+ while ((buf - putf8str) < length && index > 0)
+ {
+ UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+ }
+
+ return buf-putf8str;
+ }
+
+ while (index > 0)
+ {
+ UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+
+ if (c == 0)
+ return buf-putf8str;
+ };
+
+ return buf-putf8str;
+}
+
+int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character)
+{
+ if (ucs_character <= 0x7F)
+ return 1;
+ else if (ucs_character <= 0x7FF)
+ return 2;
+ else if (ucs_character <= 0xFFFF)
+ return 3;
+ else if (ucs_character <= 0x1FFFFF)
+ return 4;
+ else if (ucs_character <= 0x3FFFFFF)
+ return 5;
+ else if (ucs_character <= 0x7FFFFFFF)
+ return 6;
+ else
+ return 0;
+}
+
+UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer)
+{
+ UInt32 uc;
+ char c;
+
+ // Security considerations:
+ //
+ // Changed, this is now only the case for DecodeNextChar:
+ // - If we hit a zero byte, we want to return 0 without stepping
+ // the buffer pointer past the 0. th
+ //
+ // If we hit an "overlong sequence"; i.e. a character encoded
+ // in a longer multibyte string than is necessary, then we
+ // need to discard the character. This is so attackers can't
+ // disguise dangerous characters or character sequences --
+ // there is only one valid encoding for each character.
+ //
+ // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE,
+ // 0xFFFF } then we ignore them; they are not valid in UTF-8.
+
+ // This isn't actually an invalid character; it's a valid char that
+ // looks like an inverted question mark.
+#define INVALID_CHAR 0x0FFFD
+
+#define FIRST_BYTE(mask, shift) \
+ uc = (c & (mask)) << (shift);
+
+#define NEXT_BYTE(shift) \
+ c = **putf8Buffer; \
+ if (c == 0) return 0; /* end of buffer, do not advance */ \
+ if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \
+ (*putf8Buffer)++; \
+ uc |= (c & 0x3F) << shift;
+
+ c = **putf8Buffer;
+ (*putf8Buffer)++;
+ if (c == 0)
+ return 0; // End of buffer.
+
+ if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII.
+
+ // Multi-byte sequences.
+ if ((c & 0xE0) == 0xC0)
+ {
+ // Two-byte sequence.
+ FIRST_BYTE(0x1F, 6);
+ NEXT_BYTE(0);
+ if (uc < 0x80) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xF0) == 0xE0)
+ {
+ // Three-byte sequence.
+ FIRST_BYTE(0x0F, 12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x800) return INVALID_CHAR; // overlong
+ // Not valid ISO 10646, but Flash requires these to work
+ // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0)
+ // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR;
+ // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646
+ return uc;
+ }
+ else if ((c & 0xF8) == 0xF0)
+ {
+ // Four-byte sequence.
+ FIRST_BYTE(0x07, 18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x010000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xFC) == 0xF8)
+ {
+ // Five-byte sequence.
+ FIRST_BYTE(0x03, 24);
+ NEXT_BYTE(18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x0200000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xFE) == 0xFC)
+ {
+ // Six-byte sequence.
+ FIRST_BYTE(0x01, 30);
+ NEXT_BYTE(24);
+ NEXT_BYTE(18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x04000000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else
+ {
+ // Invalid.
+ return INVALID_CHAR;
+ }
+}
+
+
+void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character)
+{
+ if (ucs_character <= 0x7F)
+ {
+ // Plain single-byte ASCII.
+ pbuffer[(*pindex)++] = (char) ucs_character;
+ }
+ else if (ucs_character <= 0x7FF)
+ {
+ // Two bytes.
+ pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0xFFFF)
+ {
+ // Three bytes.
+ pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x1FFFFF)
+ {
+ // Four bytes.
+ pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x3FFFFFF)
+ {
+ // Five bytes.
+ pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x7FFFFFFF)
+ {
+ // Six bytes.
+ pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else
+ {
+ // Invalid char; don't encode anything.
+ }
+}
+
+SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length)
+{
+ SPInt len = 0;
+ if (length != -1)
+ for (int i = 0; i < length; i++)
+ {
+ len += GetEncodeCharSize(pchar[i]);
+ }
+ else
+ for (int i = 0;; i++)
+ {
+ if (pchar[i] == 0)
+ return len;
+ len += GetEncodeCharSize(pchar[i]);
+ }
+ return len;
+}
+
+void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length)
+{
+ SPInt ofs = 0;
+ if (length != -1)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ EncodeChar(pbuff, &ofs, pchar[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0;; i++)
+ {
+ if (pchar[i] == 0)
+ break;
+ EncodeChar(pbuff, &ofs, pchar[i]);
+ }
+ }
+ pbuff[ofs] = 0;
+}
+
+UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen)
+{
+ wchar_t *pbegin = pbuff;
+ if (bytesLen == -1)
+ {
+ while (1)
+ {
+ UInt32 ch = DecodeNextChar_Advance0(&putf8str);
+ if (ch == 0)
+ break;
+ else if (ch >= 0xFFFF)
+ ch = 0xFFFD;
+ *pbuff++ = wchar_t(ch);
+ }
+ }
+ else
+ {
+ const char* p = putf8str;
+ while ((p - putf8str) < bytesLen)
+ {
+ UInt32 ch = DecodeNextChar_Advance0(&p);
+ if (ch >= 0xFFFF)
+ ch = 0xFFFD;
+ *pbuff++ = wchar_t(ch);
+ }
+ }
+
+ *pbuff = 0;
+ return pbuff - pbegin;
+}
+
+
+#ifdef UTF8_UNIT_TEST
+
+// Compile this test case with something like:
+//
+// gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test
+//
+// or
+//
+// cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I..
+//
+// If possible, try running the test program with the first arg
+// pointing at the file:
+//
+// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+//
+// and examine the results by eye to make sure they are acceptable to
+// you.
+
+
+#include "base/utility.h"
+#include <stdio.h>
+
+
+bool check_equal(const char* utf8_in, const UInt32* ucs_in)
+{
+ for (;;)
+ {
+ UInt32 next_ucs = *ucs_in++;
+ UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in);
+ if (next_ucs != next_ucs_from_utf8)
+ {
+ return false;
+ }
+ if (next_ucs == 0)
+ {
+ OVR_ASSERT(next_ucs_from_utf8 == 0);
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+void log_ascii(const char* line)
+{
+ for (;;)
+ {
+ unsigned char c = (unsigned char) *line++;
+ if (c == 0)
+ {
+ // End of line.
+ return;
+ }
+ else if (c != '\n'
+ && (c < 32 || c > 127))
+ {
+ // Non-printable as plain ASCII.
+ printf("<0x%02X>", (int) c);
+ }
+ else
+ {
+ printf("%c", c);
+ }
+ }
+}
+
+
+void log_ucs(const UInt32* line)
+{
+ for (;;)
+ {
+ UInt32 uc = *line++;
+ if (uc == 0)
+ {
+ // End of line.
+ return;
+ }
+ else if (uc != '\n'
+ && (uc < 32 || uc > 127))
+ {
+ // Non-printable as plain ASCII.
+ printf("<U-%04X>", uc);
+ }
+ else
+ {
+ printf("%c", (char) uc);
+ }
+ }
+}
+
+
+// Simple canned test.
+int main(int argc, const char* argv[])
+{
+ {
+ const char* test8 = "Ignacio Castaño";
+ const UInt32 test32[] =
+ {
+ 0x49, 0x67, 0x6E, 0x61, 0x63,
+ 0x69, 0x6F, 0x20, 0x43, 0x61,
+ 0x73, 0x74, 0x61, 0xF1, 0x6F,
+ 0x00
+ };
+
+ OVR_ASSERT(check_equal(test8, test32));
+ }
+
+ // If user passed an arg, try reading the file as UTF-8 encoded text.
+ if (argc > 1)
+ {
+ const char* filename = argv[1];
+ FILE* fp = fopen(filename, "rb");
+ if (fp == NULL)
+ {
+ printf("Can't open file '%s'\n", filename);
+ return 1;
+ }
+
+ // Read lines from the file, encode/decode them, and highlight discrepancies.
+ const int LINE_SIZE = 200; // max line size
+ char line_buffer_utf8[LINE_SIZE];
+ char reencoded_utf8[6 * LINE_SIZE];
+ UInt32 line_buffer_ucs[LINE_SIZE];
+
+ int byte_counter = 0;
+ for (;;)
+ {
+ int c = fgetc(fp);
+ if (c == EOF)
+ {
+ // Done.
+ break;
+ }
+ line_buffer_utf8[byte_counter++] = c;
+ if (c == '\n' || byte_counter >= LINE_SIZE - 2)
+ {
+ // End of line. Process the line.
+ line_buffer_utf8[byte_counter++] = 0; // terminate.
+
+ // Decode into UCS.
+ const char* p = line_buffer_utf8;
+ UInt32* q = line_buffer_ucs;
+ for (;;)
+ {
+ UInt32 uc = UTF8Util::DecodeNextChar(&p);
+ *q++ = uc;
+
+ OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE);
+ OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE);
+
+ if (uc == 0) break;
+ }
+
+ // Encode back into UTF-8.
+ q = line_buffer_ucs;
+ int index = 0;
+ for (;;)
+ {
+ UInt32 uc = *q++;
+ OVR_ASSERT(index < LINE_SIZE * 6 - 6);
+ int last_index = index;
+ UTF8Util::EncodeChar(reencoded_utf8, &index, uc);
+ OVR_ASSERT(index <= last_index + 6);
+ if (uc == 0) break;
+ }
+
+ // This can be useful for debugging.
+#if 0
+ // Show the UCS and the re-encoded UTF-8.
+ log_ucs(line_buffer_ucs);
+ log_ascii(reencoded_utf8);
+#endif // 0
+
+ OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs));
+ OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs));
+
+ // Start next line.
+ byte_counter = 0;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ return 0;
+}
+
+
+#endif // UTF8_UNIT_TEST
+
+}} // namespace UTF8Util::OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.h b/LibOVR/Src/Kernel/OVR_UTF8Util.h new file mode 100644 index 0000000..bf5af46 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_UTF8Util.h @@ -0,0 +1,88 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_UTF8Util.h
+Content : UTF8 Unicode character encoding/decoding support
+Created : September 19, 2012
+Notes :
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_UTF8Util_h
+#define OVR_UTF8Util_h
+
+#include "OVR_Types.h"
+
+namespace OVR { namespace UTF8Util {
+
+//-----------------------------------------------------------------------------------
+
+// *** UTF8 string length and indexing.
+
+// Determines the length of UTF8 string in characters.
+// If source length is specified (in bytes), null 0 character is counted properly.
+SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1);
+
+// Gets a decoded UTF8 character at index; you can access up to the index returned
+// by GetLength. 0 will be returned for out of bounds access.
+UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1);
+
+// Converts UTF8 character index into byte offset.
+// -1 is returned if index was out of bounds.
+SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1);
+
+
+// *** 16-bit Unicode string Encoding/Decoding routines.
+
+// Determines the number of bytes necessary to encode a string.
+// Does not count the terminating 0 (null) character.
+SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1);
+
+// Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at
+// least GetEncodeStringSize() + 1.
+void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1);
+
+// Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available.
+// Characters over 0xFFFF are replaced with 0xFFFD.
+// Returns the length of resulting string (number of characters)
+UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1);
+
+
+// *** Individual character Encoding/Decoding.
+
+// Determined the number of bytes necessary to encode a UCS character.
+int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter);
+
+// Encodes the given UCS character into the given UTF-8 buffer.
+// Writes the data starting at buffer[offset], and
+// increments offset by the number of bytes written.
+// May write up to 6 bytes, so make sure there's room in the buffer
+void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter);
+
+// Return the next Unicode character in the UTF-8 encoded buffer.
+// Invalid UTF-8 sequences produce a U+FFFD character as output.
+// Advances *utf8_buffer past the character returned. Pointer advance
+// occurs even if the terminating 0 character is hit, since that allows
+// strings with middle '\0' characters to be supported.
+UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer);
+
+// Safer version of DecodeNextChar, which doesn't advance pointer if
+// null character is hit.
+inline UInt32 DecodeNextChar(const char** putf8Buffer)
+{
+ UInt32 ch = DecodeNextChar_Advance0(putf8Buffer);
+ if (ch == 0)
+ (*putf8Buffer)--;
+ return ch;
+}
+
+
+}} // OVR::UTF8Util
+
+#endif
diff --git a/LibOVR/Src/OVR_Device.h b/LibOVR/Src/OVR_Device.h new file mode 100644 index 0000000..4434916 --- /dev/null +++ b/LibOVR/Src/OVR_Device.h @@ -0,0 +1,627 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Device.h
+Content : Definition of HMD-related Device interfaces
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Device_h
+#define OVR_Device_h
+
+#include "OVR_DeviceConstants.h"
+#include "OVR_DeviceHandle.h"
+#include "OVR_DeviceMessages.h"
+#include "OVR_HIDDeviceBase.h"
+
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+
+namespace OVR {
+
+class SensorDevice;
+class DeviceCommon;
+class DeviceManager;
+
+// MessageHandler is a base class from which users derive to receive messages,
+// its OnMessage handler will be called for messages once it is installed on
+// a device. Same message handler can be installed on multiple devices.
+class MessageHandler
+{
+ friend class MessageHandlerImpl;
+public:
+ MessageHandler();
+ virtual ~MessageHandler();
+
+ // Returns 'true' if handler is currently installed on any devices.
+ bool IsHandlerInstalled() const;
+
+ // Should be called from derived class destructor to avoid handler
+ // being called after it exits.
+ void RemoveHandlerFromDevices();
+
+ // Returns a pointer to the internal lock object that is locked by a
+ // background thread while OnMessage() is called.
+ // This lock guaranteed to survive until ~MessageHandler.
+ Lock* GetHandlerLock() const;
+
+
+ virtual void OnMessage(const Message&) { }
+
+ // Determines if handler supports a specific message type. Can
+ // be used to filter out entire message groups. The result
+ // returned by this function shouldn't change after handler creation.
+ virtual bool SupportsMessageType(MessageType) const { return true; }
+
+private:
+ UPInt Internal[4];
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceBase
+
+// DeviceBase is the base class for all OVR Devices. It provides the following basic
+// functionality:
+// - Reports device type, manager, and associated parent (if any).
+// - Supports installable message handlers, which are notified of device events.
+// - Device objects are created through DeviceHandle::CreateDevice or more commonly
+// through DeviceEnumerator<>::CreateDevice.
+// - Created devices are reference counted, starting with RefCount of 1.
+// - Device is resources are cleaned up when it is Released, although its handles
+// may survive longer if referenced.
+
+class DeviceBase : public NewOverrideBase
+{
+ friend class DeviceHandle;
+ friend class DeviceManagerImpl;
+public:
+
+ // Enumerating DeviceBase enumerates all devices.
+ enum { EnumDeviceType = Device_All };
+
+ virtual ~DeviceBase() { }
+ virtual void AddRef();
+ virtual void Release();
+
+ virtual DeviceBase* GetParent() const;
+ virtual DeviceManager* GetManager() const;
+
+ virtual void SetMessageHandler(MessageHandler* handler);
+ virtual MessageHandler* GetMessageHandler() const;
+
+ virtual DeviceType GetType() const;
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ // returns the MessageHandler's lock
+ Lock* GetHandlerLock() const;
+protected:
+ // Internal
+ virtual DeviceCommon* getDeviceCommon() const = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceInfo
+
+// DeviceInfo describes a device and its capabilities, obtained by calling
+// GetDeviceInfo. This base class only contains device-independent functionality;
+// users will normally use a derived HMDInfo or SensorInfo classes for more
+// extensive device info.
+
+class DeviceInfo
+{
+public:
+ DeviceInfo() : InfoClassType(Device_None), Type(Device_None), Version(0)
+ { ProductName[0] = Manufacturer[0] = 0; }
+
+ enum { MaxNameLength = 32 };
+
+ // Type of device for which DeviceInfo is intended.
+ // This will be set to Device_HMD for HMDInfo structure, note that this may be
+ // different form the actual device type since (Device_None) is valid.
+ const DeviceType InfoClassType;
+ // Type of device this describes. This must be the same as InfoClassType when
+ // InfoClassType != Device_None.
+ DeviceType Type;
+ // Name string describing the product: "Oculus Rift DK1", etc.
+ char ProductName[MaxNameLength];
+ char Manufacturer[MaxNameLength];
+ unsigned Version;
+
+protected:
+ DeviceInfo(DeviceType type) : InfoClassType(type), Type(type), Version(0)
+ { ProductName[0] = Manufacturer[0] = 0; }
+ void operator = (const DeviceInfo&) { OVR_ASSERT(0); } // Assignment not allowed.
+};
+
+
+//-------------------------------------------------------------------------------------
+// DeviceEnumerationArgs provides device enumeration argumenrs for DeviceManager::EnumerateDevicesEx.
+class DeviceEnumerationArgs
+{
+public:
+ DeviceEnumerationArgs(DeviceType enumType, bool availableOnly)
+ : EnumType(enumType), AvailableOnly(availableOnly)
+ { }
+
+ // Helper; returns true if args match our enumeration criteria.
+ bool MatchRule(DeviceType type, bool available) const
+ {
+ return ((EnumType == type) || (EnumType == Device_All)) &&
+ (available || !AvailableOnly);
+ }
+
+protected:
+ DeviceType EnumType;
+ bool AvailableOnly;
+};
+
+
+// DeviceEnumerator<> is used to enumerate and create devices of specified class,
+// it is returned by calling MeviceManager::EnumerateDevices. Initially, the enumerator will
+// refer to the first device of specified type. Additional devices can be accessed by
+// calling Next().
+
+template<class T = DeviceBase>
+class DeviceEnumerator : public DeviceHandle
+{
+ friend class DeviceManager;
+ friend class DeviceManagerImpl;
+public:
+ DeviceEnumerator()
+ : DeviceHandle(), EnumArgs(Device_None, true) { }
+
+ // Next advances enumeration to the next device that first criteria.
+ // Returns false if no more devices exist that match enumeration criteria.
+ bool Next() { return enumerateNext(EnumArgs); }
+
+ // Creates an instance of the device referenced by enumerator; returns null
+ // if enumerator does not refer to a valid device or device is unavailable.
+ // If device was already created, the same object with incremented ref-count is returned.
+ T* CreateDevice() { return static_cast<T*>(DeviceHandle::CreateDevice()); }
+
+protected:
+ DeviceEnumerator(const DeviceHandle &dev, const DeviceEnumerationArgs& args)
+ : DeviceHandle(dev), EnumArgs(args)
+ { }
+
+ DeviceEnumerationArgs EnumArgs;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager
+
+// DeviceManager maintains and provides access to devices supported by OVR, such as
+// HMDs and sensors. A single instance of DeviceManager is normally created at
+// program startup, allowing devices to be enumerated and created. DeviceManager is
+// reference counted and is AddRefed by its created child devices, causing it to
+// always be the last object that is released.
+//
+// Install MessageHandler on DeviceManager to detect when devices are inserted or removed.
+//
+// The following code will create the manager and its first available HMDDevice,
+// and then release it when not needed:
+//
+// DeviceManager* manager = DeviceManager::Create();
+// HMDDevice* hmd = manager->EnumerateDevices<HMDDevice>().CreateDevice();
+//
+// if (hmd) hmd->Release();
+// if (manager) manager->Release();
+
+
+class DeviceManager : public DeviceBase
+{
+public:
+
+ DeviceManager()
+ { }
+
+ // DeviceBase implementation.
+ virtual DeviceType GetType() const { return Device_Manager; }
+ virtual DeviceManager* GetManager() const { return const_cast<DeviceManager*>(this); }
+
+
+ // EnumerateDevices enumerates all of the available devices of the specified class,
+ // returning an enumerator that references the first device. An empty enumerator is
+ // returned if no devices are available. The following APIs are exposed through
+ // DeviceEnumerator:
+ // DeviceEnumerator::GetType() - Check device type. Returns Device_None
+ // if no device was found/pointed to.
+ // DeviceEnumerator::GetDeviceInfo() - Get more information on device.
+ // DeviceEnumerator::CreateDevice() - Create an instance of device.
+ // DeviceEnumerator::Next() - Move onto next device.
+ template<class D>
+ DeviceEnumerator<D> EnumerateDevices(bool availableOnly = true)
+ {
+ // TBD: A cleaner (but less efficient) alternative is though enumeratorFromHandle.
+ DeviceEnumerator<> e = EnumerateDevicesEx(DeviceEnumerationArgs((DeviceType)D::EnumDeviceType, availableOnly));
+ return *reinterpret_cast<DeviceEnumerator<D>*>(&e);
+ }
+
+ // EnumerateDevicesEx provides internal implementation for device enumeration, enumerating
+ // devices based on dynamically specified DeviceType in DeviceEnumerationArgs.
+ // End users should call DeumerateDevices<>() instead.
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0;
+
+ // Adds a device (DeviceCreateDesc*) into Devices. Returns NULL,
+ // if unsuccessful or device is already in the list.
+ virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0;
+
+ // Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time.
+ static DeviceManager* Create();
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_Manager };
+
+protected:
+ DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args)
+ { return DeviceEnumerator<>(h, args); }
+
+ DeviceManager* getThis() { return this; }
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDInfo
+
+// This structure describes various aspects of the HMD allowing us to configure rendering.
+//
+// Currently included data:
+// - Physical screen dimensions, resolution, and eye distances.
+// (some of these will be configurable with a tool in the future).
+// These arguments allow us to properly setup projection across HMDs.
+// - DisplayDeviceName for identifying HMD screen; system-specific interpretation.
+//
+// TBD:
+// - Power on/ off?
+// - Sensor rates and capabilities
+// - Distortion radius/variables
+// - Screen update frequency
+// - Distortion needed flag
+// - Update modes:
+// Set update mode: Stereo (both sides together), mono (same in both eyes),
+// Alternating, Alternating scan-lines.
+
+class HMDInfo : public DeviceInfo
+{
+public:
+ // Size of the entire screen, in pixels.
+ unsigned HResolution, VResolution;
+ // Physical dimensions of the active screen in meters. Can be used to calculate
+ // projection center while considering IPD.
+ float HScreenSize, VScreenSize;
+ // Physical offset from the top of the screen to the eye center, in meters.
+ // This will usually, but not necessarily be half of VScreenSize.
+ float VScreenCenter;
+ // Distance from the eye to screen surface, in meters.
+ // Useful for calculating FOV and projection.
+ float EyeToScreenDistance;
+ // Distance between physical lens centers useful for calculating distortion center.
+ float LensSeparationDistance;
+ // Configured distance between the user's eye centers, in meters. Defaults to 0.064.
+ float InterpupillaryDistance;
+
+ // Radial distortion correction coefficients.
+ // The distortion assumes that the input texture coordinates will be scaled
+ // by the following equation:
+ // uvResult = uvInput * (K0 + K1 * uvLength^2 + K2 * uvLength^4)
+ // Where uvInput is the UV vector from the center of distortion in direction
+ // of the mapped pixel, uvLength is the magnitude of that vector, and uvResult
+ // the corresponding location after distortion.
+ float DistortionK[4];
+
+ float ChromaAbCorrection[4];
+
+ // Desktop coordinate position of the screen (can be negative; may not be present on all platforms)
+ int DesktopX, DesktopY;
+
+ // Windows:
+ // "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC.
+ char DisplayDeviceName[32];
+
+ // MacOS:
+ long DisplayId;
+
+
+ HMDInfo()
+ : DeviceInfo(Device_HMD),
+ HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0),
+ VScreenCenter(0), EyeToScreenDistance(0),
+ LensSeparationDistance(0), InterpupillaryDistance(0),
+ DesktopX(0), DesktopY(0), DisplayId(0)
+ {
+ DisplayDeviceName[0] = 0;
+ memset(DistortionK, 0, sizeof(DistortionK));
+ DistortionK[0] = 1;
+ ChromaAbCorrection[0] = ChromaAbCorrection[2] = 1;
+ ChromaAbCorrection[1] = ChromaAbCorrection[3] = 0;
+ }
+
+ // Operator = copies local fields only (base class must be correct already)
+ void operator = (const HMDInfo& src)
+ {
+ HResolution = src.HResolution;
+ VResolution = src.VResolution;
+ HScreenSize = src.HScreenSize;
+ VScreenSize = src.VScreenSize;
+ VScreenCenter = src.VScreenCenter;
+ EyeToScreenDistance = src.EyeToScreenDistance;
+ LensSeparationDistance = src.LensSeparationDistance;
+ InterpupillaryDistance = src.InterpupillaryDistance;
+ DistortionK[0] = src.DistortionK[0];
+ DistortionK[1] = src.DistortionK[1];
+ DistortionK[2] = src.DistortionK[2];
+ DistortionK[3] = src.DistortionK[3];
+ ChromaAbCorrection[0] = src.ChromaAbCorrection[0];
+ ChromaAbCorrection[1] = src.ChromaAbCorrection[1];
+ ChromaAbCorrection[2] = src.ChromaAbCorrection[2];
+ ChromaAbCorrection[3] = src.ChromaAbCorrection[3];
+ DesktopX = src.DesktopX;
+ DesktopY = src.DesktopY;
+ memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName));
+ DisplayId = src.DisplayId;
+ }
+
+ bool IsSameDisplay(const HMDInfo& o) const
+ {
+ return DisplayId == o.DisplayId &&
+ String::CompareNoCase(DisplayDeviceName,
+ o.DisplayDeviceName) == 0;
+ }
+
+};
+
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then.
+
+// TBD:
+// - Configure Sensor
+// - APIs to set On-Screen message, other states?
+
+class HMDDevice : public DeviceBase
+{
+public:
+ HMDDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_HMD };
+
+ virtual DeviceType GetType() const { return Device_HMD; }
+
+ // Creates a sensor associated with this HMD.
+ virtual SensorDevice* GetSensor() = 0;
+
+ // Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD.
+ // SensorDevice ptr is used to restore the 'fake' HMD (can be NULL).
+ HMDDevice* Disconnect(SensorDevice*);
+
+ // Returns 'true' if HMD device is a 'fake' HMD (was created this way or
+ // 'Disconnect' method was called).
+ bool IsDisconnected() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorRange & SensorInfo
+
+// SensorRange specifies maximum value ranges that SensorDevice hardware is configured
+// to detect. Although this range doesn't affect the scale of MessageBodyFrame values,
+// physical motions whose positive or negative magnitude is outside the specified range
+// may get clamped or misreported. Setting lower values may result in higher precision
+// tracking.
+struct SensorRange
+{
+ SensorRange(float maxAcceleration = 0.0f, float maxRotationRate = 0.0f,
+ float maxMagneticField = 0.0f)
+ : MaxAcceleration(maxAcceleration), MaxRotationRate(maxRotationRate),
+ MaxMagneticField(maxMagneticField)
+ { }
+
+ // Maximum detected acceleration in m/s^2. Up to 8*G equivalent support guaranteed,
+ // where G is ~9.81 m/s^2.
+ // Oculus DK1 HW has thresholds near: 2, 4 (default), 8, 16 G.
+ float MaxAcceleration;
+ // Maximum detected angular velocity in rad/s. Up to 8*Pi support guaranteed.
+ // Oculus DK1 HW thresholds near: 1, 2, 4, 8 Pi (default).
+ float MaxRotationRate;
+ // Maximum detectable Magnetic field strength in Gauss. Up to 2.5 Gauss support guaranteed.
+ // Oculus DK1 HW thresholds near: 0.88, 1.3, 1.9, 2.5 gauss.
+ float MaxMagneticField;
+};
+
+// SensorInfo describes capabilities of the sensor device.
+class SensorInfo : public DeviceInfo
+{
+public:
+ SensorInfo() : DeviceInfo(Device_Sensor), VendorId(0), ProductId(0)
+ {
+ SerialNumber[0] = 0;
+ }
+
+ // HID Vendor and ProductId of the device.
+ UInt16 VendorId;
+ UInt16 ProductId;
+ // MaxRanges report maximum sensor range values supported by HW.
+ SensorRange MaxRanges;
+ // Sensor (and display) serial number.
+ char SerialNumber[20];
+
+private:
+ void operator = (const SensorInfo&) { OVR_ASSERT(0); } // Assignment not allowed.
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+// SensorDevice is an interface to sensor data.
+// Install a MessageHandler of SensorDevice instance to receive MessageBodyFrame
+// notifications.
+//
+// TBD: Add Polling API? More HID interfaces?
+
+class SensorDevice : public HIDDeviceBase, public DeviceBase
+{
+public:
+ SensorDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_Sensor };
+
+ virtual DeviceType GetType() const { return Device_Sensor; }
+
+
+ // CoordinateFrame defines whether messages come in the coordinate frame
+ // of the sensor device or HMD, which has a different internal sensor.
+ // Sensors obtained form the HMD will automatically use HMD coordinates.
+ enum CoordinateFrame
+ {
+ Coord_Sensor = 0,
+ Coord_HMD = 1
+ };
+
+ virtual void SetCoordinateFrame(CoordinateFrame coordframe) = 0;
+ virtual CoordinateFrame GetCoordinateFrame() const = 0;
+
+ // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
+ // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
+ // called twice or thrice at the same 'tick'.
+ // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
+ // times for each 'tick': the first call will contain averaged values, the second
+ // and third calls will provide with most recent two recorded samples.
+ virtual void SetReportRate(unsigned rateHz) = 0;
+ // Returns currently set report rate, in Hz. If 0 - error occurred.
+ // Note, this value may be different from the one provided for SetReportRate. The return
+ // value will contain the actual rate.
+ virtual unsigned GetReportRate() const = 0;
+
+ // Sets maximum range settings for the sensor described by SensorRange.
+ // The function will fail if you try to pass values outside Maximum supported
+ // by the HW, as described by SensorInfo.
+ // Pass waitFlag == true to wait for command completion. For waitFlag == true,
+ // returns true if the range was applied successfully (no HW error).
+ // For waitFlag = false, return 'true' means that command was enqueued successfully.
+ virtual bool SetRange(const SensorRange& range, bool waitFlag = false) = 0;
+
+ // Return the current sensor range settings for the device. These may not exactly
+ // match the values applied through SetRange.
+ virtual void GetRange(SensorRange* range) const = 0;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestConfiguration
+// LatencyTestConfiguration specifies configuration information for the Oculus Latency Tester device.
+struct LatencyTestConfiguration
+{
+ LatencyTestConfiguration(const Color& threshold, bool sendSamples = false)
+ : Threshold(threshold), SendSamples(sendSamples)
+ {
+ }
+
+ // The color threshold for triggering a detected display change.
+ Color Threshold;
+ // Flag specifying whether we wish to receive a stream of color values from the sensor.
+ bool SendSamples;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestCalibrate
+// LatencyTestCalibrate specifies colors used for Latency Tester calibration.
+struct LatencyTestCalibrate
+{
+ LatencyTestCalibrate(const Color& value)
+ : Value(value)
+ {
+ }
+
+ // The color being calibrated to.
+ Color Value;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestStartTest
+// LatencyTestStartTest specifies values used when starting the Latency Tester test.
+struct LatencyTestStartTest
+{
+ LatencyTestStartTest(const Color& targetValue)
+ : TargetValue(targetValue)
+ {
+ }
+
+ // The color value that the display is being set to.
+ Color TargetValue;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDisplay
+// LatencyTestDisplay sets the mode and contents of the Latency Tester LED display.
+// See the 'Latency Tester Specification' document for more details.
+struct LatencyTestDisplay
+{
+ LatencyTestDisplay(UByte mode, UInt32 value)
+ : Mode(mode), Value(value)
+ {
+ }
+
+ UByte Mode; // The display mode that we wish to select.
+ UInt32 Value; // The value to display.
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDevice
+
+// LatencyTestDevice provides an interface to the Oculus Latency Tester which is used to test 'motion to photon' latency.
+class LatencyTestDevice : public HIDDeviceBase, public DeviceBase
+{
+public:
+ LatencyTestDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_LatencyTester };
+
+ virtual DeviceType GetType() const { return Device_LatencyTester; }
+
+ // Specifies configuration information including the threshold for triggering a detected color change,
+ // and a flag to enable a stream of sensor values (typically used for debugging).
+ virtual bool SetConfiguration(const LatencyTestConfiguration& configuration, bool waitFlag = false) = 0;
+
+ // Get configuration information from device.
+ virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0;
+
+ // Used to calibrate the latency tester at the start of a test. Calibration information is lost
+ // when power is removed from the device.
+ virtual bool SetCalibrate(const LatencyTestCalibrate& calibrate, bool waitFlag = false) = 0;
+
+ // Get calibration information from device.
+ virtual bool GetCalibrate(LatencyTestCalibrate* calibrate) = 0;
+
+ // Triggers the start of a measurement. This starts the millisecond timer on the device and
+ // causes it to respond with the 'MessageLatencyTestStarted' message.
+ virtual bool SetStartTest(const LatencyTestStartTest& start, bool waitFlag = false) = 0;
+
+ // Used to set the value displayed on the LED display panel.
+ virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0;
+
+ virtual DeviceBase* GetDevice() { return this; }
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceConstants.h b/LibOVR/Src/OVR_DeviceConstants.h new file mode 100644 index 0000000..b729d5f --- /dev/null +++ b/LibOVR/Src/OVR_DeviceConstants.h @@ -0,0 +1,38 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceConstants.h
+Content : Device constants
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceConstants_h
+#define OVR_DeviceConstants_h
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// Different device types supported by OVR; this type is reported by DeviceBase::GetType.
+//
+enum DeviceType
+{
+ Device_None = 0,
+ Device_Manager = 1,
+ Device_HMD = 2,
+ Device_Sensor = 3,
+ Device_LatencyTester = 4,
+ Device_All = 0xFF // Set for enumeration only, to enumerate all device types.
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceHandle.cpp b/LibOVR/Src/OVR_DeviceHandle.cpp new file mode 100644 index 0000000..5a0fce6 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceHandle.cpp @@ -0,0 +1,174 @@ +/************************************************************************************
+
+Filename : OVR_DeviceHandle.cpp
+Content : Implementation of device handle class
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_DeviceHandle.h"
+
+#include "OVR_DeviceImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceHandle
+
+DeviceHandle::DeviceHandle(DeviceCreateDesc* impl) : pImpl(impl)
+{
+ if (pImpl)
+ pImpl->AddRef();
+}
+
+DeviceHandle::DeviceHandle(const DeviceHandle& src) : pImpl(src.pImpl)
+{
+ if (pImpl)
+ pImpl->AddRef();
+}
+
+DeviceHandle::~DeviceHandle()
+{
+ if (pImpl)
+ pImpl->Release();
+}
+
+void DeviceHandle::operator = (const DeviceHandle& src)
+{
+ if (src.pImpl)
+ src.pImpl->AddRef();
+ if (pImpl)
+ pImpl->Release();
+ pImpl = src.pImpl;
+}
+
+DeviceBase* DeviceHandle::GetDevice_AddRef() const
+{
+ if (pImpl && pImpl->pDevice)
+ {
+ pImpl->pDevice->AddRef();
+ return pImpl->pDevice;
+ }
+ return NULL;
+}
+
+// Returns true, if the handle contains the same device ptr
+// as specified in the parameter.
+bool DeviceHandle::IsDevice(DeviceBase* pdev) const
+{
+ return (pdev && pImpl && pImpl->pDevice) ?
+ pImpl->pDevice == pdev : false;
+}
+
+DeviceType DeviceHandle::GetType() const
+{
+ return pImpl ? pImpl->Type : Device_None;
+}
+
+bool DeviceHandle::GetDeviceInfo(DeviceInfo* info) const
+{
+ return pImpl ? pImpl->GetDeviceInfo(info) : false;
+}
+bool DeviceHandle::IsAvailable() const
+{
+ // This isn't "atomically safe", but the function only returns the
+ // recent state that may change.
+ return pImpl ? (pImpl->Enumerated && pImpl->pLock->pManager) : false;
+}
+
+bool DeviceHandle::IsCreated() const
+{
+ return pImpl ? (pImpl->pDevice != 0) : false;
+}
+
+DeviceBase* DeviceHandle::CreateDevice()
+{
+ if (!pImpl)
+ return 0;
+
+ DeviceBase* device = 0;
+ Ptr<DeviceManagerImpl> manager= 0;
+
+ // Since both manager and device pointers can only be destroyed during a lock,
+ // hold it while checking for availability.
+ // AddRef to manager so that it doesn't get released on us.
+ {
+ Lock::Locker deviceLockScope(pImpl->GetLock());
+
+ if (pImpl->pDevice)
+ {
+ pImpl->pDevice->AddRef();
+ return pImpl->pDevice;
+ }
+ manager = pImpl->GetManagerImpl();
+ }
+
+ if (manager)
+ {
+ if (manager->GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ // Queue up a CreateDevice request. This fills in '&device' with AddRefed value,
+ // or keep it at null.
+ manager->GetThreadQueue()->PushCallAndWaitResult(
+ manager.GetPtr(), &DeviceManagerImpl::CreateDevice_MgrThread,
+ &device, pImpl, (DeviceBase*)0);
+ }
+ else
+ device = manager->CreateDevice_MgrThread(pImpl, (DeviceBase*)0);
+ }
+ return device;
+}
+
+void DeviceHandle::Clear()
+{
+ if (pImpl)
+ {
+ pImpl->Release();
+ pImpl = 0;
+ }
+}
+
+bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args)
+{
+ if (GetType() == Device_None)
+ return false;
+
+ Ptr<DeviceManagerImpl> managerKeepAlive;
+ Lock::Locker lockScope(pImpl->GetLock());
+
+ DeviceCreateDesc* next = pImpl;
+ // If manager was destroyed, we get removed from the list.
+ if (!pImpl->pNext)
+ return false;
+
+ managerKeepAlive = next->GetManagerImpl();
+ OVR_ASSERT(managerKeepAlive);
+
+ do {
+ next = next->pNext;
+
+ if (managerKeepAlive->Devices.IsNull(next))
+ {
+ pImpl->Release();
+ pImpl = 0;
+ return false;
+ }
+
+ } while(!args.MatchRule(next->Type, next->Enumerated));
+
+ next->AddRef();
+ pImpl->Release();
+ pImpl = next;
+
+ return true;
+}
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_DeviceHandle.h b/LibOVR/Src/OVR_DeviceHandle.h new file mode 100644 index 0000000..4ac4387 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceHandle.h @@ -0,0 +1,97 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceHandle.h
+Content : Handle to a device that was enumerated
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceHandle_h
+#define OVR_DeviceHandle_h
+
+#include "OVR_DeviceConstants.h"
+
+namespace OVR {
+
+class DeviceBase;
+class DeviceInfo;
+
+// Internal
+class DeviceCreateDesc;
+class DeviceEnumerationArgs;
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceHandle
+
+// DeviceHandle references a specific device that was enumerated; it can be assigned
+// directly from DeviceEnumerator.
+//
+// Devices represented by DeviceHandle are not necessarily created or available.
+// A device may become unavailable if, for example, it its unplugged. If the device
+// is available, it can be created by calling CreateDevice.
+//
+
+class DeviceHandle
+{
+ friend class DeviceManager;
+ friend class DeviceManagerImpl;
+ template<class B> friend class HIDDeviceImpl;
+
+public:
+ DeviceHandle() : pImpl(0) { }
+ DeviceHandle(const DeviceHandle& src);
+ ~DeviceHandle();
+
+ void operator = (const DeviceHandle& src);
+
+ bool operator == (const DeviceHandle& other) const { return pImpl == other.pImpl; }
+ bool operator != (const DeviceHandle& other) const { return pImpl != other.pImpl; }
+
+ // operator bool() returns true if Handle/Enumerator points to a valid device.
+ operator bool () const { return GetType() != Device_None; }
+
+ // Returns existing device, or NULL if !IsCreated. The returned ptr is
+ // addref-ed.
+ DeviceBase* GetDevice_AddRef() const;
+ DeviceType GetType() const;
+ bool GetDeviceInfo(DeviceInfo* info) const;
+ bool IsAvailable() const;
+ bool IsCreated() const;
+ // Returns true, if the handle contains the same device ptr
+ // as specified in the parameter.
+ bool IsDevice(DeviceBase*) const;
+
+ // Creates a device, or returns AddRefed pointer if one is already created.
+ // New devices start out with RefCount of 1.
+ DeviceBase* CreateDevice();
+
+ // Creates a device, or returns AddRefed pointer if one is already created.
+ // New devices start out with RefCount of 1. DeviceT is used to cast the
+ // DeviceBase* to a concreete type.
+ template <class DeviceT>
+ DeviceT* CreateDeviceTyped() const
+ {
+ return static_cast<DeviceT*>(DeviceHandle(*this).CreateDevice());
+ }
+
+ // Resets the device handle to uninitialized state.
+ void Clear();
+
+protected:
+ explicit DeviceHandle(DeviceCreateDesc* impl);
+ bool enumerateNext(const DeviceEnumerationArgs& args);
+ DeviceCreateDesc* pImpl;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceImpl.cpp b/LibOVR/Src/OVR_DeviceImpl.cpp new file mode 100644 index 0000000..97cc2d4 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceImpl.cpp @@ -0,0 +1,784 @@ +/************************************************************************************
+
+Filename : OVR_DeviceImpl.h
+Content : Partial back-end independent implementation of Device interfaces
+Created : October 10, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_DeviceImpl.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+#include "OVR_DeviceImpl.h"
+#include "OVR_SensorImpl.h"
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// ***** SharedLock
+
+// This is a general purpose globally shared Lock implementation that should probably be
+// moved to Kernel.
+// May in theory busy spin-wait if we hit contention on first lock creation,
+// but this shouldn't matter in practice since Lock* should be cached.
+
+
+enum { LockInitMarker = 0xFFFFFFFF };
+
+Lock* SharedLock::GetLockAddRef()
+{
+ int oldUseCount;
+
+ do {
+ oldUseCount = UseCount;
+ if (oldUseCount == LockInitMarker)
+ continue;
+
+ if (oldUseCount == 0)
+ {
+ // Initialize marker
+ if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 0, LockInitMarker))
+ {
+ Construct<Lock>(Buffer);
+ do { }
+ while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 1));
+ return toLock();
+ }
+ continue;
+ }
+
+ } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1));
+
+ return toLock();
+}
+
+void SharedLock::ReleaseLock(Lock* plock)
+{
+ OVR_UNUSED(plock);
+ OVR_ASSERT((plock = toLock()) != 0);
+
+ int oldUseCount;
+
+ do {
+ oldUseCount = UseCount;
+ OVR_ASSERT(oldUseCount != LockInitMarker);
+
+ if (oldUseCount == 1)
+ {
+ // Initialize marker
+ if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 1, LockInitMarker))
+ {
+ Destruct<Lock>(toLock());
+
+ do { }
+ while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 0));
+
+ return;
+ }
+ continue;
+ }
+
+ } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1));
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** MessageHandler
+
+// Threading notes:
+// The OnMessage() handler and SetMessageHandler are currently synchronized
+// through a separately stored shared Lock object to avoid calling the handler
+// from background thread while it's being removed.
+
+static SharedLock MessageHandlerSharedLock;
+
+
+class MessageHandlerImpl
+{
+public:
+ MessageHandlerImpl()
+ : pLock(MessageHandlerSharedLock.GetLockAddRef())
+ {
+ }
+ ~MessageHandlerImpl()
+ {
+ MessageHandlerSharedLock.ReleaseLock(pLock);
+ pLock = 0;
+ }
+
+ static MessageHandlerImpl* FromHandler(MessageHandler* handler)
+ { return (MessageHandlerImpl*)&handler->Internal; }
+ static const MessageHandlerImpl* FromHandler(const MessageHandler* handler)
+ { return (const MessageHandlerImpl*)&handler->Internal; }
+
+ // This lock is held while calling a handler and when we are applied/
+ // removed from a device.
+ Lock* pLock;
+ // List of device we are applied to.
+ List<MessageHandlerRef> UseList;
+};
+
+
+MessageHandlerRef::MessageHandlerRef(DeviceBase* device)
+ : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), pHandler(0)
+{
+}
+
+MessageHandlerRef::~MessageHandlerRef()
+{
+ {
+ Lock::Locker lockScope(pLock);
+ if (pHandler)
+ {
+ pHandler = 0;
+ RemoveNode();
+ }
+ }
+ MessageHandlerSharedLock.ReleaseLock(pLock);
+ pLock = 0;
+}
+
+void MessageHandlerRef::SetHandler(MessageHandler* handler)
+{
+ OVR_ASSERT(!handler ||
+ MessageHandlerImpl::FromHandler(handler)->pLock == pLock);
+ Lock::Locker lockScope(pLock);
+ SetHandler_NTS(handler);
+}
+
+void MessageHandlerRef::SetHandler_NTS(MessageHandler* handler)
+{
+ if (pHandler != handler)
+ {
+ if (pHandler)
+ RemoveNode();
+ pHandler = handler;
+
+ if (handler)
+ {
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler);
+ handlerImpl->UseList.PushBack(this);
+ }
+ // TBD: Call notifier on device?
+ }
+}
+
+
+MessageHandler::MessageHandler()
+{
+ OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl));
+ Construct<MessageHandlerImpl>(Internal);
+}
+
+MessageHandler::~MessageHandler()
+{
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ {
+ Lock::Locker lockedScope(handlerImpl->pLock);
+ OVR_ASSERT_LOG(handlerImpl->UseList.IsEmpty(),
+ ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this));
+ }
+
+ Destruct<MessageHandlerImpl>(handlerImpl);
+}
+
+bool MessageHandler::IsHandlerInstalled() const
+{
+ const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ Lock::Locker lockedScope(handlerImpl->pLock);
+ return handlerImpl->UseList.IsEmpty() != true;
+}
+
+
+void MessageHandler::RemoveHandlerFromDevices()
+{
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ Lock::Locker lockedScope(handlerImpl->pLock);
+
+ while(!handlerImpl->UseList.IsEmpty())
+ {
+ MessageHandlerRef* use = handlerImpl->UseList.GetFirst();
+ use->SetHandler_NTS(0);
+ }
+}
+
+Lock* MessageHandler::GetHandlerLock() const
+{
+ const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ return handlerImpl->pLock;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceBase
+
+
+// Delegate relevant implementation to DeviceRectord to avoid re-implementation in
+// every derived Device.
+void DeviceBase::AddRef()
+{
+ getDeviceCommon()->DeviceAddRef();
+}
+void DeviceBase::Release()
+{
+ getDeviceCommon()->DeviceRelease();
+}
+DeviceBase* DeviceBase::GetParent() const
+{
+ return getDeviceCommon()->pParent.GetPtr();
+}
+DeviceManager* DeviceBase::GetManager() const
+{
+ return getDeviceCommon()->pCreateDesc->GetManagerImpl();
+}
+
+void DeviceBase::SetMessageHandler(MessageHandler* handler)
+{
+ getDeviceCommon()->HandlerRef.SetHandler(handler);
+}
+MessageHandler* DeviceBase::GetMessageHandler() const
+{
+ return getDeviceCommon()->HandlerRef.GetHandler();
+}
+
+DeviceType DeviceBase::GetType() const
+{
+ return getDeviceCommon()->pCreateDesc->Type;
+}
+
+bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const
+{
+ return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info);
+ //info->Name[0] = 0;
+ //return false;
+}
+
+// returns the MessageHandler's lock
+Lock* DeviceBase::GetHandlerLock() const
+{
+ return getDeviceCommon()->HandlerRef.GetLock();
+}
+
+// Derive DeviceManagerCreateDesc to provide abstract function implementation.
+class DeviceManagerCreateDesc : public DeviceCreateDesc
+{
+public:
+ DeviceManagerCreateDesc(DeviceFactory* factory)
+ : DeviceCreateDesc(factory, Device_Manager) { }
+
+ // We don't need there on Manager since it isn't assigned to DeviceHandle.
+ virtual DeviceCreateDesc* Clone() const { return 0; }
+ virtual MatchResult MatchDevice(const DeviceCreateDesc&,
+ DeviceCreateDesc**) const { return Match_None; }
+ virtual DeviceBase* NewDeviceInstance() { return 0; }
+ virtual bool GetDeviceInfo(DeviceInfo*) const { return false; }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManagerImpl
+
+DeviceManagerImpl::DeviceManagerImpl()
+ : DeviceImpl<OVR::DeviceManager>(CreateManagerDesc(), 0)
+ //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0)
+{
+ if (pCreateDesc)
+ {
+ pCreateDesc->pLock->pManager = this;
+ }
+}
+
+DeviceManagerImpl::~DeviceManagerImpl()
+{
+ // Shutdown must've been called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+
+ // Remove all factories
+ while(!Factories.IsEmpty())
+ {
+ DeviceFactory* factory = Factories.GetFirst();
+ factory->RemovedFromManager();
+ factory->RemoveNode();
+ }
+}
+
+DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc()
+{
+ DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0);
+ if (managerDesc)
+ {
+ managerDesc->pLock = *new DeviceManagerLock;
+ }
+ return managerDesc;
+}
+
+bool DeviceManagerImpl::Initialize(DeviceBase* parent)
+{
+ OVR_UNUSED(parent);
+ if (!pCreateDesc || !pCreateDesc->pLock)
+ return false;
+ return true;
+}
+
+void DeviceManagerImpl::Shutdown()
+{
+ // Remove all device descriptors from list while the lock is held.
+ // Some descriptors may survive longer due to handles.
+ while(!Devices.IsEmpty())
+ {
+ DeviceCreateDesc* devDesc = Devices.GetFirst();
+ OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists.
+ devDesc->Enumerated = false;
+ devDesc->RemoveNode();
+ devDesc->pNext = devDesc->pPrev = 0;
+
+ if (devDesc->HandleCount == 0)
+ {
+ delete devDesc;
+ }
+ }
+ Devices.Clear();
+
+ // These must've been cleared by caller.
+ OVR_ASSERT(pCreateDesc->pDevice == 0);
+ OVR_ASSERT(pCreateDesc->pLock->pManager == 0);
+}
+
+
+// Callbacks for DeviceCreation/Release
+DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent)
+{
+ // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager,
+ // so 'this' must remain valid.
+ OVR_ASSERT(createDesc->pLock->pManager);
+
+ Lock::Locker devicesLock(GetLock());
+
+ // If device already exists, just AddRef to it.
+ if (createDesc->pDevice)
+ {
+ createDesc->pDevice->AddRef();
+ return createDesc->pDevice;
+ }
+
+ if (!parent)
+ parent = this;
+
+ DeviceBase* device = createDesc->NewDeviceInstance();
+
+ if (device)
+ {
+ if (device->getDeviceCommon()->Initialize(parent))
+ {
+ createDesc->pDevice = device;
+ }
+ else
+ {
+ // Don't go through Release() to avoid PushCall behaviour,
+ // as it is not needed here.
+ delete device;
+ device = 0;
+ }
+ }
+
+ return device;
+}
+
+Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device)
+{
+ // descKeepAlive will keep ManagerLock object alive as well,
+ // allowing us to exit gracefully.
+ Ptr<DeviceCreateDesc> descKeepAlive;
+ Lock::Locker devicesLock(GetLock());
+ DeviceCommon* devCommon = device->getDeviceCommon();
+
+ while(1)
+ {
+ UInt32 refCount = devCommon->RefCount;
+
+ if (refCount > 1)
+ {
+ if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1))
+ {
+ // We decreented from initial count higher then 1;
+ // nothing else to do.
+ return 0;
+ }
+ }
+ else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0))
+ {
+ // { 1 -> 0 } decrement succeded. Destroy this device.
+ break;
+ }
+ }
+
+ // At this point, may be releasing the device manager itself.
+ // This does not matter, however, since shutdown logic is the same
+ // in both cases. DeviceManager::Shutdown with begin shutdown process for
+ // the internal manager thread, which will eventually destroy itself.
+ // TBD: Clean thread shutdown.
+ descKeepAlive = devCommon->pCreateDesc;
+ descKeepAlive->pDevice = 0;
+ devCommon->Shutdown();
+ delete device;
+ return 0;
+}
+
+
+
+Void DeviceManagerImpl::EnumerateAllFactoryDevices()
+{
+ // 1. Mark matching devices as NOT enumerated.
+ // 2. Call factory to enumerate all HW devices, adding any device that
+ // was not matched.
+ // 3. Remove non-matching devices.
+
+ Lock::Locker deviceLock(GetLock());
+
+ DeviceCreateDesc* devDesc, *nextdevDesc;
+
+ // 1.
+ for(devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ //if (devDesc->pFactory == factory)
+ devDesc->Enumerated = false;
+ }
+
+ // 2.
+ DeviceFactory* factory = Factories.GetFirst();
+ while(!Factories.IsNull(factory))
+ {
+ EnumerateFactoryDevices(factory);
+ factory = factory->pNext;
+ }
+
+
+ // 3.
+ for(devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = nextdevDesc)
+ {
+ // In case 'devDesc' gets removed.
+ nextdevDesc = devDesc->pNext;
+
+ // Note, device might be not enumerated since it is opened and
+ // in use! Do NOT notify 'device removed' in this case (!AB)
+ if (!devDesc->Enumerated)
+ {
+ // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle.
+ CallOnDeviceRemoved(devDesc);
+
+ /*
+ if (devDesc->HandleCount == 0)
+ {
+ // Device must be dead if it ever existed, since it AddRefs to us.
+ // ~DeviceCreateDesc removes its node from list.
+ OVR_ASSERT(!devDesc->pDevice);
+ delete devDesc;
+ }
+ */
+ }
+ }
+
+ return 0;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::AddDevice_NeedsLock(
+ const DeviceCreateDesc& createDesc)
+{
+ // If found, mark as enumerated and we are done.
+ DeviceCreateDesc* descCandidate = 0;
+
+ for(DeviceCreateDesc* devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate);
+ if (mr == DeviceCreateDesc::Match_Found)
+ {
+ devDesc->Enumerated = true;
+ if (!devDesc->pDevice)
+ CallOnDeviceAdded(devDesc);
+ return devDesc;
+ }
+ }
+
+ // Update candidate (this may involve writing fields to HMDDevice createDesc).
+ if (descCandidate)
+ {
+ bool newDevice = false;
+ if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice))
+ {
+ descCandidate->Enumerated = true;
+ if (!descCandidate->pDevice || newDevice)
+ CallOnDeviceAdded(descCandidate);
+ return descCandidate;
+ }
+ }
+
+ // If not found, add new device.
+ // - This stores a new descriptor with
+ // {pDevice = 0, HandleCount = 1, Enumerated = true}
+ DeviceCreateDesc* desc = createDesc.Clone();
+ desc->pLock = pCreateDesc->pLock;
+ Devices.PushBack(desc);
+ desc->Enumerated = true;
+
+ CallOnDeviceAdded(desc);
+
+ return desc;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::FindDevice(
+ const String& path,
+ DeviceType deviceType)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceCreateDesc* devDesc;
+
+ for (devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ if ((deviceType == Device_None || deviceType == devDesc->Type) &&
+ devDesc->MatchDevice(path))
+ return devDesc;
+ }
+ return NULL;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceCreateDesc* devDesc;
+
+ for (devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ if (devDesc->MatchHIDDevice(hidDevDesc))
+ return devDesc;
+ }
+ return NULL;
+}
+
+void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceFactory* factory = Factories.GetFirst();
+ while(!Factories.IsNull(factory))
+ {
+ if (factory->DetectHIDDevice(this, hidDevDesc))
+ break;
+ factory = factory->pNext;
+ }
+
+}
+
+// Enumerates devices for a particular factory.
+Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory)
+{
+
+ class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor
+ {
+ DeviceManagerImpl* pManager;
+ DeviceFactory* pFactory;
+ public:
+ FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory)
+ : pManager(manager), pFactory(factory) { }
+
+ virtual void Visit(const DeviceCreateDesc& createDesc)
+ {
+ pManager->AddDevice_NeedsLock(createDesc);
+ }
+ };
+
+ FactoryEnumerateVisitor newDeviceVisitor(this, factory);
+ factory->EnumerateDevices(newDeviceVisitor);
+
+
+ return 0;
+}
+
+
+DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ Lock::Locker deviceLock(GetLock());
+
+ if (Devices.IsEmpty())
+ return DeviceEnumerator<>();
+
+ DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst();
+ DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args);
+
+ if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated))
+ {
+ e.Next();
+ }
+
+ return e;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceCommon
+
+void DeviceCommon::DeviceAddRef()
+{
+ RefCount++;
+}
+
+void DeviceCommon::DeviceRelease()
+{
+ while(1)
+ {
+ UInt32 refCount = RefCount;
+ OVR_ASSERT(refCount > 0);
+
+ if (refCount == 1)
+ {
+ DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl();
+ ThreadCommandQueue* queue = manager->GetThreadQueue();
+
+ // Enqueue ReleaseDevice for {1 -> 0} transition with no wait.
+ // We pass our reference ownership into the queue to destroy.
+ // It's in theory possible for another thread to re-steal our device reference,
+ // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice.
+ if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread,
+ pCreateDesc->pDevice))
+ {
+ // PushCall shouldn't fail because background thread runs while manager is
+ // alive and we are holding Manager alive through pParent chain.
+ OVR_ASSERT(false);
+ }
+
+ // Warning! At his point everything, including manager, may be dead.
+ break;
+ }
+ else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1))
+ {
+ break;
+ }
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceCreateDesc
+
+
+void DeviceCreateDesc::AddRef()
+{
+ // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock,
+ // but we leave this to caller to worry about (happens during enumeration).
+ HandleCount++;
+}
+
+void DeviceCreateDesc::Release()
+{
+ while(1)
+ {
+ UInt32 handleCount = HandleCount;
+ // HandleCount must obviously be >= 1, since we are releasing it.
+ OVR_ASSERT(handleCount > 0);
+
+ // {1 -> 0} transition may cause us to be destroyed, so require a lock.
+ if (handleCount == 1)
+ {
+ Ptr<DeviceManagerLock> lockKeepAlive;
+ Lock::Locker deviceLockScope(GetLock());
+
+ if (!HandleCount.CompareAndSet_NoSync(handleCount, 0))
+ continue;
+
+ OVR_ASSERT(pDevice == 0);
+
+ // Destroy *this if the manager was destroyed already, or Enumerated
+ // is false (device no longer available).
+ if (!GetManagerImpl() || !Enumerated)
+ {
+ lockKeepAlive = pLock;
+
+ // Remove from manager list (only matters for !Enumerated).
+ if (pNext)
+ {
+ RemoveNode();
+ pNext = pPrev = 0;
+ }
+
+ delete this;
+ }
+
+ // Available DeviceCreateDesc may survive with { HandleCount == 0 },
+ // in case it might be enumerated again later.
+ break;
+ }
+ else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1))
+ {
+ break;
+ }
+ }
+}
+
+HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor)
+{
+ if (!psensor)
+ return NULL;
+
+ OVR::DeviceManager* manager = GetManager();
+ if (manager)
+ {
+ //DeviceManagerImpl* mgrImpl = static_cast<DeviceManagerImpl*>(manager);
+ Ptr<DeviceCreateDesc> desc = getDeviceCommon()->pCreateDesc;
+ if (desc)
+ {
+ class Visitor : public DeviceFactory::EnumerateVisitor
+ {
+ Ptr<DeviceCreateDesc> Desc;
+ public:
+ Visitor(DeviceCreateDesc* desc) : Desc(desc) {}
+ virtual void Visit(const DeviceCreateDesc& createDesc)
+ {
+ Lock::Locker lock(Desc->GetLock());
+ Desc->UpdateMatchedCandidate(createDesc);
+ }
+ } visitor(desc);
+ //SensorDeviceImpl* sImpl = static_cast<SensorDeviceImpl*>(psensor);
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor);
+ }
+ }
+ }
+ }
+ return this;
+}
+
+bool HMDDevice::IsDisconnected() const
+{
+ OVR::HMDInfo info;
+ GetDeviceInfo(&info);
+ // if strlen(info.DisplayDeviceName) == 0 then
+ // this HMD is 'fake' (created using sensor).
+ return (strlen(info.DisplayDeviceName) == 0);
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_DeviceImpl.h b/LibOVR/Src/OVR_DeviceImpl.h new file mode 100644 index 0000000..8b7bf0a --- /dev/null +++ b/LibOVR/Src/OVR_DeviceImpl.h @@ -0,0 +1,425 @@ +/************************************************************************************
+
+Filename : OVR_DeviceImpl.h
+Content : Partial back-end independent implementation of Device interfaces
+Created : October 10, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceImpl_h
+#define OVR_DeviceImpl_h
+
+#include "OVR_Device.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+#include "Kernel/OVR_Threads.h"
+#include "OVR_ThreadCommandQueue.h"
+#include "OVR_HIDDevice.h"
+
+namespace OVR {
+
+class DeviceManagerImpl;
+class DeviceFactory;
+
+enum
+{
+ Oculus_VendorId = 0x2833
+};
+
+//-------------------------------------------------------------------------------------
+// Globally shared Lock implementation used for MessageHandlers.
+
+class SharedLock
+{
+public:
+
+ Lock* GetLockAddRef();
+ void ReleaseLock(Lock* plock);
+
+private:
+ Lock* toLock() { return (Lock*)Buffer; }
+
+ // UseCount and max alignment.
+ volatile int UseCount;
+ UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)];
+};
+
+
+// Wrapper for MessageHandler that includes synchronization logic.
+// References to MessageHandlers are organized in a list to allow for them to
+// easily removed with MessageHandler::RemoveAllHandlers.
+class MessageHandlerRef : public ListNode<MessageHandlerRef>
+{
+public:
+ MessageHandlerRef(DeviceBase* device);
+ ~MessageHandlerRef();
+
+ void SetHandler(MessageHandler* hander);
+
+ // Not-thread-safe version
+ void SetHandler_NTS(MessageHandler* hander);
+
+ void Call(const Message& msg)
+ {
+ Lock::Locker lockScope(pLock);
+ if (pHandler)
+ pHandler->OnMessage(msg);
+ }
+
+ Lock* GetLock() const { return pLock; }
+
+ // GetHandler() is not thread safe if used out of order across threads; nothing can be done
+ // about that.
+ MessageHandler* GetHandler() const { return pHandler; }
+ DeviceBase* GetDevice() const { return pDevice; }
+
+private:
+ Lock* pLock; // Cached global handler lock.
+ DeviceBase* pDevice;
+ MessageHandler* pHandler;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+
+// DeviceManagerLock is a synchronization lock used by DeviceManager for Devices
+// and is allocated separately for potentially longer lifetime.
+//
+// DeviceManagerLock is used for all of the following:
+// - Adding/removing devices
+// - Reporting manager lifetime (pManager != 0) for DeviceHandles
+// - Protecting device creation/shutdown.
+
+class DeviceManagerLock : public RefCountBase<DeviceManagerLock>
+{
+public:
+ Lock CreateLock;
+ DeviceManagerImpl* pManager;
+
+ DeviceManagerLock() : pManager(0) { }
+};
+
+
+// DeviceCreateDesc provides all of the information needed to create any device, a derived
+// instance of this class is created by DeviceFactory during enumeration.
+// - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0).
+// - Referenced and kept alive by DeviceHandle.
+
+class DeviceCreateDesc : public ListNode<DeviceCreateDesc>, public NewOverrideBase
+{
+ void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning.
+public:
+ DeviceCreateDesc(DeviceFactory* factory, DeviceType type)
+ : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true)
+ {
+ pNext = pPrev = 0;
+ }
+
+ virtual ~DeviceCreateDesc()
+ {
+ OVR_ASSERT(!pDevice);
+ if (pNext)
+ RemoveNode();
+ }
+
+ DeviceManagerImpl* GetManagerImpl() { return pLock->pManager; }
+ Lock* GetLock() const { return &pLock->CreateLock; }
+
+ // DeviceCreateDesc reference counting is tied to Devices list management,
+ // see comments for HandleCount.
+ void AddRef();
+ void Release();
+
+
+ // *** Device creation/matching Interface
+
+
+ // Cloning copies us to an allocated object when new device is enumerated.
+ virtual DeviceCreateDesc* Clone() const = 0;
+ // Creates a new device instance without Initializing it; the
+ // later is done my Initialize()/Shutdown() methods of the device itself.
+ virtual DeviceBase* NewDeviceInstance() = 0;
+ // Override to return device-specific info.
+ virtual bool GetDeviceInfo(DeviceInfo* info) const = 0;
+
+
+ enum MatchResult
+ {
+ Match_None,
+ Match_Found,
+ Match_Candidate
+ };
+
+ // Override to return Match_Found if descriptor matches our device.
+ // Match_Candidate can be returned, with pcandicate update, if this may be a match
+ // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called.
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const = 0;
+
+ // Called for matched candidate after all potential matches are iterated.
+ // Used to update HMDevice creation arguments from Sensor.
+ // Optional return param 'newDeviceFlag' will be set to true if the
+ // 'desc' refers to a new device; false, otherwise.
+ // Return 'false' to create new object, 'true' if done with this argument.
+ virtual bool UpdateMatchedCandidate(
+ const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL)
+ { OVR_UNUSED2(desc, newDeviceFlag); return false; }
+
+ // Matches HID device to the descriptor.
+ virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; }
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& /*path*/) { return false; }
+//protected:
+ DeviceFactory* const pFactory;
+ const DeviceType Type;
+
+ // List in which this descriptor lives. pList->CreateLock required if added/removed.
+ Ptr<DeviceManagerLock> pLock;
+
+ // Strong references to us: Incremented by Device, DeviceHandles & Enumerators.
+ // May be 0 if device not created and there are no handles.
+ // Following transitions require pList->CreateLock:
+ // {1 -> 0}: May delete & remove handle if no longer available.
+ // {0 -> 1}: Device creation is only possible if manager is still alive.
+ AtomicInt<UInt32> HandleCount;
+ // If not null, points to our created device instance. Modified during lock only.
+ DeviceBase* pDevice;
+ // True if device is marked as available during enumeration.
+ bool Enumerated;
+};
+
+
+
+// Common data present in the implementation of every DeviceBase.
+// Injected by DeviceImpl.
+class DeviceCommon
+{
+public:
+ AtomicInt<UInt32> RefCount;
+ Ptr<DeviceCreateDesc> pCreateDesc;
+ Ptr<DeviceBase> pParent;
+ MessageHandlerRef HandlerRef;
+
+ DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent)
+ : RefCount(1), pCreateDesc(createDesc), pParent(parent), HandlerRef(device)
+ {
+ }
+
+ // Device reference counting delegates to Manager thread to actually kill devices.
+ void DeviceAddRef();
+ void DeviceRelease();
+
+ Lock* GetLock() const { return pCreateDesc->GetLock(); }
+
+ virtual bool Initialize(DeviceBase* parent) = 0;
+ virtual void Shutdown() = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+// DeviceImpl address DeviceRecord implementation to a device base class B.
+// B must be derived form DeviceBase.
+
+template<class B>
+class DeviceImpl : public B, public DeviceCommon
+{
+public:
+ DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent)
+ : DeviceCommon(createDesc, getThis(), parent)
+ {
+ }
+
+ // Convenience method to avoid manager access typecasts.
+ DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; }
+
+ // Inline to avoid warnings.
+ DeviceImpl* getThis() { return this; }
+
+ // Common implementation delegate to avoid virtual inheritance and dynamic casts.
+ virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; }
+
+ /*
+ virtual void AddRef() { pCreateDesc->DeviceAddRef(); }
+ virtual void Release() { pCreateDesc->DeviceRelease(); }
+ virtual DeviceBase* GetParent() const { return pParent.GetPtr(); }
+ virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;}
+ virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); }
+ virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); }
+ virtual DeviceType GetType() const { return pCreateDesc->Type; }
+ virtual DeviceType GetType() const { return pCreateDesc->Type; }
+ */
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceFactory
+
+// DeviceFactory is maintained in DeviceManager for each separately-enumerable
+// device type; factories allow separation of unrelated enumeration code.
+
+class DeviceFactory : public ListNode<DeviceFactory>, public NewOverrideBase
+{
+public:
+
+ DeviceFactory() : pManager(0)
+ {
+ pNext = pPrev = 0;
+ }
+ virtual ~DeviceFactory() { }
+
+ DeviceManagerImpl* GetManagerImpl() { return pManager; }
+
+ // Notifiers called when we are added to/removed from a device.
+ virtual bool AddedToManager(DeviceManagerImpl* manager)
+ {
+ OVR_ASSERT(pManager == 0);
+ pManager = manager;
+ return true;
+ }
+
+ virtual void RemovedFromManager()
+ {
+ pManager = 0;
+ }
+
+
+ // *** Device Enumeration/Creation Support
+
+ // Passed to EnumerateDevices to be informed of every device detected.
+ class EnumerateVisitor
+ {
+ public:
+ virtual void Visit(const DeviceCreateDesc& createDesc) = 0;
+ };
+
+ // Enumerates factory devices by notifying EnumerateVisitor about every
+ // device that is present.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0;
+
+ // Matches vendorId/productId pair with the factory; returns 'true'
+ // if the factory can handle the device.
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+ {
+ OVR_UNUSED2(vendorId, productId);
+ return false;
+ }
+
+ // Detects the HID device and adds the DeviceCreateDesc into Devices list, if
+ // the device belongs to this factory. Returns 'false', if not.
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+ {
+ OVR_UNUSED2(pdevMgr, desc);
+ return false;
+ }
+
+protected:
+ DeviceManagerImpl* pManager;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManagerImpl
+
+// DeviceManagerImpl is a partial default DeviceManager implementation that
+// maintains a list of devices and supports their enumeration.
+
+class DeviceManagerImpl : public DeviceImpl<OVR::DeviceManager>, public ThreadCommandQueue
+{
+public:
+ DeviceManagerImpl();
+ ~DeviceManagerImpl();
+
+ // Constructor helper function to create Descriptor and manager lock during initialization.
+ static DeviceCreateDesc* CreateManagerDesc();
+
+ // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must
+ // be called by the platform-specific derived class.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Override to return ThreadCommandQueue implementation used to post commands
+ // to the background device manager thread (that must be created by Initialize).
+ virtual ThreadCommandQueue* GetThreadQueue() = 0;
+
+ // Returns the thread id of the DeviceManager.
+ virtual ThreadId GetThreadId() const = 0;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+
+ //
+ void AddFactory(DeviceFactory* factory)
+ {
+ // This lock is only needed if we call AddFactory after manager thread creation.
+ Lock::Locker scopeLock(GetLock());
+ Factories.PushBack(factory);
+ factory->AddedToManager(this);
+ }
+
+ void CallOnDeviceAdded(DeviceCreateDesc* desc)
+ {
+ HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc)));
+ }
+ void CallOnDeviceRemoved(DeviceCreateDesc* desc)
+ {
+ HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc)));
+ }
+
+ // Helper to access Common data for a device.
+ static DeviceCommon* GetDeviceCommon(DeviceBase* device)
+ {
+ return device->getDeviceCommon();
+ }
+
+
+ // Background-thread callbacks for DeviceCreation/Release. These
+ DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0);
+ Void ReleaseDevice_MgrThread(DeviceBase* device);
+
+
+ // Calls EnumerateDevices() on all factories
+ virtual Void EnumerateAllFactoryDevices();
+ // Enumerates devices for a particular factory.
+ virtual Void EnumerateFactoryDevices(DeviceFactory* factory);
+
+ virtual HIDDeviceManager* GetHIDDeviceManager() const
+ {
+ return HidDeviceManager;
+ }
+
+ // Adds device (DeviceCreateDesc*) into Devices. Returns NULL,
+ // if unsuccessful or device is already in the list.
+ virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc);
+
+ // Finds a device descriptor by path and optional type.
+ Ptr<DeviceCreateDesc> FindDevice(const String& path, DeviceType = Device_None);
+
+ // Finds HID device by HIDDeviceDesc.
+ Ptr<DeviceCreateDesc> FindHIDDevice(const HIDDeviceDesc&);
+ void DetectHIDDevice(const HIDDeviceDesc&);
+
+ // Manager Lock-protected list of devices.
+ List<DeviceCreateDesc> Devices;
+
+ // Factories used to detect and manage devices.
+ List<DeviceFactory> Factories;
+
+protected:
+ Ptr<HIDDeviceManager> HidDeviceManager;
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceMessages.h b/LibOVR/Src/OVR_DeviceMessages.h new file mode 100644 index 0000000..97eb907 --- /dev/null +++ b/LibOVR/Src/OVR_DeviceMessages.h @@ -0,0 +1,162 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceMessages.h
+Content : Definition of messages generated by devices
+Created : February 5, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceMessages_h
+#define OVR_DeviceMessages_h
+
+#include "OVR_DeviceConstants.h"
+#include "OVR_DeviceHandle.h"
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_Color.h"
+
+namespace OVR {
+
+class DeviceBase;
+class DeviceHandle;
+
+
+#define OVR_MESSAGETYPE(devName, msgIndex) ((Device_##devName << 8) | msgIndex)
+
+// MessageType identifies the structure of the Message class; based on the message,
+// casting can be used to obtain the exact value.
+enum MessageType
+{
+ // Used for unassigned message types.
+ Message_None = 0,
+
+ // Device Manager Messages
+ Message_DeviceAdded = OVR_MESSAGETYPE(Manager, 0), // A new device is detected by manager.
+ Message_DeviceRemoved = OVR_MESSAGETYPE(Manager, 1), // Existing device has been plugged/unplugged.
+ // Sensor Messages
+ Message_BodyFrame = OVR_MESSAGETYPE(Sensor, 0), // Emitted by sensor at regular intervals.
+ // Latency Tester Messages
+ Message_LatencyTestSamples = OVR_MESSAGETYPE(LatencyTester, 0),
+ Message_LatencyTestColorDetected = OVR_MESSAGETYPE(LatencyTester, 1),
+ Message_LatencyTestStarted = OVR_MESSAGETYPE(LatencyTester, 2),
+ Message_LatencyTestButton = OVR_MESSAGETYPE(LatencyTester, 3),
+
+};
+
+//-------------------------------------------------------------------------------------
+// Base class for all messages.
+class Message
+{
+public:
+ Message(MessageType type = Message_None,
+ DeviceBase* pdev = 0) : Type(type), pDevice(pdev)
+ { }
+
+ MessageType Type; // What kind of message this is.
+ DeviceBase* pDevice; // Device that emitted the message.
+};
+
+
+// Sensor BodyFrame notification.
+// Sensor uses Right-Handed coordinate system to return results, with the following
+// axis definitions:
+// - Y Up positive
+// - X Right Positive
+// - Z Back Positive
+// Rotations a counter-clockwise (CCW) while looking in the negative direction
+// of the axis. This means they are interpreted as follows:
+// - Roll is rotation around Z, counter-clockwise (tilting left) in XY plane.
+// - Yaw is rotation around Y, positive for turning left.
+// - Pitch is rotation around X, positive for pitching up.
+
+class MessageBodyFrame : public Message
+{
+public:
+ MessageBodyFrame(DeviceBase* dev)
+ : Message(Message_BodyFrame, dev), Temperature(0.0f), TimeDelta(0.0f)
+ {
+ }
+
+ Vector3f Acceleration; // Acceleration in m/s^2.
+ Vector3f RotationRate; // Angular velocity in rad/s^2.
+ Vector3f MagneticField; // Magnetic field strength in Gauss.
+ float Temperature; // Temperature reading on sensor surface, in degrees Celsius.
+ float TimeDelta; // Time passed since last Body Frame, in seconds.
+};
+
+// Sent when we receive a device status changes (e.g.:
+// Message_DeviceAdded, Message_DeviceRemoved).
+class MessageDeviceStatus : public Message
+{
+public:
+ MessageDeviceStatus(MessageType type, DeviceBase* dev, const DeviceHandle &hdev)
+ : Message(type, dev), Handle(hdev) { }
+
+ DeviceHandle Handle;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Latency Tester
+
+// Sent when we receive Latency Tester samples.
+class MessageLatencyTestSamples : public Message
+{
+public:
+ MessageLatencyTestSamples(DeviceBase* dev)
+ : Message(Message_LatencyTestSamples, dev)
+ {
+ }
+
+ Array<Color> Samples;
+};
+
+// Sent when a Latency Tester 'color detected' event occurs.
+class MessageLatencyTestColorDetected : public Message
+{
+public:
+ MessageLatencyTestColorDetected(DeviceBase* dev)
+ : Message(Message_LatencyTestColorDetected, dev)
+ {
+ }
+
+ UInt16 Elapsed;
+ Color DetectedValue;
+ Color TargetValue;
+};
+
+// Sent when a Latency Tester 'change color' event occurs.
+class MessageLatencyTestStarted : public Message
+{
+public:
+ MessageLatencyTestStarted(DeviceBase* dev)
+ : Message(Message_LatencyTestStarted, dev)
+ {
+ }
+
+ Color TargetValue;
+};
+
+// Sent when a Latency Tester 'button' event occurs.
+class MessageLatencyTestButton : public Message
+{
+public:
+ MessageLatencyTestButton(DeviceBase* dev)
+ : Message(Message_LatencyTestButton, dev)
+ {
+ }
+
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDevice.h b/LibOVR/Src/OVR_HIDDevice.h new file mode 100644 index 0000000..b7ef089 --- /dev/null +++ b/LibOVR/Src/OVR_HIDDevice.h @@ -0,0 +1,143 @@ +/************************************************************************************
+
+Filename : OVR_HIDDevice.h
+Content : Cross platform HID device interface.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDevice_h
+#define OVR_HIDDevice_h
+
+#include "OVR_HIDDeviceBase.h"
+
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Timer.h"
+
+namespace OVR {
+
+class HIDDevice;
+class DeviceManager;
+
+// HIDDeviceDesc contains interesting attributes of a HID device, including a Path
+// that can be used to create it.
+struct HIDDeviceDesc
+{
+ UInt16 VendorId;
+ UInt16 ProductId;
+ UInt16 VersionNumber;
+ UInt16 Usage;
+ UInt16 UsagePage;
+ String Path; // Platform specific.
+ String Manufacturer;
+ String Product;
+ String SerialNumber;
+};
+
+// HIDEnumerateVisitor exposes a Visit interface called for every detected device
+// by HIDDeviceManager::Enumerate.
+class HIDEnumerateVisitor
+{
+public:
+
+ // Should return true if we are interested in supporting
+ // this HID VendorId and ProductId pair.
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ { OVR_UNUSED2(vendorId, productId); return true; }
+
+ // Override to get notified about available device. Will only be called for
+ // devices that matched MatchVendorProduct.
+ virtual void Visit(HIDDevice&, const HIDDeviceDesc&) { }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDeviceManager
+
+// Internal manager for enumerating and opening HID devices.
+// If an OVR::DeviceManager is created then an OVR::HIDDeviceManager will automatically be created and can be accessed from the
+// DeviceManager by calling 'GetHIDDeviceManager()'. When using HIDDeviceManager in standalone mode, the client must call
+// 'Create' below.
+class HIDDeviceManager : public RefCountBase<HIDDeviceManager>
+{
+public:
+
+ // Creates a new HIDDeviceManager. Only one instance of HIDDeviceManager should be created at a time.
+ static HIDDeviceManager* Create();
+
+ // Enumerate HID devices using a HIDEnumerateVisitor derived visitor class.
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor) = 0;
+
+ // Open a HID device with the specified path.
+ virtual HIDDevice* Open(const String& path) = 0;
+
+protected:
+ HIDDeviceManager()
+ { }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDevice
+
+// HID device object. This is designed to be operated in synchronous
+// and asynchronous modes. With no handler set, input messages will be
+// stored and can be retrieved by calling 'Read' or 'ReadBlocking'.
+class HIDDevice : public RefCountBase<HIDDevice>, public HIDDeviceBase
+{
+public:
+
+ HIDDevice()
+ : Handler(NULL)
+ {
+ }
+
+ virtual ~HIDDevice() {}
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0;
+ virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0;
+
+// Not yet implemented.
+/*
+ virtual bool Write(UByte* data, UInt32 length) = 0;
+
+ virtual bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS) = 0;
+ virtual bool ReadBlocking(UByte* pData, UInt32 length) = 0;
+*/
+
+ class HIDHandler
+ {
+ public:
+ virtual void OnInputReport(UByte* pData, UInt32 length)
+ { OVR_UNUSED2(pData, length); }
+
+ virtual UInt64 OnTicks(UInt64 ticksMks)
+ { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; ; }
+
+ enum HIDDeviceMessageType
+ {
+ HIDDeviceMessage_DeviceAdded = 0,
+ HIDDeviceMessage_DeviceRemoved = 1
+ };
+
+ virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
+ { OVR_UNUSED1(messageType); }
+ };
+
+ void SetHandler(HIDHandler* handler)
+ { Handler = handler; }
+
+protected:
+ HIDHandler* Handler;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDeviceBase.h b/LibOVR/Src/OVR_HIDDeviceBase.h new file mode 100644 index 0000000..42fdadc --- /dev/null +++ b/LibOVR/Src/OVR_HIDDeviceBase.h @@ -0,0 +1,40 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_HIDDeviceBase.h
+Content : Definition of HID device interface.
+Created : March 11, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDeviceBase_h
+#define OVR_HIDDeviceBase_h
+
+#include "Kernel/OVR_Types.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDeviceBase
+
+// Base interface for HID devices.
+class HIDDeviceBase
+{
+public:
+
+ virtual ~HIDDeviceBase() { }
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0;
+ virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDeviceImpl.h b/LibOVR/Src/OVR_HIDDeviceImpl.h new file mode 100644 index 0000000..c5163b4 --- /dev/null +++ b/LibOVR/Src/OVR_HIDDeviceImpl.h @@ -0,0 +1,203 @@ +/************************************************************************************
+
+Filename : OVR_HIDDeviceImpl.h
+Content : Implementation of HIDDevice.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDeviceImpl_h
+#define OVR_HIDDeviceImpl_h
+
+//#include "OVR_Device.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+class HIDDeviceCreateDesc : public DeviceCreateDesc
+{
+public:
+ HIDDeviceCreateDesc(DeviceFactory* factory, DeviceType type, const HIDDeviceDesc& hidDesc)
+ : DeviceCreateDesc(factory, type), HIDDesc(hidDesc) { }
+ HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { }
+
+ virtual bool MatchDevice(const String& path)
+ {
+ // should it be case insensitive?
+ return HIDDesc.Path.CompareNoCase(path) == 0;
+ }
+
+ HIDDeviceDesc HIDDesc;
+};
+
+//-------------------------------------------------------------------------------------
+template<class B>
+class HIDDeviceImpl : public DeviceImpl<B>, public HIDDevice::HIDHandler
+{
+public:
+ HIDDeviceImpl(HIDDeviceCreateDesc* createDesc, DeviceBase* parent)
+ : DeviceImpl<B>(createDesc, parent)
+ {
+ }
+
+ // HIDDevice::Handler interface.
+ virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
+ {
+ MessageType handlerMessageType;
+ switch (messageType) {
+ case HIDDeviceMessage_DeviceAdded:
+ handlerMessageType = Message_DeviceAdded;
+ break;
+
+ case HIDDeviceMessage_DeviceRemoved:
+ handlerMessageType = Message_DeviceRemoved;
+ break;
+
+ default: OVR_ASSERT(0); return;
+ }
+
+ // Do device notification.
+ {
+ Lock::Locker scopeLock(this->HandlerRef.GetLock());
+
+ if (this->HandlerRef.GetHandler())
+ {
+ MessageDeviceStatus status(handlerMessageType, this, OVR::DeviceHandle(this->pCreateDesc));
+ this->HandlerRef.GetHandler()->OnMessage(status);
+ }
+ }
+
+ // Do device manager notification.
+ DeviceManagerImpl* manager = this->GetManagerImpl();
+ switch (handlerMessageType) {
+ case Message_DeviceAdded:
+ manager->CallOnDeviceAdded(this->pCreateDesc);
+ break;
+
+ case Message_DeviceRemoved:
+ manager->CallOnDeviceRemoved(this->pCreateDesc);
+ break;
+
+ default:;
+ }
+ }
+
+ virtual bool Initialize(DeviceBase* parent)
+ {
+ // Open HID device.
+ HIDDeviceDesc& hidDesc = *getHIDDesc();
+ HIDDeviceManager* pManager = GetHIDDeviceManager();
+
+
+ HIDDevice* device = pManager->Open(hidDesc.Path);
+ if (!device)
+ {
+ return false;
+ }
+
+ InternalDevice = *device;
+ InternalDevice->SetHandler(this);
+
+ // AddRef() to parent, forcing chain to stay alive.
+ DeviceImpl<B>::pParent = parent;
+
+ return true;
+ }
+
+ virtual void Shutdown()
+ {
+ InternalDevice->SetHandler(NULL);
+
+ // Remove the handler, if any.
+ this->HandlerRef.SetHandler(0);
+
+ DeviceImpl<B>::pParent.Clear();
+ }
+
+ DeviceManager* GetDeviceManager()
+ {
+ return DeviceImpl<B>::pCreateDesc->GetManagerImpl();
+ }
+
+ HIDDeviceManager* GetHIDDeviceManager()
+ {
+ return DeviceImpl<B>::pCreateDesc->GetManagerImpl()->GetHIDDeviceManager();
+ }
+
+
+ struct WriteData
+ {
+ enum { BufferSize = 64 };
+ UByte Buffer[64];
+ UPInt Size;
+
+ WriteData(UByte* data, UPInt size) : Size(size)
+ {
+ OVR_ASSERT(size <= BufferSize);
+ memcpy(Buffer, data, size);
+ }
+ };
+
+ bool SetFeatureReport(UByte* data, UInt32 length)
+ {
+ WriteData writeData(data, length);
+
+ // Push call with wait.
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::setFeatureReport, &result, writeData))
+ return false;
+
+ return result;
+ }
+
+ bool setFeatureReport(const WriteData& data)
+ {
+ return InternalDevice->SetFeatureReport((UByte*) data.Buffer, (UInt32) data.Size);
+ }
+
+ bool GetFeatureReport(UByte* data, UInt32 length)
+ {
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::getFeatureReport, &result, data, length))
+ return false;
+
+ return result;
+ }
+
+ bool getFeatureReport(UByte* data, UInt32 length)
+ {
+ return InternalDevice->GetFeatureReport(data, length);
+ }
+
+protected:
+ HIDDevice* GetInternalDevice() const
+ {
+ return InternalDevice;
+ }
+
+ HIDDeviceDesc* getHIDDesc() const
+ { return &getCreateDesc()->HIDDesc; }
+
+ HIDDeviceCreateDesc* getCreateDesc() const
+ { return (HIDDeviceCreateDesc*) &(*DeviceImpl<B>::pCreateDesc); }
+
+private:
+ Ptr<HIDDevice> InternalDevice;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_LatencyTestImpl.cpp b/LibOVR/Src/OVR_LatencyTestImpl.cpp new file mode 100644 index 0000000..ff420a2 --- /dev/null +++ b/LibOVR/Src/OVR_LatencyTestImpl.cpp @@ -0,0 +1,798 @@ +/************************************************************************************
+
+Filename : OVR_LatencyTestImpl.cpp
+Content : Oculus Latency Tester device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_LatencyTestImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Latency Tester specific packet data structures
+
+enum {
+ LatencyTester_VendorId = Oculus_VendorId,
+ LatencyTester_ProductId = 0x0101,
+};
+
+// Reported data is little-endian now
+static UInt16 DecodeUInt16(const UByte* buffer)
+{
+ return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]);
+}
+
+/* Unreferenced
+static SInt16 DecodeSInt16(const UByte* buffer)
+{
+ return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]);
+}*/
+
+static void UnpackSamples(const UByte* buffer, UByte* r, UByte* g, UByte* b)
+{
+ *r = buffer[0];
+ *g = buffer[1];
+ *b = buffer[2];
+}
+
+// Messages we handle.
+enum LatencyTestMessageType
+{
+ LatencyTestMessage_None = 0,
+ LatencyTestMessage_Samples = 1,
+ LatencyTestMessage_ColorDetected = 2,
+ LatencyTestMessage_TestStarted = 3,
+ LatencyTestMessage_Button = 4,
+ LatencyTestMessage_Unknown = 0x100,
+ LatencyTestMessage_SizeError = 0x101,
+};
+
+struct LatencyTestSample
+{
+ UByte Value[3];
+};
+
+struct LatencyTestSamples
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+
+ LatencyTestSample Samples[20];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 64)
+ {
+ return LatencyTestMessage_SizeError;
+ }
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+
+ for (UByte i = 0; i < SampleCount; i++)
+ {
+ UnpackSamples(buffer + 4 + (3 * i), &Samples[i].Value[0], &Samples[i].Value[1], &Samples[i].Value[2]);
+ }
+
+ return LatencyTestMessage_Samples;
+ }
+};
+
+struct LatencyTestSamplesMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestSamples Samples;
+};
+
+bool DecodeLatencyTestSamplesMessage(LatencyTestSamplesMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestSamplesMessage));
+
+ if (size < 64)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_Samples:
+ message->Type = message->Samples.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestColorDetected
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+ UInt16 Elapsed;
+ UByte TriggerValue[3];
+ UByte TargetValue[3];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 13)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+ Elapsed = DecodeUInt16(buffer + 5);
+ memcpy(TriggerValue, buffer + 7, 3);
+ memcpy(TargetValue, buffer + 10, 3);
+
+ return LatencyTestMessage_ColorDetected;
+ }
+};
+
+struct LatencyTestColorDetectedMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestColorDetected ColorDetected;
+};
+
+bool DecodeLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestColorDetectedMessage));
+
+ if (size < 13)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_ColorDetected:
+ message->Type = message->ColorDetected.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestStarted
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+ UByte TargetValue[3];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 8)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+ memcpy(TargetValue, buffer + 5, 3);
+
+ return LatencyTestMessage_TestStarted;
+ }
+};
+
+struct LatencyTestStartedMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestStarted TestStarted;
+};
+
+bool DecodeLatencyTestStartedMessage(LatencyTestStartedMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestStartedMessage));
+
+ if (size < 8)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_TestStarted:
+ message->Type = message->TestStarted.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestButton
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 5)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+
+ return LatencyTestMessage_Button;
+ }
+};
+
+struct LatencyTestButtonMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestButton Button;
+};
+
+bool DecodeLatencyTestButtonMessage(LatencyTestButtonMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestButtonMessage));
+
+ if (size < 5)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_Button:
+ message->Type = message->Button.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestConfigurationImpl
+{
+ enum { PacketSize = 5 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestConfiguration Configuration;
+
+ LatencyTestConfigurationImpl(const OVR::LatencyTestConfiguration& configuration)
+ : Configuration(configuration)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 5;
+ Buffer[1] = UByte(Configuration.SendSamples);
+ Buffer[2] = Configuration.Threshold.R;
+ Buffer[3] = Configuration.Threshold.G;
+ Buffer[4] = Configuration.Threshold.B;
+ }
+
+ void Unpack()
+ {
+ Configuration.SendSamples = Buffer[1] != 0 ? true : false;
+ Configuration.Threshold.R = Buffer[2];
+ Configuration.Threshold.G = Buffer[3];
+ Configuration.Threshold.B = Buffer[4];
+ }
+};
+
+struct LatencyTestCalibrateImpl
+{
+ enum { PacketSize = 4 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestCalibrate Calibrate;
+
+ LatencyTestCalibrateImpl(const OVR::LatencyTestCalibrate& calibrate)
+ : Calibrate(calibrate)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 7;
+ Buffer[1] = Calibrate.Value.R;
+ Buffer[2] = Calibrate.Value.G;
+ Buffer[3] = Calibrate.Value.B;
+ }
+
+ void Unpack()
+ {
+ Calibrate.Value.R = Buffer[1];
+ Calibrate.Value.G = Buffer[2];
+ Calibrate.Value.B = Buffer[3];
+ }
+};
+
+struct LatencyTestStartTestImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestStartTest StartTest;
+
+ LatencyTestStartTestImpl(const OVR::LatencyTestStartTest& startTest)
+ : StartTest(startTest)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ UInt16 commandID = 1;
+
+ Buffer[0] = 8;
+ Buffer[1] = UByte(commandID & 0xFF);
+ Buffer[2] = UByte(commandID >> 8);
+ Buffer[3] = StartTest.TargetValue.R;
+ Buffer[4] = StartTest.TargetValue.G;
+ Buffer[5] = StartTest.TargetValue.B;
+ }
+
+ void Unpack()
+ {
+// UInt16 commandID = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ StartTest.TargetValue.R = Buffer[3];
+ StartTest.TargetValue.G = Buffer[4];
+ StartTest.TargetValue.B = Buffer[5];
+ }
+};
+
+struct LatencyTestDisplayImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestDisplay Display;
+
+ LatencyTestDisplayImpl(const OVR::LatencyTestDisplay& display)
+ : Display(display)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 9;
+ Buffer[1] = Display.Mode;
+ Buffer[2] = UByte(Display.Value & 0xFF);
+ Buffer[3] = UByte((Display.Value >> 8) & 0xFF);
+ Buffer[4] = UByte((Display.Value >> 16) & 0xFF);
+ Buffer[5] = UByte((Display.Value >> 24) & 0xFF);
+ }
+
+ void Unpack()
+ {
+ Display.Mode = Buffer[1];
+ Display.Value = UInt32(Buffer[2]) |
+ (UInt32(Buffer[3]) << 8) |
+ (UInt32(Buffer[4]) << 16) |
+ (UInt32(Buffer[5]) << 24);
+ }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDeviceFactory
+
+LatencyTestDeviceFactory LatencyTestDeviceFactory::Instance;
+
+void LatencyTestDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class LatencyTestEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const LatencyTestEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ LatencyTestEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+ OVR_UNUSED(device);
+
+ LatencyTestDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+ }
+ };
+
+ LatencyTestEnumerator latencyTestEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&latencyTestEnumerator);
+}
+
+bool LatencyTestDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == LatencyTester_VendorId) && (productId == LatencyTester_ProductId));
+}
+
+bool LatencyTestDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr,
+ const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ LatencyTestDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDeviceCreateDesc
+
+DeviceBase* LatencyTestDeviceCreateDesc::NewDeviceInstance()
+{
+ return new LatencyTestDeviceImpl(this);
+}
+
+bool LatencyTestDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_LatencyTester) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr());
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr());
+ info->Type = Device_LatencyTester;
+ info->Version = 0;
+
+ if (info->InfoClassType == Device_LatencyTester)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr());
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDevice
+
+LatencyTestDeviceImpl::LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::LatencyTestDevice>(createDesc, 0)
+{
+}
+
+LatencyTestDeviceImpl::~LatencyTestDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+// Internal creation APIs.
+bool LatencyTestDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::LatencyTestDevice>::Initialize(parent))
+ {
+ LogText("OVR::LatencyTestDevice initialized.\n");
+ return true;
+ }
+
+ return false;
+}
+
+void LatencyTestDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::LatencyTestDevice>::Shutdown();
+
+ LogText("OVR::LatencyTestDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+void LatencyTestDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+ LatencyTestSamplesMessage message;
+ if (DecodeLatencyTestSamplesMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestSamplesMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestColorDetectedMessage message;
+ if (DecodeLatencyTestColorDetectedMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestColorDetectedMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestStartedMessage message;
+ if (DecodeLatencyTestStartedMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestStartedMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestButtonMessage message;
+ if (DecodeLatencyTestButtonMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestButtonMessage(&message);
+ }
+ }
+}
+
+bool LatencyTestDeviceImpl::SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (GetManagerImpl()->GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setConfiguration, configuration);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setConfiguration,
+ &result,
+ configuration))
+ {
+ return false;
+ }
+ }
+ else
+ return setConfiguration(configuration);
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setConfiguration(const OVR::LatencyTestConfiguration& configuration)
+{
+ LatencyTestConfigurationImpl ltc(configuration);
+ return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::GetConfiguration(OVR::LatencyTestConfiguration* configuration)
+{
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getConfiguration, &result, configuration))
+ return false;
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::getConfiguration(OVR::LatencyTestConfiguration* configuration)
+{
+ LatencyTestConfigurationImpl ltc(*configuration);
+ if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize))
+ {
+ ltc.Unpack();
+ *configuration = ltc.Configuration;
+ return true;
+ }
+
+ return false;
+}
+
+bool LatencyTestDeviceImpl::SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setCalibrate, calibrate);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setCalibrate,
+ &result,
+ calibrate))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setCalibrate(const OVR::LatencyTestCalibrate& calibrate)
+{
+ LatencyTestCalibrateImpl ltc(calibrate);
+ return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::GetCalibrate(OVR::LatencyTestCalibrate* calibrate)
+{
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getCalibrate, &result, calibrate))
+ return false;
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::getCalibrate(OVR::LatencyTestCalibrate* calibrate)
+{
+ LatencyTestCalibrateImpl ltc(*calibrate);
+ if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize))
+ {
+ ltc.Unpack();
+ *calibrate = ltc.Calibrate;
+ return true;
+ }
+
+ return false;
+}
+
+bool LatencyTestDeviceImpl::SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setStartTest, start);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setStartTest,
+ &result,
+ start))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setStartTest(const OVR::LatencyTestStartTest& start)
+{
+ LatencyTestStartTestImpl ltst(start);
+ return GetInternalDevice()->SetFeatureReport(ltst.Buffer, LatencyTestStartTestImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::SetDisplay(const OVR::LatencyTestDisplay& display, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue * queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setDisplay, display);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setDisplay,
+ &result,
+ display))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setDisplay(const OVR::LatencyTestDisplay& display)
+{
+ LatencyTestDisplayImpl ltd(display);
+ return GetInternalDevice()->SetFeatureReport(ltd.Buffer, LatencyTestDisplayImpl::PacketSize);
+}
+
+void LatencyTestDeviceImpl::onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message)
+{
+
+ if (message->Type != LatencyTestMessage_Samples)
+ return;
+
+ LatencyTestSamples& s = message->Samples;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestSamples samples(this);
+ for (UByte i = 0; i < s.SampleCount; i++)
+ {
+ samples.Samples.PushBack(Color(s.Samples[i].Value[0], s.Samples[i].Value[1], s.Samples[i].Value[2]));
+ }
+
+ HandlerRef.GetHandler()->OnMessage(samples);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message)
+{
+ if (message->Type != LatencyTestMessage_ColorDetected)
+ return;
+
+ LatencyTestColorDetected& s = message->ColorDetected;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestColorDetected detected(this);
+ detected.Elapsed = s.Elapsed;
+ detected.DetectedValue = Color(s.TriggerValue[0], s.TriggerValue[1], s.TriggerValue[2]);
+ detected.TargetValue = Color(s.TargetValue[0], s.TargetValue[1], s.TargetValue[2]);
+
+ HandlerRef.GetHandler()->OnMessage(detected);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestStartedMessage(LatencyTestStartedMessage* message)
+{
+ if (message->Type != LatencyTestMessage_TestStarted)
+ return;
+
+ LatencyTestStarted& ts = message->TestStarted;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestStarted started(this);
+ started.TargetValue = Color(ts.TargetValue[0], ts.TargetValue[1], ts.TargetValue[2]);
+
+ HandlerRef.GetHandler()->OnMessage(started);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestButtonMessage(LatencyTestButtonMessage* message)
+{
+ if (message->Type != LatencyTestMessage_Button)
+ return;
+
+// LatencyTestButton& s = message->Button;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageLatencyTestButton button(this);
+
+ HandlerRef.GetHandler()->OnMessage(button);
+ }
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_LatencyTestImpl.h b/LibOVR/Src/OVR_LatencyTestImpl.h new file mode 100644 index 0000000..9217f57 --- /dev/null +++ b/LibOVR/Src/OVR_LatencyTestImpl.h @@ -0,0 +1,135 @@ +/************************************************************************************
+
+Filename : OVR_LatencyTestImpl.h
+Content : Latency Tester specific implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_LatencyTestImpl_h
+#define OVR_LatencyTestImpl_h
+
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR {
+
+struct LatencyTestSamplesMessage;
+struct LatencyTestButtonMessage;
+struct LatencyTestStartedMessage;
+struct LatencyTestColorDetectedMessage;
+
+//-------------------------------------------------------------------------------------
+// LatencyTestDeviceFactory enumerates Oculus Latency Tester devices.
+class LatencyTestDeviceFactory : public DeviceFactory
+{
+public:
+ static LatencyTestDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+// Describes a single a Oculus Latency Tester device and supports creating its instance.
+class LatencyTestDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ LatencyTestDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_LatencyTester, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new LatencyTestDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory))
+ {
+ const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber));
+ }
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::LatencyTestDeviceImpl
+
+// Oculus Latency Tester interface.
+
+class LatencyTestDeviceImpl : public HIDDeviceImpl<OVR::LatencyTestDevice>
+{
+public:
+ LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc);
+ ~LatencyTestDeviceImpl();
+
+ // DeviceCommon interface.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // DeviceManagerThread::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+
+ // LatencyTesterDevice interface
+ virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false);
+ virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration);
+
+ virtual bool SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag = false);
+ virtual bool GetCalibrate(OVR::LatencyTestCalibrate* calibrate);
+
+ virtual bool SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag = false);
+ virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false);
+
+protected:
+ bool openDevice(const char** errorFormatString);
+ void closeDevice();
+ void closeDeviceOnIOError();
+
+ bool initializeRead();
+ bool processReadResult();
+
+ bool setConfiguration(const OVR::LatencyTestConfiguration& configuration);
+ bool getConfiguration(OVR::LatencyTestConfiguration* configuration);
+ bool setCalibrate(const OVR::LatencyTestCalibrate& calibrate);
+ bool getCalibrate(OVR::LatencyTestCalibrate* calibrate);
+ bool setStartTest(const OVR::LatencyTestStartTest& start);
+ bool setDisplay(const OVR::LatencyTestDisplay& display);
+
+ // Called for decoded messages
+ void onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message);
+ void onLatencyTestButtonMessage(LatencyTestButtonMessage* message);
+ void onLatencyTestStartedMessage(LatencyTestStartedMessage* message);
+ void onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message);
+
+};
+
+} // namespace OVR
+
+#endif // OVR_LatencyTestImpl_h
diff --git a/LibOVR/Src/OVR_OSX_DeviceManager.cpp b/LibOVR/Src/OVR_OSX_DeviceManager.cpp new file mode 100644 index 0000000..b2403ed --- /dev/null +++ b/LibOVR/Src/OVR_OSX_DeviceManager.cpp @@ -0,0 +1,349 @@ +/************************************************************************************
+
+Filename : OVR_OSX_DeviceManager.cpp
+Content : OSX specific DeviceManager implementation.
+Created : March 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_DeviceManager.h"
+
+// Sensor & HMD Factories
+#include "OVR_LatencyTestImpl.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_OSX_HMDDevice.h"
+#include "OVR_OSX_HIDDevice.h"
+
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Std.h"
+#include "Kernel/OVR_Log.h"
+
+#include <IOKit/hid/IOHIDManager.h>
+#include <IOKit/hid/IOHIDKeys.h>
+
+
+namespace OVR { namespace OSX {
+
+//-------------------------------------------------------------------------------------
+// **** OSX::DeviceManager
+
+DeviceManager::DeviceManager()
+{
+}
+
+DeviceManager::~DeviceManager()
+{
+ OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called"));
+}
+
+bool DeviceManager::Initialize(DeviceBase*)
+{
+ if (!DeviceManagerImpl::Initialize(0))
+ return false;
+
+ // Start the background thread.
+ pThread = *new DeviceManagerThread();
+ if (!pThread || !pThread->Start())
+ return false;
+
+ // Wait for the thread to be fully up and running.
+ pThread->StartupEvent.Wait();
+
+ // Do this now that we know the thread's run loop.
+ HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
+
+ CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
+
+ pCreateDesc->pDevice = this;
+ LogText("OVR::DeviceManager - initialized.\n");
+
+ return true;
+}
+
+void DeviceManager::Shutdown()
+{
+ LogText("OVR::DeviceManager - shutting down.\n");
+
+ CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
+
+ // Set Manager shutdown marker variable; this prevents
+ // any existing DeviceHandle objects from accessing device.
+ pCreateDesc->pLock->pManager = 0;
+
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ pThread->Shutdown();
+ pThread.Clear();
+
+ DeviceManagerImpl::Shutdown();
+}
+
+ThreadCommandQueue* DeviceManager::GetThreadQueue()
+{
+ return pThread;
+}
+
+ThreadId DeviceManager::GetThreadId() const
+{
+ return pThread->GetThreadId();
+}
+
+bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Manager) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Manager;
+ info->Version = 0;
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
+ OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
+ return true;
+}
+
+DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ // TBD: Can this be avoided in the future, once proper device notification is in place?
+ pThread->PushCall((DeviceManagerImpl*)this,
+ &DeviceManager::EnumerateAllFactoryDevices, true);
+
+ return DeviceManagerImpl::EnumerateDevicesEx(args);
+}
+
+void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *userInfo)
+{
+ DeviceManager* manager = reinterpret_cast<DeviceManager*>(userInfo);
+ OVR_UNUSED(manager);
+
+ if (flags & kCGDisplayAddFlag)
+ {
+ LogText("Display Added, id = %d\n", int(display));
+ manager->EnumerateDevices<HMDDevice>();
+ }
+ else if (flags & kCGDisplayRemoveFlag)
+ {
+ LogText("Display Removed, id = %d\n", int(display));
+ manager->EnumerateDevices<HMDDevice>();
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager Thread
+
+DeviceManagerThread::DeviceManagerThread()
+ : Thread(ThreadStackSize)
+{
+}
+
+DeviceManagerThread::~DeviceManagerThread()
+{
+}
+
+int DeviceManagerThread::Run()
+{
+
+ SetThreadName("OVR::DeviceManagerThread");
+ LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId());
+
+ // Store out the run loop ref.
+ RunLoop = CFRunLoopGetCurrent();
+
+ // Create a 'source' to enable us to signal the run loop to process the command queue.
+ CFRunLoopSourceContext sourceContext;
+ memset(&sourceContext, 0, sizeof(sourceContext));
+ sourceContext.version = 0;
+ sourceContext.info = this;
+ sourceContext.perform = &staticCommandQueueSourceCallback;
+
+ CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext);
+
+ CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
+
+
+ // Signal to the parent thread that initialization has finished.
+ StartupEvent.SetEvent();
+
+
+ ThreadCommand::PopBuffer command;
+
+ while(!IsExiting())
+ {
+ // PopCommand will reset event on empty queue.
+ if (PopCommand(&command))
+ {
+ command.Execute();
+ }
+ else
+ {
+ SInt32 exitReason = 0;
+ do {
+
+ UInt32 waitMs = INT_MAX;
+
+ // If devices have time-dependent logic registered, get the longest wait
+ // allowed based on current ticks.
+ if (!TicksNotifiers.IsEmpty())
+ {
+ UInt64 ticksMks = Timer::GetTicks();
+ UInt32 waitAllowed;
+
+ for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
+ {
+ waitAllowed = (UInt32)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
+ if (waitAllowed < waitMs)
+ waitMs = waitAllowed;
+ }
+ }
+
+ // Enter blocking run loop. We may continue until we timeout in which
+ // case it's time to service the ticks. Or if commands arrive in the command
+ // queue then the source callback will call 'CFRunLoopStop' causing this
+ // to return.
+ CFTimeInterval blockInterval = 0.001 * (double) waitMs;
+ exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false);
+
+ if (exitReason == kCFRunLoopRunFinished)
+ {
+ // Maybe this will occur during shutdown?
+ break;
+ }
+ else if (exitReason == kCFRunLoopRunStopped )
+ {
+ // Commands need processing or we're being shutdown.
+ break;
+ }
+ else if (exitReason == kCFRunLoopRunTimedOut)
+ {
+ // Timed out so that we can service our ticks callbacks.
+ continue;
+ }
+ else if (exitReason == kCFRunLoopRunHandledSource)
+ {
+ // Should never occur since the last param when we call
+ // 'CFRunLoopRunInMode' is false.
+ OVR_ASSERT(false);
+ break;
+ }
+ else
+ {
+ OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code"));
+ break;
+ }
+ }
+ while(true);
+ }
+ }
+
+
+ CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode);
+ CFRelease(CommandQueueSource);
+
+ LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId());
+
+ return 0;
+}
+
+void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext)
+{
+ DeviceManagerThread* pThread = (DeviceManagerThread*) pContext;
+ pThread->commandQueueSourceCallback();
+}
+
+void DeviceManagerThread::commandQueueSourceCallback()
+{
+ CFRunLoopStop(RunLoop);
+}
+
+bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
+{
+ TicksNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
+ {
+ if (TicksNotifiers[i] == notify)
+ {
+ TicksNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+void DeviceManagerThread::Shutdown()
+{
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ PushExitCommand(false);
+
+ // make sure CFRunLoopRunInMode is woken up
+ CFRunLoopSourceSignal(CommandQueueSource);
+ CFRunLoopWakeUp(RunLoop);
+}
+
+} // namespace OSX
+
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new DeviceManager and initializes OVR.
+DeviceManager* DeviceManager::Create()
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<OSX::DeviceManager> manager = *new OSX::DeviceManager;
+
+ if (manager)
+ {
+ if (manager->Initialize(0))
+ {
+ manager->AddFactory(&LatencyTestDeviceFactory::Instance);
+ manager->AddFactory(&SensorDeviceFactory::Instance);
+ manager->AddFactory(&OSX::HMDDeviceFactory::Instance);
+
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_OSX_DeviceManager.h b/LibOVR/Src/OVR_OSX_DeviceManager.h new file mode 100644 index 0000000..02ba661 --- /dev/null +++ b/LibOVR/Src/OVR_OSX_DeviceManager.h @@ -0,0 +1,119 @@ +/************************************************************************************
+
+Filename : OVR_OSX_DeviceManager.h
+Content : OSX specific DeviceManager header.
+Created : March 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_DeviceManager_h
+#define OVR_OSX_DeviceManager_h
+
+#include "OVR_DeviceImpl.h"
+
+#include "Kernel/OVR_Timer.h"
+
+#include <IOKit/hid/IOHIDManager.h>
+#include <CoreGraphics/CGDirectDisplay.h>
+#include <CoreGraphics/CGDisplayConfiguration.h>
+
+
+namespace OVR { namespace OSX {
+
+class DeviceManagerThread;
+
+//-------------------------------------------------------------------------------------
+// ***** OSX DeviceManager
+
+class DeviceManager : public DeviceManagerImpl
+{
+public:
+ DeviceManager();
+ ~DeviceManager();
+
+ // Initialize/Shutdown manager thread.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual ThreadCommandQueue* GetThreadQueue();
+ virtual ThreadId GetThreadId() const;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+protected:
+ static void displayReconfigurationCallBack (CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *userInfo);
+
+public: // data
+ Ptr<DeviceManagerThread> pThread;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Device Manager Background Thread
+
+class DeviceManagerThread : public Thread, public ThreadCommandQueue
+{
+ friend class DeviceManager;
+ enum { ThreadStackSize = 32 * 1024 };
+public:
+ DeviceManagerThread();
+ ~DeviceManagerThread();
+
+ virtual int Run();
+
+ // ThreadCommandQueue notifications for CommandEvent handling.
+ virtual void OnPushNonEmpty_Locked()
+ {
+ CFRunLoopSourceSignal(CommandQueueSource);
+ CFRunLoopWakeUp(RunLoop);
+ }
+
+ virtual void OnPopEmpty_Locked() {}
+
+
+ // Notifier used for different updates (EVENT or regular timing or messages).
+ class Notifier
+ {
+ public:
+
+ // Called when timing ticks are updated. // Returns the largest number of microseconds
+ // this function can wait till next call.
+ virtual UInt64 OnTicks(UInt64 ticksMks)
+ { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; }
+ };
+
+ // Add notifier that will be called at regular intervals.
+ bool AddTicksNotifier(Notifier* notify);
+ bool RemoveTicksNotifier(Notifier* notify);
+
+ CFRunLoopRef GetRunLoop()
+ { return RunLoop; }
+
+ void Shutdown();
+private:
+ CFRunLoopRef RunLoop;
+
+ CFRunLoopSourceRef CommandQueueSource;
+
+ static void staticCommandQueueSourceCallback(void* pContext);
+ void commandQueueSourceCallback();
+
+ Event StartupEvent;
+
+ // Ticks notifiers. Used for time-dependent events such as keep-alive.
+ Array<Notifier*> TicksNotifiers;
+};
+
+}} // namespace OSX::OVR
+
+#endif // OVR_OSX_DeviceManager_h
diff --git a/LibOVR/Src/OVR_OSX_HIDDevice.cpp b/LibOVR/Src/OVR_OSX_HIDDevice.cpp new file mode 100644 index 0000000..40b63c9 --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HIDDevice.cpp @@ -0,0 +1,899 @@ +/************************************************************************************
+Filename : OVR_OSX_HIDDevice.cpp
+Content : OSX HID device implementation.
+Created : February 26, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_HIDDevice.h"
+
+#include <IOKit/usb/IOUSBLib.h>
+
+namespace OVR { namespace OSX {
+
+static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5;
+
+//-------------------------------------------------------------------------------------
+// **** OSX::DeviceManager
+
+HIDDeviceManager::HIDDeviceManager(DeviceManager* manager)
+ : DevManager(manager)
+{
+ HIDManager = NULL;
+}
+
+HIDDeviceManager::~HIDDeviceManager()
+{
+}
+
+CFRunLoopRef HIDDeviceManager::getRunLoop()
+{
+ if (DevManager != NULL)
+ {
+ return DevManager->pThread->GetRunLoop();
+ }
+
+ return CFRunLoopGetCurrent();
+}
+
+bool HIDDeviceManager::initializeManager()
+{
+ if (HIDManager != NULL)
+ {
+ return true;
+ }
+
+ HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+
+ if (!HIDManager)
+ {
+ return false;
+ }
+
+ // Create a Matching Dictionary
+ CFMutableDictionaryRef matchDict =
+ CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ // Specify a device manufacturer in the Matching Dictionary
+ UInt32 vendorId = Oculus_VendorId;
+ CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId);
+ CFDictionarySetValue(matchDict,
+ CFSTR(kIOHIDVendorIDKey),
+ vendorIdRef);
+ // Register the Matching Dictionary to the HID Manager
+ IOHIDManagerSetDeviceMatching(HIDManager, matchDict);
+ CFRelease(vendorIdRef);
+ CFRelease(matchDict);
+
+ // Register a callback for USB device detection with the HID Manager
+ IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this);
+
+ IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode);
+
+ return true;
+}
+
+bool HIDDeviceManager::Initialize()
+{
+ return initializeManager();
+}
+
+void HIDDeviceManager::Shutdown()
+{
+ OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'."));
+ CFRelease(HIDManager);
+
+ LogText("OVR::OSX::HIDDeviceManager - shutting down.\n");
+}
+
+bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult)
+{
+
+ CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName);
+
+ if (!ref)
+ {
+ return false;
+ }
+
+ if (CFGetTypeID(ref) != CFNumberGetTypeID())
+ {
+ return false;
+ }
+
+ CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult);
+
+ return true;
+}
+
+bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+
+ if (!getVendorId(device, &(pDevDesc->VendorId)))
+ {
+ return false;
+ }
+
+ if (!getProductId(device, &(pDevDesc->ProductId)))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result))
+ {
+ return false;
+ }
+
+ pDevDesc->UsagePage = result;
+
+
+ if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result))
+ {
+ return false;
+ }
+
+ pDevDesc->Usage = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+ return getSerialNumberString(device, &(pDevDesc->SerialNumber));
+}
+
+bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
+{
+
+ // Regardless of whether they fail we'll try and get the remaining.
+ getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer));
+ getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product));
+
+ return true;
+}
+
+bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device,
+ CFStringRef propertyName,
+ String* pResult)
+{
+
+ CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName);
+
+ if (!str)
+ {
+ return false;
+ }
+
+ CFIndex length = CFStringGetLength(str);
+ CFRange range = CFRangeMake(0, length);
+
+ // Test the conversion first to get required buffer size.
+ CFIndex bufferLength;
+ CFIndex numberOfChars = CFStringGetBytes(str,
+ range,
+ kCFStringEncodingUTF8,
+ (char) '?',
+ FALSE,
+ NULL,
+ 0,
+ &bufferLength);
+
+ if (numberOfChars == 0)
+ {
+ return false;
+ }
+
+ // Now allocate buffer.
+ char* buffer = new char[bufferLength+1];
+
+ numberOfChars = CFStringGetBytes(str,
+ range,
+ kCFStringEncodingUTF8,
+ (char) '?',
+ FALSE,
+ (UInt8*) buffer,
+ bufferLength,
+ NULL);
+ OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed."));
+
+ buffer[bufferLength] = '\0';
+ *pResult = String(buffer);
+
+ return true;
+}
+
+bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult)
+{
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result))
+ {
+ return false;
+ }
+
+ *pResult = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult)
+{
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result))
+ {
+ return false;
+ }
+
+ *pResult = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult)
+{
+ SInt32 result;
+
+ if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result))
+ {
+ return false;
+ }
+
+ *pResult = result;
+
+ return true;
+}
+
+bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult)
+{
+
+ if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath)
+{
+
+ String transport;
+ if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport))
+ {
+ return false;
+ }
+
+ UInt16 vendorId;
+ if (!getVendorId(device, &vendorId))
+ {
+ return false;
+ }
+
+ UInt16 productId;
+ if (!getProductId(device, &productId))
+ {
+ return false;
+ }
+
+ String serialNumber;
+ if (!getSerialNumberString(device, &serialNumber))
+ {
+ return false;
+ }
+
+
+ StringBuffer buffer;
+ buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s",
+ transport.ToCStr(),
+ vendorId,
+ productId,
+ serialNumber.ToCStr());
+
+ *pPath = String(buffer);
+
+ return true;
+}
+
+bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
+{
+ if (!initializeManager())
+ {
+ return false;
+ }
+
+
+ CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager);
+ if (!deviceSet)
+ return false;
+
+ CFIndex deviceCount = CFSetGetCount(deviceSet);
+
+ // Allocate a block of memory and read the set into it.
+ IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount);
+ CFSetGetValues(deviceSet, (const void **) devices);
+
+
+ // Iterate over devices.
+ for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++)
+ {
+ IOHIDDeviceRef hidDev = devices[deviceIndex];
+
+ if (!hidDev)
+ {
+ continue;
+ }
+
+ HIDDeviceDesc devDesc;
+
+ if (getPath(hidDev, &(devDesc.Path)) &&
+ initVendorProductVersion(hidDev, &devDesc) &&
+ enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) &&
+ initUsage(hidDev, &devDesc))
+ {
+ initStrings(hidDev, &devDesc);
+ initSerialNumber(hidDev, &devDesc);
+
+ // Look for the device to check if it is already opened.
+ Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc);
+ // if device exists and it is opened then most likely the CreateHIDFile
+ // will fail; therefore, we just set Enumerated to 'true' and continue.
+ if (existingDevice && existingDevice->pDevice)
+ {
+ existingDevice->Enumerated = true;
+ continue;
+ }
+
+ // Construct minimal device that the visitor callback can get feature reports from.
+ OSX::HIDDevice device(this, hidDev);
+
+ enumVisitor->Visit(device, devDesc);
+ }
+ }
+
+ OVR_FREE(devices);
+ CFRelease(deviceSet);
+
+ return true;
+}
+
+OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
+{
+
+ Ptr<OSX::HIDDevice> device = *new OSX::HIDDevice(this);
+
+ if (!device->HIDInitialize(path))
+ {
+ return NULL;
+ }
+
+ device->AddRef();
+
+ return device;
+}
+
+bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc)
+{
+
+ if (!initVendorProductVersion(device, desc))
+ {
+ return false;
+ }
+
+ if (!initUsage(device, desc))
+ {
+ return false;
+ }
+
+ if (!initSerialNumber(device, desc))
+ {
+ return false;
+ }
+
+ initStrings(device, desc);
+
+ return true;
+}
+
+// New USB device specified in the matching dictionary has been added (callback function)
+void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext,
+ IOReturn inResult,
+ void *inSender,
+ IOHIDDeviceRef inIOHIDDeviceRef)
+{
+ HIDDeviceManager* hidMgr = static_cast<HIDDeviceManager*>(inContext);
+ HIDDeviceDesc hidDevDesc;
+ hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path);
+ hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc);
+
+ hidMgr->DevManager->DetectHIDDevice(hidDevDesc);
+}
+
+//-------------------------------------------------------------------------------------
+// **** OSX::HIDDevice
+
+HIDDevice::HIDDevice(HIDDeviceManager* manager)
+ : HIDManager(manager), InMinimalMode(false)
+{
+ Device = NULL;
+ RepluggedNotificationPort = 0;
+}
+
+// This is a minimal constructor used during enumeration for us to pass
+// a HIDDevice to the visit function (so that it can query feature reports).
+HIDDevice::HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device)
+: HIDManager(manager), Device(device), InMinimalMode(true)
+{
+ RepluggedNotificationPort = 0;
+}
+
+HIDDevice::~HIDDevice()
+{
+ if (!InMinimalMode)
+ {
+ HIDShutdown();
+ }
+}
+
+bool HIDDevice::HIDInitialize(const String& path)
+{
+
+ DevDesc.Path = path;
+
+ if (!openDevice())
+ {
+ LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr());
+ return false;
+ }
+
+ // Setup notification for when a device is unplugged and plugged back in.
+ if (!setupDevicePluggedInNotification())
+ {
+ LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in.");
+ closeDevice(false);
+ return false;
+ }
+
+ HIDManager->DevManager->pThread->AddTicksNotifier(this);
+
+
+ LogText("OVR::OSX::HIDDevice - Opened '%s'\n"
+ " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
+ DevDesc.Path.ToCStr(),
+ DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
+ DevDesc.SerialNumber.ToCStr());
+
+ return true;
+}
+
+bool HIDDevice::initInfo()
+{
+ // Device must have been successfully opened.
+ OVR_ASSERT(Device);
+
+
+ // Get report lengths.
+ SInt32 bufferLength;
+ bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ InputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ OutputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ FeatureReportBufferLength = (UInt16) bufferLength;
+
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ // Get device desc.
+ if (!HIDManager->getFullDesc(Device, &DevDesc))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
+ return false;
+ }
+
+ return true;
+}
+
+void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator)
+{
+ HIDDevice* pDevice = (HIDDevice*) pContext;
+ pDevice->deviceAddedCallback(iterator);
+}
+
+void HIDDevice::deviceAddedCallback(io_iterator_t iterator)
+{
+
+ if (Device == NULL)
+ {
+ if (openDevice())
+ {
+ LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr());
+
+ Ptr<DeviceCreateDesc> existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc);
+ if (existingHIDDev && existingHIDDev->pDevice)
+ {
+ HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev);
+ }
+ }
+ }
+
+ // Reset callback.
+ while (IOIteratorNext(iterator))
+ ;
+}
+
+bool HIDDevice::openDevice()
+{
+
+ // Have to iterate through devices again to generate paths.
+ CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager);
+ CFIndex deviceCount = CFSetGetCount(deviceSet);
+
+ // Allocate a block of memory and read the set into it.
+ IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount);
+ CFSetGetValues(deviceSet, (const void **) devices);
+
+
+ // Iterate over devices.
+ IOHIDDeviceRef device = NULL;
+
+ for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++)
+ {
+ IOHIDDeviceRef tmpDevice = devices[deviceIndex];
+
+ if (!tmpDevice)
+ {
+ continue;
+ }
+
+ String path;
+ if (!HIDManager->getPath(tmpDevice, &path))
+ {
+ continue;
+ }
+
+ if (path == DevDesc.Path)
+ {
+ device = tmpDevice;
+ break;
+ }
+ }
+
+
+ OVR_FREE(devices);
+
+ if (!device)
+ {
+ CFRelease(deviceSet);
+ return false;
+ }
+
+ // Attempt to open device.
+ if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice)
+ != kIOReturnSuccess)
+ {
+ CFRelease(deviceSet);
+ return false;
+ }
+
+ // Retain the device before we release the set.
+ CFRetain(device);
+ CFRelease(deviceSet);
+
+
+ Device = device;
+
+
+ if (!initInfo())
+ {
+ IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice);
+ CFRelease(Device);
+ Device = NULL;
+ return false;
+ }
+
+
+ // Setup the Run Loop and callbacks.
+ IOHIDDeviceScheduleWithRunLoop(Device,
+ HIDManager->getRunLoop(),
+ kCFRunLoopDefaultMode);
+
+ IOHIDDeviceRegisterInputReportCallback(Device,
+ ReadBuffer,
+ ReadBufferSize,
+ staticHIDReportCallback,
+ this);
+
+ IOHIDDeviceRegisterRemovalCallback(Device,
+ staticDeviceRemovedCallback,
+ this);
+
+ return true;
+}
+
+void HIDDevice::HIDShutdown()
+{
+
+ HIDManager->DevManager->pThread->RemoveTicksNotifier(this);
+
+ if (Device != NULL) // Device may already have been closed if unplugged.
+ {
+ closeDevice(false);
+ }
+
+ IOObjectRelease(RepluggedNotification);
+ if (RepluggedNotificationPort)
+ IONotificationPortDestroy(RepluggedNotificationPort);
+
+ LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr());
+}
+
+bool HIDDevice::setupDevicePluggedInNotification()
+{
+
+ // Setup notification when devices are plugged in.
+ RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+
+ CFRunLoopSourceRef notificationRunLoopSource =
+ IONotificationPortGetRunLoopSource(RepluggedNotificationPort);
+
+ CFRunLoopAddSource(HIDManager->getRunLoop(),
+ notificationRunLoopSource,
+ kCFRunLoopDefaultMode);
+
+ CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+ // Have to specify vendorId and productId. Doesn't seem to accept additional
+ // things like serial number.
+ SInt32 vendorId = DevDesc.VendorId;
+ CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type,
+ &vendorId);
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef);
+ CFRelease(numberRef);
+
+ SInt32 deviceProductId = DevDesc.ProductId;
+ numberRef = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type,
+ &deviceProductId);
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef);
+ CFRelease(numberRef);
+
+ kern_return_t result =
+ IOServiceAddMatchingNotification(RepluggedNotificationPort,
+ kIOMatchedNotification,
+ matchingDict,
+ staticDeviceAddedCallback,
+ this,
+ &RepluggedNotification);
+
+ if (result != KERN_SUCCESS)
+ {
+ CFRelease(RepluggedNotificationPort);
+ RepluggedNotificationPort = 0;
+ return false;
+ }
+
+ // Iterate through to arm.
+ while (IOIteratorNext(RepluggedNotification))
+ {
+ }
+
+ return true;
+}
+
+void HIDDevice::closeDevice(bool wasUnplugged)
+{
+ OVR_ASSERT(Device != NULL);
+
+ if (!wasUnplugged)
+ {
+ // Clear the registered callbacks.
+ IOHIDDeviceRegisterInputReportCallback(Device,
+ ReadBuffer,
+ InputReportBufferLength,
+ NULL,
+ this);
+
+ IOHIDDeviceRegisterRemovalCallback(Device, NULL, this);
+
+ IOHIDDeviceUnscheduleFromRunLoop(Device,
+ HIDManager->getRunLoop(),
+ kCFRunLoopDefaultMode);
+ IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone);
+ }
+
+ CFRelease(Device);
+ Device = NULL;
+
+ LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr());
+}
+
+void HIDDevice::staticHIDReportCallback(void* pContext,
+ IOReturn result,
+ void* pSender,
+ IOHIDReportType reportType,
+ uint32_t reportId,
+ uint8_t* pReport,
+ CFIndex reportLength)
+{
+ HIDDevice* pDevice = (HIDDevice*) pContext;
+ return pDevice->hidReportCallback(pReport, (UInt32)reportLength);
+}
+
+void HIDDevice::hidReportCallback(UByte* pData, UInt32 length)
+{
+
+ // We got data.
+ if (Handler)
+ {
+ Handler->OnInputReport(pData, length);
+ }
+}
+
+void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender)
+{
+ HIDDevice* pDevice = (HIDDevice*) pContext;
+ pDevice->deviceRemovedCallback();
+}
+
+void HIDDevice::deviceRemovedCallback()
+{
+ Ptr<HIDDevice> _this(this); // prevent from release
+
+ Ptr<DeviceCreateDesc> existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc);
+ if (existingHIDDev && existingHIDDev->pDevice)
+ {
+ HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev);
+ }
+ closeDevice(true);
+}
+
+CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device)
+{
+ const UInt32 safeBuffSize = 256;
+ char nameBuff[safeBuffSize];
+ OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device);
+
+ return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII);
+}
+
+bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
+{
+
+ if (!Device)
+ return false;
+
+ UByte reportID = data[0];
+
+ if (reportID == 0)
+ {
+ // Not using reports so remove from data packet.
+ data++;
+ length--;
+ }
+
+ IOReturn result = IOHIDDeviceSetReport( Device,
+ kIOHIDReportTypeFeature,
+ reportID,
+ data,
+ length);
+
+ return (result == kIOReturnSuccess);
+}
+
+bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
+{
+ if (!Device)
+ return false;
+
+ CFIndex bufferLength = length;
+
+ // Report id is in first byte of the buffer.
+ IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength);
+
+ return (result == kIOReturnSuccess);
+}
+
+UInt64 HIDDevice::OnTicks(UInt64 ticksMks)
+{
+
+ if (Handler)
+ {
+ return Handler->OnTicks(ticksMks);
+ }
+
+ return DeviceManagerThread::Notifier::OnTicks(ticksMks);
+}
+
+HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::DeviceManager* devManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<OSX::HIDDeviceManager> manager = *new OSX::HIDDeviceManager(devManager);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OSX
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new HIDDeviceManager and initializes OVR.
+HIDDeviceManager* HIDDeviceManager::Create()
+{
+ OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet."));
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<OSX::HIDDeviceManager> manager = *new OSX::HIDDeviceManager(NULL);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_OSX_HIDDevice.h b/LibOVR/Src/OVR_OSX_HIDDevice.h new file mode 100644 index 0000000..b9b3fc5 --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HIDDevice.h @@ -0,0 +1,149 @@ +/************************************************************************************
+Filename : OVR_OSX_HIDDevice.h
+Content : OSX HID device implementation.
+Created : February 26, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_HIDDevice_h
+#define OVR_OSX_HIDDevice_h
+
+#include "OVR_HIDDevice.h"
+
+#include "OVR_OSX_DeviceManager.h"
+
+#include <IOKit/IOKitLib.h>
+
+namespace OVR { namespace OSX {
+
+class HIDDeviceManager;
+
+//-------------------------------------------------------------------------------------
+// ***** OSX HIDDevice
+
+class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
+{
+private:
+ friend class HIDDeviceManager;
+
+public:
+ HIDDevice(HIDDeviceManager* manager);
+
+ // This is a minimal constructor used during enumeration for us to pass
+ // a HIDDevice to the visit function (so that it can query feature reports).
+ HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device);
+
+ virtual ~HIDDevice();
+
+ bool HIDInitialize(const String& path);
+ void HIDShutdown();
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length);
+ virtual bool GetFeatureReport(UByte* data, UInt32 length);
+
+ bool Write(UByte* data, UInt32 length);
+
+ bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS);
+ bool ReadBlocking(UByte* pData, UInt32 length);
+
+
+ // DeviceManagerThread::Notifier
+ UInt64 OnTicks(UInt64 ticksMks);
+
+private:
+ bool initInfo();
+ bool openDevice();
+ void closeDevice(bool wasUnplugged);
+ bool setupDevicePluggedInNotification();
+ CFStringRef generateRunLoopModeString(IOHIDDeviceRef device);
+
+ static void staticHIDReportCallback(void* pContext,
+ IOReturn result,
+ void* pSender,
+ IOHIDReportType reportType,
+ uint32_t reportId,
+ uint8_t* pReport,
+ CFIndex reportLength);
+ void hidReportCallback(UByte* pData, UInt32 length);
+
+ static void staticDeviceRemovedCallback(void* pContext,
+ IOReturn result,
+ void* pSender);
+ void deviceRemovedCallback();
+
+ static void staticDeviceAddedCallback(void* pContext,
+ io_iterator_t iterator);
+ void deviceAddedCallback(io_iterator_t iterator);
+
+ bool InMinimalMode;
+ HIDDeviceManager* HIDManager;
+ IOHIDDeviceRef Device;
+ HIDDeviceDesc DevDesc;
+
+ enum { ReadBufferSize = 96 };
+ UByte ReadBuffer[ReadBufferSize];
+
+ UInt16 InputReportBufferLength;
+ UInt16 OutputReportBufferLength;
+ UInt16 FeatureReportBufferLength;
+
+ IONotificationPortRef RepluggedNotificationPort;
+ io_iterator_t RepluggedNotification;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OSX HIDDeviceManager
+
+class HIDDeviceManager : public OVR::HIDDeviceManager
+{
+ friend class HIDDevice;
+
+public:
+ HIDDeviceManager(OSX::DeviceManager* Manager);
+ virtual ~HIDDeviceManager();
+
+ virtual bool Initialize();
+ virtual void Shutdown();
+
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
+ virtual OVR::HIDDevice* Open(const String& path);
+
+ static HIDDeviceManager* CreateInternal(DeviceManager* manager);
+
+private:
+ CFRunLoopRef getRunLoop();
+ bool initializeManager();
+ bool initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc);
+ bool getVendorId(IOHIDDeviceRef device, UInt16* pResult);
+ bool getProductId(IOHIDDeviceRef device, UInt16* pResult);
+ bool getLocationId(IOHIDDeviceRef device, SInt32* pResult);
+ bool getSerialNumberString(IOHIDDeviceRef device, String* pResult);
+ bool getPath(IOHIDDeviceRef device, String* pPath);
+ bool getIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* pResult);
+ bool getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult);
+ bool getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc);
+
+ static void staticDeviceMatchingCallback(void *inContext,
+ IOReturn inResult,
+ void *inSender,
+ IOHIDDeviceRef inIOHIDDeviceRef);
+
+ DeviceManager* DevManager;
+
+ IOHIDManagerRef HIDManager;
+};
+
+}} // namespace OVR::OSX
+
+#endif // OVR_OSX_HIDDevice_h
diff --git a/LibOVR/Src/OVR_OSX_HMDDevice.cpp b/LibOVR/Src/OVR_OSX_HMDDevice.cpp new file mode 100644 index 0000000..df9003d --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HMDDevice.cpp @@ -0,0 +1,326 @@ +/************************************************************************************
+
+Filename : OVR_OSX_HMDDevice.cpp
+Content : OSX Interface to HMD - detects HMD display
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_HMDDevice.h"
+#include <CoreGraphics/CGDirectDisplay.h>
+#include <CoreGraphics/CGDisplayConfiguration.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+#include <IOKit/graphics/IOGraphicsLib.h>
+
+namespace OVR { namespace OSX {
+
+//-------------------------------------------------------------------------------------
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory,
+ UInt32 vend, UInt32 prod, const String& displayDeviceName, long dispId)
+ : DeviceCreateDesc(factory, Device_HMD),
+ DisplayDeviceName(displayDeviceName),
+ DesktopX(0), DesktopY(0), Contents(0),
+ HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0),
+ DisplayId(dispId)
+{
+ /* //??????????
+ char idstring[9];
+ idstring[0] = 'A'-1+((vend>>10) & 31);
+ idstring[1] = 'A'-1+((vend>>5) & 31);
+ idstring[2] = 'A'-1+((vend>>0) & 31);
+ snprintf(idstring+3, 5, "%04d", prod);
+ DeviceId = idstring;*/
+ DeviceId = DisplayDeviceName;
+}
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, Device_HMD),
+ DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName),
+ DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents),
+ HResolution(other.HResolution), VResolution(other.VResolution),
+ HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize),
+ DisplayId(other.DisplayId)
+{
+}
+
+HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const
+{
+ if ((other.Type != Device_HMD) || (other.pFactory != pFactory))
+ return Match_None;
+
+ // There are several reasons we can come in here:
+ // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc
+ // - Require exact device DeviceId/DeviceName match
+ // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc
+ // - This DeviceId is empty; becomes candidate
+ // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc
+ // - This other.DeviceId is empty; becomes candidate
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ if ((DeviceId == s2.DeviceId) &&
+ (DisplayId == s2.DisplayId))
+ {
+ // Non-null DeviceId may match while size is different if screen size was overwritten
+ // by SensorDisplayInfo in prior iteration.
+ if (!DeviceId.IsEmpty() ||
+ ((HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize)) )
+ {
+ *pcandidate = 0;
+ return Match_Found;
+ }
+ }
+
+
+ // DisplayInfo takes precedence, although we try to match it first.
+ if ((HResolution == s2.HResolution) &&
+ (VResolution == s2.VResolution) &&
+ (HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize))
+ {
+ if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ *pcandidate = 0;
+ return Match_Found;
+ }
+
+ // SensorDisplayInfo may override resolution settings, so store as candidiate.
+ if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0)
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+ // OTHER HMD Monitor desc may initialize DeviceName/Id
+ else if (DeviceId.IsEmpty() && DisplayId == 0)
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ return Match_None;
+}
+
+
+bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, bool* newDeviceFlag)
+{
+ // This candidate was the the "best fit" to apply sensor DisplayInfo to.
+ OVR_ASSERT(other.Type == Device_HMD);
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ // Force screen size on resolution from SensorDisplayInfo.
+ // We do this because USB detection is more reliable as compared to HDMI EDID,
+ // which may be corrupted by splitter reporting wrong monitor
+ if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0)
+ {
+ // disconnected HMD: replace old descriptor by the 'fake' one.
+ HScreenSize = s2.HScreenSize;
+ VScreenSize = s2.VScreenSize;
+ Contents |= Contents_Screen;
+
+ if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion)
+ {
+ memcpy(DistortionK, s2.DistortionK, sizeof(float)*4);
+ Contents |= Contents_Distortion;
+ }
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else if (DeviceId.IsEmpty())
+ {
+ // This branch is executed when 'fake' HMD descriptor is being replaced by
+ // the real one.
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else
+ {
+ if (newDeviceFlag) *newDeviceFlag = false;
+ }
+
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceFactory
+
+HMDDeviceFactory HMDDeviceFactory::Instance;
+
+void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+ CGDirectDisplayID Displays[32];
+ uint32_t NDisplays = 0;
+ CGGetOnlineDisplayList(32, Displays, &NDisplays);
+
+ for (int i = 0; i < NDisplays; i++)
+ {
+ io_service_t port = CGDisplayIOServicePort(Displays[i]);
+ CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kIODisplayMatchingInfo);
+
+ uint32_t vendor = CGDisplayVendorNumber(Displays[i]);
+ uint32_t product = CGDisplayModelNumber(Displays[i]);
+ unsigned mwidth = (unsigned)CGDisplayPixelsWide(Displays[i]);
+ unsigned mheight = (unsigned)CGDisplayPixelsHigh(Displays[i]);
+ CGRect desktop = CGDisplayBounds(Displays[i]);
+
+ if (vendor == 16082 && product == 1)
+ {
+ char idstring[9];
+ idstring[0] = 'A'-1+((vendor>>10) & 31);
+ idstring[1] = 'A'-1+((vendor>>5) & 31);
+ idstring[2] = 'A'-1+((vendor>>0) & 31);
+ snprintf(idstring+3, 5, "%04d", product);
+
+ HMDDeviceCreateDesc hmdCreateDesc(this, vendor, product, idstring, Displays[i]);
+
+ if (hmdCreateDesc.Is7Inch())
+ {
+ // Physical dimension of SLA screen.
+ hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, mwidth, mheight, 0.14976f, 0.0936f);
+ }
+ else
+ {
+ hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, mwidth, mheight, 0.12096f, 0.0756f);
+ }
+ OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %x:%x\n", vendor, product));
+
+ // Notify caller about detected device. This will call EnumerateAddDevice
+ // if the this is the first time device was detected.
+ visitor.Visit(hmdCreateDesc);
+ }
+ CFRelease(DispInfo);
+ }
+}
+
+DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance()
+{
+ return new HMDDevice(this);
+}
+
+bool HMDDeviceCreateDesc::Is7Inch() const
+{
+ return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch);
+}
+
+bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_HMD) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ bool is7Inch = Is7Inch();
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength,
+ is7Inch ? "Oculus Rift DK1" : "Oculus Rift DK1-Prototype");
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR");
+ info->Type = Device_HMD;
+ info->Version = 0;
+
+ // Display detection.
+ if (info->InfoClassType == Device_HMD)
+ {
+ HMDInfo* hmdInfo = static_cast<HMDInfo*>(info);
+
+ hmdInfo->DesktopX = DesktopX;
+ hmdInfo->DesktopY = DesktopY;
+ hmdInfo->HResolution = HResolution;
+ hmdInfo->VResolution = VResolution;
+ hmdInfo->HScreenSize = HScreenSize;
+ hmdInfo->VScreenSize = VScreenSize;
+ hmdInfo->VScreenCenter = VScreenSize * 0.5f;
+ hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable.
+ hmdInfo->LensSeparationDistance = 0.0635f;
+
+ if (Contents & Contents_Distortion)
+ {
+ memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4);
+ }
+ else
+ {
+ if (is7Inch)
+ {
+ // 7" screen.
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.22f;
+ hmdInfo->DistortionK[2] = 0.24f;
+ hmdInfo->EyeToScreenDistance = 0.041f;
+
+ hmdInfo->ChromaAbCorrection[0] = 0.996f;
+ hmdInfo->ChromaAbCorrection[1] = -0.004f;
+ hmdInfo->ChromaAbCorrection[2] = 1.014f;
+ hmdInfo->ChromaAbCorrection[3] = 0.0f;
+ }
+ else
+ {
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.18f;
+ hmdInfo->DistortionK[2] = 0.115f;
+ hmdInfo->EyeToScreenDistance = 0.0387f;
+ }
+ }
+
+ OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName),
+ DisplayDeviceName.ToCStr());
+ hmdInfo->DisplayId = DisplayId;
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDevice
+
+HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc)
+ : OVR::DeviceImpl<OVR::HMDDevice>(createDesc, 0)
+{
+}
+HMDDevice::~HMDDevice()
+{
+}
+
+bool HMDDevice::Initialize(DeviceBase* parent)
+{
+ pParent = parent;
+ return true;
+}
+void HMDDevice::Shutdown()
+{
+ pParent.Clear();
+}
+
+OVR::SensorDevice* HMDDevice::GetSensor()
+{
+ // Just return first sensor found since we have no way to match it yet.
+ OVR::SensorDevice* sensor = GetManager()->EnumerateDevices<SensorDevice>().CreateDevice();
+ if (sensor)
+ sensor->SetCoordinateFrame(SensorDevice::Coord_HMD);
+ return sensor;
+}
+
+
+}} // namespace OVR::OSX
+
+
diff --git a/LibOVR/Src/OVR_OSX_HMDDevice.h b/LibOVR/Src/OVR_OSX_HMDDevice.h new file mode 100644 index 0000000..37c34ed --- /dev/null +++ b/LibOVR/Src/OVR_OSX_HMDDevice.h @@ -0,0 +1,136 @@ +/************************************************************************************
+
+Filename : OVR_OSX_HMDDevice.h
+Content : OSX HMDDevice implementation
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_OSX_HMDDevice_h
+#define OVR_OSX_HMDDevice_h
+
+#include "OVR_DeviceImpl.h"
+#include <Kernel/OVR_String.h>
+
+namespace OVR { namespace OSX {
+
+class HMDDevice;
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDeviceFactory enumerates attached Oculus HMD devices.
+//
+// This is currently done by matching monitor device strings.
+
+class HMDDeviceFactory : public DeviceFactory
+{
+public:
+ static HMDDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+class HMDDeviceCreateDesc : public DeviceCreateDesc
+{
+ friend class HMDDevice;
+
+protected:
+ enum
+ {
+ Contents_Screen = 1,
+ Contents_Distortion = 2,
+ Contents_7Inch = 4,
+ };
+
+public:
+
+ HMDDeviceCreateDesc(DeviceFactory* factory,
+ UInt32 vendor, UInt32 product, const String& displayDeviceName, long dispId);
+ HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new HMDDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const;
+
+ virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
+ {
+ DesktopX = x;
+ DesktopY = y;
+ HResolution = hres;
+ VResolution = vres;
+ HScreenSize = hsize;
+ VScreenSize = vsize;
+ Contents |= Contents_Screen;
+ }
+
+ void SetDistortion(const float* dks)
+ {
+ for (int i = 0; i < 4; i++)
+ DistortionK[i] = dks[i];
+ Contents |= Contents_Distortion;
+ }
+
+ void Set7Inch() { Contents |= Contents_7Inch; }
+
+ bool Is7Inch() const;
+
+protected:
+ String DeviceId;
+ String DisplayDeviceName;
+ int DesktopX, DesktopY;
+ unsigned Contents;
+ unsigned HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ long DisplayId;
+ float DistortionK[4];
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then wrappig it in SensorFusion.
+
+class HMDDevice : public DeviceImpl<OVR::HMDDevice>
+{
+public:
+ HMDDevice(HMDDeviceCreateDesc* createDesc);
+ ~HMDDevice();
+
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Query associated sensor.
+ virtual OVR::SensorDevice* GetSensor();
+};
+
+
+}} // namespace OVR::OSX
+
+#endif // OVR_OSX_HMDDevice_h
+
diff --git a/LibOVR/Src/OVR_OSX_SensorDevice.cpp b/LibOVR/Src/OVR_OSX_SensorDevice.cpp new file mode 100644 index 0000000..da0726c --- /dev/null +++ b/LibOVR/Src/OVR_OSX_SensorDevice.cpp @@ -0,0 +1,45 @@ +/************************************************************************************
+
+Filename : OVR_OSX_SensorDevice.cpp
+Content : OSX SensorDevice implementation
+Created : March 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_OSX_HMDDevice.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR { namespace OSX {
+
+} // namespace OSX
+
+//-------------------------------------------------------------------------------------
+void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor)
+{
+
+ OSX::HMDDeviceCreateDesc hmdCreateDesc(&OSX::HMDDeviceFactory::Instance, 1, 1, "", 0);
+
+ hmdCreateDesc.SetScreenParameters( 0, 0,
+ displayInfo.HResolution, displayInfo.VResolution,
+ displayInfo.HScreenSize, displayInfo.VScreenSize);
+
+ if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion)
+ hmdCreateDesc.SetDistortion(displayInfo.DistortionK);
+ if (displayInfo.HScreenSize > 0.14f)
+ hmdCreateDesc.Set7Inch();
+
+ visitor.Visit(hmdCreateDesc);
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_SensorFilter.cpp b/LibOVR/Src/OVR_SensorFilter.cpp new file mode 100644 index 0000000..6bd71b5 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFilter.cpp @@ -0,0 +1,201 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFilter.cpp
+Content : Basic filtering of sensor data
+Created : March 7, 2013
+Authors : Steve LaValle, Anna Yershova
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_SensorFilter.h"
+
+namespace OVR {
+
+Vector3f SensorFilter::Total() const
+{
+ Vector3f total = Vector3f(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < Size; i++)
+ total += Elements[i];
+ return total;
+}
+
+Vector3f SensorFilter::Mean() const
+{
+ Vector3f total = Vector3f(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < Size; i++)
+ total += Elements[i];
+ return total / (float) Size;
+}
+
+Vector3f SensorFilter::Median() const
+{
+ int half_window = (int) Size / 2;
+ float sortx[MaxFilterSize];
+ float resultx = 0.0f;
+
+ float sorty[MaxFilterSize];
+ float resulty = 0.0f;
+
+ float sortz[MaxFilterSize];
+ float resultz = 0.0f;
+
+ for (int i = 0; i < Size; i++)
+ {
+ sortx[i] = Elements[i].x;
+ sorty[i] = Elements[i].y;
+ sortz[i] = Elements[i].z;
+ }
+ for (int j = 0; j <= half_window; j++)
+ {
+ int minx = j;
+ int miny = j;
+ int minz = j;
+ for (int k = j + 1; k < Size; k++)
+ {
+ if (sortx[k] < sortx[minx]) minx = k;
+ if (sorty[k] < sorty[miny]) miny = k;
+ if (sortz[k] < sortz[minz]) minz = k;
+ }
+ const float tempx = sortx[j];
+ const float tempy = sorty[j];
+ const float tempz = sortz[j];
+ sortx[j] = sortx[minx];
+ sortx[minx] = tempx;
+
+ sorty[j] = sorty[miny];
+ sorty[miny] = tempy;
+
+ sortz[j] = sortz[minz];
+ sortz[minz] = tempz;
+ }
+ resultx = sortx[half_window];
+ resulty = sorty[half_window];
+ resultz = sortz[half_window];
+
+ return Vector3f(resultx, resulty, resultz);
+}
+
+// Only the diagonal of the covariance matrix.
+Vector3f SensorFilter::Variance() const
+{
+ Vector3f mean = Mean();
+ Vector3f total = Vector3f(0.0f, 0.0f, 0.0f);
+ for (int i = 0; i < Size; i++)
+ {
+ total.x += (Elements[i].x - mean.x) * (Elements[i].x - mean.x);
+ total.y += (Elements[i].y - mean.y) * (Elements[i].y - mean.y);
+ total.z += (Elements[i].z - mean.z) * (Elements[i].z - mean.z);
+ }
+ return total / (float) Size;
+}
+
+// Should be a 3x3 matrix returned, but OVR_math.h doesn't have one
+Matrix4f SensorFilter::Covariance() const
+{
+ Vector3f mean = Mean();
+ Matrix4f total = Matrix4f(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
+ for (int i = 0; i < Size; i++)
+ {
+ total.M[0][0] += (Elements[i].x - mean.x) * (Elements[i].x - mean.x);
+ total.M[1][0] += (Elements[i].y - mean.y) * (Elements[i].x - mean.x);
+ total.M[2][0] += (Elements[i].z - mean.z) * (Elements[i].x - mean.x);
+ total.M[1][1] += (Elements[i].y - mean.y) * (Elements[i].y - mean.y);
+ total.M[2][1] += (Elements[i].z - mean.z) * (Elements[i].y - mean.y);
+ total.M[2][2] += (Elements[i].z - mean.z) * (Elements[i].z - mean.z);
+ }
+ total.M[0][1] = total.M[1][0];
+ total.M[0][2] = total.M[2][0];
+ total.M[1][2] = total.M[2][1];
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ total.M[i][j] *= 1.0f / Size;
+ return total;
+}
+
+Vector3f SensorFilter::PearsonCoefficient() const
+{
+ Matrix4f cov = Covariance();
+ Vector3f pearson = Vector3f();
+ pearson.x = cov.M[0][1]/(sqrt(cov.M[0][0])*sqrt(cov.M[1][1]));
+ pearson.y = cov.M[1][2]/(sqrt(cov.M[1][1])*sqrt(cov.M[2][2]));
+ pearson.z = cov.M[2][0]/(sqrt(cov.M[2][2])*sqrt(cov.M[0][0]));
+
+ return pearson;
+}
+
+
+Vector3f SensorFilter::SavitzkyGolaySmooth8() const
+{
+ OVR_ASSERT(Size >= 8);
+ return GetPrev(0)*0.41667f +
+ GetPrev(1)*0.33333f +
+ GetPrev(2)*0.25f +
+ GetPrev(3)*0.16667f +
+ GetPrev(4)*0.08333f -
+ GetPrev(6)*0.08333f -
+ GetPrev(7)*0.16667f;
+}
+
+
+Vector3f SensorFilter::SavitzkyGolayDerivative4() const
+{
+ OVR_ASSERT(Size >= 4);
+ return GetPrev(0)*0.3f +
+ GetPrev(1)*0.1f -
+ GetPrev(2)*0.1f -
+ GetPrev(3)*0.3f;
+}
+
+Vector3f SensorFilter::SavitzkyGolayDerivative5() const
+{
+ OVR_ASSERT(Size >= 5);
+ return GetPrev(0)*0.2f +
+ GetPrev(1)*0.1f -
+ GetPrev(3)*0.1f -
+ GetPrev(4)*0.2f;
+}
+
+Vector3f SensorFilter::SavitzkyGolayDerivative12() const
+{
+ OVR_ASSERT(Size >= 12);
+ return GetPrev(0)*0.03846f +
+ GetPrev(1)*0.03147f +
+ GetPrev(2)*0.02448f +
+ GetPrev(3)*0.01748f +
+ GetPrev(4)*0.01049f +
+ GetPrev(5)*0.0035f -
+ GetPrev(6)*0.0035f -
+ GetPrev(7)*0.01049f -
+ GetPrev(8)*0.01748f -
+ GetPrev(9)*0.02448f -
+ GetPrev(10)*0.03147f -
+ GetPrev(11)*0.03846f;
+}
+
+Vector3f SensorFilter::SavitzkyGolayDerivativeN(int n) const
+{
+ OVR_ASSERT(Size >= n);
+ int m = (n-1)/2;
+ Vector3f result = Vector3f();
+ for (int k = 1; k <= m; k++)
+ {
+ int ind1 = m - k;
+ int ind2 = n - m + k - 1;
+ result += (GetPrev(ind1) - GetPrev(ind2)) * (float) k;
+ }
+ float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f));
+ result = result*coef;
+ return result;
+}
+
+
+
+
+} //namespace OVR
\ No newline at end of file diff --git a/LibOVR/Src/OVR_SensorFilter.h b/LibOVR/Src/OVR_SensorFilter.h new file mode 100644 index 0000000..18665b1 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFilter.h @@ -0,0 +1,99 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFilter.h
+Content : Basic filtering of sensor data
+Created : March 7, 2013
+Authors : Steve LaValle, Anna Yershova
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFilter_h
+#define OVR_SensorFilter_h
+
+#include "Kernel/OVR_Math.h"
+
+
+namespace OVR {
+
+// This class maintains a sliding window of sensor data taken over time and implements
+// various simple filters, most of which are linear functions of the data history.
+class SensorFilter
+{
+ enum
+ {
+ MaxFilterSize = 100,
+ DefaultFilterSize = 20
+ };
+
+private:
+ int LastIdx; // The index of the last element that was added to the array
+ int Size; // The window size (number of elements)
+ Vector3f Elements[MaxFilterSize];
+
+public:
+ // Create a new filter with default size
+ SensorFilter()
+ {
+ LastIdx = -1;
+ Size = DefaultFilterSize;
+ };
+
+ // Create a new filter with size i
+ SensorFilter(int i)
+ {
+ OVR_ASSERT(i <= MaxFilterSize);
+ LastIdx = -1;
+ Size = i;
+ };
+
+
+ // Create a new element to the filter
+ void AddElement (const Vector3f &e)
+ {
+ if (LastIdx == Size - 1)
+ LastIdx = 0;
+ else
+ LastIdx++;
+
+ Elements[LastIdx] = e;
+ };
+
+ // Get element i. 0 is the most recent, 1 is one step ago, 2 is two steps ago, ...
+ Vector3f GetPrev(int i) const
+ {
+ OVR_ASSERT(i >= 0); //
+ int idx = (LastIdx - i);
+ if (idx < 0) // Fix the wraparound case
+ idx += Size;
+ OVR_ASSERT(idx >= 0); // Multiple wraparounds not allowed
+ return Elements[idx];
+ };
+
+ // Simple statistics
+ Vector3f Total() const;
+ Vector3f Mean() const;
+ Vector3f Median() const;
+ Vector3f Variance() const; // The diagonal of covariance matrix
+ Matrix4f Covariance() const;
+ Vector3f PearsonCoefficient() const;
+
+ // A popular family of smoothing filters and smoothed derivatives
+ Vector3f SavitzkyGolaySmooth8() const;
+ Vector3f SavitzkyGolayDerivative4() const;
+ Vector3f SavitzkyGolayDerivative5() const;
+ Vector3f SavitzkyGolayDerivative12() const;
+ Vector3f SavitzkyGolayDerivativeN(int n) const;
+
+ ~SensorFilter() {};
+};
+
+} //namespace OVR
+
+#endif // OVR_SensorFilter_h
diff --git a/LibOVR/Src/OVR_SensorFusion.cpp b/LibOVR/Src/OVR_SensorFusion.cpp new file mode 100644 index 0000000..78dd128 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFusion.cpp @@ -0,0 +1,378 @@ +/************************************************************************************
+
+Filename : OVR_SensorFusion.cpp
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_SensorFusion.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor Fusion
+
+SensorFusion::SensorFusion(SensorDevice* sensor)
+ : Handler(getThis()), pDelegate(0),
+ Gain(0.05f), YawMult(1), EnableGravity(true), Stage(0), DeltaT(0.001f),
+ EnablePrediction(false), PredictionDT(0.03f),
+ FRawMag(10), FAccW(20), FAngV(20),
+ TiltCondCount(0), TiltErrorAngle(0),
+ TiltErrorAxis(0,1,0),
+ MagCondCount(0), MagReady(false), MagCalibrated(false), MagReferenced(false),
+ MagRefQ(0, 0, 0, 1), MagRefM(0), MagRefYaw(0), YawErrorAngle(0), MagRefDistance(0.15f),
+ YawErrorCount(0), YawCorrectionActivated(false), YawCorrectionInProgress(false),
+ EnableYawCorrection(false)
+{
+ if (sensor)
+ AttachToSensor(sensor);
+ MagCalibrationMatrix.SetIdentity();
+}
+
+SensorFusion::~SensorFusion()
+{
+}
+
+
+bool SensorFusion::AttachToSensor(SensorDevice* sensor)
+{
+
+ if (sensor != NULL)
+ {
+ MessageHandler* pCurrentHandler = sensor->GetMessageHandler();
+
+ if (pCurrentHandler == &Handler)
+ {
+ Reset();
+ return true;
+ }
+
+ if (pCurrentHandler != NULL)
+ {
+ OVR_DEBUG_LOG(
+ ("SensorFusion::AttachToSensor failed - sensor %p already has handler", sensor));
+ return false;
+ }
+ }
+
+ if (Handler.IsHandlerInstalled())
+ {
+ Handler.RemoveHandlerFromDevices();
+ }
+
+ if (sensor != NULL)
+ {
+ sensor->SetMessageHandler(&Handler);
+ }
+
+ Reset();
+ return true;
+}
+
+
+
+
+void SensorFusion::handleMessage(const MessageBodyFrame& msg)
+{
+ if (msg.Type != Message_BodyFrame)
+ return;
+
+ // Put the sensor readings into convenient local variables
+ Vector3f angVel = msg.RotationRate;
+ Vector3f rawAccel = msg.Acceleration;
+ Vector3f mag = msg.MagneticField;
+
+ // Set variables accessible through the class API
+ DeltaT = msg.TimeDelta;
+ AngV = msg.RotationRate;
+ AngV.y *= YawMult; // Warning: If YawMult != 1, then AngV is not true angular velocity
+ A = rawAccel;
+
+ // Allow external access to uncalibrated magnetometer values
+ RawMag = mag;
+
+ // Apply the calibration parameters to raw mag
+ if (HasMagCalibration())
+ {
+ mag.x += MagCalibrationMatrix.M[0][3];
+ mag.y += MagCalibrationMatrix.M[1][3];
+ mag.z += MagCalibrationMatrix.M[2][3];
+ }
+
+ // Provide external access to calibrated mag values
+ // (if the mag is not calibrated, then the raw value is returned)
+ CalMag = mag;
+
+ float angVelLength = angVel.Length();
+ float accLength = rawAccel.Length();
+
+
+ // Acceleration in the world frame (Q is current HMD orientation)
+ Vector3f accWorld = Q.Rotate(rawAccel);
+
+ // Keep track of time
+ Stage++;
+ float currentTime = Stage * DeltaT; // Assumes uniform time spacing
+
+ // Insert current sensor data into filter history
+ FRawMag.AddElement(RawMag);
+ FAccW.AddElement(accWorld);
+ FAngV.AddElement(angVel);
+
+ // Update orientation Q based on gyro outputs. This technique is
+ // based on direct properties of the angular velocity vector:
+ // Its direction is the current rotation axis, and its magnitude
+ // is the rotation rate (rad/sec) about that axis. Our sensor
+ // sampling rate is so fast that we need not worry about integral
+ // approximation error (not yet, anyway).
+ if (angVelLength > 0.0f)
+ {
+ Vector3f rotAxis = angVel / angVelLength;
+ float halfRotAngle = angVelLength * DeltaT * 0.5f;
+ float sinHRA = sin(halfRotAngle);
+ Quatf deltaQ(rotAxis.x*sinHRA, rotAxis.y*sinHRA, rotAxis.z*sinHRA, cos(halfRotAngle));
+
+ Q = Q * deltaQ;
+ }
+
+ // The quaternion magnitude may slowly drift due to numerical error,
+ // so it is periodically normalized.
+ if (Stage % 5000 == 0)
+ Q.Normalize();
+
+ // Maintain the uncorrected orientation for later use by predictive filtering
+ QUncorrected = Q;
+
+ // Perform tilt correction using the accelerometer data. This enables
+ // drift errors in pitch and roll to be corrected. Note that yaw cannot be corrected
+ // because the rotation axis is parallel to the gravity vector.
+ if (EnableGravity)
+ {
+ // Correcting for tilt error by using accelerometer data
+ const float gravityEpsilon = 0.4f;
+ const float angVelEpsilon = 0.1f; // Relatively slow rotation
+ const int tiltPeriod = 50; // Req'd time steps of stability
+ const float maxTiltError = 0.05f;
+ const float minTiltError = 0.01f;
+
+ // This condition estimates whether the only measured acceleration is due to gravity
+ // (the Rift is not linearly accelerating). It is often wrong, but tends to average
+ // out well over time.
+ if ((fabs(accLength - 9.81f) < gravityEpsilon) &&
+ (angVelLength < angVelEpsilon))
+ TiltCondCount++;
+ else
+ TiltCondCount = 0;
+
+ // After stable measurements have been taken over a sufficiently long period,
+ // estimate the amount of tilt error and calculate the tilt axis for later correction.
+ if (TiltCondCount >= tiltPeriod)
+ { // Update TiltErrorEstimate
+ TiltCondCount = 0;
+ // Use an average value to reduce noice (could alternatively use an LPF)
+ Vector3f accWMean = FAccW.Mean();
+ // Project the acceleration vector into the XZ plane
+ Vector3f xzAcc = Vector3f(accWMean.x, 0.0f, accWMean.z);
+ // The unit normal of xzAcc will be the rotation axis for tilt correction
+ Vector3f tiltAxis = Vector3f(xzAcc.z, 0.0f, -xzAcc.x).Normalized();
+ Vector3f yUp = Vector3f(0.0f, 1.0f, 0.0f);
+ // This is the amount of rotation
+ float tiltAngle = yUp.Angle(accWMean);
+ // Record values if the tilt error is intolerable
+ if (tiltAngle > maxTiltError)
+ {
+ TiltErrorAngle = tiltAngle;
+ TiltErrorAxis = tiltAxis;
+ }
+ }
+
+ // This part performs the actual tilt correction as needed
+ if (TiltErrorAngle > minTiltError)
+ {
+ if ((TiltErrorAngle > 0.4f)&&(Stage < 8000))
+ { // Tilt completely to correct orientation
+ Q = Quatf(TiltErrorAxis, -TiltErrorAngle) * Q;
+ TiltErrorAngle = 0.0f;
+ }
+ else
+ {
+ //LogText("Performing tilt correction - Angle: %f Axis: %f %f %f\n",
+ // TiltErrorAngle,TiltErrorAxis.x,TiltErrorAxis.y,TiltErrorAxis.z);
+ //float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f;
+ // This uses agressive correction steps while your head is moving fast
+ float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f*(5.0f*angVelLength+1.0f);
+ // Incrementally "untilt" by a small step size
+ Q = Quatf(TiltErrorAxis, deltaTiltAngle) * Q;
+ TiltErrorAngle += deltaTiltAngle;
+ }
+ }
+ }
+
+ // Yaw drift correction based on magnetometer data. This corrects the part of the drift
+ // that the accelerometer cannot handle.
+ // This will only work if the magnetometer has been enabled, calibrated, and a reference
+ // point has been set.
+ const float maxAngVelLength = 3.0f;
+ const int magWindow = 5;
+ const float yawErrorMax = 0.1f;
+ const float yawErrorMin = 0.01f;
+ const int yawErrorCountLimit = 50;
+ const float yawRotationStep = 0.00002f;
+
+ if (angVelLength < maxAngVelLength)
+ MagCondCount++;
+ else
+ MagCondCount = 0;
+
+ YawCorrectionInProgress = false;
+ if (EnableYawCorrection && MagReady && (currentTime > 2.0f) && (MagCondCount >= magWindow) &&
+ (Q.Distance(MagRefQ) < MagRefDistance))
+ {
+ // Use rotational invariance to bring reference mag value into global frame
+ Vector3f grefmag = MagRefQ.Rotate(GetCalibratedMagValue(MagRefM));
+ // Bring current (averaged) mag reading into global frame
+ Vector3f gmag = Q.Rotate(GetCalibratedMagValue(FRawMag.Mean()));
+ // Calculate the reference yaw in the global frame
+ float gryaw = atan2(grefmag.x,grefmag.z);
+ // Calculate the current yaw in the global frame
+ float gyaw = atan2(gmag.x,gmag.z);
+ //LogText("Yaw error estimate: %f\n",YawErrorAngle);
+ // The difference between reference and current yaws is the perceived error
+ YawErrorAngle = AngleDifference(gyaw,gryaw);
+ // If the perceived error is large, keep count
+ if ((fabs(YawErrorAngle) > yawErrorMax) && (!YawCorrectionActivated))
+ YawErrorCount++;
+ // After enough iterations of high perceived error, start the correction process
+ if (YawErrorCount > yawErrorCountLimit)
+ YawCorrectionActivated = true;
+ // If the perceived error becomes small, turn off the yaw correction
+ if ((fabs(YawErrorAngle) < yawErrorMin) && YawCorrectionActivated)
+ {
+ YawCorrectionActivated = false;
+ YawErrorCount = 0;
+ }
+ // Perform the actual yaw correction, due to previously detected, large yaw error
+ if (YawCorrectionActivated)
+ {
+ YawCorrectionInProgress = true;
+ int sign = (YawErrorAngle > 0.0f) ? 1 : -1;
+ // Incrementally "unyaw" by a small step size
+ Q = Quatf(Vector3f(0.0f,1.0f,0.0f), -yawRotationStep * sign) * Q;
+ }
+ }
+}
+
+
+ // This is a simple predictive filter based only on extrapolating the smoothed, current angular velocity.
+ // Note that both QP (the predicted future orientation) and Q (the current orientation) are both maintained.
+Quatf SensorFusion::GetPredictedOrientation()
+{
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ Quatf qP = QUncorrected;
+ if (EnablePrediction) {
+#if 1
+ Vector3f angVelF = FAngV.SavitzkyGolaySmooth8();
+ float angVelFL = angVelF.Length();
+
+ if (angVelFL > 0.001f)
+ {
+ Vector3f rotAxisP = angVelF / angVelFL;
+ float halfRotAngleP = angVelFL * PredictionDT * 0.5f;
+ float sinaHRAP = sin(halfRotAngleP);
+ Quatf deltaQP(rotAxisP.x*sinaHRAP, rotAxisP.y*sinaHRAP,
+ rotAxisP.z*sinaHRAP, cos(halfRotAngleP));
+ qP = QUncorrected * deltaQP;
+ }
+#else
+ Quatd qpd = Quatd(Q.x,Q.y,Q.z,Q.w);
+ int predictionStages = (int)(PredictionDT / DeltaT);
+ Vector3f aa = FAngV.SavitzkyGolayDerivative12();
+ Vector3d aad = Vector3d(aa.x,aa.y,aa.z);
+ Vector3f angVelF = FAngV.SavitzkyGolaySmooth8();
+ Vector3d avkd = Vector3d(angVelF.x,angVelF.y,angVelF.z);
+ for (int i = 0; i < predictionStages; i++)
+ {
+ double angVelLengthd = avkd.Length();
+ Vector3d rotAxisd = avkd / angVelLengthd;
+ double halfRotAngled = angVelLengthd * DeltaT * 0.5;
+ double sinHRAd = sin(halfRotAngled);
+ Quatd deltaQd = Quatd(rotAxisd.x*sinHRAd, rotAxisd.y*sinHRAd, rotAxisd.z*sinHRAd,
+ cos(halfRotAngled));
+ qpd = qpd * deltaQd;
+ // Update vel
+ avkd += aad;
+ }
+ qP = Quatf((float)qpd.x,(float)qpd.y,(float)qpd.z,(float)qpd.w);
+#endif
+ }
+ return qP;
+}
+
+
+Vector3f SensorFusion::GetCalibratedMagValue(const Vector3f& rawMag) const
+{
+ Vector3f mag = rawMag;
+ OVR_ASSERT(HasMagCalibration());
+ mag.x += MagCalibrationMatrix.M[0][3];
+ mag.y += MagCalibrationMatrix.M[1][3];
+ mag.z += MagCalibrationMatrix.M[2][3];
+ return mag;
+}
+
+
+void SensorFusion::SetMagReference(const Quatf& q, const Vector3f& rawMag)
+{
+ MagRefQ = q;
+ MagRefM = rawMag;
+
+ float pitch, roll, yaw;
+ Q.GetEulerAngles<Axis_X, Axis_Z, Axis_Y>(&pitch, &roll, &yaw);
+ MagRefYaw = yaw;
+ MagReferenced = true;
+ if (MagCalibrated)
+ MagReady = true;
+}
+
+
+float SensorFusion::AngleDifference(float theta1, float theta2)
+{
+ float x = theta1 - theta2;
+ if (x > Math<float>::Pi)
+ return x - Math<float>::TwoPi;
+ if (x < -Math<float>::Pi)
+ return x + Math<float>::TwoPi;
+ return x;
+}
+
+
+SensorFusion::BodyFrameHandler::~BodyFrameHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+void SensorFusion::BodyFrameHandler::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_BodyFrame)
+ pFusion->handleMessage(static_cast<const MessageBodyFrame&>(msg));
+ if (pFusion->pDelegate)
+ pFusion->pDelegate->OnMessage(msg);
+}
+
+bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const
+{
+ return (type == Message_BodyFrame);
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_SensorFusion.h b/LibOVR/Src/OVR_SensorFusion.h new file mode 100644 index 0000000..8b88ea2 --- /dev/null +++ b/LibOVR/Src/OVR_SensorFusion.h @@ -0,0 +1,268 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFusion.h
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFusion_h
+#define OVR_SensorFusion_h
+
+#include "OVR_Device.h"
+#include "OVR_SensorFilter.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** SensorFusion
+
+// SensorFusion class accumulates Sensor notification messages to keep track of
+// orientation, which involves integrating the gyro and doing correction with gravity.
+// Orientation is reported as a quaternion, from which users can obtain either the
+// rotation matrix or Euler angles.
+//
+// The class can operate in two ways:
+// - By user manually passing MessageBodyFrame messages to the OnMessage() function.
+// - By attaching SensorFusion to a SensorDevice, in which case it will
+// automatically handle notifications from that device.
+
+class SensorFusion : public NewOverrideBase
+{
+public:
+ SensorFusion(SensorDevice* sensor = 0);
+ ~SensorFusion();
+
+ // Attaches this SensorFusion to a sensor device, from which it will receive
+ // notification messages. If a sensor is attached, manual message notification
+ // is not necessary. Calling this function also resets SensorFusion state.
+ bool AttachToSensor(SensorDevice* sensor);
+
+ // Returns true if this Sensor fusion object is attached to a sensor.
+ bool IsAttachedToSensor() const { return Handler.IsHandlerInstalled(); }
+
+ void SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; }
+
+ bool IsGravityEnabled() const { return EnableGravity;}
+
+ void SetYawCorrectionEnabled(bool enableYawCorrection) { EnableYawCorrection = enableYawCorrection; }
+
+ // Yaw correction is set up to work
+ bool IsYawCorrectionEnabled() const { return EnableYawCorrection;}
+
+ // Yaw correction is currently working (forcing a corrective yaw rotation)
+ bool IsYawCorrectionInProgress() const { return YawCorrectionInProgress;}
+
+ // Store the calibration matrix for the magnetometer
+ void SetMagCalibration(const Matrix4f& m)
+ {
+ MagCalibrationMatrix = m;
+ MagCalibrated = true;
+ if (MagReferenced)
+ MagReady = true;
+ }
+
+ // True only if the mag has calibration values stored
+ bool HasMagCalibration() const { return MagCalibrated;}
+
+ // Force the mag into the uncalibrated state
+ void ClearMagCalibration()
+ {
+ MagCalibrated = false;
+ MagReady = false;
+ }
+
+ // Set the magnetometer's reference orientation for use in yaw correction
+ // The supplied mag is an uncalibrated value
+ void SetMagReference(const Quatf& q, const Vector3f& rawMag);
+ // Default to current HMD orientation
+ void SetMagReference() { SetMagReference(Q, RawMag); }
+
+ bool HasMagReference() const { return MagReferenced; }
+
+ void ClearMagReference()
+ {
+ MagReferenced = false;
+ MagReady = false;
+ }
+
+ bool IsMagReady() const { return MagReady; }
+
+ void SetMagRefDistance(const float d) { MagRefDistance = d; }
+
+ // Notifies SensorFusion object about a new BodyFrame message from a sensor.
+ // Should be called by user if not attaching to a sensor.
+ void OnMessage(const MessageBodyFrame& msg)
+ {
+ OVR_ASSERT(!IsAttachedToSensor());
+ handleMessage(msg);
+ }
+
+ // Obtain the current accumulated orientation.
+ Quatf GetOrientation() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return Q;
+ }
+
+ // Use a predictive filter to estimate the future orientation
+ Quatf GetPredictedOrientation();
+
+ // Obtain the last absolute acceleration reading, in m/s^2.
+ Vector3f GetAcceleration() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return A;
+ }
+
+ // Obtain the last angular velocity reading, in rad/s.
+ Vector3f GetAngularVelocity() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return AngV;
+ }
+ // Obtain the last magnetometer reading, in Gauss
+ Vector3f GetMagnetometer() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return RawMag;
+ }
+ // Obtain the filtered magnetometer reading, in Gauss
+ Vector3f GetFilteredMagnetometer() const
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return FRawMag.Mean();
+ }
+ // Obtain the calibrated magnetometer reading (direction and field strength)
+ Vector3f GetCalibratedMagnetometer() const
+ {
+ OVR_ASSERT(MagCalibrated);
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ return CalMag;
+ }
+
+ Vector3f GetCalibratedMagValue(const Vector3f& rawMag) const;
+
+ float GetMagRefYaw() const
+ {
+ return MagRefYaw;
+ }
+
+ float GetYawErrorAngle() const
+ {
+ return YawErrorAngle;
+ }
+ // For later
+ //Vector3f GetGravity() const;
+
+ // Resets the current orientation
+ void Reset()
+ {
+ Lock::Locker lockScope(Handler.GetHandlerLock());
+ Q = Quatf();
+ QUncorrected = Quatf();
+
+ Stage = 0;
+ }
+
+ // Configuration
+
+ // Gain used to correct gyro with accel. Default value is appropriate for typical use.
+ float GetAccelGain() const { return Gain; }
+ void SetAccelGain(float ag) { Gain = ag; }
+
+ // Multiplier for yaw rotation (turning); setting this higher than 1 (the default) can allow the game
+ // to be played without auxillary rotation controls, possibly making it more immersive. Whether this is more
+ // or less likely to cause motion sickness is unknown.
+ float GetYawMultiplier() const { return YawMult; }
+ void SetYawMultiplier(float y) { YawMult = y; }
+
+ void SetDelegateMessageHandler(MessageHandler* handler)
+ { pDelegate = handler; }
+
+ // Prediction functions.
+ // Prediction delta specifes how much prediction should be applied in seconds; it should in
+ // general be under the average rendering latency. Call GetPredictedOrientation() to get
+ // predicted orientation.
+ float GetPredictionDelta() const { return PredictionDT; }
+ void SetPrediction(float dt, bool enable = true) { PredictionDT = dt; EnablePrediction = enable; }
+ void SetPredictionEnabled(bool enable = true) { EnablePrediction = enable; }
+ bool IsPredictionEnabled() { return EnablePrediction; }
+
+ // Methods for magnetometer calibration
+ static float AngleDifference(float theta1, float theta2);
+ static Vector3f CalculateSphereCenter(Vector3f p1, Vector3f p2,
+ Vector3f p3, Vector3f p4);
+
+
+private:
+ SensorFusion* getThis() { return this; }
+
+ // Internal handler for messages; bypasses error checking.
+ void handleMessage(const MessageBodyFrame& msg);
+
+ class BodyFrameHandler : public MessageHandler
+ {
+ SensorFusion* pFusion;
+ public:
+ BodyFrameHandler(SensorFusion* fusion) : pFusion(fusion) { }
+ ~BodyFrameHandler();
+
+ virtual void OnMessage(const Message& msg);
+ virtual bool SupportsMessageType(MessageType type) const;
+ };
+
+ Quatf Q;
+ Quatf QUncorrected;
+ Vector3f A;
+ Vector3f AngV;
+ Vector3f CalMag;
+ Vector3f RawMag;
+ unsigned int Stage;
+ float DeltaT;
+ BodyFrameHandler Handler;
+ MessageHandler* pDelegate;
+ float Gain;
+ float YawMult;
+ volatile bool EnableGravity;
+
+ bool EnablePrediction;
+ float PredictionDT;
+
+ SensorFilter FRawMag;
+ SensorFilter FAccW;
+ SensorFilter FAngV;
+
+ int TiltCondCount;
+ float TiltErrorAngle;
+ Vector3f TiltErrorAxis;
+
+ bool EnableYawCorrection;
+ Matrix4f MagCalibrationMatrix;
+ bool MagCalibrated;
+ int MagCondCount;
+ bool MagReferenced;
+ float MagRefDistance;
+ bool MagReady;
+ Quatf MagRefQ;
+ Vector3f MagRefM;
+ float MagRefYaw;
+ float YawErrorAngle;
+ int YawErrorCount;
+ bool YawCorrectionInProgress;
+ bool YawCorrectionActivated;
+
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_SensorImpl.cpp b/LibOVR/Src/OVR_SensorImpl.cpp new file mode 100644 index 0000000..fb322df --- /dev/null +++ b/LibOVR/Src/OVR_SensorImpl.cpp @@ -0,0 +1,882 @@ +/************************************************************************************
+
+Filename : OVR_SensorImpl.cpp
+Content : Oculus Sensor device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_SensorImpl.h"
+
+// HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo.
+
+#include "Kernel/OVR_Timer.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Sensor-specific packet data structures
+
+enum {
+ Sensor_VendorId = Oculus_VendorId,
+ Sensor_ProductId = 0x0001,
+
+ // ST's VID used originally; should be removed in the future
+ Sensor_OldVendorId = 0x0483,
+ Sensor_OldProductId = 0x5750,
+
+ Sensor_DefaultReportRate = 500, // Hz
+ Sensor_MaxReportRate = 1000 // Hz
+};
+
+// Reported data is little-endian now
+static UInt16 DecodeUInt16(const UByte* buffer)
+{
+ return (UInt16(buffer[1]) << 8) | UInt16(buffer[0]);
+}
+
+static SInt16 DecodeSInt16(const UByte* buffer)
+{
+ return (SInt16(buffer[1]) << 8) | SInt16(buffer[0]);
+}
+
+static UInt32 DecodeUInt32(const UByte* buffer)
+{
+ return (buffer[0]) | UInt32(buffer[1] << 8) | UInt32(buffer[2] << 16) | UInt32(buffer[3] << 24);
+}
+
+static float DecodeFloat(const UByte* buffer)
+{
+ union {
+ UInt32 U;
+ float F;
+ };
+
+ U = DecodeUInt32(buffer);
+ return F;
+}
+
+
+static void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z)
+{
+ // Sign extending trick
+ // from http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
+ struct {SInt32 x:21;} s;
+
+ *x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3);
+ *y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) |
+ ((buffer[5] & 0xC0) >> 6);
+ *z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1);
+}
+
+// Messages we care for
+enum TrackerMessageType
+{
+ TrackerMessage_None = 0,
+ TrackerMessage_Sensors = 1,
+ TrackerMessage_Unknown = 0x100,
+ TrackerMessage_SizeError = 0x101,
+};
+
+struct TrackerSample
+{
+ SInt32 AccelX, AccelY, AccelZ;
+ SInt32 GyroX, GyroY, GyroZ;
+};
+
+
+struct TrackerSensors
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+ UInt16 LastCommandID;
+ SInt16 Temperature;
+
+ TrackerSample Samples[3];
+
+ SInt16 MagX, MagY, MagZ;
+
+ TrackerMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 62)
+ return TrackerMessage_SizeError;
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+ LastCommandID = DecodeUInt16(buffer + 4);
+ Temperature = DecodeSInt16(buffer + 6);
+
+ //if (SampleCount > 2)
+ // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount));
+
+ // Only unpack as many samples as there actually are
+ UByte iterationCount = (SampleCount > 2) ? 3 : SampleCount;
+
+ for (UByte i = 0; i < iterationCount; i++)
+ {
+ UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ);
+ UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ);
+ }
+
+ MagX = DecodeSInt16(buffer + 56);
+ MagY = DecodeSInt16(buffer + 58);
+ MagZ = DecodeSInt16(buffer + 60);
+
+ return TrackerMessage_Sensors;
+ }
+};
+
+struct TrackerMessage
+{
+ TrackerMessageType Type;
+ TrackerSensors Sensors;
+};
+
+bool DecodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(TrackerMessage));
+
+ if (size < 4)
+ {
+ message->Type = TrackerMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case TrackerMessage_Sensors:
+ message->Type = message->Sensors.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = TrackerMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None);
+}
+
+
+// ***** SensorRangeImpl Implementation
+
+// Sensor HW only accepts specific maximum range values, used to maximize
+// the 16-bit sensor outputs. Use these ramps to specify and report appropriate values.
+static const UInt16 AccelRangeRamp[] = { 2, 4, 8, 16 };
+static const UInt16 GyroRangeRamp[] = { 250, 500, 1000, 2000 };
+static const UInt16 MagRangeRamp[] = { 880, 1300, 1900, 2500 };
+
+static UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count,
+ float val, float factor, const char* label)
+{
+ UInt16 threshold = (UInt16)(val * factor);
+
+ for (unsigned i = 0; i<count; i++)
+ {
+ if (ramp[i] >= threshold)
+ return ramp[i];
+ }
+ OVR_DEBUG_LOG(("SensorDevice::SetRange - %s clamped to %0.4f",
+ label, float(ramp[count-1]) / factor));
+ OVR_UNUSED2(factor, label);
+ return ramp[count-1];
+}
+
+// SensorScaleImpl provides buffer packing logic for the Sensor Range
+// record that can be applied to DK1 sensor through Get/SetFeature. We expose this
+// through SensorRange class, which has different units.
+struct SensorRangeImpl
+{
+ enum { PacketSize = 8 };
+ UByte Buffer[PacketSize];
+
+ UInt16 CommandId;
+ UInt16 AccelScale;
+ UInt16 GyroScale;
+ UInt16 MagScale;
+
+ SensorRangeImpl(const SensorRange& r, UInt16 commandId = 0)
+ {
+ SetSensorRange(r, commandId);
+ }
+
+ void SetSensorRange(const SensorRange& r, UInt16 commandId = 0)
+ {
+ CommandId = commandId;
+ AccelScale = SelectSensorRampValue(AccelRangeRamp, sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]),
+ r.MaxAcceleration, (1.0f / 9.81f), "MaxAcceleration");
+ GyroScale = SelectSensorRampValue(GyroRangeRamp, sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]),
+ r.MaxRotationRate, Math<float>::RadToDegreeFactor, "MaxRotationRate");
+ MagScale = SelectSensorRampValue(MagRangeRamp, sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]),
+ r.MaxMagneticField, 1000.0f, "MaxMagneticField");
+ Pack();
+ }
+
+ void GetSensorRange(SensorRange* r)
+ {
+ r->MaxAcceleration = AccelScale * 9.81f;
+ r->MaxRotationRate = DegreeToRad((float)GyroScale);
+ r->MaxMagneticField= MagScale * 0.001f;
+ }
+
+ static SensorRange GetMaxSensorRange()
+ {
+ return SensorRange(AccelRangeRamp[sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]) - 1] * 9.81f,
+ GyroRangeRamp[sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]) - 1] *
+ Math<float>::DegreeToRadFactor,
+ MagRangeRamp[sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]) - 1] * 0.001f);
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 4;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = UByte(AccelScale);
+ Buffer[4] = UByte(GyroScale & 0xFF);
+ Buffer[5] = UByte(GyroScale >> 8);
+ Buffer[6] = UByte(MagScale & 0xFF);
+ Buffer[7] = UByte(MagScale >> 8);
+ }
+
+ void Unpack()
+ {
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ AccelScale= Buffer[3];
+ GyroScale = Buffer[4] | (UInt16(Buffer[5]) << 8);
+ MagScale = Buffer[6] | (UInt16(Buffer[7]) << 8);
+ }
+};
+
+
+// Sensor configuration command, ReportId == 2.
+
+struct SensorConfigImpl
+{
+ enum { PacketSize = 7 };
+ UByte Buffer[PacketSize];
+
+ // Flag values for Flags.
+ enum {
+ Flag_RawMode = 0x01,
+ Flag_CallibrationTest = 0x02, // Internal test mode
+ Flag_UseCallibration = 0x04,
+ Flag_AutoCallibration = 0x08,
+ Flag_MotionKeepAlive = 0x10,
+ Flag_CommandKeepAlive = 0x20,
+ Flag_SensorCoordinates = 0x40
+ };
+
+ UInt16 CommandId;
+ UByte Flags;
+ UInt16 PacketInterval;
+ UInt16 KeepAliveIntervalMs;
+
+ SensorConfigImpl() : CommandId(0), Flags(0), PacketInterval(0), KeepAliveIntervalMs(0)
+ {
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 2;
+ }
+
+ void SetSensorCoordinates(bool sensorCoordinates)
+ { Flags = (Flags & ~Flag_SensorCoordinates) | (sensorCoordinates ? Flag_SensorCoordinates : 0); }
+ bool IsUsingSensorCoordinates() const
+ { return (Flags & Flag_SensorCoordinates) != 0; }
+
+ void Pack()
+ {
+ Buffer[0] = 2;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = Flags;
+ Buffer[4] = UByte(PacketInterval);
+ Buffer[5] = UByte(KeepAliveIntervalMs & 0xFF);
+ Buffer[6] = UByte(KeepAliveIntervalMs >> 8);
+ }
+
+ void Unpack()
+ {
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ Flags = Buffer[3];
+ PacketInterval = Buffer[4];
+ KeepAliveIntervalMs= Buffer[5] | (UInt16(Buffer[6]) << 8);
+ }
+
+};
+
+
+// SensorKeepAlive - feature report that needs to be sent at regular intervals for sensor
+// to receive commands.
+struct SensorKeepAliveImpl
+{
+ enum { PacketSize = 5 };
+ UByte Buffer[PacketSize];
+
+ UInt16 CommandId;
+ UInt16 KeepAliveIntervalMs;
+
+ SensorKeepAliveImpl(UInt16 interval = 0, UInt16 commandId = 0)
+ : CommandId(commandId), KeepAliveIntervalMs(interval)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 8;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = UByte(KeepAliveIntervalMs & 0xFF);
+ Buffer[4] = UByte(KeepAliveIntervalMs >> 8);
+ }
+
+ void Unpack()
+ {
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ KeepAliveIntervalMs= Buffer[3] | (UInt16(Buffer[4]) << 8);
+ }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDisplayInfoImpl
+SensorDisplayInfoImpl::SensorDisplayInfoImpl()
+ : CommandId(0), DistortionType(Base_None)
+{
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 9;
+}
+
+void SensorDisplayInfoImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ DistortionType = Buffer[3];
+ HResolution = DecodeUInt16(Buffer+4);
+ VResolution = DecodeUInt16(Buffer+6);
+ HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f);
+ VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f);
+ VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f);
+ LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f);
+ EyeToScreenDistance[0] = DecodeUInt32(Buffer+24) * (1/1000000.f);
+ EyeToScreenDistance[1] = DecodeUInt32(Buffer+28) * (1/1000000.f);
+ DistortionK[0] = DecodeFloat(Buffer+32);
+ DistortionK[1] = DecodeFloat(Buffer+36);
+ DistortionK[2] = DecodeFloat(Buffer+40);
+ DistortionK[3] = DecodeFloat(Buffer+44);
+ DistortionK[4] = DecodeFloat(Buffer+48);
+ DistortionK[5] = DecodeFloat(Buffer+52);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceFactory
+
+SensorDeviceFactory SensorDeviceFactory::Instance;
+
+void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class SensorEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const SensorEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+ SensorDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+
+ // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially
+ // mismatching monitor information (in case wrong EDID is reported by splitter),
+ // or to create a new "virtualized" HMD Device.
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor);
+ }
+ }
+ }
+ };
+
+ //double start = Timer::GetProfileSeconds();
+
+ SensorEnumerator sensorEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator);
+
+ //double totalSeconds = Timer::GetProfileSeconds() - start;
+}
+
+bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == Sensor_VendorId) && (productId == Sensor_ProductId)) ||
+ ((vendorId == Sensor_OldVendorId) && (productId == Sensor_OldProductId));
+}
+
+bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ SensorDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceCreateDesc
+
+DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance()
+{
+ return new SensorDeviceImpl(this);
+}
+
+bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Sensor) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, HIDDesc.Product.ToCStr());
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, HIDDesc.Manufacturer.ToCStr());
+ info->Type = Device_Sensor;
+ info->Version = 0;
+
+ if (info->InfoClassType == Device_Sensor)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange();
+ OVR_strcpy(sinfo->SerialNumber, sizeof(sinfo->SerialNumber),HIDDesc.SerialNumber.ToCStr());
+ }
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::SensorDevice>(createDesc, 0),
+ Coordinates(SensorDevice::Coord_Sensor),
+ HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default.
+ NextKeepAliveTicks(0),
+ MaxValidRange(SensorRangeImpl::GetMaxSensorRange())
+{
+ SequenceValid = false;
+ LastSampleCount= 0;
+ LastTimestamp = 0;
+
+ OldCommandId = 0;
+}
+
+SensorDeviceImpl::~SensorDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+// Internal creation APIs.
+bool SensorDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::SensorDevice>::Initialize(parent))
+ {
+ openDevice();
+
+ LogText("OVR::SensorDevice initialized.\n");
+
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::openDevice()
+{
+
+ // Read the currently configured range from sensor.
+ SensorRangeImpl sr(SensorRange(), 0);
+
+ if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ sr.Unpack();
+ sr.GetSensorRange(&CurrentRange);
+ }
+
+
+ // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default.
+ SensorDisplayInfoImpl displayInfo;
+ if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+ Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ?
+ Coord_HMD : Coord_Sensor;
+ }
+
+ // Read/Apply sensor config.
+ setCoordinateFrame(Coordinates);
+ setReportRate(Sensor_DefaultReportRate);
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+}
+
+void SensorDeviceImpl::closeDeviceOnError()
+{
+ LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr());
+ NextKeepAliveTicks = 0;
+}
+
+void SensorDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::SensorDevice>::Shutdown();
+
+ LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+
+void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+
+ TrackerMessage message;
+ if (DecodeTrackerMessage(&message, pData, length))
+ {
+ processed = true;
+ onTrackerMessage(&message);
+ }
+ }
+}
+
+UInt64 SensorDeviceImpl::OnTicks(UInt64 ticksMks)
+{
+
+ if (ticksMks >= NextKeepAliveTicks)
+ {
+ // Use 3-seconds keep alive by default.
+ UInt64 keepAliveDelta = Timer::MksPerSecond * 3;
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ // OnTicks is called from background thread so we don't need to add this to the command queue.
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+
+ // Emit keep-alive every few seconds.
+ NextKeepAliveTicks = ticksMks + keepAliveDelta;
+ }
+ return NextKeepAliveTicks - ticksMks;
+}
+
+bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag)
+{
+ bool result = 0;
+ ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range);
+ }
+
+ if (!threadQueue->PushCallAndWaitResult(this,
+ &SensorDeviceImpl::setRange,
+ &result,
+ range))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+void SensorDeviceImpl::GetRange(SensorRange* range) const
+{
+ Lock::Locker lockScope(GetLock());
+ *range = CurrentRange;
+}
+
+bool SensorDeviceImpl::setRange(const SensorRange& range)
+{
+ SensorRangeImpl sr(range);
+
+ if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ Lock::Locker lockScope(GetLock());
+ sr.GetSensorRange(&CurrentRange);
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true);
+}
+
+SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const
+{
+ return Coordinates;
+}
+
+Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe)
+{
+
+ Coordinates = coordframe;
+
+ // Read the original coordinate frame, then try to change it.
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ scfg.SetSensorCoordinates(coordframe == Coord_Sensor);
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+
+ // Re-read the state, in case of older firmware that doesn't support Sensor coordinates.
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD;
+ }
+ else
+ {
+ HWCoordinates = Coord_HMD;
+ }
+ return 0;
+}
+
+void SensorDeviceImpl::SetReportRate(unsigned rateHz)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true);
+}
+
+unsigned SensorDeviceImpl::GetReportRate() const
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ return Sensor_MaxReportRate / (scfg.PacketInterval + 1);
+ }
+ return 0; // error
+}
+
+Void SensorDeviceImpl::setReportRate(unsigned rateHz)
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ if (rateHz > Sensor_MaxReportRate)
+ rateHz = Sensor_MaxReportRate;
+ else if (rateHz == 0)
+ rateHz = Sensor_DefaultReportRate;
+
+ scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1);
+
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+ return 0;
+}
+
+void SensorDeviceImpl::SetMessageHandler(MessageHandler* handler)
+{
+ if (handler)
+ {
+ SequenceValid = false;
+ DeviceBase::SetMessageHandler(handler);
+ }
+ else
+ {
+ DeviceBase::SetMessageHandler(handler);
+ }
+}
+
+// Sensor reports data in the following coordinate system:
+// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down.
+// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right.
+
+
+// We need to convert it to the following RHS coordinate system:
+// X right, Y Up, Z Back (out of screen)
+//
+Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float ax = (float)sample.AccelX;
+ float ay = (float)sample.AccelY;
+ float az = (float)sample.AccelZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az);
+ return val * 0.0001f;
+}
+
+
+Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update,
+ bool convertHMDToSensor = false)
+{
+ // Note: Y and Z are swapped in comparison to the Accel.
+ // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases.
+ if (!convertHMDToSensor)
+ {
+ return Vector3f( (float)update.MagX,
+ (float)update.MagZ,
+ (float)update.MagY) * 0.0001f;
+ }
+
+ return Vector3f( (float)update.MagX,
+ (float)update.MagY,
+ -(float)update.MagZ) * 0.0001f;
+}
+
+Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float gx = (float)sample.GyroX;
+ float gy = (float)sample.GyroY;
+ float gz = (float)sample.GyroZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz);
+ return val * 0.0001f;
+}
+
+
+void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message)
+{
+ if (message->Type != TrackerMessage_Sensors)
+ return;
+
+ const float timeUnit = (1.0f / 1000.f);
+ TrackerSensors& s = message->Sensors;
+
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+
+ if (SequenceValid)
+ {
+ unsigned timestampDelta;
+
+ if (s.Timestamp < LastTimestamp)
+ timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp);
+ else
+ timestampDelta = (s.Timestamp - LastTimestamp);
+
+ // If we missed a small number of samples, replicate the last sample.
+ if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254))
+ {
+ if (HandlerRef.GetHandler())
+ {
+ MessageBodyFrame sensors(this);
+ sensors.TimeDelta = (timestampDelta - LastSampleCount) * timeUnit;
+ sensors.Acceleration = LastAcceleration;
+ sensors.RotationRate = LastRotationRate;
+ sensors.MagneticField = LastMagneticField;
+ sensors.Temperature = LastTemperature;
+
+ HandlerRef.GetHandler()->OnMessage(sensors);
+ }
+ }
+ }
+ else
+ {
+ LastAcceleration = Vector3f(0);
+ LastRotationRate = Vector3f(0);
+ LastMagneticField= Vector3f(0);
+ LastTemperature = 0;
+ SequenceValid = true;
+ }
+
+ LastSampleCount = s.SampleCount;
+ LastTimestamp = s.Timestamp;
+
+ bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD);
+
+ if (HandlerRef.GetHandler())
+ {
+ MessageBodyFrame sensors(this);
+ UByte iterations = s.SampleCount;
+
+ if (s.SampleCount > 3)
+ {
+ iterations = 3;
+ sensors.TimeDelta = (s.SampleCount - 2) * timeUnit;
+ }
+ else
+ {
+ sensors.TimeDelta = timeUnit;
+ }
+
+ for (UByte i = 0; i < iterations; i++)
+ {
+ sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.MagneticField= MagFromBodyFrameUpdate(s, convertHMDToSensor);
+ sensors.Temperature = s.Temperature * 0.01f;
+ HandlerRef.GetHandler()->OnMessage(sensors);
+ // TimeDelta for the last two sample is always fixed.
+ sensors.TimeDelta = timeUnit;
+ }
+
+ LastAcceleration = sensors.Acceleration;
+ LastRotationRate = sensors.RotationRate;
+ LastMagneticField= sensors.MagneticField;
+ LastTemperature = sensors.Temperature;
+ }
+ else
+ {
+ UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1);
+ LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastMagneticField = MagFromBodyFrameUpdate(s, convertHMDToSensor);
+ LastTemperature = s.Temperature * 0.01f;
+ }
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_SensorImpl.h b/LibOVR/Src/OVR_SensorImpl.h new file mode 100644 index 0000000..890d621 --- /dev/null +++ b/LibOVR/Src/OVR_SensorImpl.h @@ -0,0 +1,208 @@ +/************************************************************************************
+
+Filename : OVR_SensorImpl.h
+Content : Sensor device specific implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorImpl_h
+#define OVR_SensorImpl_h
+
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR {
+
+struct TrackerMessage;
+class ExternalVisitor;
+
+//-------------------------------------------------------------------------------------
+// SensorDeviceFactory enumerates Oculus Sensor devices.
+class SensorDeviceFactory : public DeviceFactory
+{
+public:
+ static SensorDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+// Describes a single a Oculus Sensor device and supports creating its instance.
+class SensorDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ SensorDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_Sensor, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new SensorDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_Sensor) && (pFactory == other.pFactory))
+ {
+ const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber));
+ }
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::SensorDisplayInfoImpl
+
+// DisplayInfo obtained from sensor; these values are used to report distortion
+// settings and other coefficients.
+// Older SensorDisplayInfo will have all zeros, causing the library to apply hard-coded defaults.
+// Currently, only resolutions and sizes are used.
+struct SensorDisplayInfoImpl
+{
+ enum { PacketSize = 56 };
+ UByte Buffer[PacketSize];
+
+ enum
+ {
+ Mask_BaseFmt = 0x0f,
+ Mask_OptionFmts = 0xf0,
+ Base_None = 0,
+ Base_ScreenOnly = 1,
+ Base_Distortion = 2,
+ };
+
+ UInt16 CommandId;
+ UByte DistortionType;
+ UInt16 HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ float VCenter;
+ float LensSeparation;
+ float EyeToScreenDistance[2];
+ float DistortionK[6];
+
+ SensorDisplayInfoImpl();
+
+ void Unpack();
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::SensorDeviceImpl
+
+// Oculus Sensor interface.
+
+class SensorDeviceImpl : public HIDDeviceImpl<OVR::SensorDevice>
+{
+public:
+ SensorDeviceImpl(SensorDeviceCreateDesc* createDesc);
+ ~SensorDeviceImpl();
+
+
+ // DeviceCommaon interface
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual void SetMessageHandler(MessageHandler* handler);
+
+ // HIDDevice::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+ virtual UInt64 OnTicks(UInt64 ticksMks);
+
+ // HMD-Mounted sensor has a different coordinate frame.
+ virtual void SetCoordinateFrame(CoordinateFrame coordframe);
+ virtual CoordinateFrame GetCoordinateFrame() const;
+
+ // SensorDevice interface
+ virtual bool SetRange(const SensorRange& range, bool waitFlag);
+ virtual void GetRange(SensorRange* range) const;
+
+ // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
+ // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
+ // called twice or thrice at the same 'tick'.
+ // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
+ // times for each 'tick': the first call will contain averaged values, the second
+ // and third calls will provide with most recent two recorded samples.
+ virtual void SetReportRate(unsigned rateHz);
+ // Returns currently set report rate, in Hz. If 0 - error occurred.
+ // Note, this value may be different from the one provided for SetReportRate. The return
+ // value will contain the actual rate.
+ virtual unsigned GetReportRate() const;
+
+ // Hack to create HMD device from sensor display info.
+ static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor);
+protected:
+
+ void openDevice();
+ void closeDeviceOnError();
+
+ Void setCoordinateFrame(CoordinateFrame coordframe);
+ bool setRange(const SensorRange& range);
+
+ Void setReportRate(unsigned rateHz);
+
+ // Called for decoded messages
+ void onTrackerMessage(TrackerMessage* message);
+
+ // Helpers to reduce casting.
+/*
+ SensorDeviceCreateDesc* getCreateDesc() const
+ { return (SensorDeviceCreateDesc*)pCreateDesc.GetPtr(); }
+
+ HIDDeviceDesc* getHIDDesc() const
+ { return &getCreateDesc()->HIDDesc; }
+*/
+
+ // Set if the sensor is located on the HMD.
+ // Older prototype firmware doesn't support changing HW coordinates,
+ // so we track its state.
+ CoordinateFrame Coordinates;
+ CoordinateFrame HWCoordinates;
+ UInt64 NextKeepAliveTicks;
+
+ bool SequenceValid;
+ SInt16 LastTimestamp;
+ UByte LastSampleCount;
+ float LastTemperature;
+ Vector3f LastAcceleration;
+ Vector3f LastRotationRate;
+ Vector3f LastMagneticField;
+
+ // Current sensor range obtained from device.
+ SensorRange MaxValidRange;
+ SensorRange CurrentRange;
+
+ UInt16 OldCommandId;
+};
+
+
+} // namespace OVR
+
+#endif // OVR_SensorImpl_h
diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.cpp b/LibOVR/Src/OVR_ThreadCommandQueue.cpp new file mode 100644 index 0000000..d9dbab3 --- /dev/null +++ b/LibOVR/Src/OVR_ThreadCommandQueue.cpp @@ -0,0 +1,370 @@ +/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_ThreadCommandQueue.cpp
+Content : Command queue for operations executed on a thread
+Created : October 29, 2012
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "OVR_ThreadCommandQueue.h"
+
+namespace OVR {
+
+
+//------------------------------------------------------------------------
+// ***** CircularBuffer
+
+// CircularBuffer is a FIFO buffer implemented in a single block of memory,
+// which allows writing and reading variable-size data chucks. Write fails
+// if buffer is full.
+
+class CircularBuffer
+{
+ enum {
+ AlignSize = 16,
+ AlignMask = AlignSize - 1
+ };
+
+ UByte* pBuffer;
+ UPInt Size;
+ UPInt Tail; // Byte offset of next item to be popped.
+ UPInt Head; // Byte offset of where next push will take place.
+ UPInt End; // When Head < Tail, this is used instead of Size.
+
+ inline UPInt roundUpSize(UPInt size)
+ { return (size + AlignMask) & ~(UPInt)AlignMask; }
+
+public:
+
+ CircularBuffer(UPInt size)
+ : Size(size), Tail(0), Head(0), End(0)
+ {
+ pBuffer = (UByte*)OVR_ALLOC_ALIGNED(roundUpSize(size), AlignSize);
+ }
+ ~CircularBuffer()
+ {
+ // For ThreadCommands, we must consume everything before shutdown.
+ OVR_ASSERT(IsEmpty());
+ OVR_FREE_ALIGNED(pBuffer);
+ }
+
+ bool IsEmpty() const { return (Head == Tail); }
+
+ // Allocates a state block of specified size and advances pointers,
+ // returning 0 if buffer is full.
+ UByte* Write(UPInt size);
+
+ // Returns a pointer to next available data block; 0 if none available.
+ UByte* ReadBegin()
+ { return (Head != Tail) ? (pBuffer + Tail) : 0; }
+ // Consumes data of specified size; this must match size passed to Write.
+ void ReadEnd(UPInt size);
+};
+
+
+// Allocates a state block of specified size and advances pointers,
+// returning 0 if buffer is full.
+UByte* CircularBuffer::Write(UPInt size)
+{
+ UByte* p = 0;
+
+ size = roundUpSize(size);
+ // Since this is circular buffer, always allow at least one item.
+ OVR_ASSERT(size < Size/2);
+
+ if (Head >= Tail)
+ {
+ OVR_ASSERT(End == 0);
+
+ if (size <= (Size - Head))
+ {
+ p = pBuffer + Head;
+ Head += size;
+ }
+ else if (size < Tail)
+ {
+ p = pBuffer;
+ End = Head;
+ Head = size;
+ OVR_ASSERT(Head != Tail);
+ }
+ }
+ else
+ {
+ OVR_ASSERT(End != 0);
+
+ if ((Tail - Head) > size)
+ {
+ p = pBuffer + Head;
+ Head += size;
+ OVR_ASSERT(Head != Tail);
+ }
+ }
+
+ return p;
+}
+
+void CircularBuffer::ReadEnd(UPInt size)
+{
+ OVR_ASSERT(Head != Tail);
+ size = roundUpSize(size);
+
+ Tail += size;
+ if (Tail == End)
+ {
+ Tail = End = 0;
+ }
+ else if (Tail == Head)
+ {
+ OVR_ASSERT(End == 0);
+ Tail = Head = 0;
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommand
+
+ThreadCommand::PopBuffer::~PopBuffer()
+{
+ if (Size)
+ Destruct<ThreadCommand>(toCommand());
+}
+
+void ThreadCommand::PopBuffer::InitFromBuffer(void* data)
+{
+ ThreadCommand* cmd = (ThreadCommand*)data;
+ OVR_ASSERT(cmd->Size <= MaxSize);
+
+ if (Size)
+ Destruct<ThreadCommand>(toCommand());
+ Size = cmd->Size;
+ memcpy(Buffer, (void*)cmd, Size);
+}
+
+void ThreadCommand::PopBuffer::Execute()
+{
+ ThreadCommand* command = toCommand();
+ OVR_ASSERT(command);
+ command->Execute();
+ if (NeedsWait())
+ GetEvent()->PulseEvent();
+}
+
+//-------------------------------------------------------------------------------------
+
+class ThreadCommandQueueImpl : public NewOverrideBase
+{
+ typedef ThreadCommand::NotifyEvent NotifyEvent;
+ friend class ThreadCommandQueue;
+
+public:
+
+ ThreadCommandQueueImpl(ThreadCommandQueue* queue)
+ : pQueue(queue), CommandBuffer(2048),
+ ExitEnqueued(false), ExitProcessed(false)
+ {
+ }
+ ~ThreadCommandQueueImpl();
+
+
+ bool PushCommand(const ThreadCommand& command);
+ bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
+
+
+ // ExitCommand is used by notify us that Thread is shutting down.
+ struct ExitCommand : public ThreadCommand
+ {
+ ThreadCommandQueueImpl* pImpl;
+
+ ExitCommand(ThreadCommandQueueImpl* impl, bool wait)
+ : ThreadCommand(sizeof(ExitCommand), wait, true), pImpl(impl) { }
+
+ virtual void Execute() const
+ {
+ Lock::Locker lock(&pImpl->QueueLock);
+ pImpl->ExitProcessed = true;
+ }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ExitCommand>(p, *this); }
+ };
+
+
+ NotifyEvent* AllocNotifyEvent_NTS()
+ {
+ NotifyEvent* p = AvailableEvents.GetFirst();
+
+ if (!AvailableEvents.IsNull(p))
+ p->RemoveNode();
+ else
+ p = new NotifyEvent;
+ return p;
+ }
+
+ void FreeNotifyEvent_NTS(NotifyEvent* p)
+ {
+ AvailableEvents.PushBack(p);
+ }
+
+ void FreeNotifyEvents_NTS()
+ {
+ while(!AvailableEvents.IsEmpty())
+ {
+ NotifyEvent* p = AvailableEvents.GetFirst();
+ p->RemoveNode();
+ delete p;
+ }
+ }
+
+ ThreadCommandQueue* pQueue;
+ Lock QueueLock;
+ volatile bool ExitEnqueued;
+ volatile bool ExitProcessed;
+ List<NotifyEvent> AvailableEvents;
+ List<NotifyEvent> BlockedProducers;
+ CircularBuffer CommandBuffer;
+};
+
+
+
+ThreadCommandQueueImpl::~ThreadCommandQueueImpl()
+{
+ Lock::Locker lock(&QueueLock);
+ OVR_ASSERT(BlockedProducers.IsEmpty());
+ FreeNotifyEvents_NTS();
+}
+
+bool ThreadCommandQueueImpl::PushCommand(const ThreadCommand& command)
+{
+ ThreadCommand::NotifyEvent* completeEvent = 0;
+ ThreadCommand::NotifyEvent* queueAvailableEvent = 0;
+
+ // Repeat writing command into buffer until it is available.
+ do {
+
+ { // Lock Scope
+ Lock::Locker lock(&QueueLock);
+
+ if (queueAvailableEvent)
+ {
+ FreeNotifyEvent_NTS(queueAvailableEvent);
+ queueAvailableEvent = 0;
+ }
+
+ // Don't allow any commands after PushExitCommand() is called.
+ if (ExitEnqueued && !command.ExitFlag)
+ return false;
+
+
+ bool bufferWasEmpty = CommandBuffer.IsEmpty();
+ UByte* buffer = CommandBuffer.Write(command.GetSize());
+ if (buffer)
+ {
+ ThreadCommand* c = command.CopyConstruct(buffer);
+ if (c->NeedsWait())
+ completeEvent = c->pEvent = AllocNotifyEvent_NTS();
+ // Signal-waker consumer when we add data to buffer.
+ if (bufferWasEmpty)
+ pQueue->OnPushNonEmpty_Locked();
+ break;
+ }
+
+ queueAvailableEvent = AllocNotifyEvent_NTS();
+ BlockedProducers.PushBack(queueAvailableEvent);
+ } // Lock Scope
+
+ queueAvailableEvent->Wait();
+
+ } while(1);
+
+ // Command was enqueued, wait if necessary.
+ if (completeEvent)
+ {
+ completeEvent->Wait();
+ Lock::Locker lock(&QueueLock);
+ FreeNotifyEvent_NTS(completeEvent);
+ }
+
+ return true;
+}
+
+
+// Pops the next command from the thread queue, if any is available.
+bool ThreadCommandQueueImpl::PopCommand(ThreadCommand::PopBuffer* popBuffer)
+{
+ Lock::Locker lock(&QueueLock);
+
+ UByte* buffer = CommandBuffer.ReadBegin();
+ if (!buffer)
+ {
+ // Notify thread while in lock scope, enabling initialization of wait.
+ pQueue->OnPopEmpty_Locked();
+ return false;
+ }
+
+ popBuffer->InitFromBuffer(buffer);
+ CommandBuffer.ReadEnd(popBuffer->GetSize());
+
+ if (!BlockedProducers.IsEmpty())
+ {
+ ThreadCommand::NotifyEvent* queueAvailableEvent = BlockedProducers.GetFirst();
+ queueAvailableEvent->RemoveNode();
+ queueAvailableEvent->PulseEvent();
+ // Event is freed later by waiter.
+ }
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+
+ThreadCommandQueue::ThreadCommandQueue()
+{
+ pImpl = new ThreadCommandQueueImpl(this);
+}
+ThreadCommandQueue::~ThreadCommandQueue()
+{
+ delete pImpl;
+}
+
+bool ThreadCommandQueue::PushCommand(const ThreadCommand& command)
+{
+ return pImpl->PushCommand(command);
+}
+
+bool ThreadCommandQueue::PopCommand(ThreadCommand::PopBuffer* popBuffer)
+{
+ return pImpl->PopCommand(popBuffer);
+}
+
+void ThreadCommandQueue::PushExitCommand(bool wait)
+{
+ // Exit is processed in two stages:
+ // - First, ExitEnqueued flag is set to block further commands from queuing up.
+ // - Second, the actual exit call is processed on the consumer thread, flushing
+ // any prior commands.
+ // IsExiting() only returns true after exit has flushed.
+ {
+ Lock::Locker lock(&pImpl->QueueLock);
+ if (pImpl->ExitEnqueued)
+ return;
+ pImpl->ExitEnqueued = true;
+ }
+
+ PushCommand(ThreadCommandQueueImpl::ExitCommand(pImpl, wait));
+}
+
+bool ThreadCommandQueue::IsExiting() const
+{
+ return pImpl->ExitProcessed;
+}
+
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.h b/LibOVR/Src/OVR_ThreadCommandQueue.h new file mode 100644 index 0000000..98d6228 --- /dev/null +++ b/LibOVR/Src/OVR_ThreadCommandQueue.h @@ -0,0 +1,308 @@ +/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_ThreadCommandQueue.h
+Content : Command queue for operations executed on a thread
+Created : October 29, 2012
+Author : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_ThreadCommandQueue_h
+#define OVR_ThreadCommandQueue_h
+
+#include "Kernel/OVR_Types.h"
+#include "Kernel/OVR_List.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Threads.h"
+
+namespace OVR {
+
+class ThreadCommand;
+class ThreadCommandQueue;
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommand
+
+// ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue.
+class ThreadCommand
+{
+public:
+
+ // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the
+ // calling (producer) thread when command is completed or queue slot is available.
+ class NotifyEvent : public ListNode<NotifyEvent>, public NewOverrideBase
+ {
+ Event E;
+ public:
+ NotifyEvent() { }
+
+ void Wait() { E.Wait(); }
+ void PulseEvent() { E.PulseEvent(); }
+ };
+
+ // ThreadCommand::PopBuffer is temporary storage for a command popped off
+ // by ThreadCommandQueue::PopCommand.
+ class PopBuffer
+ {
+ enum { MaxSize = 256 };
+
+ UPInt Size;
+ union {
+ UByte Buffer[MaxSize];
+ UPInt Align;
+ };
+
+ ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; }
+
+ public:
+ PopBuffer() : Size(0) { }
+ ~PopBuffer();
+
+ void InitFromBuffer(void* data);
+
+ bool HasCommand() const { return Size != 0; }
+ UPInt GetSize() const { return Size; }
+ bool NeedsWait() const { return toCommand()->NeedsWait(); }
+ NotifyEvent* GetEvent() const { return toCommand()->pEvent; }
+
+ // Execute the command and also notifies caller to finish waiting,
+ // if necessary.
+ void Execute();
+ };
+
+ UInt16 Size;
+ bool WaitFlag;
+ bool ExitFlag; // Marks the last exit command.
+ NotifyEvent* pEvent;
+
+ ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false)
+ : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { }
+ virtual ~ThreadCommand() { }
+
+ bool NeedsWait() const { return WaitFlag; }
+ UPInt GetSize() const { return Size; }
+
+ virtual void Execute() const = 0;
+ // Copy constructor used for serializing this to memory buffer.
+ virtual ThreadCommand* CopyConstruct(void* p) const = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// CleanType is a template that strips 'const' and '&' modifiers from the argument type;
+// for example, typename CleanType<A&>::Type is equivalent to A.
+template<class T> struct CleanType { typedef T Type; };
+template<class T> struct CleanType<T&> { typedef T Type; };
+template<class T> struct CleanType<const T> { typedef T Type; };
+template<class T> struct CleanType<const T&> { typedef T Type; };
+
+// SelfType is a template that yields the argument type. This helps avoid conflicts with
+// automatic template argument deduction for function calls when identical argument
+// is already defined.
+template<class T> struct SelfType { typedef T Type; };
+
+
+
+//-------------------------------------------------------------------------------------
+// ThreadCommand specializations for member functions with different number of
+// arguments and argument types.
+
+// Used to return nothing from a ThreadCommand, to avoid problems with 'void'.
+struct Void
+{
+ Void() {}
+ Void(int) {}
+};
+
+// ThreadCommand for member function with 0 arguments.
+template<class C, class R>
+class ThreadCommandMF0 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)();
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)()) :
+ (void)(pClass->*pFn)();
+ }
+
+public:
+ ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF0), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF0>(p, *this); }
+};
+
+
+// ThreadCommand for member function with 1 argument.
+template<class C, class R, class A0>
+class ThreadCommandMF1 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)(A0);
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+ typename CleanType<A0>::Type AVal0;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) :
+ (void)(pClass->*pFn)(AVal0);
+ }
+
+public:
+ ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF1), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF1>(p, *this); }
+};
+
+// ThreadCommand for member function with 2 arguments.
+template<class C, class R, class A0, class A1>
+class ThreadCommandMF2 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)(A0, A1);
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+ typename CleanType<A0>::Type AVal0;
+ typename CleanType<A1>::Type AVal1;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) :
+ (void)(pClass->*pFn)(AVal0, AVal1);
+ }
+
+public:
+ ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF2), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF2>(p, *this); }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommandQueue
+
+// ThreadCommandQueue is a queue of executable function-call commands intended to be
+// serviced by a single consumer thread. Commands are added to the queue with PushCall
+// and removed with PopCall; they are processed in FIFO order. Multiple producer threads
+// are supported and will be blocked if internal data buffer is full.
+
+class ThreadCommandQueue
+{
+public:
+
+ ThreadCommandQueue();
+ virtual ~ThreadCommandQueue();
+
+
+ // Pops the next command from the thread queue, if any is available.
+ // The command should be executed by calling popBuffer->Execute().
+ // Returns 'false' if no command is available at the time of the call.
+ bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
+
+ // Generic implementaion of PushCommand; enqueues a command for execution.
+ // Returns 'false' if push failed, usually indicating thread shutdown.
+ bool PushCommand(const ThreadCommand& command);
+
+ //
+ void PushExitCommand(bool wait);
+
+ // Returns 'true' once ExitCommand has been processed, so the thread can shut down.
+ bool IsExiting() const;
+
+
+ // These two virtual functions serve as notifications for derived
+ // thread waiting.
+ virtual void OnPushNonEmpty_Locked() { }
+ virtual void OnPopEmpty_Locked() { }
+
+
+ // *** PushCall with no result
+
+ // Enqueue a member function of 'this' class to be called on consumer thread.
+ // By default the function returns immediately; set 'wait' argument to 'true' to
+ // wait for completion.
+ template<class C, class R>
+ bool PushCall(R (C::*fn)(), bool wait = false)
+ { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, 0, wait)); }
+ template<class C, class R, class A0>
+ bool PushCall(R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, 0, a0, wait)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCall(R (C::*fn)(A0, A1),
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, 0, a0, a1, wait)); }
+ // Enqueue a specified member function call of class C.
+ // By default the function returns immediately; set 'wait' argument to 'true' to
+ // wait for completion.
+ template<class C, class R>
+ bool PushCall(C* p, R (C::*fn)(), bool wait = false)
+ { return PushCommand(ThreadCommandMF0<C,R>(p, fn, 0, wait)); }
+ template<class C, class R, class A0>
+ bool PushCall(C* p, R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, 0, a0, wait)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCall(C* p, R (C::*fn)(A0, A1),
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, 0, a0, a1, wait)); }
+
+
+ // *** PushCall with Result
+
+ // Enqueue a member function of 'this' class call and wait for call to complete
+ // on consumer thread before returning.
+ template<class C, class R>
+ bool PushCallAndWaitResult(R (C::*fn)(), R* ret)
+ { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, ret, true)); }
+ template<class C, class R, class A0>
+ bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, ret, a0, true)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret,
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, ret, a0, a1, true)); }
+ // Enqueue a member function call for class C and wait for the call to complete
+ // on consumer thread before returning.
+ template<class C, class R>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret)
+ { return PushCommand(ThreadCommandMF0<C,R>(p, fn, ret, true)); }
+ template<class C, class R, class A0>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, ret, a0, true)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret,
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, ret, a0, a1, true)); }
+
+private:
+ class ThreadCommandQueueImpl* pImpl;
+};
+
+
+}
+
+#endif // OVR_ThreadCommandQueue_h
diff --git a/LibOVR/Src/OVR_Win32_DeviceManager.cpp b/LibOVR/Src/OVR_Win32_DeviceManager.cpp new file mode 100644 index 0000000..f7bb9eb --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceManager.cpp @@ -0,0 +1,423 @@ +/************************************************************************************
+
+Filename : OVR_Win32_DeviceManager.cpp
+Content : Win32 implementation of DeviceManager.
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_DeviceManager.h"
+
+// Sensor & HMD Factories
+#include "OVR_SensorImpl.h"
+#include "OVR_LatencyTestImpl.h"
+#include "OVR_Win32_HMDDevice.h"
+#include "OVR_Win32_DeviceStatus.h"
+#include "OVR_Win32_HIDDevice.h"
+
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Std.h"
+#include "Kernel/OVR_Log.h"
+
+DWORD Debug_WaitedObjectCount = 0;
+
+namespace OVR { namespace Win32 {
+
+
+//-------------------------------------------------------------------------------------
+// **** Win32::DeviceManager
+
+DeviceManager::DeviceManager()
+{
+ HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
+}
+
+DeviceManager::~DeviceManager()
+{
+ // make sure Shutdown was called.
+ OVR_ASSERT(!pThread);
+}
+
+bool DeviceManager::Initialize(DeviceBase*)
+{
+ if (!DeviceManagerImpl::Initialize(0))
+ return false;
+
+ pThread = *new DeviceManagerThread(this);
+ if (!pThread || !pThread->Start())
+ return false;
+
+ pCreateDesc->pDevice = this;
+ LogText("OVR::DeviceManager - initialized.\n");
+ return true;
+}
+
+void DeviceManager::Shutdown()
+{
+ LogText("OVR::DeviceManager - shutting down.\n");
+
+ // Set Manager shutdown marker variable; this prevents
+ // any existing DeviceHandle objects from accessing device.
+ pCreateDesc->pLock->pManager = 0;
+
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ pThread->PushExitCommand(false);
+ pThread->DetachDeviceManager();
+ pThread.Clear();
+
+ DeviceManagerImpl::Shutdown();
+}
+
+ThreadCommandQueue* DeviceManager::GetThreadQueue()
+{
+ return pThread;
+}
+
+bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Manager) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Manager;
+ info->Version = 0;
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
+ OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
+ return true;
+}
+
+DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ // TBD: Can this be avoided in the future, once proper device notification is in place?
+ if (GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ pThread->PushCall((DeviceManagerImpl*)this,
+ &DeviceManager::EnumerateAllFactoryDevices, true);
+ }
+ else
+ DeviceManager::EnumerateAllFactoryDevices();
+
+ return DeviceManagerImpl::EnumerateDevicesEx(args);
+}
+
+ThreadId DeviceManager::GetThreadId() const
+{
+ return pThread->GetThreadId();
+}
+
+bool DeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const
+{
+ if (GetHIDDeviceManager())
+ return static_cast<HIDDeviceManager*>(GetHIDDeviceManager())->GetHIDDeviceDesc(path, pdevDesc);
+ return false;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager Thread
+
+DeviceManagerThread::DeviceManagerThread(DeviceManager* pdevMgr)
+ : Thread(ThreadStackSize), hCommandEvent(0), pDeviceMgr(pdevMgr)
+{
+ // Create a non-signaled manual-reset event.
+ hCommandEvent = ::CreateEvent(0, TRUE, FALSE, 0);
+ if (!hCommandEvent)
+ return;
+
+ // Must add event before starting.
+ AddOverlappedEvent(0, hCommandEvent);
+
+ // Create device messages object.
+ pStatusObject = *new DeviceStatus(this);
+}
+
+DeviceManagerThread::~DeviceManagerThread()
+{
+ // Remove overlapped event [0], after thread service exit.
+ if (hCommandEvent)
+ {
+ RemoveOverlappedEvent(0, hCommandEvent);
+ ::CloseHandle(hCommandEvent);
+ hCommandEvent = 0;
+ }
+}
+
+int DeviceManagerThread::Run()
+{
+ ThreadCommand::PopBuffer command;
+
+ SetThreadName("OVR::DeviceManagerThread");
+ LogText("OVR::DeviceManagerThread - running (ThreadId=0x%X).\n", GetThreadId());
+
+ if (!pStatusObject->Initialize())
+ {
+ LogText("OVR::DeviceManagerThread - failed to initialize MessageObject.\n");
+ }
+
+ while(!IsExiting())
+ {
+ // PopCommand will reset event on empty queue.
+ if (PopCommand(&command))
+ {
+ command.Execute();
+ }
+ else
+ {
+ DWORD eventIndex = 0;
+ do {
+ UPInt numberOfWaitHandles = WaitHandles.GetSize();
+ Debug_WaitedObjectCount = (DWORD)numberOfWaitHandles;
+
+ DWORD waitMs = INFINITE;
+
+ // If devices have time-dependent logic registered, get the longest wait
+ // allowed based on current ticks.
+ if (!TicksNotifiers.IsEmpty())
+ {
+ UInt64 ticksMks = Timer::GetTicks();
+ DWORD waitAllowed;
+
+ for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
+ {
+ waitAllowed = (DWORD)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
+ if (waitAllowed < waitMs)
+ waitMs = waitAllowed;
+ }
+ }
+
+ // Wait for event signals or window messages.
+ eventIndex = MsgWaitForMultipleObjects((DWORD)numberOfWaitHandles, &WaitHandles[0], FALSE, waitMs, QS_ALLINPUT);
+
+ if (eventIndex != WAIT_FAILED)
+ {
+ if (eventIndex == WAIT_TIMEOUT)
+ continue;
+
+ // TBD: Does this ever apply?
+ OVR_ASSERT(eventIndex < WAIT_ABANDONED_0);
+
+ if (eventIndex == WAIT_OBJECT_0)
+ {
+ // Handle [0] services commands.
+ break;
+ }
+ else if (eventIndex == WAIT_OBJECT_0 + numberOfWaitHandles)
+ {
+ // Handle Windows messages.
+ pStatusObject->ProcessMessages();
+ }
+ else
+ {
+ // Notify waiting device that its event is signaled.
+ unsigned i = eventIndex - WAIT_OBJECT_0;
+ OVR_ASSERT(i < numberOfWaitHandles);
+ if (WaitNotifiers[i])
+ WaitNotifiers[i]->OnOverlappedEvent(WaitHandles[i]);
+ }
+ }
+
+ } while(eventIndex != WAIT_FAILED);
+
+ }
+ }
+
+ pStatusObject->ShutDown();
+
+ LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%X).\n", GetThreadId());
+ return 0;
+}
+
+bool DeviceManagerThread::AddOverlappedEvent(Notifier* notify, HANDLE hevent)
+{
+ WaitNotifiers.PushBack(notify);
+ WaitHandles.PushBack(hevent);
+
+ OVR_ASSERT(WaitNotifiers.GetSize() <= MAXIMUM_WAIT_OBJECTS);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveOverlappedEvent(Notifier* notify, HANDLE hevent)
+{
+ // [0] is reserved for thread commands with notify of null, but we still
+ // can use this function to remove it.
+ for (UPInt i = 0; i < WaitNotifiers.GetSize(); i++)
+ {
+ if ((WaitNotifiers[i] == notify) && (WaitHandles[i] == hevent))
+ {
+ WaitNotifiers.RemoveAt(i);
+ WaitHandles.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
+{
+ TicksNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
+ {
+ if (TicksNotifiers[i] == notify)
+ {
+ TicksNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DeviceManagerThread::AddMessageNotifier(Notifier* notify)
+{
+ MessageNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveMessageNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++)
+ {
+ if (MessageNotifiers[i] == notify)
+ {
+ MessageNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DeviceManagerThread::OnMessage(MessageType type, const String& devicePath)
+{
+ Notifier::DeviceMessageType notifierMessageType = Notifier::DeviceMessage_DeviceAdded;
+ if (type == DeviceAdded)
+ {
+ }
+ else if (type == DeviceRemoved)
+ {
+ notifierMessageType = Notifier::DeviceMessage_DeviceRemoved;
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+
+ bool error = false;
+ bool deviceFound = false;
+ for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++)
+ {
+ if (MessageNotifiers[i] &&
+ MessageNotifiers[i]->OnDeviceMessage(notifierMessageType, devicePath, &error))
+ {
+ // The notifier belonged to a device with the specified device name so we're done.
+ deviceFound = true;
+ break;
+ }
+ }
+ if (type == DeviceAdded && !deviceFound)
+ {
+ Lock::Locker devMgrLock(&DevMgrLock);
+ // a new device was connected. Go through all device factories and
+ // try to detect the device using HIDDeviceDesc.
+ HIDDeviceDesc devDesc;
+ if (pDeviceMgr->GetHIDDeviceDesc(devicePath, &devDesc))
+ {
+ Lock::Locker deviceLock(pDeviceMgr->GetLock());
+ DeviceFactory* factory = pDeviceMgr->Factories.GetFirst();
+ while(!pDeviceMgr->Factories.IsNull(factory))
+ {
+ if (factory->DetectHIDDevice(pDeviceMgr, devDesc))
+ {
+ deviceFound = true;
+ break;
+ }
+ factory = factory->pNext;
+ }
+ }
+ }
+
+ if (!deviceFound && strstr(devicePath.ToCStr(), "#OVR00"))
+ {
+ Ptr<DeviceManager> pmgr;
+ {
+ Lock::Locker devMgrLock(&DevMgrLock);
+ pmgr = pDeviceMgr;
+ }
+ // HMD plugged/unplugged
+ // This is not a final solution to enumerate HMD devices and get
+ // a first available handle. This won't work with multiple rifts.
+ // @TODO (!AB)
+ pmgr->EnumerateDevices<HMDDevice>();
+ }
+
+ return !error;
+}
+
+void DeviceManagerThread::DetachDeviceManager()
+{
+ Lock::Locker devMgrLock(&DevMgrLock);
+ pDeviceMgr = NULL;
+}
+
+} // namespace Win32
+
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+
+// Creates a new DeviceManager and initializes OVR.
+DeviceManager* DeviceManager::Create()
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Win32::DeviceManager> manager = *new Win32::DeviceManager;
+
+ if (manager)
+ {
+ if (manager->Initialize(0))
+ {
+ manager->AddFactory(&SensorDeviceFactory::Instance);
+ manager->AddFactory(&LatencyTestDeviceFactory::Instance);
+ manager->AddFactory(&Win32::HMDDeviceFactory::Instance);
+
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+
+ }
+
+ return manager.GetPtr();
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_Win32_DeviceManager.h b/LibOVR/Src/OVR_Win32_DeviceManager.h new file mode 100644 index 0000000..3b86c67 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceManager.h @@ -0,0 +1,146 @@ +/************************************************************************************
+
+Filename : OVR_Win32_DeviceManager.h
+Content : Win32-specific DeviceManager header.
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_DeviceManager_h
+#define OVR_Win32_DeviceManager_h
+
+#include "OVR_DeviceImpl.h"
+#include "OVR_Win32_DeviceStatus.h"
+
+#include "Kernel/OVR_Timer.h"
+
+
+namespace OVR { namespace Win32 {
+
+class DeviceManagerThread;
+
+//-------------------------------------------------------------------------------------
+// ***** Win32 DeviceManager
+
+class DeviceManager : public DeviceManagerImpl
+{
+public:
+ DeviceManager();
+ ~DeviceManager();
+
+ // Initialize/Shutdowncreate and shutdown manger thread.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual ThreadCommandQueue* GetThreadQueue();
+ virtual ThreadId GetThreadId() const;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ // Fills HIDDeviceDesc by using the path.
+ // Returns 'true' if successful, 'false' otherwise.
+ bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const;
+
+ Ptr<DeviceManagerThread> pThread;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Device Manager Background Thread
+
+class DeviceManagerThread : public Thread, public ThreadCommandQueue, public DeviceStatus::Notifier
+{
+ friend class DeviceManager;
+ enum { ThreadStackSize = 32 * 1024 };
+public:
+ DeviceManagerThread(DeviceManager* pdevMgr);
+ ~DeviceManagerThread();
+
+ virtual int Run();
+
+ // ThreadCommandQueue notifications for CommandEvent handling.
+ virtual void OnPushNonEmpty_Locked() { ::SetEvent(hCommandEvent); }
+ virtual void OnPopEmpty_Locked() { ::ResetEvent(hCommandEvent); }
+
+
+ // Notifier used for different updates (EVENT or regular timing or messages).
+ class Notifier
+ {
+ public:
+ // Called when overlapped I/O handle is signaled.
+ virtual void OnOverlappedEvent(HANDLE hevent) { OVR_UNUSED1(hevent); }
+
+ // Called when timing ticks are updated.
+ // Returns the largest number of microseconds this function can
+ // wait till next call.
+ virtual UInt64 OnTicks(UInt64 ticksMks)
+ { OVR_UNUSED1(ticksMks); return Timer::MksPerSecond * 1000; }
+
+ enum DeviceMessageType
+ {
+ DeviceMessage_DeviceAdded = 0,
+ DeviceMessage_DeviceRemoved = 1,
+ };
+
+ // Called to notify device object.
+ virtual bool OnDeviceMessage(DeviceMessageType messageType,
+ const String& devicePath,
+ bool* error)
+ { OVR_UNUSED3(messageType, devicePath, error); return false; }
+ };
+
+
+ // Adds device's OVERLAPPED structure for I/O.
+ // After it's added, Overlapped object will be signaled if a message arrives.
+ bool AddOverlappedEvent(Notifier* notify, HANDLE hevent);
+ bool RemoveOverlappedEvent(Notifier* notify, HANDLE hevent);
+
+ // Add notifier that will be called at regular intervals.
+ bool AddTicksNotifier(Notifier* notify);
+ bool RemoveTicksNotifier(Notifier* notify);
+
+ bool AddMessageNotifier(Notifier* notify);
+ bool RemoveMessageNotifier(Notifier* notify);
+
+ // DeviceStatus::Notifier interface.
+ bool OnMessage(MessageType type, const String& devicePath);
+
+ void DetachDeviceManager();
+
+private:
+ bool threadInitialized() { return hCommandEvent != 0; }
+
+ // Event used to wake us up thread commands are enqueued.
+ HANDLE hCommandEvent;
+
+ // Event notifications for devices whose OVERLAPPED I/O we service.
+ // This list is modified through AddDeviceOverlappedEvent.
+ // WaitHandles[0] always == hCommandEvent, with null device.
+ Array<HANDLE> WaitHandles;
+ Array<Notifier*> WaitNotifiers;
+
+ // Ticks notifiers - used for time-dependent events such as keep-alive.
+ Array<Notifier*> TicksNotifiers;
+
+ // Message notifiers.
+ Array<Notifier*> MessageNotifiers;
+
+ // Object that manages notifications originating from Windows messages.
+ Ptr<DeviceStatus> pStatusObject;
+
+ Lock DevMgrLock;
+ // pDeviceMgr should be accessed under DevMgrLock
+ DeviceManager* pDeviceMgr; // back ptr, no addref.
+};
+
+}} // namespace Win32::OVR
+
+#endif // OVR_Win32_DeviceManager_h
diff --git a/LibOVR/Src/OVR_Win32_DeviceStatus.cpp b/LibOVR/Src/OVR_Win32_DeviceStatus.cpp new file mode 100644 index 0000000..80ec4f9 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceStatus.cpp @@ -0,0 +1,350 @@ +/************************************************************************************
+
+Filename : OVR_Win32_DeviceStatus.cpp
+Content : Win32 implementation of DeviceStatus.
+Created : January 24, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_DeviceStatus.h"
+
+#include "OVR_Win32_HIDDevice.h"
+
+#include "Kernel/OVR_Log.h"
+
+#include <dbt.h>
+
+namespace OVR { namespace Win32 {
+
+static TCHAR windowClassName[] = TEXT("LibOVR_DeviceStatus_WindowClass");
+
+//-------------------------------------------------------------------------------------
+DeviceStatus::DeviceStatus(Notifier* const pClient)
+ : pNotificationClient(pClient), LastTimerId(0)
+{
+}
+
+bool DeviceStatus::Initialize()
+{
+
+ WNDCLASS wndClass;
+ wndClass.style = CS_HREDRAW | CS_VREDRAW;
+ wndClass.lpfnWndProc = WindowsMessageCallback;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = 0;
+ wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = windowClassName;
+
+ if (!RegisterClass(&wndClass))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to register window class."));
+ return false;
+ }
+
+ // We're going to create a 'message-only' window. This will be hidden, can't be enumerated etc.
+ // To do this we supply 'HWND_MESSAGE' as the hWndParent.
+ // http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
+ hMessageWindow = CreateWindow( windowClassName,
+ windowClassName,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ HWND_MESSAGE,
+ NULL,
+ 0,
+ this); // Pass this object via the CREATESTRUCT mechanism
+ // so that we can attach it to the window user data.
+
+ if (hMessageWindow == NULL)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to create window."));
+ return false;
+ }
+
+ // According to MS, topmost windows receive WM_DEVICECHANGE faster.
+ ::SetWindowPos(hMessageWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ UpdateWindow(hMessageWindow);
+
+
+ // Register notification for additional HID messages.
+ HIDDeviceManager* hidDeviceManager = new HIDDeviceManager(NULL);
+ HidGuid = hidDeviceManager->GetHIDGuid();
+ hidDeviceManager->Release();
+
+ DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
+
+ ZeroMemory(¬ificationFilter, sizeof(notificationFilter));
+ notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+ notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ //notificationFilter.dbcc_classguid = hidguid;
+
+ // We need DEVICE_NOTIFY_ALL_INTERFACE_CLASSES to detect
+ // HDMI plug/unplug events.
+ hDeviceNotify = RegisterDeviceNotification(
+ hMessageWindow,
+ ¬ificationFilter,
+ DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE);
+
+ if (hDeviceNotify == NULL)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to register for device notifications."));
+ return false;
+ }
+
+ return true;
+}
+
+void DeviceStatus::ShutDown()
+{
+ OVR_ASSERT(hMessageWindow);
+
+ if (!UnregisterDeviceNotification(hDeviceNotify))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to unregister device notification."));
+ }
+
+ PostMessage(hMessageWindow, WM_CLOSE, 0, 0);
+
+ while (hMessageWindow != NULL)
+ {
+ ProcessMessages();
+ Sleep(1);
+ }
+
+ if (!UnregisterClass(windowClassName, NULL))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to unregister window class."));
+ }
+}
+
+DeviceStatus::~DeviceStatus()
+{
+ OVR_ASSERT_LOG(hMessageWindow == NULL, ("Need to call 'ShutDown' from DeviceManagerThread."));
+}
+
+void DeviceStatus::ProcessMessages()
+{
+ OVR_ASSERT_LOG(hMessageWindow != NULL, ("Need to call 'Initialize' before first use."));
+
+ MSG msg;
+
+ // Note WM_DEVICECHANGED messages are dispatched but not retrieved by PeekMessage.
+ // I think this is because they are pending, non-queued messages.
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+bool DeviceStatus::MessageCallback(WORD messageType, const String& devicePath)
+{
+ bool rv = true;
+ if (messageType == DBT_DEVICEARRIVAL)
+ {
+ rv = pNotificationClient->OnMessage(Notifier::DeviceAdded, devicePath);
+ }
+ else if (messageType == DBT_DEVICEREMOVECOMPLETE)
+ {
+ pNotificationClient->OnMessage(Notifier::DeviceRemoved, devicePath);
+ }
+ else
+ {
+ OVR_ASSERT(0);
+ }
+ return rv;
+}
+
+void DeviceStatus::CleanupRecoveryTimer(UPInt index)
+{
+ ::KillTimer(hMessageWindow, RecoveryTimers[index].TimerId);
+ RecoveryTimers.RemoveAt(index);
+}
+
+DeviceStatus::RecoveryTimerDesc*
+DeviceStatus::FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex)
+{
+ for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i)
+ {
+ RecoveryTimerDesc* pdesc = &RecoveryTimers[i];
+ if (pdesc->TimerId == timerId)
+ {
+ *pindex = i;
+ return pdesc;
+ }
+ }
+ return NULL;
+}
+
+void DeviceStatus::FindAndCleanupRecoveryTimer(const String& devicePath)
+{
+ for (UPInt i = 0, n = RecoveryTimers.GetSize(); i < n; ++i)
+ {
+ RecoveryTimerDesc* pdesc = &RecoveryTimers[i];
+ if (pdesc->DevicePath.CompareNoCase(devicePath))
+ {
+ CleanupRecoveryTimer(i);
+ break;
+ }
+ }
+}
+
+LRESULT CALLBACK DeviceStatus::WindowsMessageCallback( HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CREATE:
+ {
+ // Setup window user data with device status object pointer.
+ LPCREATESTRUCT create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam);
+ void *lpCreateParam = create_struct->lpCreateParams;
+ DeviceStatus *pDeviceStatus = reinterpret_cast<DeviceStatus*>(lpCreateParam);
+
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pDeviceStatus));
+ }
+ return 0; // Return 0 for successfully handled WM_CREATE.
+
+ case WM_DEVICECHANGE:
+ {
+ WORD loword = LOWORD(wParam);
+
+ if (loword != DBT_DEVICEARRIVAL &&
+ loword != DBT_DEVICEREMOVECOMPLETE)
+ {
+ // Ignore messages other than device arrive and remove complete
+ // (we're not handling intermediate ones).
+ return TRUE; // Grant WM_DEVICECHANGE request.
+ }
+
+ DEV_BROADCAST_DEVICEINTERFACE* hdr;
+ hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam;
+
+ if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ {
+ // Ignore non interface device messages.
+ return TRUE; // Grant WM_DEVICECHANGE request.
+ }
+
+ LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ OVR_ASSERT(userData != NULL);
+
+ // Call callback on device messages object with the device path.
+ DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
+ String devicePath(hdr->dbcc_name);
+
+ // check if HID device caused the event...
+ if (pDeviceStatus->HidGuid == hdr->dbcc_classguid)
+ {
+ // check if recovery timer is already running; stop it and
+ // remove it, if so.
+ pDeviceStatus->FindAndCleanupRecoveryTimer(devicePath);
+
+ if (!pDeviceStatus->MessageCallback(loword, devicePath))
+ {
+ // hmmm.... unsuccessful
+ if (loword == DBT_DEVICEARRIVAL)
+ {
+ // Windows sometimes may return errors ERROR_SHARING_VIOLATION and
+ // ERROR_FILE_NOT_FOUND when trying to open an USB device via
+ // CreateFile. Need to start a recovery timer that will try to
+ // re-open the device again.
+ OVR_DEBUG_LOG(("Adding failed, recovering through a timer..."));
+ UINT_PTR tid = ::SetTimer(hwnd, ++pDeviceStatus->LastTimerId,
+ USBRecoveryTimeInterval, NULL);
+ RecoveryTimerDesc rtDesc;
+ rtDesc.TimerId = tid;
+ rtDesc.DevicePath = devicePath;
+ rtDesc.NumAttempts= 0;
+ pDeviceStatus->RecoveryTimers.PushBack(rtDesc);
+ // wrap around the timer counter, avoid timerId == 0...
+ if (pDeviceStatus->LastTimerId + 1 == 0)
+ pDeviceStatus->LastTimerId = 0;
+ }
+ }
+ }
+ // Check if Oculus HDMI device was plugged/unplugged, preliminary
+ // filtering. (is there any way to get GUID? !AB)
+ //else if (strstr(devicePath.ToCStr(), "DISPLAY#"))
+ else if (strstr(devicePath.ToCStr(), "#OVR00"))
+ {
+ pDeviceStatus->MessageCallback(loword, devicePath);
+ }
+ }
+ return TRUE; // Grant WM_DEVICECHANGE request.
+
+ case WM_TIMER:
+ {
+ if (wParam != 0)
+ {
+ LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ OVR_ASSERT(userData != NULL);
+
+ // Call callback on device messages object with the device path.
+ DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
+
+ // Check if we have recovery timer running (actually, we must be!)
+ UPInt rtIndex;
+ RecoveryTimerDesc* prtDesc = pDeviceStatus->FindRecoveryTimer(wParam, &rtIndex);
+ if (prtDesc)
+ {
+ if (pDeviceStatus->MessageCallback(DBT_DEVICEARRIVAL, prtDesc->DevicePath))
+ {
+ OVR_DEBUG_LOG(("Recovered, adding is successful, cleaning up the timer..."));
+ // now it is successful, kill the timer and cleanup
+ pDeviceStatus->CleanupRecoveryTimer(rtIndex);
+ }
+ else
+ {
+ if (++prtDesc->NumAttempts >= MaxUSBRecoveryAttempts)
+ {
+ OVR_DEBUG_LOG(("Failed to recover USB after %d attempts, path = '%s', aborting...",
+ prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr()));
+ pDeviceStatus->CleanupRecoveryTimer(rtIndex);
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("Failed to recover USB, %d attempts, path = '%s'",
+ prtDesc->NumAttempts, prtDesc->DevicePath.ToCStr()));
+ }
+ }
+ }
+ }
+ }
+ return 0;
+
+ case WM_CLOSE:
+ {
+ LONG_PTR userData = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ OVR_ASSERT(userData != NULL);
+ DeviceStatus* pDeviceStatus = (DeviceStatus*) userData;
+ pDeviceStatus->hMessageWindow = NULL;
+
+ DestroyWindow(hwnd);
+ }
+ return 0; // We processed the WM_CLOSE message.
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0; // We processed the WM_DESTROY message.
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+}} // namespace OVR::Win32
diff --git a/LibOVR/Src/OVR_Win32_DeviceStatus.h b/LibOVR/Src/OVR_Win32_DeviceStatus.h new file mode 100644 index 0000000..820e3a5 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_DeviceStatus.h @@ -0,0 +1,101 @@ +/************************************************************************************
+
+Filename : OVR_Win32_DeviceStatus.h
+Content : Win32-specific DeviceStatus header.
+Created : January 24, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_DeviceStatus_h
+#define OVR_Win32_DeviceStatus_h
+
+#include <windows.h>
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_Array.h"
+
+namespace OVR { namespace Win32 {
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceStatus
+//
+// DeviceStatus abstracts the handling of windows messages of interest for
+// example the WM_DEVICECHANGED message which occurs when a device is plugged/unplugged.
+// The device manager thread creates an instance of this class and passes its pointer
+// in the constructor. That thread is also responsible for periodically calling 'ProcessMessages'
+// to process queued windows messages. The client is notified via the 'OnMessage' method
+// declared in the 'DeviceMessages::Notifier' interface.
+class DeviceStatus : public RefCountBase<DeviceStatus>
+{
+public:
+
+ // Notifier used for device messages.
+ class Notifier
+ {
+ public:
+ enum MessageType
+ {
+ DeviceAdded = 0,
+ DeviceRemoved = 1,
+ };
+
+ virtual bool OnMessage(MessageType type, const String& devicePath)
+ { OVR_UNUSED2(type, devicePath); return true; }
+ };
+
+ DeviceStatus(Notifier* const pClient);
+ ~DeviceStatus();
+
+ void operator = (const DeviceStatus&); // No assignment implementation.
+
+ bool Initialize();
+ void ShutDown();
+
+ void ProcessMessages();
+
+private:
+ enum
+ {
+ MaxUSBRecoveryAttempts = 20,
+ USBRecoveryTimeInterval = 500 // ms
+ };
+ struct RecoveryTimerDesc
+ {
+ UINT_PTR TimerId;
+ String DevicePath;
+ unsigned NumAttempts;
+ };
+
+ static LRESULT CALLBACK WindowsMessageCallback( HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+
+ bool MessageCallback(WORD messageType, const String& devicePath);
+
+ void CleanupRecoveryTimer(UPInt index);
+ RecoveryTimerDesc* FindRecoveryTimer(UINT_PTR timerId, UPInt* pindex);
+ void FindAndCleanupRecoveryTimer(const String& devicePath);
+
+private: // data
+ Notifier* const pNotificationClient; // Don't reference count a back-pointer.
+
+ HWND hMessageWindow;
+ HDEVNOTIFY hDeviceNotify;
+
+ UINT_PTR LastTimerId;
+ Array<RecoveryTimerDesc> RecoveryTimers;
+
+ GUID HidGuid;
+};
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_DeviceStatus_h
diff --git a/LibOVR/Src/OVR_Win32_HIDDevice.cpp b/LibOVR/Src/OVR_Win32_HIDDevice.cpp new file mode 100644 index 0000000..8c04bcf --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HIDDevice.cpp @@ -0,0 +1,637 @@ +/************************************************************************************
+
+Filename : OVR_Win32_HIDDevice.cpp
+Content : Win32 HID device implementation.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_HIDDevice.h"
+#include "OVR_Win32_DeviceManager.h"
+
+#include "Kernel/OVR_System.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace Win32 {
+
+//-------------------------------------------------------------------------------------
+// HIDDevicePathWrapper is a simple class used to extract HID device file path
+// through SetupDiGetDeviceInterfaceDetail. We use a class since this is a bit messy.
+class HIDDevicePathWrapper
+{
+ SP_INTERFACE_DEVICE_DETAIL_DATA_A* pData;
+public:
+ HIDDevicePathWrapper() : pData(0) { }
+ ~HIDDevicePathWrapper() { if (pData) OVR_FREE(pData); }
+
+ const char* GetPath() const { return pData ? pData->DevicePath : 0; }
+
+ bool InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata);
+};
+
+bool HIDDevicePathWrapper::InitPathFromInterfaceData(HDEVINFO hdevInfoSet, SP_DEVICE_INTERFACE_DATA* pidata)
+{
+ DWORD detailSize = 0;
+ // SetupDiGetDeviceInterfaceDetailA returns "not enough buffer error code"
+ // doe size request. Just check valid size.
+ SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, NULL, 0, &detailSize, NULL);
+ if (!detailSize ||
+ ((pData = (SP_INTERFACE_DEVICE_DETAIL_DATA_A*)OVR_ALLOC(detailSize)) == 0))
+ return false;
+ pData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A);
+
+ if (!SetupDiGetDeviceInterfaceDetailA(hdevInfoSet, pidata, pData, detailSize, NULL, NULL))
+ return false;
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// **** Win32::DeviceManager
+
+HIDDeviceManager::HIDDeviceManager(DeviceManager* manager)
+ : Manager(manager)
+{
+ hHidLib = ::LoadLibraryA("hid.dll");
+ OVR_ASSERT_LOG(hHidLib, ("Couldn't load Win32 'hid.dll'."));
+
+ OVR_RESOLVE_HIDFUNC(HidD_GetHidGuid);
+ OVR_RESOLVE_HIDFUNC(HidD_SetNumInputBuffers);
+ OVR_RESOLVE_HIDFUNC(HidD_GetFeature);
+ OVR_RESOLVE_HIDFUNC(HidD_SetFeature);
+ OVR_RESOLVE_HIDFUNC(HidD_GetAttributes);
+ OVR_RESOLVE_HIDFUNC(HidD_GetManufacturerString);
+ OVR_RESOLVE_HIDFUNC(HidD_GetProductString);
+ OVR_RESOLVE_HIDFUNC(HidD_GetSerialNumberString);
+ OVR_RESOLVE_HIDFUNC(HidD_GetPreparsedData);
+ OVR_RESOLVE_HIDFUNC(HidD_FreePreparsedData);
+ OVR_RESOLVE_HIDFUNC(HidP_GetCaps);
+
+ if (HidD_GetHidGuid)
+ HidD_GetHidGuid(&HidGuid);
+}
+
+HIDDeviceManager::~HIDDeviceManager()
+{
+ ::FreeLibrary(hHidLib);
+}
+
+bool HIDDeviceManager::Initialize()
+{
+ return true;
+}
+
+void HIDDeviceManager::Shutdown()
+{
+ LogText("OVR::Win32::HIDDeviceManager - shutting down.\n");
+}
+
+bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
+{
+ HDEVINFO hdevInfoSet;
+ SP_DEVICE_INTERFACE_DATA interfaceData;
+ interfaceData.cbSize = sizeof(interfaceData);
+
+ // Get handle to info data set describing all available HIDs.
+ hdevInfoSet = SetupDiGetClassDevsA(&HidGuid, NULL, NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT);
+ if (hdevInfoSet == INVALID_HANDLE_VALUE)
+ return false;
+
+ for(int deviceIndex = 0;
+ SetupDiEnumDeviceInterfaces(hdevInfoSet, NULL, &HidGuid, deviceIndex, &interfaceData);
+ deviceIndex++)
+ {
+ // For each device, we extract its file path and open it to get attributes,
+ // such as vendor and product id. If anything goes wrong, we move onto next device.
+ HIDDevicePathWrapper pathWrapper;
+ if (!pathWrapper.InitPathFromInterfaceData(hdevInfoSet, &interfaceData))
+ continue;
+
+ // Look for the device to check if it is already opened.
+ Ptr<DeviceCreateDesc> existingDevice = Manager->FindDevice(pathWrapper.GetPath());
+ // if device exists and it is opened then most likely the CreateHIDFile
+ // will fail; therefore, we just set Enumerated to 'true' and continue.
+ if (existingDevice && existingDevice->pDevice)
+ {
+ existingDevice->Enumerated = true;
+ continue;
+ }
+
+ // open device in non-exclusive mode for detection...
+ HANDLE hidDev = CreateHIDFile(pathWrapper.GetPath(), false);
+ if (hidDev == INVALID_HANDLE_VALUE)
+ continue;
+
+ HIDDeviceDesc devDesc;
+ devDesc.Path = pathWrapper.GetPath();
+ if (initVendorProductVersion(hidDev, &devDesc) &&
+ enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) &&
+ initUsage(hidDev, &devDesc))
+ {
+ initStrings(hidDev, &devDesc);
+
+ // Construct minimal device that the visitor callback can get feature reports from.
+ Win32::HIDDevice device(this, hidDev);
+ enumVisitor->Visit(device, devDesc);
+ }
+
+ ::CloseHandle(hidDev);
+ }
+
+ SetupDiDestroyDeviceInfoList(hdevInfoSet);
+ return true;
+}
+
+bool HIDDeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const
+{
+ // open device in non-exclusive mode for detection...
+ HANDLE hidDev = CreateHIDFile(path, false);
+ if (hidDev == INVALID_HANDLE_VALUE)
+ return false;
+
+ pdevDesc->Path = path;
+ getFullDesc(hidDev, pdevDesc);
+
+ ::CloseHandle(hidDev);
+ return true;
+}
+
+OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
+{
+
+ Ptr<Win32::HIDDevice> device = *new Win32::HIDDevice(this);
+
+ if (device->HIDInitialize(path))
+ {
+ device->AddRef();
+ return device;
+ }
+
+ return NULL;
+}
+
+bool HIDDeviceManager::getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+
+ if (!initVendorProductVersion(hidDev, desc))
+ {
+ return false;
+ }
+
+ if (!initUsage(hidDev, desc))
+ {
+ return false;
+ }
+
+ initStrings(hidDev, desc);
+
+ return true;
+}
+
+bool HIDDeviceManager::initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+ HIDD_ATTRIBUTES attr;
+ attr.Size = sizeof(attr);
+ if (!HidD_GetAttributes(hidDev, &attr))
+ return false;
+ desc->VendorId = attr.VendorID;
+ desc->ProductId = attr.ProductID;
+ desc->VersionNumber = attr.VersionNumber;
+ return true;
+}
+
+bool HIDDeviceManager::initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+ bool result = false;
+ HIDP_CAPS caps;
+ HIDP_PREPARSED_DATA* preparsedData = 0;
+
+ if (!HidD_GetPreparsedData(hidDev, &preparsedData))
+ return false;
+
+ if (HidP_GetCaps(preparsedData, &caps) == HIDP_STATUS_SUCCESS)
+ {
+ desc->Usage = caps.Usage;
+ desc->UsagePage = caps.UsagePage;
+ result = true;
+ }
+ HidD_FreePreparsedData(preparsedData);
+ return result;
+}
+
+void HIDDeviceManager::initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const
+{
+ // Documentation mentions 126 as being the max for USB.
+ wchar_t strBuffer[196];
+
+ // HidD_Get*String functions return nothing in buffer on failure,
+ // so it's ok to do this without further error checking.
+ strBuffer[0] = 0;
+ HidD_GetManufacturerString(hidDev, strBuffer, sizeof(strBuffer));
+ desc->Manufacturer = strBuffer;
+
+ strBuffer[0] = 0;
+ HidD_GetProductString(hidDev, strBuffer, sizeof(strBuffer));
+ desc->Product = strBuffer;
+
+ strBuffer[0] = 0;
+ HidD_GetSerialNumberString(hidDev, strBuffer, sizeof(strBuffer));
+ desc->SerialNumber = strBuffer;
+}
+
+//-------------------------------------------------------------------------------------
+// **** Win32::HIDDevice
+
+HIDDevice::HIDDevice(HIDDeviceManager* manager)
+ : HIDManager(manager), inMinimalMode(false), Device(0), ReadRequested(false)
+{
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+}
+
+// This is a minimal constructor used during enumeration for us to pass
+// a HIDDevice to the visit function (so that it can query feature reports).
+HIDDevice::HIDDevice(HIDDeviceManager* manager, HANDLE device)
+ : HIDManager(manager), inMinimalMode(true), Device(device), ReadRequested(true)
+{
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+}
+
+HIDDevice::~HIDDevice()
+{
+ if (!inMinimalMode)
+ {
+ HIDShutdown();
+ }
+}
+
+bool HIDDevice::HIDInitialize(const String& path)
+{
+
+ DevDesc.Path = path;
+
+ if (!openDevice())
+ {
+ LogText("OVR::Win32::HIDDevice - Failed to open HIDDevice: ", path);
+ return false;
+ }
+
+
+ HIDManager->Manager->pThread->AddTicksNotifier(this);
+ HIDManager->Manager->pThread->AddMessageNotifier(this);
+
+ LogText("OVR::Win32::HIDDevice - Opened '%s'\n"
+ " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
+ DevDesc.Path.ToCStr(),
+ DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
+ DevDesc.SerialNumber.ToCStr());
+
+ return true;
+}
+
+bool HIDDevice::initInfo()
+{
+ // Device must have been successfully opened.
+ OVR_ASSERT(Device);
+
+ // Get report lengths.
+ HIDP_PREPARSED_DATA* preparsedData = 0;
+ if (!HIDManager->HidD_GetPreparsedData(Device, &preparsedData))
+ {
+ return false;
+ }
+
+ HIDP_CAPS caps;
+ if (HIDManager->HidP_GetCaps(preparsedData, &caps) != HIDP_STATUS_SUCCESS)
+ {
+ HIDManager->HidD_FreePreparsedData(preparsedData);
+ return false;
+ }
+
+ InputReportBufferLength = caps.InputReportByteLength;
+ OutputReportBufferLength = caps.OutputReportByteLength;
+ FeatureReportBufferLength= caps.FeatureReportByteLength;
+ HIDManager->HidD_FreePreparsedData(preparsedData);
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ // Get device desc.
+ if (!HIDManager->getFullDesc(Device, &DevDesc))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDevice::openDevice()
+{
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ Device = HIDManager->CreateHIDFile(DevDesc.Path.ToCStr());
+ if (Device == INVALID_HANDLE_VALUE)
+ {
+ OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.",
+ ::GetLastError()));
+ Device = 0;
+ return false;
+ }
+
+ if (!HIDManager->HidD_SetNumInputBuffers(Device, 128))
+ {
+ OVR_ASSERT_LOG(false, ("Failed 'HidD_SetNumInputBuffers' while initializing device."));
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+
+ // Create a manual-reset non-signaled event.
+ ReadOverlapped.hEvent = ::CreateEvent(0, TRUE, FALSE, 0);
+
+ if (!ReadOverlapped.hEvent)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to create event."));
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+ if (!initInfo())
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info."));
+
+ ::CloseHandle(ReadOverlapped.hEvent);
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+ if (!initializeRead())
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get intialize read for HIDDevice."));
+
+ ::CloseHandle(ReadOverlapped.hEvent);
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ ::CloseHandle(Device);
+ Device = 0;
+ return false;
+ }
+
+ return true;
+}
+
+void HIDDevice::HIDShutdown()
+{
+
+ HIDManager->Manager->pThread->RemoveTicksNotifier(this);
+ HIDManager->Manager->pThread->RemoveMessageNotifier(this);
+
+ closeDevice();
+ LogText("OVR::Win32::HIDDevice - Closed '%s'\n", DevDesc.Path.ToCStr());
+}
+
+bool HIDDevice::initializeRead()
+{
+
+ if (!ReadRequested)
+ {
+ HIDManager->Manager->pThread->AddOverlappedEvent(this, ReadOverlapped.hEvent);
+ ReadRequested = true;
+ }
+
+ // Read resets the event...
+ while(::ReadFile(Device, ReadBuffer, InputReportBufferLength, 0, &ReadOverlapped))
+ {
+ processReadResult();
+ }
+
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ // Some other error (such as unplugged).
+ closeDeviceOnIOError();
+ return false;
+ }
+
+ return true;
+}
+
+bool HIDDevice::processReadResult()
+{
+
+ OVR_ASSERT(ReadRequested);
+
+ DWORD bytesRead = 0;
+
+ if (GetOverlappedResult(Device, &ReadOverlapped, &bytesRead, FALSE))
+ {
+ // We've got data.
+ if (Handler)
+ {
+ Handler->OnInputReport(ReadBuffer, bytesRead);
+ }
+
+ // TBD: Not needed?
+ // Event should be reset by Read call...
+ ReadOverlapped.Pointer = 0;
+ ReadOverlapped.Internal = 0;
+ ReadOverlapped.InternalHigh = 0;
+ return true;
+ }
+ else
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ closeDeviceOnIOError();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+void HIDDevice::closeDevice()
+{
+ if (ReadRequested)
+ {
+ HIDManager->Manager->pThread->RemoveOverlappedEvent(this, ReadOverlapped.hEvent);
+ ReadRequested = false;
+ // Must call this to avoid Win32 assertion; CloseHandle is not enough.
+ ::CancelIo(Device);
+ }
+
+ ::CloseHandle(ReadOverlapped.hEvent);
+ memset(&ReadOverlapped, 0, sizeof(OVERLAPPED));
+
+ ::CloseHandle(Device);
+ Device = 0;
+}
+
+void HIDDevice::closeDeviceOnIOError()
+{
+ LogText("OVR::Win32::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr());
+ closeDevice();
+}
+
+bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
+{
+ if (!ReadRequested)
+ return false;
+
+ return HIDManager->HidD_SetFeature(Device, data, (ULONG) length) != FALSE;
+}
+
+bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
+{
+ if (!ReadRequested)
+ return false;
+
+ return HIDManager->HidD_GetFeature(Device, data, (ULONG) length) != FALSE;
+}
+
+void HIDDevice::OnOverlappedEvent(HANDLE hevent)
+{
+ OVR_UNUSED(hevent);
+ OVR_ASSERT(hevent == ReadOverlapped.hEvent);
+
+ if (processReadResult())
+ {
+ // Proceed to read again.
+ initializeRead();
+ }
+}
+
+UInt64 HIDDevice::OnTicks(UInt64 ticksMks)
+{
+ if (Handler)
+ {
+ return Handler->OnTicks(ticksMks);
+ }
+
+ return DeviceManagerThread::Notifier::OnTicks(ticksMks);
+}
+
+bool HIDDevice::OnDeviceMessage(DeviceMessageType messageType,
+ const String& devicePath,
+ bool* error)
+{
+
+ // Is this the correct device?
+ if (DevDesc.Path.CompareNoCase(devicePath) != 0)
+ {
+ return false;
+ }
+
+ if (messageType == DeviceMessage_DeviceAdded && !Device)
+ {
+ // A closed device has been re-added. Try to reopen.
+ if (!openDevice())
+ {
+ LogError("OVR::Win32::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", devicePath.ToCStr());
+ *error = true;
+ return true;
+ }
+
+ LogText("OVR::Win32::HIDDevice - Reopened device '%s'\n", devicePath.ToCStr());
+ }
+
+ HIDHandler::HIDDeviceMessageType handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceAdded;
+ if (messageType == DeviceMessage_DeviceAdded)
+ {
+ }
+ else if (messageType == DeviceMessage_DeviceRemoved)
+ {
+ handlerMessageType = HIDHandler::HIDDeviceMessage_DeviceRemoved;
+ }
+ else
+ {
+ OVR_ASSERT(0);
+ }
+
+ if (Handler)
+ {
+ Handler->OnDeviceMessage(handlerMessageType);
+ }
+
+ *error = false;
+ return true;
+}
+
+HIDDeviceManager* HIDDeviceManager::CreateInternal(Win32::DeviceManager* devManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Win32::HIDDeviceManager> manager = *new Win32::HIDDeviceManager(devManager);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace Win32
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new HIDDeviceManager and initializes OVR.
+HIDDeviceManager* HIDDeviceManager::Create()
+{
+ OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet."));
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Win32::HIDDeviceManager> manager = *new Win32::HIDDeviceManager(NULL);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_Win32_HIDDevice.h b/LibOVR/Src/OVR_Win32_HIDDevice.h new file mode 100644 index 0000000..efe308d --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HIDDevice.h @@ -0,0 +1,194 @@ +/************************************************************************************
+
+Filename : OVR_Win32_HIDDevice.h
+Content : Win32 HID device implementation.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_HIDDevice_h
+#define OVR_Win32_HIDDevice_h
+
+#include "OVR_HIDDevice.h"
+#include "OVR_Win32_DeviceManager.h"
+
+#include <windows.h>
+#include <setupapi.h>
+
+//-------------------------------------------------------------------------------------
+// Define needed "hidsdi.h" functionality to avoid requiring DDK installation.
+// #include "hidsdi.h"
+
+#ifndef _HIDSDI_H
+#define _HIDSDI_H
+#include <pshpack4.h>
+
+#define HIDP_STATUS_SUCCESS (0x11 << 16)
+struct HIDP_PREPARSED_DATA;
+
+struct HIDD_ATTRIBUTES
+{
+ ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES)
+ USHORT VendorID;
+ USHORT ProductID;
+ USHORT VersionNumber;
+};
+
+struct HIDP_CAPS
+{
+ USHORT Usage;
+ USHORT UsagePage;
+ USHORT InputReportByteLength;
+ USHORT OutputReportByteLength;
+ USHORT FeatureReportByteLength;
+ USHORT Reserved[17];
+
+ USHORT NumberLinkCollectionNodes;
+ USHORT NumberInputButtonCaps;
+ USHORT NumberInputValueCaps;
+ USHORT NumberInputDataIndices;
+ USHORT NumberOutputButtonCaps;
+ USHORT NumberOutputValueCaps;
+ USHORT NumberOutputDataIndices;
+ USHORT NumberFeatureButtonCaps;
+ USHORT NumberFeatureValueCaps;
+ USHORT NumberFeatureDataIndices;
+};
+
+#include <poppack.h>
+#endif
+
+
+namespace OVR { namespace Win32 {
+
+class HIDDeviceManager;
+class DeviceManager;
+
+//-------------------------------------------------------------------------------------
+// ***** Win32 HIDDevice
+
+class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
+{
+public:
+
+ HIDDevice(HIDDeviceManager* manager);
+
+ // This is a minimal constructor used during enumeration for us to pass
+ // a HIDDevice to the visit function (so that it can query feature reports).
+ HIDDevice(HIDDeviceManager* manager, HANDLE device);
+
+ ~HIDDevice();
+
+ bool HIDInitialize(const String& path);
+ void HIDShutdown();
+
+ // OVR::HIDDevice
+ bool SetFeatureReport(UByte* data, UInt32 length);
+ bool GetFeatureReport(UByte* data, UInt32 length);
+
+
+ // DeviceManagerThread::Notifier
+ void OnOverlappedEvent(HANDLE hevent);
+ UInt64 OnTicks(UInt64 ticksMks);
+ bool OnDeviceMessage(DeviceMessageType messageType, const String& devicePath, bool* error);
+
+private:
+ bool openDevice();
+ bool initInfo();
+ bool initializeRead();
+ bool processReadResult();
+ void closeDevice();
+ void closeDeviceOnIOError();
+
+ bool inMinimalMode;
+ HIDDeviceManager* HIDManager;
+ HANDLE Device;
+ HIDDeviceDesc DevDesc;
+
+ OVERLAPPED ReadOverlapped;
+ bool ReadRequested;
+
+ enum { ReadBufferSize = 96 };
+ UByte ReadBuffer[ReadBufferSize];
+
+ UInt16 InputReportBufferLength;
+ UInt16 OutputReportBufferLength;
+ UInt16 FeatureReportBufferLength;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Win32 HIDDeviceManager
+
+class HIDDeviceManager : public OVR::HIDDeviceManager
+{
+ friend class HIDDevice;
+public:
+
+ HIDDeviceManager(DeviceManager* manager);
+ virtual ~HIDDeviceManager();
+
+ virtual bool Initialize();
+ virtual void Shutdown();
+
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
+ virtual OVR::HIDDevice* Open(const String& path);
+
+ // Fills HIDDeviceDesc by using the path.
+ // Returns 'true' if successful, 'false' otherwise.
+ bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const;
+
+ GUID GetHIDGuid() { return HidGuid; }
+
+ static HIDDeviceManager* CreateInternal(DeviceManager* manager);
+
+private:
+
+ DeviceManager* Manager; // Back pointer can just be a raw pointer.
+
+ HMODULE hHidLib;
+ GUID HidGuid;
+
+ // Macros to declare and resolve needed functions from library.
+#define OVR_DECLARE_HIDFUNC(func, rettype, args) \
+typedef rettype (__stdcall *PFn_##func) args; \
+PFn_##func func;
+#define OVR_RESOLVE_HIDFUNC(func) \
+func = (PFn_##func)::GetProcAddress(hHidLib, #func)
+
+ OVR_DECLARE_HIDFUNC(HidD_GetHidGuid, void, (GUID *hidGuid));
+ OVR_DECLARE_HIDFUNC(HidD_SetNumInputBuffers, BOOLEAN, (HANDLE hidDev, ULONG numberBuffers));
+ OVR_DECLARE_HIDFUNC(HidD_GetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_SetFeature, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetAttributes, BOOLEAN, (HANDLE hidDev, HIDD_ATTRIBUTES *attributes));
+ OVR_DECLARE_HIDFUNC(HidD_GetManufacturerString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetProductString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetSerialNumberString, BOOLEAN, (HANDLE hidDev, PVOID buffer, ULONG bufferLength));
+ OVR_DECLARE_HIDFUNC(HidD_GetPreparsedData, BOOLEAN, (HANDLE hidDev, HIDP_PREPARSED_DATA **preparsedData));
+ OVR_DECLARE_HIDFUNC(HidD_FreePreparsedData, BOOLEAN, (HIDP_PREPARSED_DATA *preparsedData));
+ OVR_DECLARE_HIDFUNC(HidP_GetCaps, NTSTATUS,(HIDP_PREPARSED_DATA *preparsedData, HIDP_CAPS* caps));
+
+ HANDLE CreateHIDFile(const char* path, bool exclusiveAccess = true) const
+ {
+ return ::CreateFileA(path, GENERIC_WRITE|GENERIC_READ,
+ (!exclusiveAccess) ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0x0,
+ NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ }
+
+ // Helper functions to fill in HIDDeviceDesc from open device handle.
+ bool initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const;
+ bool initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const;
+ void initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const;
+
+ bool getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const;
+};
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_HIDDevice_h
diff --git a/LibOVR/Src/OVR_Win32_HMDDevice.cpp b/LibOVR/Src/OVR_Win32_HMDDevice.cpp new file mode 100644 index 0000000..095d3a8 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HMDDevice.cpp @@ -0,0 +1,418 @@ +/************************************************************************************
+
+Filename : OVR_Win32_HMDDevice.cpp
+Content : Win32 Interface to HMD - detects HMD display
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_HMDDevice.h"
+
+#include "OVR_Win32_DeviceManager.h"
+
+#include <tchar.h>
+
+namespace OVR { namespace Win32 {
+
+//-------------------------------------------------------------------------------------
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory,
+ const String& deviceId, const String& displayDeviceName)
+ : DeviceCreateDesc(factory, Device_HMD),
+ DeviceId(deviceId), DisplayDeviceName(displayDeviceName),
+ DesktopX(0), DesktopY(0), Contents(0),
+ HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0)
+{
+}
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, Device_HMD),
+ DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName),
+ DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents),
+ HResolution(other.HResolution), VResolution(other.VResolution),
+ HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize)
+{
+}
+
+HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const
+{
+ if ((other.Type != Device_HMD) || (other.pFactory != pFactory))
+ return Match_None;
+
+ // There are several reasons we can come in here:
+ // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc
+ // - Require exact device DeviceId/DeviceName match
+ // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc
+ // - This DeviceId is empty; becomes candidate
+ // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc
+ // - This other.DeviceId is empty; becomes candidate
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ if ((DeviceId == s2.DeviceId) &&
+ (DisplayDeviceName == s2.DisplayDeviceName))
+ {
+ // Non-null DeviceId may match while size is different if screen size was overwritten
+ // by SensorDisplayInfo in prior iteration.
+ if (!DeviceId.IsEmpty() ||
+ ((HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize)) )
+ {
+ *pcandidate = 0;
+ return Match_Found;
+ }
+ }
+
+
+ // DisplayInfo takes precedence, although we try to match it first.
+ if ((HResolution == s2.HResolution) &&
+ (VResolution == s2.VResolution) &&
+ (HScreenSize == s2.HScreenSize) &&
+ (VScreenSize == s2.VScreenSize))
+ {
+ if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ *pcandidate = 0;
+ return Match_Found;
+ }
+
+ // SensorDisplayInfo may override resolution settings, so store as candidate.
+ if (s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+ // OTHER HMD Monitor desc may initialize DeviceName/Id
+ else if (DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ return Match_None;
+}
+
+
+bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other,
+ bool* newDeviceFlag)
+{
+ // This candidate was the the "best fit" to apply sensor DisplayInfo to.
+ OVR_ASSERT(other.Type == Device_HMD);
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ // Force screen size on resolution from SensorDisplayInfo.
+ // We do this because USB detection is more reliable as compared to HDMI EDID,
+ // which may be corrupted by splitter reporting wrong monitor
+ if (s2.DeviceId.IsEmpty())
+ {
+ HScreenSize = s2.HScreenSize;
+ VScreenSize = s2.VScreenSize;
+ Contents |= Contents_Screen;
+
+ if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion)
+ {
+ memcpy(DistortionK, s2.DistortionK, sizeof(float)*4);
+ Contents |= Contents_Distortion;
+ }
+ DeviceId = s2.DeviceId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else if (DeviceId.IsEmpty())
+ {
+ DeviceId = s2.DeviceId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else
+ {
+ if (newDeviceFlag) *newDeviceFlag = false;
+ }
+
+ return true;
+}
+
+bool HMDDeviceCreateDesc::MatchDevice(const String& path)
+{
+ return DeviceId.CompareNoCase(path) == 0;
+}
+
+//-------------------------------------------------------------------------------------
+
+
+const wchar_t* FormatDisplayStateFlags(wchar_t* buff, int length, DWORD flags)
+{
+ buff[0] = 0;
+ if (flags & DISPLAY_DEVICE_ACTIVE)
+ wcscat_s(buff, length, L"Active ");
+ if (flags & DISPLAY_DEVICE_MIRRORING_DRIVER)
+ wcscat_s(buff, length, L"Mirroring_Driver ");
+ if (flags & DISPLAY_DEVICE_MODESPRUNED)
+ wcscat_s(buff, length, L"ModesPruned ");
+ if (flags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ wcscat_s(buff, length, L"Primary ");
+ if (flags & DISPLAY_DEVICE_REMOVABLE)
+ wcscat_s(buff, length, L"Removable ");
+ if (flags & DISPLAY_DEVICE_VGA_COMPATIBLE)
+ wcscat_s(buff, length, L"VGA_Compatible ");
+ return buff;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Callback for monitor enumeration to store all the monitor handles
+
+// Used to capture all the active monitor handles
+struct MonitorSet
+{
+ enum { MaxMonitors = 8 };
+ HMONITOR Monitors[MaxMonitors];
+ int MonitorCount;
+};
+
+BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ MonitorSet* monitorSet = (MonitorSet*)dwData;
+ if (monitorSet->MonitorCount > MonitorSet::MaxMonitors)
+ return FALSE;
+
+ monitorSet->Monitors[monitorSet->MonitorCount] = hMonitor;
+ monitorSet->MonitorCount++;
+ return TRUE;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceFactory
+
+HMDDeviceFactory HMDDeviceFactory::Instance;
+
+void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+ MonitorSet monitors;
+ monitors.MonitorCount = 0;
+ // Get all the monitor handles
+ EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&monitors);
+
+ bool foundHMD = false;
+
+ // DeviceManager* manager = getManager();
+ DISPLAY_DEVICE dd, ddm;
+ UINT i, j;
+
+ for (i = 0;
+ (ZeroMemory(&dd, sizeof(dd)), dd.cb = sizeof(dd),
+ EnumDisplayDevices(0, i, &dd, 0)) != 0; i++)
+ {
+
+ /*
+ wchar_t buff[500], flagsBuff[200];
+
+ swprintf_s(buff, 500, L"\nDEV: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n",
+ dd.DeviceName, dd.DeviceString,
+ dd.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, dd.StateFlags),
+ dd.DeviceID, dd.DeviceKey);
+ ::OutputDebugString(buff);
+ */
+
+ for (j = 0;
+ (ZeroMemory(&ddm, sizeof(ddm)), ddm.cb = sizeof(ddm),
+ EnumDisplayDevices(dd.DeviceName, j, &ddm, 0)) != 0; j++)
+ {
+ /*
+ wchar_t mbuff[500];
+ swprintf_s(mbuff, 500, L"MON: \"%s\" \"%s\" 0x%08x=%s\n \"%s\" \"%s\"\n",
+ ddm.DeviceName, ddm.DeviceString,
+ ddm.StateFlags, FormatDisplayStateFlags(flagsBuff, 200, ddm.StateFlags),
+ ddm.DeviceID, ddm.DeviceKey);
+ ::OutputDebugString(mbuff);
+ */
+
+ // Our monitor hardware has string "RTD2205" in it
+ // Nate's device "CVT0003"
+ if (wcsstr(ddm.DeviceID, L"RTD2205") ||
+ wcsstr(ddm.DeviceID, L"CVT0003") ||
+ wcsstr(ddm.DeviceID, L"MST0030") ||
+ wcsstr(ddm.DeviceID, L"OVR00") ) // Part of Oculus EDID.
+ {
+ String deviceId(ddm.DeviceID);
+ String displayDeviceName(ddm.DeviceName);
+
+ // The default monitor coordinates
+ int mx = 0;
+ int my = 0;
+ int mwidth = 1280;
+ int mheight = 800;
+
+ // Find the matching MONITORINFOEX for this device so we can get the
+ // screen coordinates
+ MONITORINFOEX info;
+ for (int m=0; m < monitors.MonitorCount; m++)
+ {
+ info.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfo(monitors.Monitors[m], &info);
+ if (_tcsstr(ddm.DeviceName, info.szDevice) == ddm.DeviceName)
+ { // If the device name starts with the monitor name
+ // then we found the matching DISPLAY_DEVICE and MONITORINFO
+ // so we can gather the monitor coordinates
+ mx = info.rcMonitor.left;
+ my = info.rcMonitor.top;
+ //mwidth = info.rcMonitor.right - info.rcMonitor.left;
+ //mheight = info.rcMonitor.bottom - info.rcMonitor.top;
+ break;
+ }
+ }
+
+ HMDDeviceCreateDesc hmdCreateDesc(this, deviceId, displayDeviceName);
+
+ if (hmdCreateDesc.Is7Inch())
+ {
+ // Physical dimension of SLA screen.
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f);
+ }
+ else
+ {
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f);
+ }
+
+ OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n",
+ deviceId.ToCStr(), displayDeviceName.ToCStr()));
+
+ // Notify caller about detected device. This will call EnumerateAddDevice
+ // if the this is the first time device was detected.
+ visitor.Visit(hmdCreateDesc);
+ foundHMD = true;
+ break;
+ }
+ }
+ }
+
+ // Real HMD device is not found; however, we still may have a 'fake' HMD
+ // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo.
+ // Need to find it and set 'Enumerated' to true to avoid Removal notification.
+ if (!foundHMD)
+ {
+ Ptr<DeviceCreateDesc> hmdDevDesc = getManager()->FindDevice("", Device_HMD);
+ if (hmdDevDesc)
+ hmdDevDesc->Enumerated = true;
+ }
+}
+
+DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance()
+{
+ return new HMDDevice(this);
+}
+
+bool HMDDeviceCreateDesc::Is7Inch() const
+{
+ return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch);
+}
+
+bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_HMD) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ bool is7Inch = Is7Inch();
+
+ OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength,
+ is7Inch ? "Oculus Rift DK1" : "Oculus Rift DK1-Prototype");
+ OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR");
+ info->Type = Device_HMD;
+ info->Version = 0;
+
+ // Display detection.
+ if (info->InfoClassType == Device_HMD)
+ {
+ HMDInfo* hmdInfo = static_cast<HMDInfo*>(info);
+
+ hmdInfo->DesktopX = DesktopX;
+ hmdInfo->DesktopY = DesktopY;
+ hmdInfo->HResolution = HResolution;
+ hmdInfo->VResolution = VResolution;
+ hmdInfo->HScreenSize = HScreenSize;
+ hmdInfo->VScreenSize = VScreenSize;
+ hmdInfo->VScreenCenter = VScreenSize * 0.5f;
+ hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable.
+ hmdInfo->LensSeparationDistance = 0.0635f;
+
+ if (Contents & Contents_Distortion)
+ {
+ memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4);
+ }
+ else
+ {
+ if (is7Inch)
+ {
+ // 7" screen.
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.22f;
+ hmdInfo->DistortionK[2] = 0.24f;
+ hmdInfo->EyeToScreenDistance = 0.041f;
+
+ hmdInfo->ChromaAbCorrection[0] = 0.996f;
+ hmdInfo->ChromaAbCorrection[1] = -0.004f;
+ hmdInfo->ChromaAbCorrection[2] = 1.014f;
+ hmdInfo->ChromaAbCorrection[3] = 0.0f;
+ }
+ else
+ {
+ hmdInfo->DistortionK[0] = 1.0f;
+ hmdInfo->DistortionK[1] = 0.18f;
+ hmdInfo->DistortionK[2] = 0.115f;
+ hmdInfo->EyeToScreenDistance = 0.0387f;
+ }
+ }
+
+ OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName),
+ DisplayDeviceName.ToCStr());
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDevice
+
+HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc)
+ : OVR::DeviceImpl<OVR::HMDDevice>(createDesc, 0)
+{
+}
+HMDDevice::~HMDDevice()
+{
+}
+
+bool HMDDevice::Initialize(DeviceBase* parent)
+{
+ pParent = parent;
+ return true;
+}
+void HMDDevice::Shutdown()
+{
+ pParent.Clear();
+}
+
+OVR::SensorDevice* HMDDevice::GetSensor()
+{
+ // Just return first sensor found since we have no way to match it yet.
+ OVR::SensorDevice* sensor = GetManager()->EnumerateDevices<SensorDevice>().CreateDevice();
+ if (sensor)
+ sensor->SetCoordinateFrame(SensorDevice::Coord_HMD);
+ return sensor;
+}
+
+}} // namespace OVR::Win32
+
+
diff --git a/LibOVR/Src/OVR_Win32_HMDDevice.h b/LibOVR/Src/OVR_Win32_HMDDevice.h new file mode 100644 index 0000000..f653de9 --- /dev/null +++ b/LibOVR/Src/OVR_Win32_HMDDevice.h @@ -0,0 +1,133 @@ +/************************************************************************************
+
+Filename : OVR_Win32_HMDDevice.h
+Content : Win32 HMDDevice implementation
+Created : September 21, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_HMDDevice_h
+#define OVR_Win32_HMDDevice_h
+
+#include "OVR_Win32_DeviceManager.h"
+
+namespace OVR { namespace Win32 {
+
+class HMDDevice;
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDeviceFactory enumerates attached Oculus HMD devices.
+//
+// This is currently done by matching monitor device strings.
+
+class HMDDeviceFactory : public DeviceFactory
+{
+public:
+ static HMDDeviceFactory Instance;
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+class HMDDeviceCreateDesc : public DeviceCreateDesc
+{
+ friend class HMDDevice;
+
+protected:
+ enum
+ {
+ Contents_Screen = 1,
+ Contents_Distortion = 2,
+ Contents_7Inch = 4,
+ };
+ String DeviceId;
+ String DisplayDeviceName;
+ int DesktopX, DesktopY;
+ unsigned Contents;
+ unsigned HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ float DistortionK[4];
+
+public:
+ HMDDeviceCreateDesc(DeviceFactory* factory,
+ const String& deviceId, const String& displayDeviceName);
+ HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new HMDDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const;
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& path);
+
+ virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
+ {
+ DesktopX = x;
+ DesktopY = y;
+ HResolution = hres;
+ VResolution = vres;
+ HScreenSize = hsize;
+ VScreenSize = vsize;
+ Contents |= Contents_Screen;
+ }
+ void SetDistortion(const float* dks)
+ {
+ for (int i = 0; i < 4; i++)
+ DistortionK[i] = dks[i];
+ Contents |= Contents_Distortion;
+ }
+
+ void Set7Inch() { Contents |= Contents_7Inch; }
+
+ bool Is7Inch() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then wrappig it in SensorFusion.
+
+class HMDDevice : public DeviceImpl<OVR::HMDDevice>
+{
+public:
+ HMDDevice(HMDDeviceCreateDesc* createDesc);
+ ~HMDDevice();
+
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Query associated sensor.
+ virtual OVR::SensorDevice* GetSensor();
+};
+
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_HMDDevice_h
+
diff --git a/LibOVR/Src/OVR_Win32_SensorDevice.cpp b/LibOVR/Src/OVR_Win32_SensorDevice.cpp new file mode 100644 index 0000000..ce02b9e --- /dev/null +++ b/LibOVR/Src/OVR_Win32_SensorDevice.cpp @@ -0,0 +1,47 @@ +/************************************************************************************
+
+Filename : OVR_Win32_SensorDevice.cpp
+Content : Win32 SensorDevice implementation
+Created : March 12, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "OVR_Win32_SensorDevice.h"
+
+#include "OVR_Win32_HMDDevice.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR { namespace Win32 {
+
+} // namespace Win32
+
+//-------------------------------------------------------------------------------------
+void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo
+ (const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor)
+{
+
+ Win32::HMDDeviceCreateDesc hmdCreateDesc(&Win32::HMDDeviceFactory::Instance, String(), String());
+ hmdCreateDesc.SetScreenParameters( 0, 0,
+ displayInfo.HResolution, displayInfo.VResolution,
+ displayInfo.HScreenSize, displayInfo.VScreenSize);
+
+ if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion)
+ hmdCreateDesc.SetDistortion(displayInfo.DistortionK);
+ if (displayInfo.HScreenSize > 0.14f)
+ hmdCreateDesc.Set7Inch();
+
+ visitor.Visit(hmdCreateDesc);
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_Win32_SensorDevice.h b/LibOVR/Src/OVR_Win32_SensorDevice.h new file mode 100644 index 0000000..730620e --- /dev/null +++ b/LibOVR/Src/OVR_Win32_SensorDevice.h @@ -0,0 +1,24 @@ +/************************************************************************************
+
+Filename : OVR_Win32_SensorDevice.h
+Content : Win32 SensorDevice implementation
+Created : March 12, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Win32_SensorDevice_h
+#define OVR_Win32_SensorDevice_h
+
+namespace OVR { namespace Win32 {
+
+}} // namespace OVR::Win32
+
+#endif // OVR_Win32_SensorDevice_h
+
diff --git a/LibOVR/Src/Util/Util_LatencyTest.cpp b/LibOVR/Src/Util/Util_LatencyTest.cpp new file mode 100644 index 0000000..1a1c303 --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest.cpp @@ -0,0 +1,560 @@ +/************************************************************************************
+
+Filename : Util_LatencyTest.cpp
+Content : Wraps the lower level LatencyTester interface and adds functionality.
+Created : February 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "Util_LatencyTest.h"
+
+#include "../Kernel/OVR_Log.h"
+#include "../Kernel/OVR_Timer.h"
+
+namespace OVR { namespace Util {
+
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5;
+static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions.
+static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4;
+static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000;
+static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000;
+static const Color CALIBRATE_BLACK(0, 0, 0);
+static const Color CALIBRATE_WHITE(255, 255, 255);
+static const Color COLOR1(0, 0, 0);
+static const Color COLOR2(255, 255, 255);
+static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255);
+static const float BIG_FLOAT = 1000000.0f;
+static const float SMALL_FLOAT = -1000000.0f;
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest
+
+LatencyTest::LatencyTest(LatencyTestDevice* device)
+ : Handler(getThis())
+{
+ if (device != NULL)
+ {
+ SetDevice(device);
+ }
+
+ reset();
+
+ srand(Timer::GetTicksMs());
+}
+
+LatencyTest::~LatencyTest()
+{
+ clearMeasurementResults();
+}
+
+bool LatencyTest::SetDevice(LatencyTestDevice* device)
+{
+
+ if (device != Device)
+ {
+ if (device != NULL)
+ {
+ if (device->GetMessageHandler() != NULL)
+ {
+ OVR_DEBUG_LOG(
+ ("LatencyTest::AttachToDevice failed - device %p already has handler", device));
+ return false;
+ }
+ }
+
+ if (Device != NULL)
+ {
+ Device->SetMessageHandler(0);
+ }
+ Device = device;
+
+ if (Device != NULL)
+ {
+ Device->SetMessageHandler(&Handler);
+
+ // Set trigger threshold.
+ LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming.
+ Device->SetConfiguration(configuration, true);
+ }
+ }
+
+ return true;
+}
+
+UInt32 LatencyTest::getRandomComponent(UInt32 range)
+{
+ UInt32 val = rand() % range;
+ return val;
+}
+
+void LatencyTest::BeginTest()
+{
+ if (State == State_WaitingForButton)
+ {
+ // Set color to black and wait a while.
+ RenderColor = CALIBRATE_BLACK;
+
+ State = State_WaitingForSettlePreCalibrationColorBlack;
+ OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION);
+ }
+}
+
+void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage)
+{
+ // For debugging.
+/* if (msg.Type == Message_LatencyTestSamples)
+ {
+ MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg;
+
+ if (pSamples->Samples.GetSize() > 0)
+ {
+ // Just show the first one for now.
+ Color c = pSamples->Samples[0];
+ OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B));
+ }
+ return;
+ }
+*/
+
+ if (latencyTestMessage == LatencyTest_Timer)
+ {
+ if (!Device)
+ {
+ reset();
+ return;
+ }
+
+ if (State == State_WaitingForSettlePreCalibrationColorBlack)
+ {
+ // Send calibrate message to device and wait a while.
+ LatencyTestCalibrate calibrate(CALIBRATE_BLACK);
+ Device->SetCalibrate(calibrate);
+
+ State = State_WaitingForSettlePostCalibrationColorBlack;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePostCalibrationColorBlack)
+ {
+ // Change color to white and wait a while.
+ RenderColor = CALIBRATE_WHITE;
+
+ State = State_WaitingForSettlePreCalibrationColorWhite;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePreCalibrationColorWhite)
+ {
+ // Send calibrate message to device and wait a while.
+ LatencyTestCalibrate calibrate(CALIBRATE_WHITE);
+ Device->SetCalibrate(calibrate);
+
+ State = State_WaitingForSettlePostCalibrationColorWhite;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePostCalibrationColorWhite)
+ {
+ // Calibration is done. Switch to color 1 and wait for it to settle.
+ RenderColor = COLOR1;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ else if (State == State_WaitingForSettlePostMeasurement)
+ {
+ // Prepare for next measurement.
+
+ // Create a new result object.
+ MeasurementResult* pResult = new MeasurementResult();
+ Results.PushBack(pResult);
+
+ State = State_WaitingToTakeMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement."));
+ }
+ else if (State == State_WaitingForTestStarted)
+ {
+ // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next.
+ getActiveResult()->TimedOutWaitingForTestStarted = true;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'."));
+ OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ else if (State == State_WaitingForColorDetected)
+ {
+ // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next.
+ getActiveResult()->TimedOutWaitingForColorDetected = true;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'."));
+ OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ }
+ else if (latencyTestMessage == LatencyTest_ProcessInputs)
+ {
+ if (State == State_WaitingToTakeMeasurement)
+ {
+ if (!Device)
+ {
+ reset();
+ return;
+ }
+
+ // Send 'StartTest' feature report with opposite target color.
+ if (RenderColor == COLOR1)
+ {
+ RenderColor = COLOR2;
+ }
+ else
+ {
+ RenderColor = COLOR1;
+ }
+
+ getActiveResult()->TargetColor = RenderColor;
+
+ // Record time so we can determine usb roundtrip time.
+ getActiveResult()->StartTestTicksMicroS = Timer::GetTicks();
+
+ LatencyTestStartTest startTest(RenderColor);
+ Device->SetStartTest(startTest);
+
+ State = State_WaitingForTestStarted;
+ OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted."));
+
+ setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED);
+ }
+ }
+ else if (msg.Type == Message_LatencyTestButton)
+ {
+ BeginTest();
+ }
+ else if (msg.Type == Message_LatencyTestStarted)
+ {
+ if (State == State_WaitingForTestStarted)
+ {
+ clearTimer();
+
+ // Record time so we can determine usb roundtrip time.
+ getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks();
+
+ State = State_WaitingForColorDetected;
+ OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected."));
+
+ setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED);
+ }
+ }
+ else if (msg.Type == Message_LatencyTestColorDetected)
+ {
+ if (State == State_WaitingForColorDetected)
+ {
+ // Record time to detect color.
+ MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg;
+ UInt16 elapsedTime = pDetected->Elapsed;
+ OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime));
+
+ getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime;
+
+ if (areResultsComplete())
+ {
+ // We're done.
+ processResults();
+ reset();
+ }
+ else
+ {
+ // Run another measurement.
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ }
+ }
+ else if (msg.Type == Message_DeviceRemoved)
+ {
+ reset();
+ }
+}
+
+LatencyTest::MeasurementResult* LatencyTest::getActiveResult()
+{
+ OVR_ASSERT(!Results.IsEmpty());
+ return Results.GetLast();
+}
+
+void LatencyTest::setTimer(UInt32 timeMilliS)
+{
+ ActiveTimerMilliS = timeMilliS;
+}
+
+void LatencyTest::clearTimer()
+{
+ ActiveTimerMilliS = 0;
+}
+
+void LatencyTest::reset()
+{
+ clearMeasurementResults();
+ State = State_WaitingForButton;
+
+ HaveOldTime = false;
+ ActiveTimerMilliS = 0;
+}
+
+void LatencyTest::clearMeasurementResults()
+{
+ while(!Results.IsEmpty())
+ {
+ MeasurementResult* pElem = Results.GetFirst();
+ pElem->RemoveNode();
+ delete pElem;
+ }
+}
+
+LatencyTest::LatencyTestHandler::~LatencyTestHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg)
+{
+ pLatencyTestUtil->handleMessage(msg);
+}
+
+void LatencyTest::ProcessInputs()
+{
+ updateForTimeouts();
+ handleMessage(Message(), LatencyTest_ProcessInputs);
+}
+
+bool LatencyTest::DisplayScreenColor(Color& colorToDisplay)
+{
+ updateForTimeouts();
+
+ if (State == State_WaitingForButton)
+ {
+ return false;
+ }
+
+ colorToDisplay = RenderColor;
+ return true;
+}
+
+const char* LatencyTest::GetResultsString()
+{
+ if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr())
+ {
+ ReturnedResultString = ResultsString;
+ return ReturnedResultString.ToCStr();
+ }
+
+ return NULL;
+}
+
+bool LatencyTest::areResultsComplete()
+{
+ UInt32 initialMeasurements = 0;
+
+ UInt32 measurements1to2 = 0;
+ UInt32 measurements2to1 = 0;
+
+ MeasurementResult* pCurr = Results.GetFirst();
+ while(true)
+ {
+ // Process.
+ if (!pCurr->TimedOutWaitingForTestStarted &&
+ !pCurr->TimedOutWaitingForColorDetected)
+ {
+ initialMeasurements++;
+
+ if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE)
+ {
+ if (pCurr->TargetColor == COLOR2)
+ {
+ measurements1to2++;
+ }
+ else
+ {
+ measurements2to1++;
+ }
+ }
+ }
+
+ if (Results.IsLast(pCurr))
+ {
+ break;
+ }
+ pCurr = Results.GetNext(pCurr);
+ }
+
+ if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES &&
+ measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void LatencyTest::processResults()
+{
+
+ UInt32 minTime1To2 = UINT_MAX;
+ UInt32 maxTime1To2 = 0;
+ float averageTime1To2 = 0.0f;
+ UInt32 minTime2To1 = UINT_MAX;
+ UInt32 maxTime2To1 = 0;
+ float averageTime2To1 = 0.0f;
+
+ float minUSBTripMilliS = BIG_FLOAT;
+ float maxUSBTripMilliS = SMALL_FLOAT;
+ float averageUSBTripMilliS = 0.0f;
+ UInt32 countUSBTripTime = 0;
+
+ UInt32 measurementsCount = 0;
+ UInt32 measurements1to2 = 0;
+ UInt32 measurements2to1 = 0;
+
+ MeasurementResult* pCurr = Results.GetFirst();
+ UInt32 count = 0;
+ while(true)
+ {
+ count++;
+
+ if (!pCurr->TimedOutWaitingForTestStarted &&
+ !pCurr->TimedOutWaitingForColorDetected)
+ {
+ measurementsCount++;
+
+ if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE)
+ {
+ if (pCurr->TargetColor == COLOR2)
+ {
+ measurements1to2++;
+
+ if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS;
+
+ minTime1To2 = Alg::Min(elapsed, minTime1To2);
+ maxTime1To2 = Alg::Max(elapsed, maxTime1To2);
+
+ averageTime1To2 += (float) elapsed;
+ }
+ }
+ else
+ {
+ measurements2to1++;
+
+ if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS;
+
+ minTime2To1 = Alg::Min(elapsed, minTime2To1);
+ maxTime2To1 = Alg::Max(elapsed, maxTime2To1);
+
+ averageTime2To1 += (float) elapsed;
+ }
+ }
+
+ float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS);
+ minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS);
+ maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS);
+ averageUSBTripMilliS += usbRountripElapsedMilliS;
+ countUSBTripTime++;
+ }
+ }
+
+ if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES &&
+ measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ break;
+ }
+
+ if (Results.IsLast(pCurr))
+ {
+ break;
+ }
+ pCurr = Results.GetNext(pCurr);
+ }
+
+ averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES;
+ averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES;
+
+ averageUSBTripMilliS /= countUSBTripTime;
+
+ float finalResult = 0.5f * (averageTime1To2 + averageTime2To1);
+ finalResult += averageUSBTripMilliS;
+
+ ResultsString.Clear();
+ ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]",
+ finalResult,
+ minTime1To2, averageTime1To2, maxTime1To2,
+ minTime2To1, averageTime2To1, maxTime2To1,
+ minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS,
+ DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount);
+}
+
+void LatencyTest::updateForTimeouts()
+{
+ if (!HaveOldTime)
+ {
+ HaveOldTime = true;
+ OldTime = Timer::GetTicksMs();
+ return;
+ }
+
+ UInt32 newTime = Timer::GetTicksMs();
+ UInt32 elapsedMilliS = newTime - OldTime;
+ if (newTime < OldTime)
+ {
+ elapsedMilliS = OldTime - newTime;
+ elapsedMilliS = UINT_MAX - elapsedMilliS;
+ }
+ OldTime = newTime;
+
+ elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often.
+
+
+ if (ActiveTimerMilliS == 0)
+ {
+ return;
+ }
+
+ if (elapsedMilliS >= ActiveTimerMilliS)
+ {
+ ActiveTimerMilliS = 0;
+ handleMessage(Message(), LatencyTest_Timer);
+ return;
+ }
+
+ ActiveTimerMilliS -= elapsedMilliS;
+}
+
+}} // namespace OVR::Util
diff --git a/LibOVR/Src/Util/Util_LatencyTest.h b/LibOVR/Src/Util/Util_LatencyTest.h new file mode 100644 index 0000000..47f98f7 --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest.h @@ -0,0 +1,160 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_LatencyTest.h
+Content : Wraps the lower level LatencyTesterDevice and adds functionality.
+Created : February 14, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_LatencyTest_h
+#define OVR_Util_LatencyTest_h
+
+#include "../OVR_Device.h"
+
+#include "../Kernel/OVR_String.h"
+#include "../Kernel/OVR_List.h"
+
+namespace OVR { namespace Util {
+
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest
+//
+// LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling
+// of a latency test. A single test is composed of a series of individual latency measurements
+// which are used to derive min, max, and an average latency value.
+//
+// Developers are required to call the following methods:
+// SetDevice - Sets the LatencyTestDevice to be used for the tests.
+// ProcessInputs - This should be called at the same place in the code where the game engine
+// reads the headset orientation from LibOVR (typically done by calling
+// 'GetOrientation' on the SensorFusion object). Calling this at the right time
+// enables us to measure the same latency that occurs for headset orientation
+// changes.
+// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly
+// beneath it. The color of these pixels can be set by drawing a small
+// quad at the end of the rendering stage. The quad should be small
+// such that it doesn't significantly impact the rendering of the scene,
+// but large enough to be 'seen' by the sensor. See the SDK
+// documentation for more information.
+// GetResultsString - Call this to get a string containing the most recent results.
+// If the string has already been gotten then NULL will be returned.
+// The string pointer will remain valid until the next time this
+// method is called.
+//
+
+class LatencyTest : public NewOverrideBase
+{
+public:
+ LatencyTest(LatencyTestDevice* device = NULL);
+ ~LatencyTest();
+
+ // Set the Latency Tester device that we'll use to send commands to and receive
+ // notification messages from.
+ bool SetDevice(LatencyTestDevice* device);
+
+ // Returns true if this LatencyTestUtil has a Latency Tester device.
+ bool HasDevice() const
+ { return Handler.IsHandlerInstalled(); }
+
+ void ProcessInputs();
+ bool DisplayScreenColor(Color& colorToDisplay);
+ const char* GetResultsString();
+
+ // Begin test. Equivalent to pressing the button on the latency tester.
+ void BeginTest();
+
+private:
+ LatencyTest* getThis() { return this; }
+
+ enum LatencyTestMessageType
+ {
+ LatencyTest_None,
+ LatencyTest_Timer,
+ LatencyTest_ProcessInputs,
+ };
+
+ UInt32 getRandomComponent(UInt32 range);
+ void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None);
+ void reset();
+ void setTimer(UInt32 timeMilliS);
+ void clearTimer();
+
+ class LatencyTestHandler : public MessageHandler
+ {
+ LatencyTest* pLatencyTestUtil;
+ public:
+ LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { }
+ ~LatencyTestHandler();
+
+ virtual void OnMessage(const Message& msg);
+ };
+
+ bool areResultsComplete();
+ void processResults();
+ void updateForTimeouts();
+
+ Ptr<LatencyTestDevice> Device;
+ LatencyTestHandler Handler;
+
+ enum TesterState
+ {
+ State_WaitingForButton,
+ State_WaitingForSettlePreCalibrationColorBlack,
+ State_WaitingForSettlePostCalibrationColorBlack,
+ State_WaitingForSettlePreCalibrationColorWhite,
+ State_WaitingForSettlePostCalibrationColorWhite,
+ State_WaitingToTakeMeasurement,
+ State_WaitingForTestStarted,
+ State_WaitingForColorDetected,
+ State_WaitingForSettlePostMeasurement
+ };
+ TesterState State;
+
+ bool HaveOldTime;
+ UInt32 OldTime;
+ UInt32 ActiveTimerMilliS;
+
+ Color RenderColor;
+
+ struct MeasurementResult : public ListNode<MeasurementResult>, public NewOverrideBase
+ {
+ MeasurementResult()
+ : DeviceMeasuredElapsedMilliS(0),
+ TimedOutWaitingForTestStarted(false),
+ TimedOutWaitingForColorDetected(false),
+ StartTestTicksMicroS(0),
+ TestStartedTicksMicroS(0)
+ {}
+
+ Color TargetColor;
+
+ UInt32 DeviceMeasuredElapsedMilliS;
+
+ bool TimedOutWaitingForTestStarted;
+ bool TimedOutWaitingForColorDetected;
+
+ UInt64 StartTestTicksMicroS;
+ UInt64 TestStartedTicksMicroS;
+ };
+
+ List<MeasurementResult> Results;
+ void clearMeasurementResults();
+
+ MeasurementResult* getActiveResult();
+
+ StringBuffer ResultsString;
+ String ReturnedResultString;
+};
+
+}} // namespace OVR::Util
+
+#endif // OVR_Util_LatencyTest_h
diff --git a/LibOVR/Src/Util/Util_MagCalibration.cpp b/LibOVR/Src/Util/Util_MagCalibration.cpp new file mode 100644 index 0000000..f3e72f5 --- /dev/null +++ b/LibOVR/Src/Util/Util_MagCalibration.cpp @@ -0,0 +1,180 @@ +/************************************************************************************
+
+Filename : Util_MagCalibration.cpp
+Content : Procedures for calibrating the magnetometer
+Created : April 16, 2013
+Authors : Steve LaValle, Andrew Reisse
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "Util_MagCalibration.h"
+
+namespace OVR { namespace Util {
+
+void MagCalibration::BeginAutoCalibration(SensorFusion& sf)
+{
+ Status = Mag_AutoCalibrating;
+ // This is a "hard" reset of the mag, so need to clear stored values
+ sf.ClearMagCalibration();
+ SampleCount = 0;
+}
+
+unsigned MagCalibration::UpdateAutoCalibration(SensorFusion& sf)
+{
+ if (Status != Mag_AutoCalibrating)
+ return Status;
+
+ Quatf q = sf.GetOrientation();
+ Vector3f m = sf.GetMagnetometer();
+
+ InsertIfAcceptable(q, m);
+
+ if ((SampleCount == 4) && (Status == Mag_AutoCalibrating))
+ SetCalibration(sf);
+
+ return Status;
+
+}
+
+void MagCalibration::BeginManualCalibration(SensorFusion& sf)
+{
+ Status = Mag_ManuallyCalibrating;
+ sf.ClearMagCalibration();
+ SampleCount = 0;
+}
+
+bool MagCalibration::IsAcceptableSample(const Quatf& q, const Vector3f& m)
+{
+ switch (SampleCount)
+ {
+ // Initial sample is always acceptable
+ case 0:
+ return true;
+ break;
+ case 1:
+ return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&&
+ ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq);
+ break;
+ case 2:
+ return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&&
+ (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&&
+ ((m - MagSamples[0]).LengthSq() > MinMagDistanceSq)&&
+ ((m - MagSamples[1]).LengthSq() > MinMagDistanceSq);
+ break;
+ case 3:
+ return (q.DistanceSq(QuatSamples[0]) > MinQuatDistanceSq)&&
+ (q.DistanceSq(QuatSamples[1]) > MinQuatDistanceSq)&&
+ (q.DistanceSq(QuatSamples[2]) > MinQuatDistanceSq)&&
+ ((PointToPlaneDistance(MagSamples[0],MagSamples[1],MagSamples[2],m) > MinMagDistance)||
+ (PointToPlaneDistance(MagSamples[1],MagSamples[2],m,MagSamples[0]) > MinMagDistance)||
+ (PointToPlaneDistance(MagSamples[2],m,MagSamples[0],MagSamples[1]) > MinMagDistance)||
+ (PointToPlaneDistance(m,MagSamples[0],MagSamples[1],MagSamples[2]) > MinMagDistance));
+ }
+
+ return false;
+}
+
+
+bool MagCalibration::InsertIfAcceptable(const Quatf& q, const Vector3f& m)
+{
+ if (IsAcceptableSample(q, m))
+ {
+ MagSamples[SampleCount] = m;
+ QuatSamples[SampleCount] = q;
+ SampleCount++;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool MagCalibration::SetCalibration(SensorFusion& sf)
+{
+ if (SampleCount < 4)
+ return false;
+
+ MagCenter = CalculateSphereCenter(MagSamples[0],MagSamples[1],MagSamples[2],MagSamples[3]);
+ Matrix4f calMat = Matrix4f();
+ calMat.M[0][3] = -MagCenter.x;
+ calMat.M[1][3] = -MagCenter.y;
+ calMat.M[2][3] = -MagCenter.z;
+ sf.SetMagCalibration(calMat);
+ Status = Mag_Calibrated;
+ //LogText("MagCenter: %f %f %f\n",MagCenter.x,MagCenter.y,MagCenter.z);
+
+ return true;
+}
+
+
+// Calculate the center of a sphere that passes through p1, p2, p3, p4
+Vector3f MagCalibration::CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4)
+{
+ Matrix4f A;
+ int i;
+ Vector3f p[4];
+ p[0] = p1;
+ p[1] = p2;
+ p[2] = p3;
+ p[3] = p4;
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x;
+ A.M[i][1] = p[i].y;
+ A.M[i][2] = p[i].z;
+ A.M[i][3] = 1.0f;
+ }
+ float m11 = A.Determinant();
+ OVR_ASSERT(m11 != 0.0f);
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z;
+ A.M[i][1] = p[i].y;
+ A.M[i][2] = p[i].z;
+ A.M[i][3] = 1.0f;
+ }
+ float m12 = A.Determinant();
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z;
+ A.M[i][1] = p[i].x;
+ A.M[i][2] = p[i].z;
+ A.M[i][3] = 1.0f;
+ }
+ float m13 = A.Determinant();
+
+ for (i = 0; i < 4; i++)
+ {
+ A.M[i][0] = p[i].x*p[i].x + p[i].y*p[i].y + p[i].z*p[i].z;
+ A.M[i][1] = p[i].x;
+ A.M[i][2] = p[i].y;
+ A.M[i][3] = 1.0f;
+ }
+ float m14 = A.Determinant();
+
+ float c = 0.5f / m11;
+ return Vector3f(c*m12, -c*m13, c*m14);
+}
+
+// Distance from p4 to the nearest point on a plane through p1, p2, p3
+float MagCalibration::PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4)
+{
+ Vector3f v1 = p1 - p2;
+ Vector3f v2 = p1 - p3;
+ Vector3f planeNormal = v1.Cross(v2);
+ planeNormal.Normalize();
+ return (fabs((planeNormal * p4) - planeNormal * p1));
+}
+
+}}
diff --git a/LibOVR/Src/Util/Util_MagCalibration.h b/LibOVR/Src/Util/Util_MagCalibration.h new file mode 100644 index 0000000..9371125 --- /dev/null +++ b/LibOVR/Src/Util/Util_MagCalibration.h @@ -0,0 +1,115 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_MagCalibration.h
+Content : Procedures for calibrating the magnetometer
+Created : April 16, 2013
+Authors : Steve LaValle, Andrew Reisse
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_MagCalibration_h
+#define OVR_Util_MagCalibration_h
+
+#include "../OVR_SensorFusion.h"
+#include "../Kernel/OVR_String.h"
+#include "../Kernel/OVR_Log.h"
+
+namespace OVR { namespace Util {
+
+class MagCalibration
+{
+public:
+ enum MagStatus
+ {
+ Mag_Uninitialized = 0,
+ Mag_AutoCalibrating = 1,
+ Mag_ManuallyCalibrating = 2,
+ Mag_Calibrated = 3,
+ };
+
+ MagCalibration() :
+ Status(Mag_Uninitialized),
+ MinMagDistance(0.3f), MinQuatDistance(0.5f),
+ SampleCount(0)
+ {
+ MinMagDistanceSq = MinMagDistance * MinMagDistance;
+ MinQuatDistanceSq = MinQuatDistance * MinQuatDistance;
+ }
+
+ // Methods that are useful for either auto or manual calibration
+ bool IsUnitialized() const { return Status == Mag_Uninitialized; }
+ bool IsCalibrated() const { return Status == Mag_Calibrated; }
+ int NumberOfSamples() const { return SampleCount; }
+ int RequiredSampleCount() const { return 4; }
+ void ClearCalibration(SensorFusion& sf)
+ {
+ Status = Mag_Uninitialized;
+ SampleCount = 0;
+ sf.ClearMagCalibration();
+ };
+
+ // Methods for automatic magnetometer calibration
+ void BeginAutoCalibration(SensorFusion& sf);
+ unsigned UpdateAutoCalibration(SensorFusion& sf);
+ bool IsAutoCalibrating() const { return Status == Mag_AutoCalibrating; }
+
+ // Methods for building a manual (user-guided) calibraton procedure
+ void BeginManualCalibration(SensorFusion& sf);
+ bool IsAcceptableSample(const Quatf& q, const Vector3f& m);
+ bool InsertIfAcceptable(const Quatf& q, const Vector3f& m);
+ // Returns true if successful, requiring that SampleCount = 4
+ bool SetCalibration(SensorFusion& sf);
+ bool IsManuallyCalibrating() const { return Status == Mag_ManuallyCalibrating; }
+
+ // This is the minimum acceptable distance (Euclidean) between raw
+ // magnetometer values to be acceptable for usage in calibration.
+ void SetMinMagDistance(float dist)
+ {
+ MinMagDistance = dist;
+ MinMagDistanceSq = MinMagDistance * MinMagDistance;
+ }
+
+ // The minimum acceptable distance (4D Euclidean) between orientations
+ // to be acceptable for calibration usage.
+ void SetMinQuatDistance(float dist)
+ {
+ MinQuatDistance = dist;
+ MinQuatDistanceSq = MinQuatDistance * MinQuatDistance;
+ }
+
+ // A result of the calibration, which is the center of a sphere that
+ // roughly approximates the magnetometer data.
+ Vector3f GetMagCenter() const { return MagCenter; }
+
+private:
+ // Determine the unique sphere through 4 non-coplanar points
+ Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4);
+
+ // Distance from p4 to the nearest point on a plane through p1, p2, p3
+ float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2,
+ const Vector3f& p3, const Vector3f& p4);
+
+ Vector3f MagCenter;
+ unsigned Status;
+ float MinMagDistance;
+ float MinQuatDistance;
+ float MinMagDistanceSq;
+ float MinQuatDistanceSq;
+
+ unsigned SampleCount;
+ Vector3f MagSamples[4];
+ Quatf QuatSamples[4];
+
+};
+
+}}
+
+#endif
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.cpp b/LibOVR/Src/Util/Util_Render_Stereo.cpp new file mode 100644 index 0000000..8986e30 --- /dev/null +++ b/LibOVR/Src/Util/Util_Render_Stereo.cpp @@ -0,0 +1,311 @@ +/************************************************************************************
+
+Filename : Util_Render_Stereo.cpp
+Content : Stereo rendering configuration implementation
+Created : October 22, 2012
+Authors : Michael Antonov, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#include "Util_Render_Stereo.h"
+
+namespace OVR { namespace Util { namespace Render {
+
+
+//-----------------------------------------------------------------------------------
+
+// DistortionFnInverse computes the inverse of the distortion function on an argument.
+float DistortionConfig::DistortionFnInverse(float r)
+{
+ OVR_ASSERT((r <= 10.0f));
+
+ float s, d;
+ float delta = r * 0.25f;
+
+ s = r * 0.5f;
+ d = fabs(r - DistortionFn(s));
+
+ for (int i = 0; i < 20; i++)
+ {
+ float sUp = s + delta;
+ float sDown = s - delta;
+ float dUp = fabs(r - DistortionFn(sUp));
+ float dDown = fabs(r - DistortionFn(sDown));
+
+ if (dUp < d)
+ {
+ s = sUp;
+ d = dUp;
+ }
+ else if (dDown < d)
+ {
+ s = sDown;
+ d = dDown;
+ }
+ else
+ {
+ delta *= 0.5f;
+ }
+ }
+
+ return s;
+}
+
+
+//-----------------------------------------------------------------------------------
+// **** StereoConfig Implementation
+
+StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp)
+ : Mode(mode),
+ InterpupillaryDistance(0.064f), AspectMultiplier(1.0f),
+ FullView(vp), DirtyFlag(true),
+ YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0),
+ OrthoPixelOffset(0)
+{
+ // And default distortion for it.
+ Distortion.SetCoefficients(1.0f, 0.22f, 0.24f);
+ Distortion.Scale = 1.0f; // Will be computed later.
+
+ // Fit left of the image.
+ DistortionFitX = -1.0f;
+ DistortionFitY = 0.0f;
+
+ // Initialize "fake" default HMD values for testing without HMD plugged in.
+ // These default values match those returned by the HMD.
+ HMD.HResolution = 1280;
+ HMD.VResolution = 800;
+ HMD.HScreenSize = 0.14976f;
+ HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f);
+ HMD.InterpupillaryDistance = InterpupillaryDistance;
+ HMD.LensSeparationDistance = 0.0635f;
+ HMD.EyeToScreenDistance = 0.041f;
+ HMD.DistortionK[0] = Distortion.K[0];
+ HMD.DistortionK[1] = Distortion.K[1];
+ HMD.DistortionK[2] = Distortion.K[2];
+ HMD.DistortionK[3] = 0;
+
+ Set2DAreaFov(DegreeToRad(85.0f));
+}
+
+void StereoConfig::SetFullViewport(const Viewport& vp)
+{
+ if (vp != FullView)
+ {
+ FullView = vp;
+ DirtyFlag = true;
+ }
+}
+
+void StereoConfig::SetHMDInfo(const HMDInfo& hmd)
+{
+ HMD = hmd;
+ Distortion.K[0] = hmd.DistortionK[0];
+ Distortion.K[1] = hmd.DistortionK[1];
+ Distortion.K[2] = hmd.DistortionK[2];
+ Distortion.K[3] = hmd.DistortionK[3];
+
+ Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1],
+ hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]);
+
+ DirtyFlag = true;
+}
+
+void StereoConfig::SetDistortionFitPointVP(float x, float y)
+{
+ DistortionFitX = x;
+ DistortionFitY = y;
+ DirtyFlag = true;
+}
+
+void StereoConfig::SetDistortionFitPointPixels(float x, float y)
+{
+ DistortionFitX = (4 * x / float(FullView.w)) - 1.0f;
+ DistortionFitY = (2 * y / float(FullView.h)) - 1.0f;
+ DirtyFlag = true;
+}
+
+void StereoConfig::Set2DAreaFov(float fovRadians)
+{
+ Area2DFov = fovRadians;
+ DirtyFlag = true;
+}
+
+
+const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye)
+{
+ static const UByte eyeParamIndices[3] = { 0, 0, 1 };
+
+ updateIfDirty();
+ OVR_ASSERT(eye < sizeof(eyeParamIndices));
+ return EyeRenderParams[eyeParamIndices[eye]];
+}
+
+
+void StereoConfig::updateComputedState()
+{
+ // Need to compute all of the following:
+ // - Aspect Ratio
+ // - FOV
+ // - Projection offsets for 3D
+ // - Distortion XCenterOffset
+ // - Update 2D
+ // - Initialize EyeRenderParams
+
+ // Compute aspect ratio. Stereo mode cuts width in half.
+ Aspect = float(FullView.w) / float(FullView.h);
+ Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f;
+ Aspect *= AspectMultiplier;
+
+ updateDistortionOffsetAndScale();
+
+ // Compute Vertical FOV based on distance, distortion, etc.
+ // Distance from vertical center to render vertical edge perceived through the lens.
+ // This will be larger then normal screen size due to magnification & distortion.
+ //
+ // This percievedHalfRTDistance equation should hold as long as the render target
+ // and display have the same aspect ratios. What we'd like to know is where the edge
+ // of the render target will on the perceived screen surface. With NO LENS,
+ // the answer would be:
+ //
+ // halfRTDistance = (VScreenSize / 2) * aspect *
+ // DistortionFn_Inverse( DistortionScale / aspect )
+ //
+ // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios
+ // cancel out, so we get:
+ //
+ // halfRTDistance = (VScreenSize / 2) * DistortionScale
+ //
+ if (Mode == Stereo_None)
+ {
+ YFov = DegreeToRad(80.0f);
+ }
+ else
+ {
+ float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale;
+ YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance);
+ }
+
+ updateProjectionOffset();
+ update2D();
+ updateEyeParams();
+
+ DirtyFlag = false;
+}
+
+void StereoConfig::updateDistortionOffsetAndScale()
+{
+ // Distortion center shift is stored separately, since it isn't affected
+ // by the eye distance.
+ float lensOffset = HMD.LensSeparationDistance * 0.5f;
+ float lensShift = HMD.HScreenSize * 0.25f - lensOffset;
+ float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize;
+ Distortion.XCenterOffset= lensViewportShift;
+
+ // Compute distortion scale from DistortionFitX & DistortionFitY.
+ // Fit value of 0.0 means "no fit".
+ if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f))
+ {
+ Distortion.Scale = 1.0f;
+ }
+ else
+ {
+ // Convert fit value to distortion-centered coordinates before fit radius
+ // calculation.
+ float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h);
+ float dx = DistortionFitX - Distortion.XCenterOffset;
+ float dy = DistortionFitY / stereoAspect;
+ float fitRadius = sqrt(dx * dx + dy * dy);
+ Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius;
+ }
+}
+
+void StereoConfig::updateProjectionOffset()
+{
+ // Post-projection viewport coordinates range from (-1.0, 1.0), with the
+ // center of the left viewport falling at (1/4) of horizontal screen size.
+ // We need to shift this projection center to match with the lens center;
+ // note that we don't use the IPD here due to collimated light property of the lens.
+ // We compute this shift in physical units (meters) to
+ // correct for different screen sizes and then rescale to viewport coordinates.
+ float viewCenter = HMD.HScreenSize * 0.25f;
+ float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f;
+ ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize;
+}
+
+void StereoConfig::update2D()
+{
+ // Orthographic projection fakes a screen at a distance of 0.8m from the
+ // eye, where hmd screen projection surface is at 0.05m distance.
+ // This introduces an extra off-center pixel projection shift based on eye distance.
+ // This offCenterShift is the pixel offset of the other camera's center
+ // in your reference camera based on surface distance.
+ float metersToPixels = (HMD.HResolution / HMD.HScreenSize);
+ float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance;
+ float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance;
+ float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels;
+ float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f;
+ float rightPixelCenter = lensDistanceScreenPixels * 0.5f;
+ float pixelDifference = leftPixelCenter - rightPixelCenter;
+
+ // This computes the number of pixels that fit within specified 2D FOV (assuming
+ // distortion scaling will be done).
+ float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance;
+ float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale;
+ FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize;
+
+ // Create orthographic matrix.
+ Matrix4f& m = OrthoCenter;
+ m.SetIdentity();
+ m.M[0][0] = FovPixels / (FullView.w * 0.5f);
+ m.M[1][1] = -FovPixels / FullView.h;
+ m.M[0][3] = 0;
+ m.M[1][3] = 0;
+ m.M[2][2] = 0;
+
+ float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f;
+ OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels;
+}
+
+void StereoConfig::updateEyeParams()
+{
+ // Projection matrix for the center eye, which the left/right matrices are based on.
+ Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 1000.0f);
+
+ switch(Mode)
+ {
+ case Stereo_None:
+ {
+ EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter);
+ }
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ {
+ Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter,
+ projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter;
+
+ EyeRenderParams[0].Init(StereoEye_Left,
+ Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h),
+ +InterpupillaryDistance * 0.5f, // World view shift.
+ projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0),
+ &Distortion);
+ EyeRenderParams[1].Init(StereoEye_Right,
+ Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h),
+ -InterpupillaryDistance * 0.5f,
+ projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0),
+ &Distortion);
+ }
+ break;
+ }
+
+}
+
+
+}}} // OVR::Util::Render
+
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.h b/LibOVR/Src/Util/Util_Render_Stereo.h new file mode 100644 index 0000000..9dc110c --- /dev/null +++ b/LibOVR/Src/Util/Util_Render_Stereo.h @@ -0,0 +1,299 @@ +/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_Render_Stereo.h
+Content : Sample stereo rendering configuration classes.
+Created : October 22, 2012
+Authors : Michael Antonov
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_Render_Stereo_h
+#define OVR_Util_Render_Stereo_h
+
+#include "../OVR_Device.h"
+
+namespace OVR { namespace Util { namespace Render {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Stereo Enumerations
+
+// StereoMode describes rendering modes that can be used by StereoConfig.
+// These modes control whether stereo rendering is used or not (Stereo_None),
+// and how it is implemented.
+enum StereoMode
+{
+ Stereo_None = 0,
+ Stereo_LeftRight_Multipass = 1
+};
+
+
+// StereoEye specifies which eye we are rendering for; it is used to
+// retrieve StereoEyeParams.
+enum StereoEye
+{
+ StereoEye_Center,
+ StereoEye_Left,
+ StereoEye_Right
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Viewport
+
+// Viewport describes a rectangular area used for rendering, in pixels.
+struct Viewport
+{
+ int x, y;
+ int w, h;
+
+ Viewport() {}
+ Viewport(int x1, int y1, int w1, int h1) : x(x1), y(y1), w(w1), h(h1) { }
+
+ bool operator == (const Viewport& vp) const
+ { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); }
+ bool operator != (const Viewport& vp) const
+ { return !operator == (vp); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** DistortionConfig
+
+// DistortionConfig Provides controls for the distortion shader.
+// - K[0] - K[3] are coefficients for the distortion function.
+// - XCenterOffset is the offset of lens distortion center from the
+// center of one-eye screen half. [-1, 1] Range.
+// - Scale is a factor of how much larger will the input image be,
+// with a factor of 1.0f being no scaling. An inverse of this
+// value is applied to sampled UV coordinates (1/Scale).
+// - ChromaticAberration is an array of parameters for controlling
+// additional Red and Blue scaling in order to reduce chromatic aberration
+// caused by the Rift lenses.
+class DistortionConfig
+{
+public:
+ DistortionConfig(float k0 = 1.0f, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f)
+ : XCenterOffset(0), YCenterOffset(0), Scale(1.0f)
+ {
+ SetCoefficients(k0, k1, k2, k3);
+ SetChromaticAberration();
+ }
+
+ void SetCoefficients(float k0, float k1 = 0.0f, float k2 = 0.0f, float k3 = 0.0f)
+ { K[0] = k0; K[1] = k1; K[2] = k2; K[3] = k3; }
+
+ void SetChromaticAberration(float red1 = 1.0f, float red2 = 0.0f, float blue1 = 1.0f, float blue2 = 0.0f)
+ { ChromaticAberration[0] = red1; ChromaticAberration[1] = red2; ChromaticAberration[2] = blue1; ChromaticAberration[3] = blue2; }
+
+
+ // DistortionFn applies distortion equation to the argument. The returned
+ // value should match distortion equation used in shader.
+ float DistortionFn(float r) const
+ {
+ float rsq = r * r;
+ float scale = r * (K[0] + K[1] * rsq + K[2] * rsq * rsq + K[3] * rsq * rsq * rsq);
+ return scale;
+ }
+
+ // DistortionFnInverse computes the inverse of the distortion function on an argument.
+ float DistortionFnInverse(float r);
+
+ float K[4];
+ float XCenterOffset, YCenterOffset;
+ float Scale;
+
+ float ChromaticAberration[4]; // Additional per-channel scaling is applied after distortion:
+ // Index [0] - Red channel constant coefficient.
+ // Index [1] - Red channel r^2 coefficient.
+ // Index [2] - Blue channel constant coefficient.
+ // Index [3] - Blue channel r^2 coefficient.
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** StereoEyeParams
+
+// StereoEyeParams describes RenderDevice configuration needed to render
+// the scene for one eye.
+class StereoEyeParams
+{
+public:
+ StereoEye Eye;
+ Viewport VP; // Viewport that we are rendering to
+ const DistortionConfig* pDistortion;
+
+ Matrix4f ViewAdjust; // Translation to be applied to view matrix.
+ Matrix4f Projection; // Projection matrix used with this eye.
+ Matrix4f OrthoProjection; // Orthographic projection used with this eye.
+
+ void Init(StereoEye eye, const Viewport &vp, float vofs,
+ const Matrix4f& proj, const Matrix4f& orthoProj,
+ const DistortionConfig* distortion = 0)
+ {
+ Eye = eye;
+ VP = vp;
+ ViewAdjust = Matrix4f::Translation(Vector3f(vofs,0,0));
+ Projection = proj;
+ OrthoProjection = orthoProj;
+ pDistortion = distortion;
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** StereoConfig
+
+// StereoConfig maintains a scene stereo state and allow switching between different
+// stereo rendering modes. To support rendering, StereoConfig keeps track of HMD
+// variables such as screen size, eye-to-screen distance and distortion, and computes
+// extra data such as FOV and distortion center offsets based on it. Rendering
+// parameters are returned though StereoEyeParams for each eye.
+//
+// Beyond regular 3D projection, this class supports rendering a 2D orthographic
+// surface for UI and text. The 2D surface will be defined as fitting within a 2D
+// field of view (85 degrees by default) and used [-1,1] coordinate system with
+// square pixels. The (0,0) coordinate corresponds to eye center location
+// that is properly adjusted during rendering through SterepRenderParams::Adjust2D.
+// Genreally speaking, text outside [-1,1] coordinate range will not be readable.
+
+class StereoConfig
+{
+public:
+
+ StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass,
+ const Viewport& fullViewport = Viewport(0,0, 1280,800));
+
+
+ // *** Modifiable State Access
+
+ // Sets a stereo rendering mode and updates internal cached
+ // state (matrices, per-eye view) based on it.
+ void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; }
+ StereoMode GetStereoMode() const { return Mode; }
+
+ // Sets HMD parameters; also initializes distortion coefficients.
+ void SetHMDInfo(const HMDInfo& hmd);
+ const HMDInfo& GetHMDInfo() const { return HMD; }
+
+ // Query physical eye-to-screen distance in meters, which combines screen-to-lens and
+ // and lens-to-eye pupil distances. Modifying this value adjusts FOV.
+ float GetEyeToScreenDistance() const { return HMD.EyeToScreenDistance; }
+ void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; }
+
+ // Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm).
+ void SetIPD(float ipd) { InterpupillaryDistance = ipd; DirtyFlag = true; }
+ float GetIPD() const { return InterpupillaryDistance; }
+
+ // Set full render target viewport; for HMD this includes both eyes.
+ void SetFullViewport(const Viewport& vp);
+ const Viewport& GetFullViewport() const { return FullView; }
+
+ // Aspect ratio defaults to ((w/h)*multiplier) computed per eye.
+ // Aspect multiplier allows adjusting aspect ratio consistently for Stereo/NoStereo.
+ void SetAspectMultiplier(float m) { AspectMultiplier = m; DirtyFlag = true; }
+ float GetAspectMultiplier() const { return AspectMultiplier; }
+
+
+ // For the distorted image to fill rendered viewport, input texture render target needs to be
+ // scaled by DistortionScale before sampling. The scale factor is computed by fitting a point
+ // on of specified radius from a distortion center, more easily specified as a coordinate.
+ // SetDistortionFitPointVP sets the (x,y) coordinate of the point that scale will be "fit" to,
+ // assuming [-1,1] coordinate range for full left-eye viewport. A fit point is a location
+ // where source (pre-distortion) and target (post-distortion) image match each other.
+ // For the right eye, the interpretation of 'u' will be inverted.
+ void SetDistortionFitPointVP(float x, float y);
+ // SetDistortionFitPointPixels sets the (x,y) coordinate of the point that scale will be "fit" to,
+ // specified in pixeld for full left-eye texture.
+ void SetDistortionFitPointPixels(float x, float y);
+
+ // Changes all distortion settings.
+ // Note that setting HMDInfo also changes Distortion coefficients.
+ void SetDistortionConfig(const DistortionConfig& d) { Distortion = d; DirtyFlag = true; }
+
+ // Modify distortion coefficients; useful for adjustment tweaking.
+ void SetDistortionK(int i, float k) { Distortion.K[i] = k; DirtyFlag = true; }
+ float GetDistortionK(int i) const { return Distortion.K[i]; }
+
+ // Sets the fieldOfView that the 2D coordinate area stretches to.
+ void Set2DAreaFov(float fovRadians);
+
+
+ // *** Computed State
+
+ // Return current aspect ratio.
+ float GetAspect() { updateIfDirty(); return Aspect; }
+
+ // Return computed vertical FOV in radians/degrees.
+ float GetYFOVRadians() { updateIfDirty(); return YFov; }
+ float GetYFOVDegrees() { return RadToDegree(GetYFOVRadians()); }
+
+ // Query horizontal projection center offset as a distance away from the
+ // one-eye [-1,1] unit viewport.
+ // Positive return value should be used for left eye, negative for right eye.
+ float GetProjectionCenterOffset() { updateIfDirty(); return ProjectionCenterOffset; }
+
+ // GetDistortionConfig isn't const because XCenterOffset bay need to be recomputed.
+ const DistortionConfig& GetDistortionConfig() { updateIfDirty(); return Distortion; }
+
+ // Returns DistortionScale factor by which input texture size is increased to make
+ // post-distortion result distortion fit the viewport.
+ float GetDistortionScale() { updateIfDirty(); return Distortion.Scale; }
+
+ // Returns the size of a pixel within 2D coordinate system.
+ float Get2DUnitPixel() { updateIfDirty(); return (2.0f / (FovPixels * Distortion.Scale)); }
+
+ // Returns full set of Stereo rendering parameters for the specified eye.
+ const StereoEyeParams& GetEyeRenderParams(StereoEye eye);
+
+private:
+
+ void updateIfDirty() { if (DirtyFlag) updateComputedState(); }
+ void updateComputedState();
+
+ void updateDistortionOffsetAndScale();
+ void updateProjectionOffset();
+ void update2D();
+ void updateEyeParams();
+
+
+ // *** Modifiable State
+
+ StereoMode Mode;
+ float InterpupillaryDistance;
+ float AspectMultiplier; // Multiplied into aspect ratio to change it.
+ HMDInfo HMD;
+ DistortionConfig Distortion;
+ float DistortionFitX, DistortionFitY; // In [-1,1] half-screen viewport units.
+ Viewport FullView; // Entire window viewport.
+
+ float Area2DFov; // FOV range mapping to [-1, 1] 2D area.
+
+ // *** Computed State
+
+ bool DirtyFlag; // Set when any if the modifiable state changed.
+ float YFov; // Vertical FOV.
+ float Aspect; // Aspect ratio: (w/h)*AspectMultiplier.
+ float ProjectionCenterOffset;
+ StereoEyeParams EyeRenderParams[2];
+
+
+ // ** 2D Rendering
+
+ // Number of 2D pixels in the FOV. This defines [-1,1] coordinate range for 2D.
+ float FovPixels;
+ Matrix4f OrthoCenter;
+ float OrthoPixelOffset;
+};
+
+
+}}} // OVR::Util::Render
+
+#endif
|