Add files

This commit is contained in:
2025-01-29 10:55:49 +01:00
commit 98fba39c36
1017 changed files with 403715 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
/************************************************************************************
Filename : OVR_Alg.cpp
Content : Static lookup tables for Alg functions
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Types.h"
namespace OVR {
namespace Alg {
//------------------------------------------------------------------------
extern const uint8_t 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 uint8_t 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};
} // namespace Alg
} // namespace OVR

View File

@ -0,0 +1,854 @@
/************************************************************************************
Filename : OVR_Array.h
Content : Template implementation for Array
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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) {}
size_t GetMinCapacity() const {
return 0;
}
size_t GetGranularity() const {
return 4;
}
bool NeverShrinking() const {
return 1;
}
size_t GetCapacity() const {
return Capacity;
}
void SetCapacity(size_t capacity) {
Capacity = capacity;
}
private:
size_t 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) {}
size_t GetMinCapacity() const {
return MinCapacity;
}
size_t GetGranularity() const {
return Granularity;
}
bool NeverShrinking() const {
return NeverShrink;
}
size_t GetCapacity() const {
return Capacity;
}
void SetCapacity(size_t capacity) {
Capacity = capacity;
}
private:
size_t 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() {
if (Data) {
Allocator::DestructArray(Data, Size);
Allocator::Free(Data);
}
}
size_t GetCapacity() const {
return Policy.GetCapacity();
}
void ClearAndRelease() {
if (Data) {
Allocator::DestructArray(Data, Size);
Allocator::Free(Data);
Data = 0;
}
Size = 0;
Policy.SetCapacity(0);
}
void Reserve(size_t 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 {
size_t 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);
size_t 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(size_t newSize) {
size_t 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;
size_t 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(size_t size) : BaseType() {
Resize(size);
}
ArrayData(const SelfType& a) : BaseType(a.Policy) {
Append(a.Data, a.Size);
}
void Resize(size_t newSize) {
size_t 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);
OVR_ASSERT(this->Data != NULL);
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[], size_t count) {
if (count) {
size_t 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, size_t size) : BaseType(), DefaultValue(defval) {
Resize(size);
}
ArrayDataCC(const SelfType& a) : BaseType(a.Policy), DefaultValue(a.DefaultValue) {
Append(a.Data, a.Size);
}
void Resize(size_t newSize) {
size_t 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[], size_t count) {
if (count) {
size_t 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(size_t size) : Data(size) {}
ArrayBase(const SelfType& a) : Data(a.Data) {}
ArrayBase(const ValueType& defval) : Data(defval) {}
ArrayBase(const ValueType& defval, size_t 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();
}
size_t GetSize() const {
return Data.Size;
}
int GetSizeI() const {
return (int)Data.Size;
}
bool IsEmpty() const {
return Data.Size == 0;
}
size_t GetCapacity() const {
return Data.GetCapacity();
}
size_t GetNumBytes() const {
return Data.GetCapacity() * sizeof(ValueType);
}
void ClearAndRelease() {
Data.ClearAndRelease();
}
void Clear() {
Data.Resize(0);
}
void Resize(size_t newSize) {
Data.Resize(newSize);
}
// Reserve can only increase the capacity
void Reserve(size_t newCapacity) {
if (newCapacity > Data.GetCapacity())
Data.Reserve(newCapacity);
}
// Basic access.
ValueType& At(size_t index) {
OVR_ASSERT(
(Data.Data) &&
(index < Data.Size)); // Asserting that Data.Data is valid helps static analysis tools.
return Data.Data[index];
}
const ValueType& At(size_t index) const {
OVR_ASSERT((Data.Data) && (index < Data.Size));
return Data.Data[index];
}
ValueType ValueAt(size_t index) const {
OVR_ASSERT((Data.Data) && (index < Data.Size));
return Data.Data[index];
}
// Basic access.
ValueType& operator[](size_t index) {
OVR_ASSERT((Data.Data) && (index < Data.Size));
return Data.Data[index];
}
const ValueType& operator[](size_t index) const {
OVR_ASSERT((Data.Data) && (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(size_t count = 1) {
OVR_ASSERT(Data.Size >= count);
Data.Resize(Data.Size - count);
}
ValueType& PushDefault() {
Data.PushBack(ValueType());
return Back();
}
ValueType Pop() {
OVR_ASSERT((Data.Data) && (Data.Size > 0));
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());
OVR_ASSERT((Data.Data != NULL) || (Data.Size == 0));
for (size_t i = 0; i < Data.Size; i++) {
*(Data.Data + i) = a[i];
}
return *this;
}
// Removing multiple elements from the array.
void RemoveMultipleAt(size_t index, size_t 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.
// If order of elements in the array is not important then use
// RemoveAtUnordered, that could be much faster than the regular
// RemoveAt.
void RemoveAt(size_t index) {
OVR_ASSERT((Data.Data) && (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;
}
}
// Removes an element from the array without respecting of original order of
// elements for better performance. Do not use on array where order of elements
// is important, otherwise use it instead of regular RemoveAt().
void RemoveAtUnordered(size_t index) {
OVR_ASSERT((Data.Data) && (index < Data.Size));
if (Data.Size == 1) {
Clear();
} else {
// copy the last element into the 'index' position
// and decrement the size (instead of moving all elements
// in [index + 1 .. size - 1] range).
const size_t lastElemIndex = Data.Size - 1;
if (index < lastElemIndex) {
AllocatorType::Destruct(Data.Data + index);
AllocatorType::Construct(Data.Data + index, Data.Data[lastElemIndex]);
}
AllocatorType::Destruct(Data.Data + lastElemIndex);
--Data.Size;
}
}
// Insert the given object at the given index shifting all the elements up.
void InsertAt(size_t 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(size_t index, size_t 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 (size_t 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[], size_t count) {
Data.Append(other, count);
}
class Iterator {
SelfType* pArray;
intptr_t CurIndex;
public:
Iterator() : pArray(0), CurIndex(-1) {}
Iterator(SelfType* parr, intptr_t 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 < (intptr_t)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);
}
intptr_t 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);
}
intptr_t GetIndex() const {
return CurIndex;
}
};
Iterator Begin() {
return Iterator(this);
}
Iterator End() {
return Iterator(this, (intptr_t)GetSize());
}
Iterator Last() {
return Iterator(this, (intptr_t)GetSize() - 1);
}
class ConstIterator {
const SelfType* pArray;
intptr_t CurIndex;
public:
ConstIterator() : pArray(0), CurIndex(-1) {}
ConstIterator(const SelfType* parr, intptr_t 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);
}
intptr_t 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();
}
intptr_t GetIndex() const {
return CurIndex;
}
};
ConstIterator Begin() const {
return ConstIterator(this);
}
ConstIterator End() const {
return ConstIterator(this, (intptr_t)GetSize());
}
ConstIterator Last() const {
return ConstIterator(this, (intptr_t)GetSize() - 1);
}
// C++11 ranged-based for loop support.
Iterator begin() {
return Begin();
}
Iterator end() {
return End();
}
ConstIterator begin() const {
return Begin();
}
ConstIterator end() const {
return End();
}
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() {}
explicit Array(size_t 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() {}
explicit ArrayPOD(size_t 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() {}
explicit ArrayCPP(size_t 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, size_t 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;
}
};
} // namespace OVR
#endif

View File

@ -0,0 +1,134 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Atomic.h"
#include "OVR_Allocator.h"
#ifdef OVR_ENABLE_THREADS
// Include Windows 8-Metro compatible Synchronization API
#if defined(OVR_OS_MS) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
#include <synchapi.h>
#endif
namespace OVR {
// ***** Windows Lock implementation
#if defined(OVR_OS_MS)
// ***** 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, (DWORD)spinCount, OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO));
#else
::InitializeCriticalSectionAndSpinCount(
&cs, (DWORD)spinCount); // This is available with WindowsXP+.
#endif
}
Lock::~Lock() {
DeleteCriticalSection(&cs);
}
#endif
//-------------------------------------------------------------------------------------
// ***** 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, oldUseCount_tmp;
do {
oldUseCount = UseCount;
if (oldUseCount == (int)LockInitMarker)
continue;
if (oldUseCount == 0) {
// Initialize marker
int tmp_zero = 0;
int tmp_LockInitMarker = LockInitMarker;
if (UseCount.compare_exchange_strong(tmp_zero, LockInitMarker)) {
Construct<Lock>(Buffer);
do {
} while (UseCount.compare_exchange_weak(tmp_LockInitMarker, 1));
return toLock();
}
continue;
}
oldUseCount_tmp = oldUseCount;
} while (
!UseCount.compare_exchange_weak(oldUseCount_tmp, oldUseCount + 1, std::memory_order_relaxed));
return toLock();
}
void SharedLock::ReleaseLock(Lock* plock) {
OVR_UNUSED(plock);
OVR_ASSERT(plock == toLock());
int oldUseCount, oldUseCount_tmp;
do {
oldUseCount = UseCount;
OVR_ASSERT(oldUseCount != (int)LockInitMarker);
if (oldUseCount == 1) {
// Initialize marker
int tmp_one = 1;
int tmp_LockInitMarker = LockInitMarker;
if (UseCount.compare_exchange_strong(tmp_one, LockInitMarker)) {
Destruct<Lock>(toLock());
do {
} while (!UseCount.compare_exchange_weak(tmp_LockInitMarker, 0));
return;
}
continue;
}
oldUseCount_tmp = oldUseCount;
} while (
!UseCount.compare_exchange_weak(oldUseCount_tmp, oldUseCount - 1, std::memory_order_relaxed));
}
} // namespace OVR
#endif // OVR_ENABLE_THREADS

View File

@ -0,0 +1,216 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Atomic_h
#define OVR_Atomic_h
#include "OVR_Types.h"
#include <atomic>
// Include System thread functionality.
#if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE)
#include "OVR_Win32_IncludeWindows.h"
#else
#include <pthread.h>
#endif
namespace OVR {
//-----------------------------------------------------------------------------------
// ***** Lock
// Lock is a simplest and most efficient mutual-exclusion lock class.
// Unlike Mutex, it cannot be waited on.
class Lock {
#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_MS)
CRITICAL_SECTION cs;
public:
Lock(unsigned spinCount = 10000); // Mutexes with non-zero spin counts usually result in better
// performance.
~Lock();
// Locking functions.
inline void DoLock() {
::EnterCriticalSection(&cs);
}
inline void Unlock() {
::LeaveCriticalSection(&cs);
}
inline bool TryLock() {
return (::TryEnterCriticalSection(&cs) == TRUE);
}
#else
pthread_mutex_t mutex;
public:
static pthread_mutexattr_t RecursiveAttr;
static bool RecursiveAttrInit;
Lock(unsigned spinCount = 0) // To do: Support spin count, probably via a custom lock
// implementation.
{
OVR_UNUSED(spinCount);
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);
}
inline bool TryLock() {
return (pthread_mutex_trylock(&mutex) == 0);
}
#endif // OVR_ENABLE_THREDS
public:
// Locker class, used for automatic locking
class Locker {
Lock* pLock;
public:
Locker(Lock* plock) {
pLock = plock;
if (plock)
pLock->DoLock();
}
~Locker() {
Release();
}
void Release() {
if (pLock)
pLock->Unlock();
pLock = nullptr;
}
};
// Unlocker class, used for automatic unlocking
class Unlocker {
// OVR_NON_COPYABLE(Unlocker);
Lock* mLock;
public:
Unlocker(Lock* lock) : mLock(lock) {}
~Unlocker() {
Release();
}
void Release() {
if (mLock)
mLock->Unlock();
mLock = nullptr;
}
};
};
//-------------------------------------------------------------------------------------
// Globally shared Lock implementation used for MessageHandlers, etc.
class SharedLock {
public:
SharedLock() : UseCount(0) {}
Lock* GetLockAddRef();
void ReleaseLock(Lock* plock);
private:
Lock* toLock() {
return (Lock*)Buffer;
}
// UseCount and max alignment.
std::atomic<int> UseCount;
uint64_t Buffer[(sizeof(Lock) + sizeof(uint64_t) - 1) / sizeof(uint64_t)];
};
//-------------------------------------------------------------------------------------
// Thin locking wrapper around data
template <class T>
class LockedData {
public:
LockedData(Lock& lock) : TheLock(lock) {}
LockedData& operator=(const LockedData& /*rhs*/) {
OVR_ASSERT(false);
return *this;
}
T Get() {
Lock::Locker locker(&TheLock);
return Instance;
}
void Set(const T& value) {
Lock::Locker locker(&TheLock);
Instance = value;
}
// Returns true if the value has changed.
// Returns false if the value has not changed.
bool GetIfChanged(T& value) {
Lock::Locker locker(&TheLock);
if (value != Instance) {
value = Instance;
return true;
}
return false;
}
protected:
T Instance;
Lock& TheLock;
};
} // namespace OVR
#endif

View File

@ -0,0 +1,42 @@
/************************************************************************************
PublicHeader: Kernel
Filename : OVR_Callbacks.cpp
Content : Callback library
Created : Nov 17, 2014
Author : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Callbacks.h"
namespace OVR {
// Global emitter lock
//
// Add/remove operations on callbacks happen infrequently, and are already fairly
// serialized in order of construction by design. Therefore contention for this
// lock between call()/shutdown() is the main concern and is also rare.
Lock* CallbackEmitterBase::GetEmitterLock() {
static Lock lock;
return &lock;
}
} // namespace OVR

View File

@ -0,0 +1,282 @@
/************************************************************************************
PublicHeader: Kernel
Filename : OVR_Callbacks.h
Content : Callback library
Created : June 20, 2014
Author : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Callbacks_h
#define OVR_Callbacks_h
#include "OVR_CallbacksInternal.h"
#include "OVR_String.h" // For CallbackHash
#include "OVR_Hash.h" // For CallbackHash
namespace OVR {
//-----------------------------------------------------------------------------
// CallbackEmitter
//
// Emitter of callbacks.
// Thread-safety: All public members may be safely called concurrently.
template <class DelegateT>
class CallbackEmitter : public NewOverrideBase {
public:
CallbackEmitter();
~CallbackEmitter();
// Add a listener.
bool AddListener(CallbackListener<DelegateT>* listener);
// Get the current number of listeners. Note that this can change as other threads
// add listeners to the emitter.
int GetListenerCount() const;
bool HasListeners() const {
return Emitter->HasListeners();
}
void Call() {
Emitter->Call();
}
template <class Param1>
void Call(Param1* p1) {
Emitter->Call(p1);
}
template <class Param1>
void Call(Param1& p1) {
Emitter->Call(p1);
}
template <class Param1, class Param2>
void Call(Param1* p1, Param2* p2) {
Emitter->Call(p1, p2);
}
template <class Param1, class Param2>
void Call(Param1& p1, Param2& p2) {
Emitter->Call(p1, p2);
}
template <class Param1, class Param2, class Param3>
void Call(Param1* p1, Param2* p2, Param3* p3) {
Emitter->Call(p1, p2, p3);
}
template <class Param1, class Param2, class Param3>
void Call(Param1& p1, Param2& p2, Param3& p3) {
Emitter->Call(p1, p2, p3);
}
// Remove all listeners and prevent further listeners from being added.
void Shutdown();
protected:
Ptr<FloatingCallbackEmitter<DelegateT>> Emitter;
};
//-----------------------------------------------------------------------------
// CallbackListener
//
// Listener for callbacks.
// Thread-safety: Operations on a listener are not thread-safe.
// The listener may only listen to *one emitter* at a time.
template <class DelegateT>
class CallbackListener : public NewOverrideBase {
friend class CallbackEmitter<DelegateT>;
public:
CallbackListener();
~CallbackListener();
// Stop listening to callbacks.
// And set a new handler for callbacks.
void SetHandler(DelegateT handler);
// Is listening to an emitter at this instant?
// If the Emitter has shutdown, then this may inaccurately return true.
bool IsListening() const;
// Stops listening to callbacks.
void Cancel();
protected:
/// Internal data:
// Reference to the associated listener.
Ptr<FloatingCallbackListener<DelegateT>> FloatingListener;
// Reference to the associated emitter.
Ptr<FloatingCallbackEmitter<DelegateT>> FloatingEmitter;
DelegateT Handler;
};
//-----------------------------------------------------------------------------
// Template Implementation: CallbackEmitter
template <class DelegateT>
CallbackEmitter<DelegateT>::CallbackEmitter() {
Emitter = *new FloatingCallbackEmitter<DelegateT>;
}
template <class DelegateT>
CallbackEmitter<DelegateT>::~CallbackEmitter() {
Emitter->Shutdown();
// Emitter goes out of scope here.
}
template <class DelegateT>
bool CallbackEmitter<DelegateT>::AddListener(CallbackListener<DelegateT>* listener) {
// The listener object can only be attached to one emitter at a time.
// The caller should explicitly Cancel() a listener before listening
// to a new emitter, even if it is the same emitter.
OVR_ASSERT(!listener->FloatingEmitter && !listener->FloatingListener);
if (listener->FloatingEmitter || listener->FloatingListener) {
// Cancel any previous listening
listener->Cancel();
}
// Set the floating listener and emitter
listener->FloatingListener = *new FloatingCallbackListener<DelegateT>(listener->Handler);
listener->FloatingEmitter = Emitter.GetPtr();
// The remaining input checks are performed inside.
return Emitter->AddListener(listener->FloatingListener);
}
template <class DelegateT>
int CallbackEmitter<DelegateT>::GetListenerCount() const {
return Emitter->Listeners.GetSizeI();
}
template <class DelegateT>
void CallbackEmitter<DelegateT>::Shutdown() {
Emitter->Shutdown();
}
//-----------------------------------------------------------------------------
// Template Implementation: CallbackListener
template <class DelegateT>
CallbackListener<DelegateT>::CallbackListener() {
// Listener is null until a handler is set.
}
template <class DelegateT>
CallbackListener<DelegateT>::~CallbackListener() {
Cancel();
}
template <class DelegateT>
void CallbackListener<DelegateT>::Cancel() {
if (FloatingListener) {
FloatingListener->EnterCancelState();
}
if (FloatingEmitter) {
if (FloatingListener) {
FloatingEmitter->OnListenerCancel(FloatingListener);
}
}
// FloatingEmitter goes out of scope here.
FloatingEmitter = nullptr;
// FloatingListener goes out of scope here.
FloatingListener = nullptr;
}
template <class DelegateT>
void CallbackListener<DelegateT>::SetHandler(DelegateT handler) {
Cancel();
Handler = handler;
}
template <class DelegateT>
bool CallbackListener<DelegateT>::IsListening() const {
if (!FloatingListener.GetPtr()) {
return false;
}
return FloatingListener->IsValid();
}
//-----------------------------------------------------------------------------
// CallbackHash
//
// A hash containing CallbackEmitters
template <class DelegateT>
class CallbackHash : public NewOverrideBase {
typedef Hash<String, CallbackEmitter<DelegateT>*, String::HashFunctor> HashTable;
public:
~CallbackHash() {
Clear();
}
void Clear() {
for (auto ii = Table.Begin(); ii != Table.End(); ++ii) {
delete ii->Second;
}
Table.Clear();
}
CallbackEmitter<DelegateT>* GetKey(String key) {
CallbackEmitter<DelegateT>** emitter = Table.Get(key);
if (emitter) {
return *emitter;
}
return nullptr;
}
void AddListener(String key, CallbackListener<DelegateT>* listener) {
CallbackEmitter<DelegateT>** pEmitter = Table.Get(key);
CallbackEmitter<DelegateT>* emitter = nullptr;
if (!pEmitter) {
emitter = new CallbackEmitter<DelegateT>;
Table.Add(key, emitter);
} else {
emitter = *pEmitter;
}
emitter->AddListener(listener);
}
void RemoveKey(String key) {
CallbackEmitter<DelegateT>** emitter = Table.Get(key);
if (emitter) {
delete *emitter;
Table.Remove(key);
}
}
protected:
HashTable Table; // Hash table
};
} // namespace OVR
#endif // OVR_Callbacks_h

View File

@ -0,0 +1,338 @@
/************************************************************************************
PublicHeader: Kernel
Filename : OVR_CallbacksInternal.h
Content : Callback library
Created : Nov 11, 2014
Author : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_CallbacksInternal_h
#define OVR_CallbacksInternal_h
#include "OVR_Atomic.h"
#include "OVR_RefCount.h"
#include "OVR_Delegates.h"
#include "OVR_Array.h"
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
#include <atomic>
#endif
namespace OVR {
template <class DelegateT>
class FloatingCallbackEmitter; // Floating emitter object
template <class DelegateT>
class CallbackEmitter;
template <class DelegateT>
class FloatingCallbackListener; // Floating listener object
template <class DelegateT>
class CallbackListener;
//-----------------------------------------------------------------------------
// FloatingCallbackEmitter
//
// The Call() function is not thread-safe.
// TBD: Should we add a thread-safe Call() option to constructor?
class CallbackEmitterBase {
protected:
static Lock* GetEmitterLock();
};
template <class DelegateT>
class FloatingCallbackEmitter : public CallbackEmitterBase,
public RefCountBase<FloatingCallbackEmitter<DelegateT>> {
friend class CallbackEmitter<DelegateT>;
FloatingCallbackEmitter()
: IsShutdown(false),
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
ListenersExist(false),
#endif
DirtyListenersCache(0) {
}
public:
typedef Array<Ptr<FloatingCallbackListener<DelegateT>>> ListenerPtrArray;
~FloatingCallbackEmitter() {
OVR_ASSERT(Listeners.GetSizeI() == 0);
// ListenersCache will be emptied here.
}
bool AddListener(FloatingCallbackListener<DelegateT>* listener);
bool HasListeners() const {
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
return ListenersExist.load(std::memory_order_relaxed);
#else
// This code still has a data race
return (Listeners.GetSizeI() > 0);
#endif
}
void Shutdown();
// Called from the listener object as it is transitioning to canceled state.
// The listener's mutex is not held during this call.
void OnListenerCancel(FloatingCallbackListener<DelegateT>* listener);
public:
void Call();
template <class Param1>
void Call(Param1* p1);
template <class Param1>
void Call(Param1& p1);
template <class Param1, class Param2>
void Call(Param1* p1, Param2* p2);
template <class Param1, class Param2>
void Call(Param1& p1, Param2& p2);
template <class Param1, class Param2, class Param3>
void Call(Param1* p1, Param2* p2, Param3* p3);
template <class Param1, class Param2, class Param3>
void Call(Param1& p1, Param2& p2, Param3& p3);
protected:
// Is the emitter shut down? This prevents more listeners from being added during shutdown.
bool IsShutdown;
// Array of added listeners.
ListenerPtrArray Listeners;
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
std::atomic<bool> ListenersExist;
#endif
// Is the cache dirty? This avoids locking and memory allocation in steady state.
std::atomic<uint32_t> DirtyListenersCache = {0};
// Cache of listeners used by the Call() function.
ListenerPtrArray ListenersCacheForCalls;
// Update the ListenersCache array in response to an insertion or removal.
// This is how AddListener() insertions get rolled into the listeners array.
// This is how RemoveListener() removals get purged from the cache.
void updateListenersCache() {
if (DirtyListenersCache != 0) {
Lock::Locker locker(GetEmitterLock());
// TBD: Should memory allocation be further reduced here?
ListenersCacheForCalls = Listeners;
DirtyListenersCache = 0;
}
}
// Without holding a lock, find and remove the given listener from the array of listeners.
void noLockFindAndRemoveListener(FloatingCallbackListener<DelegateT>* listener) {
const int count = Listeners.GetSizeI();
for (int i = 0; i < count; ++i) {
if (Listeners[i] == listener) {
Listeners.RemoveAt(i);
// After removing it from the array, set the dirty flag.
// Note: Because the flag is atomic, a portable memory fence is implied.
DirtyListenersCache = 1;
break;
}
}
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
if (Listeners.GetSizeI() < 1) {
ListenersExist.store(false, std::memory_order_relaxed);
}
#endif
}
};
//-----------------------------------------------------------------------------
// FloatingCallbackListener
//
// Internal implementation class for the CallbackListener.
// This can only be associated with one CallbackListener object for its lifetime.
template <class DelegateT>
class FloatingCallbackListener : public RefCountBase<FloatingCallbackListener<DelegateT>> {
public:
FloatingCallbackListener(DelegateT handler);
~FloatingCallbackListener();
void EnterCancelState();
bool IsValid() const {
return Handler.IsValid();
}
// TBD: Should these be binned to reduce the lock count?
// Boost does not do that. And I am worried about deadlocks when misused.
mutable Lock ListenerLock;
// Handler function
DelegateT Handler;
};
//-----------------------------------------------------------------------------
// Template Implementation: FloatingCallbackEmitter
template <class DelegateT>
bool FloatingCallbackEmitter<DelegateT>::AddListener(
FloatingCallbackListener<DelegateT>* listener) {
Lock::Locker locker(GetEmitterLock());
if (IsShutdown) {
return false;
}
// Add the listener to our list
Listeners.PushBack(listener);
// After adding it to the array, set the dirty flag.
// Note: Because the flag is atomic, a portable memory fence is implied.
DirtyListenersCache = 1;
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
ListenersExist.store(true, std::memory_order_relaxed);
#endif
return true;
}
// Called from the listener object as it is transitioning to canceled state.
// The listener's mutex is not held during this call.
template <class DelegateT>
void FloatingCallbackEmitter<DelegateT>::OnListenerCancel(
FloatingCallbackListener<DelegateT>* listener) {
Lock::Locker locker(GetEmitterLock());
// If not shut down,
// Note that if it is shut down then there will be no listeners in the array.
if (!IsShutdown) {
// Remove it.
noLockFindAndRemoveListener(listener);
}
}
template <class DelegateT>
void FloatingCallbackEmitter<DelegateT>::Shutdown() {
Lock::Locker locker(GetEmitterLock());
IsShutdown = true;
Listeners.ClearAndRelease();
// Note: Because the flag is atomic, a portable memory fence is implied.
DirtyListenersCache = 1;
#if !defined(OVR_CC_MSVC) || (OVR_CC_VERSION > 1600) // Newer than VS2010
ListenersExist.store(false, std::memory_order_relaxed);
#endif
}
//-----------------------------------------------------------------------------
// Call function
//
// (1) Update the cache of listener references, if it has changed.
// (2) For each listener,
// (a) Hold ListenerLock.
// (b) If listener handler is valid, call the handler.
#define OVR_EMITTER_CALL_BODY(params) \
updateListenersCache(); \
if (IsShutdown) \
return; /* Pure optimization. It is fine if this races. */ \
const int count = ListenersCacheForCalls.GetSizeI(); \
for (int i = 0; i < count; ++i) { \
Lock::Locker locker(&ListenersCacheForCalls[i]->ListenerLock); \
if (ListenersCacheForCalls[i]->Handler.IsValid()) { \
ListenersCacheForCalls[i]->Handler params; /* Using a macro for this line. */ \
} \
}
template <class DelegateT>
void FloatingCallbackEmitter<DelegateT>::Call() {
OVR_EMITTER_CALL_BODY(())
}
template <class DelegateT>
template <class Param1>
void FloatingCallbackEmitter<DelegateT>::Call(Param1* p1) {
OVR_EMITTER_CALL_BODY((p1))
}
template <class DelegateT>
template <class Param1>
void FloatingCallbackEmitter<DelegateT>::Call(Param1& p1) {
OVR_EMITTER_CALL_BODY((p1))
}
template <class DelegateT>
template <class Param1, class Param2>
void FloatingCallbackEmitter<DelegateT>::Call(Param1* p1, Param2* p2) {
OVR_EMITTER_CALL_BODY((p1, p2))
}
template <class DelegateT>
template <class Param1, class Param2>
void FloatingCallbackEmitter<DelegateT>::Call(Param1& p1, Param2& p2) {
OVR_EMITTER_CALL_BODY((p1, p2))
}
template <class DelegateT>
template <class Param1, class Param2, class Param3>
void FloatingCallbackEmitter<DelegateT>::Call(Param1* p1, Param2* p2, Param3* p3) {
OVR_EMITTER_CALL_BODY((p1, p2, p3))
}
template <class DelegateT>
template <class Param1, class Param2, class Param3>
void FloatingCallbackEmitter<DelegateT>::Call(Param1& p1, Param2& p2, Param3& p3) {
OVR_EMITTER_CALL_BODY((p1, p2, p3))
}
#undef OVR_EMITTER_CALL_BODY
//-----------------------------------------------------------------------------
// Template Implementation: FloatingCallbackListener
template <class DelegateT>
FloatingCallbackListener<DelegateT>::FloatingCallbackListener(DelegateT handler)
: Handler(handler) {
OVR_ASSERT(Handler.IsValid());
}
template <class DelegateT>
FloatingCallbackListener<DelegateT>::~FloatingCallbackListener() {
OVR_ASSERT(!Handler.IsValid());
}
template <class DelegateT>
void FloatingCallbackListener<DelegateT>::EnterCancelState() {
ListenerLock.DoLock();
Handler.Invalidate();
ListenerLock.Unlock();
}
} // namespace OVR
#endif // OVR_CallbacksInternal_h

View File

@ -0,0 +1,68 @@
/************************************************************************************
Filename : OVR_Color.h
Content : Contains color struct.
Created : February 7, 2013
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Color_h
#define OVR_Color_h
#include "OVR_Types.h"
namespace OVR {
struct Color {
uint8_t R, G, B, A;
Color() {
#if defined(OVR_BUILD_DEBUG)
R = G = B = A = 0;
#endif
}
// 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;
}
};
} // namespace OVR
#endif

View File

@ -0,0 +1,244 @@
/************************************************************************************
Filename : OVR_ContainerAllocator.h
Content : Template allocators and constructors for containers.
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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(size_t size) {
return OVR_ALLOC(size);
}
static void* Realloc(void* p, size_t 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*, size_t) {}
static void ConstructArray(void* p, size_t count, const T& source) {
uint8_t* pdata = (uint8_t*)p;
for (size_t i = 0; i < count; ++i, pdata += sizeof(T))
*(T*)pdata = source;
}
static void ConstructArray(void* p, size_t count, const T* psource) {
memcpy(p, psource, sizeof(T) * count);
}
static void Destruct(T*) {}
static void DestructArray(T*, size_t) {}
static void CopyArrayForward(T* dst, const T* src, size_t count) {
memmove(dst, src, count * sizeof(T));
}
static void CopyArrayBackward(T* dst, const T* src, size_t 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, size_t count) {
uint8_t* pdata = (uint8_t*)p;
for (size_t i = 0; i < count; ++i, pdata += sizeof(T))
Construct(pdata);
}
static void ConstructArray(void* p, size_t count, const T& source) {
uint8_t* pdata = (uint8_t*)p;
for (size_t i = 0; i < count; ++i, pdata += sizeof(T))
Construct(pdata, source);
}
static void ConstructArray(void* p, size_t count, const T* psource) {
uint8_t* pdata = (uint8_t*)p;
for (size_t 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, size_t count) {
for (size_t i = 0; i < count; ++i, ++p)
p->~T();
}
static void CopyArrayForward(T* dst, const T* src, size_t count) {
memmove(dst, src, count * sizeof(T));
}
static void CopyArrayBackward(T* dst, const T* src, size_t 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, size_t count) {
uint8_t* pdata = (uint8_t*)p;
for (size_t i = 0; i < count; ++i, pdata += sizeof(T))
Construct(pdata);
}
static void ConstructArray(void* p, size_t count, const T& source) {
uint8_t* pdata = (uint8_t*)p;
for (size_t i = 0; i < count; ++i, pdata += sizeof(T))
Construct(pdata, source);
}
static void ConstructArray(void* p, size_t count, const T* psource) {
uint8_t* pdata = (uint8_t*)p;
for (size_t 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, size_t count) {
for (size_t i = 0; i < count; ++i, ++p)
p->~T();
}
static void CopyArrayForward(T* dst, const T* src, size_t count) {
for (size_t i = 0; i < count; ++i)
dst[i] = src[i];
}
static void CopyArrayBackward(T* dst, const T* src, size_t count) {
for (size_t 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> {};
} // namespace OVR
#endif

View File

@ -0,0 +1,87 @@
/************************************************************************************
Filename : OVR_DLLHelper.h
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#pragma once
#ifdef _WIN32
#include <Windows.h>
#include <type_traits>
namespace OVR {
///////////////////////////////////////////////////////////////////////////////////////////////////
// Example usage
//
// Write one of these for each of the modules (DLLs) you want to use functions from.
// The following class can be safely declared at global scope.
// class Kernel32API {
// public:
// decltype(GetModuleFileNameA)* getModuleFileNameA = dllHelper.Load("GetModuleFileNameA");
//
// protected:
// DllHelper dllHelper{"Kernel32.dll"};
// };
//
// Use the class declared above:
// void main() {
// Kernel32API kernel32Api;
//
// if(kernel32Api.getModuleFileNameA)
// kernel32Api.getModuleFileNameA(NULL, path, sizeof(path));
// }
///////////////////////////////////////////////////////////////////////////////////////////////////
// DllHelper instances can be declared at global scope in most cases.
class DllHelper {
public:
// Example moduleFileName: "kernel32.dll"
explicit DllHelper(const char* moduleFileName) : moduleHandle(::LoadLibraryA(moduleFileName)) {}
~DllHelper() {
::FreeLibrary(moduleHandle);
}
class ProcPtr {
public:
explicit ProcPtr(FARPROC ptr) : procPtr(ptr) {}
template <typename T, typename = std::enable_if_t<std::is_function_v<T>>>
operator T*() const {
return reinterpret_cast<T*>(procPtr);
}
private:
FARPROC procPtr;
};
// Example proc name: "GetModuleFileName"
ProcPtr Load(const char* procName) const {
return ProcPtr(GetProcAddress(moduleHandle, procName));
}
protected:
HMODULE moduleHandle;
};
} // namespace OVR
#endif // _WIN32

View File

@ -0,0 +1,817 @@
/************************************************************************************
Filename : OVR_DebugHelp.h
Content : Platform-independent exception handling interface
Created : October 6, 2014
Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_DebugHelp_h
#define OVR_DebugHelp_h
#include "OVR_Types.h"
#include "OVR_String.h"
#include "OVR_Threads.h"
#include "OVR_Atomic.h"
#include "OVR_Nullptr.h"
#include "OVR_System.h"
#include <stdio.h>
#include <time.h>
#if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64)
#include "OVR_Win32_IncludeWindows.h"
#elif defined(OVR_OS_APPLE)
#include <pthread.h>
#include <mach/thread_status.h>
#include <mach/mach_types.h>
extern "C" void* MachHandlerThreadFunctionStatic(void*);
extern "C" int catch_mach_exception_raise_state_identity_OVR(
mach_port_t,
mach_port_t,
mach_port_t,
exception_type_t,
mach_exception_data_type_t*,
mach_msg_type_number_t,
int*,
thread_state_t,
mach_msg_type_number_t,
thread_state_t,
mach_msg_type_number_t*);
#elif defined(OVR_OS_LINUX)
#include <pthread.h>
#endif
OVR_DISABLE_MSVC_WARNING(4351) // new behavior: elements of array will be default initialized
namespace OVR {
// Thread identifiers
// typedef void* ThreadHandle; // Already defined by OVR Threads. Same as Windows thread
// handle, Unix pthread_t.
// typedef void* ThreadId; // Already defined by OVR Threads. Used by Windows as DWORD
// thread id, by Unix as pthread_t.
typedef uintptr_t ThreadSysId; // System thread identifier. Used by Windows the same as ThreadId
// (DWORD), thread_act_t on Mac/BSD, lwp id on Linux.
// Thread constants
// To do: Move to OVR Threads
#define OVR_THREADHANDLE_INVALID ((ThreadHandle*)nullptr)
#define OVR_THREADID_INVALID ((ThreadId*)nullptr)
#define OVR_THREADSYSID_INVALID ((uintptr_t)0)
OVR::ThreadSysId ConvertThreadHandleToThreadSysId(OVR::ThreadHandle threadHandle);
OVR::ThreadHandle ConvertThreadSysIdToThreadHandle(
OVR::ThreadSysId threadSysId); // The returned handle must be freed with FreeThreadHandle.
void FreeThreadHandle(OVR::ThreadHandle threadHandle); // Frees the handle returned by
// ConvertThreadSysIdToThreadHandle.
OVR::ThreadSysId GetCurrentThreadSysId();
void GetOSVersionName(char* versionName, size_t versionNameCapacity);
// CPUContext
#if defined(OVR_OS_MS)
typedef CONTEXT CPUContext;
#elif defined(OVR_OS_MAC)
struct CPUContext {
x86_thread_state_t threadState; // This works for both x86 and x64.
x86_float_state_t floatState;
x86_debug_state_t debugState;
x86_avx_state_t avxState;
x86_exception_state exceptionState;
CPUContext() {
memset(this, 0, sizeof(CPUContext));
}
};
#elif defined(OVR_OS_LINUX)
typedef int CPUContext; // To do.
#endif
// Force OVRIsDebuggerPresent to return false
void ForceDebuggerNotPresent();
// Allow debugger check to proceded as normal
void ClearDebuggerNotPresent();
// Tells if the current process appears to be running under a debugger. Does not attempt to
// detect the case of stealth debuggers (malware-related for example).
bool OVRIsDebuggerPresent();
// Exits the process with the given exit code.
#if !defined(OVR_NORETURN)
#if defined(OVR_CC_MSVC)
#define OVR_NORETURN __declspec(noreturn)
#else
#define OVR_NORETURN __attribute__((noreturn))
#endif
#endif
OVR_NORETURN void ExitProcess(intptr_t processReturnValue);
// Returns the instruction pointer of the caller for the position right after the call.
OVR_NO_INLINE void* GetInstructionAddress();
// Returns the instruction pointer of the call to the caller of this function.
// This is a macro defined as with the following C declaration:
// void* GetInstructionAddress();
#ifndef OVRGetReturnAddress
#if defined(_MSC_VER)
#define OVRGetReturnAddress() _ReturnAddress()
#else // GCC, clang
#define OVRGetReturnAddress() __builtin_return_address(0)
#endif
#endif
// Returns the stack base and limit addresses for the given thread, or for the current thread if the
// threadHandle is default.
// The stack limit is a value less than the stack base on most platforms, as stacks usually grow
// downward.
// Some platforms (e.g. Microsoft) have dynamically resizing stacks, in which case the stack limit
// reflects the current limit.
void GetThreadStackBounds(
void*& pStackBase,
void*& pStackLimit,
ThreadHandle threadHandle = OVR_THREADHANDLE_INVALID);
enum MemoryAccess { kMANone = 0x00, kMARead = 0x01, kMAWrite = 0x02, kMAExecute = 0x04 };
// Returns MemoryAccess flags. Returns kMAUnknown for unknown access.
int GetMemoryAccess(const void* p);
/// Used by KillCdeclFunction and RestoreCdeclFunction
///
struct SavedFunction {
void* Function;
uint8_t Size;
uint8_t Data[15];
void* FunctionImplementation; // Points to the original function, if possible to use it.
SavedFunction() : Function(nullptr), Size(0), FunctionImplementation(nullptr) {}
SavedFunction(int) {} // Intentionally uninitialized
};
/// Overwrites the implementation of a statically linked function with an implementation
/// that unilaterally returns the given int32_t value. Works regardless of the arguments
/// passed to that function by the caller. This version is specific to cdecl functions
/// as opposed to Microsoft stdcall functions. Requires the ability to use VirtualProtect
/// to change the code memory to be writable. Returns true if the operation was successful.
///
/// Since this function overwrites the memory of the existing function implementation,
/// it reequires the function to have at least enough bytes for this. If functionReturnValue
/// is zero then pFunction must be at least three bytes in size. If functionReturnValue is
/// non-zero then pFunction must be at least six bytes in size.
///
/// Example usage:
/// int __cdecl _CrtIsValidHeapPointer(const void* heapPtr);
///
/// void main(int, char*[]){
/// KillCdeclFunction(_CrtIsValidHeapPointer, TRUE); // Make _CrtIsValidHeapPointer always
/// return true.
/// }
///
bool KillCdeclFunction(
void* pFunction,
int32_t functionReturnValue,
SavedFunction* pSavedFunction = nullptr);
/// This version is for functions that return void. It causes them to immediately return.
///
/// Example usage:
/// void __cdecl _CrtCheckMemory();
///
/// void main(int, char*[]){
/// KillCdeclFunction(_CrtCheckMemory);
/// }
///
bool KillCdeclFunction(void* pFunction, SavedFunction* pSavedFunction = nullptr);
/// RedirectCdeclFunction
///
/// Upon success, pSavedFunction is modified to contain a saved copy of the modified bytes.
/// Upon failure, pSavedFunction is not modified.
/// RestoreCdeclFunction can be used to restore the bytes saved by pSavedFunction.
///
/// Example usage:
/// void* MyMalloc(size_t n)
/// { ... }
/// RedirectCdeclFunction(malloc, MyMalloc);
///
bool RedirectCdeclFunction(
void* pFunction,
const void* pDestFunction,
OVR::SavedFunction* pSavedFunction = nullptr);
/// Restores a function which was previously killed by KillCdeclFunction.
///
/// Example usage:
/// void main(int, char*[]){
/// SavedFunction savedFunction
/// KillCdeclFunction(_CrtCheckMemory, &savedFunction);
/// [...]
/// RestoreCdeclFunction(&savedFunction);
/// }
///
bool RestoreCdeclFunction(SavedFunction* pSavedFunction);
/// Smart class which temporarily kills a function and restores it upon scope completion.
///
/// Example usage:
/// void main(int, char*[]){
/// TempCdeclFunctionKill tempKill(_CrtIsValidHeapPointer, TRUE);
/// [...]
/// }
///
struct TempCdeclFunctionKill {
TempCdeclFunctionKill(void* pFunction, int32_t functionReturnValue)
: Success(false), FunctionPtr(nullptr), SavedFunctionData() {
Success = KillCdeclFunction(pFunction, functionReturnValue, &SavedFunctionData);
}
TempCdeclFunctionKill(void* pFunction)
: Success(false), FunctionPtr(nullptr), SavedFunctionData() {
Success = KillCdeclFunction(pFunction, &SavedFunctionData);
}
~TempCdeclFunctionKill() {
if (Success)
RestoreCdeclFunction(&SavedFunctionData);
}
bool Success;
void* FunctionPtr;
SavedFunction SavedFunctionData;
};
/// Class which implements copying the executable bytes of a function to a newly allocated page.
/// This is useful for when doing some kinds of function interception and overriding at runtime.
///
/// Example usage:
/// void main(int, char*[]){
/// CopiedFunction strlenCopy(strlen);
/// size_t n = strlen("test"); // Will execute through the newly allocated version of strlen.
/// }
///
class CopiedFunction {
public:
CopiedFunction(const void* pFunction = nullptr, size_t size = 0);
~CopiedFunction();
const void* Copy(const void* pFunction, size_t size);
void Free();
const void* GetFunction() const {
return Function;
}
protected:
const void* GetRealFunctionLocation(const void* pFunction);
void* Function;
};
// OVR_MAX_PATH
// Max file path length (for most uses).
// To do: move this to OVR_File.
#if !defined(OVR_MAX_PATH)
#if defined( \
OVR_OS_MS) // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
#define OVR_MAX_PATH \
260 // Windows can use paths longer than this in some cases (network paths, UNC paths).
#else
#define OVR_MAX_PATH 1024 // This isn't a strict limit on all Unix-based platforms.
#endif
#endif
// ModuleHandle
#if defined(OVR_OS_MS)
typedef void* ModuleHandle; // from LoadLibrary()
#elif defined(OVR_OS_APPLE) || defined(OVR_OS_UNIX)
typedef void* ModuleHandle; // from dlopen()
#endif
#define OVR_MODULEHANDLE_INVALID ((ModuleHandle*)nullptr)
// Module info constants
static const ModuleHandle kMIHandleInvalid = OVR_MODULEHANDLE_INVALID;
static const uint64_t kMIAddressInvalid = 0xffffffffffffffffull;
static const uint64_t kMISizeInvalid = 0xffffffffffffffffull;
static const int32_t kMILineNumberInvalid = -1;
static const int32_t kMIFunctionOffsetInvalid = -1;
static const uint64_t kMIBaseAddressInvalid = 0xffffffffffffffffull;
static const uint64_t kMIBaseAddressUnspecified = 0xffffffffffffffffull;
struct ModuleInfo {
ModuleHandle handle;
uint64_t baseAddress; // The actual runtime base address of the module. May be different from
// the base address specified in the debug symbol file, because the module may be placed at a
// different address on startup.
uint64_t size;
char filePath[OVR_MAX_PATH]; // UTF8 encoded
char name[32]; // UTF8 encoded
char type[8]; // Unix-specific. e.g. __TEXT
char permissions[8]; // Unix specific. e.g. "drwxr-xr-x"
ModuleInfo()
: handle(kMIHandleInvalid), baseAddress(kMIBaseAddressInvalid), size(0), filePath(), name() {}
};
// Refers to symbol info for an instruction address.
// Info includes function name, source code file/line, and source code itself.
struct SymbolInfo {
uint64_t address;
uint64_t size;
const ModuleInfo* pModuleInfo;
char filePath[OVR_MAX_PATH];
int32_t fileLineNumber;
char function[384]; // This is a fixed size because we need to use it during application
// exceptions.
int32_t functionOffset;
char sourceCode[1024]; // This is a string representing the code itself and not a file path to the
// code.
SymbolInfo()
: address(kMIAddressInvalid),
size(kMISizeInvalid),
pModuleInfo(nullptr),
filePath(),
fileLineNumber(kMILineNumberInvalid),
function(),
functionOffset(kMIFunctionOffsetInvalid),
sourceCode() {}
};
// Implements support for reading thread lists, module lists, backtraces, and backtrace symbols.
class SymbolLookup {
public:
SymbolLookup();
~SymbolLookup() = default;
// Every successful call to Initialize must be eventually matched by a call to Shutdown.
// Shutdown should be called if and only if Initialize returns true.
static bool Initialize();
static bool IsInitialized();
static void Shutdown();
void AddSourceCodeDirectory(const char* pDirectory);
// Should be disabled when within an exception handler.
void EnableMemoryAllocation(bool enabled);
// Refresh our view of the symbols and modules present within the current process.
bool Refresh();
// Retrieves the backtrace (call stack) of the given thread. There may be some per-platform
// restrictions on this.
// Returns the number written, which will be <= addressArrayCapacity.
// This may not work on some platforms unless stack frames are enabled.
// For Microsoft platforms the platformThreadContext is CONTEXT*.
// For Apple platforms the platformThreadContext is x86_thread_state_t* or arm_thread_state_t*.
// If threadSysIdHelp is non-zero, it may be used by the implementation to help produce a better
// backtrace.
static size_t GetBacktrace(
void* addressArray[],
size_t addressArrayCapacity,
size_t skipCount = 0,
void* platformThreadContext = nullptr,
OVR::ThreadSysId threadSysIdHelp = OVR_THREADSYSID_INVALID);
// Retrieves the backtrace for the given ThreadHandle.
// Returns the number written, which will be <= addressArrayCapacity.
static size_t GetBacktraceFromThreadHandle(
void* addressArray[],
size_t addressArrayCapacity,
size_t skipCount = 0,
OVR::ThreadHandle threadHandle = OVR_THREADHANDLE_INVALID);
// Retrieves the backtrace for the given ThreadSysId.
// Returns the number written, which will be <= addressArrayCapacity.
static size_t GetBacktraceFromThreadSysId(
void* addressArray[],
size_t addressArrayCapacity,
size_t skipCount = 0,
OVR::ThreadSysId threadSysId = OVR_THREADSYSID_INVALID);
enum ModuleSort { ModuleSortNone = 0, ModuleSortByAddress, ModuleSortByName };
// Gets a list of the modules (e.g. DLLs) present in the current process.
// Writes as many ModuleInfos as possible to pModuleInfoArray.
// Returns the required count of ModuleInfos, which will be > moduleInfoArrayCapacity if the
// capacity needs to be larger.
static size_t GetModuleInfoArray(
ModuleInfo* pModuleInfoArray,
size_t moduleInfoArrayCapacity,
ModuleSort moduleSort = ModuleSortNone);
// Retrieves a list of the current threads. Unless the process is paused the list is volatile.
// Returns the required capacity, which may be larger than threadArrayCapacity.
// Either array can be NULL to specify that it's not written to.
// For Windows the caller needs to CloseHandle the returned ThreadHandles. This can be done by
// calling DoneThreadList.
static size_t GetThreadList(
ThreadHandle* threadHandleArray,
ThreadSysId* threadSysIdArray,
size_t threadArrayCapacity);
// Frees any references to thread handles or ids returned by GetThreadList;
static void DoneThreadList(
ThreadHandle* threadHandleArray,
ThreadSysId* threadSysIdArray,
size_t threadArrayCount);
// Writes a given thread's callstack with symbols to the given output.
// It may not be safe to call this from an exception handler, as sOutput allocates memory.
bool ReportThreadCallstack(
OVR::String& sOutput,
size_t skipCount = 0,
ThreadSysId threadSysId = OVR_THREADSYSID_INVALID);
// Writes all thread's callstacks with symbols to the given output.
// It may not be safe to call this from an exception handler, as sOutput allocates memory.
bool ReportThreadCallstacks(OVR::String& sOutput, size_t skipCount = 0);
// Writes all loaded modules to the given output string.
// It may not be safe to call this from an exception handler, as sOutput allocates memory.
bool ReportModuleInformation(OVR::String& sOutput);
// Retrieves symbol info for the given address.
bool LookupSymbol(uint64_t address, SymbolInfo& symbolInfo);
bool LookupSymbols(uint64_t* addressArray, SymbolInfo* pSymbolInfoArray, size_t arraySize);
// The returned ModuleInfo points to an internal structure. This function assumes that the
// internal cached ModuleInfo array is valid. If modules are dynamically added or removed
// during runtime then the array may be partially out of date.
// May return NULL if there was no found module for the address.
const ModuleInfo* GetModuleInfoForAddress(uint64_t address);
const ModuleInfo& GetModuleInfoForCurrentModule();
protected:
bool RefreshModuleList();
protected:
// True by default. If true then we allow allocating memory (and as a
// result provide less information). This is useful for when in an
// exception handler.
bool AllowMemoryAllocation;
bool ModuleListUpdated;
// Cached list of modules we use. This is a fixed size because
// we need to use it during application exceptions.
ModuleInfo ModuleInfoArray[256];
size_t ModuleInfoArraySize;
// The ModuleInfo for the current module, which is often needed and so we make a member for it.
ModuleInfo currentModuleInfo;
};
// ExceptionInfo
// We need to be careful to avoid data types that can allocate memory while we are
// handling an exception, as the memory system may be corrupted at that point in time.
struct ExceptionInfo {
tm time; // GM time.
time_t timeVal; // GM time_t (seconds since 1970).
void* backtrace[64];
size_t backtraceCount;
ThreadHandle threadHandle; //
ThreadSysId threadSysId; //
char threadName[32]; // Cannot be an allocating String object.
void* pExceptionInstructionAddress;
void* pExceptionMemoryAddress;
CPUContext cpuContext;
char exceptionDescription[1024]; // Cannot be an allocating String object.
SymbolInfo symbolInfo; // SymbolInfo for the exception location.
#if defined(OVR_OS_MS)
EXCEPTION_RECORD exceptionRecord; // This is a Windows SDK struct.
#elif defined(OVR_OS_APPLE)
uint64_t exceptionType; // e.g. EXC_BAD_INSTRUCTION, EXC_BAD_ACCESS, etc.
uint32_t cpuExceptionId; // The x86/x64 CPU trap id.
uint32_t cpuExceptionIdError; // The x86/x64 CPU trap id extra info.
int64_t machExceptionData[4]; // Kernel exception code info.
int machExceptionDataCount; // Count of valid entries.
#endif
ExceptionInfo();
};
// Implments support for asynchronous exception handling and basic exception report generation.
// If you are implementing exception handling for a commercial application and want auto-uploading
// functionality you may want to consider using something like Google Breakpad. This exception
// handler
// is for in-application debug/diagnostic services, though it can write a report that has similar
// information to Breakpad or OS-provided reports such as Apple .crash files.
//
// Example usage:
// ExceptionHandler exceptionHandler;
//
// int main(int, char**)
// {
// exceptionHandler.Enable(true);
// exceptionHandler.SetExceptionListener(pSomeListener, 0); // Optional listener hook.
// }
//
class ExceptionHandler {
public:
ExceptionHandler();
~ExceptionHandler();
// Enables or disables handling by installing or uninstalling an exception handler.
// If you merely want to temporarily pause handling then it may be better to cause PauseHandling,
// as that's a lighter weight solution.
bool Enable(bool enable);
// Pauses handling. Exceptions will be caught but immediately passed on to the next handler
// without taking any action. Pauses are additive and calls to Pause(true) must be eventually
// matched to Pause(false). This function can be called from multiple threads simultaneously.
// Returns the new pause count.
int PauseHandling(bool pause);
// Some report info can be considered private information of the user, such as the current process
// list,
// computer name, IP address or other identifying info, etc. We should not report this information
// for
// external users unless they agree to this.
void EnableReportPrivacy(bool enable);
struct ExceptionListener {
virtual ~ExceptionListener() {}
virtual int HandleException(
uintptr_t userValue,
ExceptionHandler* pExceptionHandler,
ExceptionInfo* pExceptionInfo,
const char* reportFilePath) = 0;
};
void SetExceptionListener(ExceptionListener* pExceptionListener, uintptr_t userValue);
// What we do after handling the exception.
enum ExceptionResponse {
kERContinue, // Continue execution. Will result in the exception being re-generated unless the
// application has fixed the cause. Similar to Windows
// EXCEPTION_CONTINUE_EXECUTION.
kERHandle, // Causes the OS to handle the exception as it normally would. Similar to Windows
// EXCEPTION_EXECUTE_HANDLER.
kERTerminate, // Exit the application.
kERThrow, // Re-throw the exception. Other handlers may catch it. Similar to Windows
// EXCEPTION_CONTINUE_SEARCH.
kERDefault // Usually set to kERTerminate.
};
void SetExceptionResponse(ExceptionResponse er) {
exceptionResponse = er;
}
// Allws you to add an arbitrary description of the current application, which will be added to
// exception reports.
void SetAppDescription(const char* appDescription);
// Specifies how much info the minidump has, but also how large it gets.
enum MinidumpInfoLevel {
kMILNone, // Don't generate a .mdmp file.
kMILSmall, // Will result in a .mdmp file that's about 10-30 KiB
kMILMedium, // Will result in a .mdmp file that's about 5-15 MiB
kMILLarge // Will result in a .mdmp file that's about 50-150 MiB.
};
void SetMinidumpInfoLevel(MinidumpInfoLevel level) {
minidumpInfoLevel = level;
}
MinidumpInfoLevel GetMinidumpInfoLevel() const {
return minidumpInfoLevel;
}
// If the report path has a "%s" in its name, then assume the path is a sprintf format and write
// it
// with the %s specified as a date/time string.
// The report path can be "default" to signify that you want to use the default user location.
// Passing NULL for exceptionReportPath or exceptionMinidumpPath causes those files to not be
// written.
// By default both the exceptionReportPath and exceptionMinidumpPath are NULL.
// Example usage:
// handler.SetExceptionPaths("/Users/Current/Exceptions/Exception %s.txt");
void SetExceptionPaths(
const char* exceptionReportPath,
const char* exceptionMinidumpPath = nullptr);
// Calls SetExceptionPaths in the appropriate convention for each operating system
// Windows: %AppData%\Organization\Application
// Mac: ~/Library/Logs/DiagnosticReports/Organization/Application"
// Linux: ~/Library/Organization/Application
// exceptionFormat and minidumpFormat define the file names in the format above
// with the %s specified as a date/time string.
void SetPathsFromNames(
const char* organizationName,
const char* ApplicationName,
const char* exceptionFormat = "Exception Report (%s).txt",
const char* minidumpFormat = "Exception Minidump (%s).mdmp");
// Allows you to specify base directories for code paths, which can be used to associate exception
// addresses to lines
// of code as opposed to just file names and line numbers, or function names plus binary offsets.
void SetCodeBaseDirectoryPaths(
const char* codeBaseDirectoryPathArray[],
size_t codeBaseDirectoryPathCount);
// Writes lines into the current report.
void WriteReportLine(const char* pLine);
void WriteReportLineF(const char* format, ...);
// Retrieves a directory path which ends with a path separator.
// Returns the required strlen of the path.
// Guarantees the presence of the directory upon returning true.
static size_t GetCrashDumpDirectory(char* directoryPath, size_t directoryPathCapacity);
// Retrieves a directory path for a specific organization and application.
// Returns the required strlen of the path.
// Guarantees the presence of the directory upon returning true.
static void GetCrashDumpDirectoryFromNames(
char* path,
const char* organizationName,
const char* ApplicationName);
// Given an exception report at a given file path, returns a string suitable for displaying in a
// message
// box or similar user interface during the handling of an exception. The returned string must be
// passed
// to FreeMessageBoxText when complete.
static const char* GetExceptionUIText(const char* exceptionReportPath);
static void FreeExceptionUIText(const char* messageBoxText);
// Writes a deadlock report similar to an exception report. Since there is no allocation risk, an
// exception handler is created for this log.
static void ReportDeadlock(
const char* threadName,
const char* organizationName = nullptr,
const char* applicationName = nullptr);
protected:
// Write one log line to log file, console, syslog, and debug output.
// If LogFile is null, it will not write to the log file.
// The buffer must be null-terminated.
void writeLogLine(const char* buffer, int length);
void WriteExceptionDescription();
void WriteReport(const char* reportType);
void WriteThreadCallstack(
ThreadHandle threadHandle,
ThreadSysId threadSysId,
const char* additionalInfo);
void WriteMiniDump();
protected:
// Runtime constants
bool enabled;
std::atomic<int> pauseCount = {0}; // 0 means unpaused. 1+ means paused.
bool reportPrivacyEnabled; // Defaults to true.
ExceptionResponse exceptionResponse; // Defaults to kERHandle
ExceptionListener* exceptionListener;
uintptr_t exceptionListenerUserValue;
String appDescription;
String codeBasePathArray[6]; // 6 is arbitrary.
char reportFilePath[OVR_MAX_PATH]; // May be an encoded path, in that it has "%s" in it or is
// named "default". See reporFiletPathActual for the runtime
// actual report path.
MinidumpInfoLevel minidumpInfoLevel;
char miniDumpFilePath[OVR_MAX_PATH];
FILE* LogFile; // Can/should we use OVR Files for this?
char scratchBuffer[4096];
// Runtime variables
bool exceptionOccurred;
std::atomic<uint32_t> handlingBusy = {0};
char reportFilePathActual[OVR_MAX_PATH];
char minidumpFilePathActual[OVR_MAX_PATH];
int terminateReturnValue;
ExceptionInfo exceptionInfo;
SymbolLookup symbolLookup;
#if defined(OVR_OS_MS)
void* vectoredHandle;
LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
LPEXCEPTION_POINTERS pExceptionPointers;
friend LONG WINAPI Win32ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers);
LONG ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers);
// handles exception in a new thread, used for stack overflow
static unsigned WINAPI ExceptionHandlerThreadExec(void* callingHandler);
#elif defined(OVR_OS_APPLE)
struct SavedExceptionPorts {
SavedExceptionPorts() : count(0) {
memset(this, 0, sizeof(SavedExceptionPorts));
}
mach_msg_type_number_t count;
exception_mask_t masks[6];
exception_handler_t ports[6];
exception_behavior_t behaviors[6];
thread_state_flavor_t flavors[6];
};
friend void* ::MachHandlerThreadFunctionStatic(void*);
friend int ::catch_mach_exception_raise_state_identity_OVR(
mach_port_t,
mach_port_t,
mach_port_t,
exception_type_t,
mach_exception_data_type_t*,
mach_msg_type_number_t,
int*,
thread_state_t,
mach_msg_type_number_t,
thread_state_t,
mach_msg_type_number_t*);
bool InitMachExceptionHandler();
void ShutdownMachExceptionHandler();
void* MachHandlerThreadFunction();
kern_return_t HandleMachException(
mach_port_t port,
mach_port_t thread,
mach_port_t task,
exception_type_t exceptionType,
mach_exception_data_type_t* pExceptionDetail,
mach_msg_type_number_t exceptionDetailCount,
int* pFlavor,
thread_state_t pOldState,
mach_msg_type_number_t oldStateCount,
thread_state_t pNewState,
mach_msg_type_number_t* pNewStateCount);
kern_return_t ForwardMachException(
mach_port_t thread,
mach_port_t task,
exception_type_t exceptionType,
mach_exception_data_t pExceptionDetail,
mach_msg_type_number_t exceptionDetailCount);
bool machHandlerInitialized;
mach_port_t machExceptionPort;
SavedExceptionPorts machExceptionPortsSaved;
volatile bool machThreadShouldContinue;
volatile bool machThreadExecuting;
pthread_t machThread;
#elif defined(OVR_OS_LINUX)
// To do.
#endif
};
// Identifies basic exception types for the CreateException function.
enum CreateExceptionType {
kCETAccessViolation, // Read or write to inaccessable memory.
kCETAlignment, // Misaligned read or write.
kCETDivideByZero, // Integer divide by zero.
kCETFPU, // Floating point / VPU exception.
kCETIllegalInstruction, // Illegal opcode.
kCETStackCorruption, // Stack frame was corrupted.
kCETStackOverflow, // Stack ran out of space, often due to infinite recursion.
kCETTrap // System/OS trap (system call).
};
// Creates an exception of the given type, primarily for testing.
void CreateException(CreateExceptionType exceptionType);
//-----------------------------------------------------------------------------
// GUI Exception Listener
//
// When this exception handler is called, it will verify that the application
// is not being debugged at that instant. If not, then it will present a GUI
// to the user containing error information.
// Initially the exception listener is not silenced.
class GUIExceptionListener : public ExceptionHandler::ExceptionListener {
public:
GUIExceptionListener();
virtual int HandleException(
uintptr_t userValue,
ExceptionHandler* pExceptionHandler,
ExceptionInfo* pExceptionInfo,
const char* reportFilePath) OVR_OVERRIDE;
protected:
ExceptionHandler Handler;
};
} // namespace OVR
OVR_RESTORE_MSVC_WARNING()
#endif // OVR_DebugHelp_h

View File

@ -0,0 +1,468 @@
/************************************************************************************
Filename : OVR_Delegates.h
Content : C++ Delegates
Created : June 15, 2014
Authors : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
/*
Based on The Impossibly Fast C++ Delegates by Sergey Ryazanov from
http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx (2005)
*/
/*
Usage:
Declare a delegate with a void (int) signature, also known as a
function that returns void and has one parameter that is an int:
typedef Delegate1<void, int> MyDelegate;
MyDelegate d;
Point the delegate to a member function:
d.SetMember<A, &A::TestFunctionA>(&a);
d = MyDelegate::FromMember<A, &A::TestFunctionA>(&a);
Point the delegate to a const member function:
d.SetConstMember<C, &C::TestFunctionA>(&c);
d = MyDelegate::FromConstMember<C, &C::TestFunctionA>(&c);
Point the delegate to a free function:
d.SetFree<&FreeFunctionX>();
d = MyDelegate::FromFree<&FreeFunctionX>();
Invoke the function via the delegate (works for all 3 cases):
d(1000);
By default the delegates are uninitialized.
To clear an array of delegates quickly just zero the memory.
This implementation is nicer than FastDelegates in my opinion
because it is simple and easy to read. It is a little slower
for virtual functions, but the size of the delegate is small,
and it will only get better as compilers improve.
*/
#ifndef OVR_Delegates_h
#define OVR_Delegates_h
#include "OVR_Types.h"
namespace OVR {
template <class ret_type>
class Delegate0 {
typedef ret_type (*StubPointer)(void*);
typedef Delegate0<ret_type> this_type;
void* _object;
StubPointer _stub;
inline Delegate0(void* object, StubPointer stub) {
_object = object;
_stub = stub;
}
// Stubs
template <ret_type (*F)()>
static inline ret_type FreeStub(void* /*object*/) {
return (F)();
}
template <class T, ret_type (T::*F)()>
static inline ret_type MemberStub(void* object) {
T* p = static_cast<T*>(object);
return (p->*F)();
}
template <class T, ret_type (T::*F)() const>
static inline ret_type ConstMemberStub(void* object) {
T* p = static_cast<T*>(object);
return (p->*F)();
}
public:
inline Delegate0() : _object(0), _stub(0) {}
// Function invocation
inline ret_type operator()() const {
return (*_stub)(_object);
}
// Use stub pointer as a validity flag and equality checker
inline bool operator==(const this_type& rhs) const {
return _object == rhs._object && _stub == rhs._stub;
}
inline bool operator!=(const this_type& rhs) const {
return _object != rhs._object || _stub != rhs._stub;
}
inline bool IsValid() const {
return _stub != 0;
}
inline bool operator!() const {
return _stub == 0;
}
inline void Invalidate() {
_stub = 0;
}
// Delegate creation from a function
template <ret_type (*F)()>
static inline this_type FromFree() {
return this_type(0, &FreeStub<F>);
}
template <class T, ret_type (T::*F)()>
static inline this_type FromMember(T* object) {
return this_type(object, &MemberStub<T, F>);
}
template <class T, ret_type (T::*F)() const>
static inline this_type FromConstMember(T const* object) {
return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
}
// In-place assignment to a different function
template <ret_type (*F)()>
inline void SetFree() {
*this = FromFree<F>();
}
template <class T, ret_type (T::*F)()>
inline void SetMember(T* object) {
*this = FromMember<T, F>(object);
}
template <class T, ret_type (T::*F)() const>
inline void SetConstMember(T const* object) {
*this = FromConstMember<T, F>(object);
}
};
template <class ret_type, class arg1_type>
class Delegate1 {
typedef ret_type (*StubPointer)(void*, arg1_type);
typedef Delegate1<ret_type, arg1_type> this_type;
void* _object;
StubPointer _stub;
inline Delegate1(void* object, StubPointer stub) {
_object = object;
_stub = stub;
}
// Stubs
template <ret_type (*F)(arg1_type)>
static inline ret_type FreeStub(void* /*object*/, arg1_type a1) {
return (F)(a1);
}
template <class T, ret_type (T::*F)(arg1_type)>
static inline ret_type MemberStub(void* object, arg1_type a1) {
T* p = static_cast<T*>(object);
return (p->*F)(a1);
}
template <class T, ret_type (T::*F)(arg1_type) const>
static inline ret_type ConstMemberStub(void* object, arg1_type a1) {
T* p = static_cast<T*>(object);
return (p->*F)(a1);
}
public:
inline Delegate1() : _object(0), _stub(0) {}
// Function invocation
inline ret_type operator()(arg1_type a1) const {
return (*_stub)(_object, a1);
}
// Use stub pointer as a validity flag and equality checker
inline bool operator==(const this_type& rhs) const {
return _object == rhs._object && _stub == rhs._stub;
}
inline bool operator!=(const this_type& rhs) const {
return _object != rhs._object || _stub != rhs._stub;
}
inline bool IsValid() const {
return _stub != 0;
}
inline bool operator!() const {
return _stub == 0;
}
inline void Invalidate() {
_stub = 0;
}
// Delegate creation from a function
template <ret_type (*F)(arg1_type)>
static inline this_type FromFree() {
return this_type(0, &FreeStub<F>);
}
template <class T, ret_type (T::*F)(arg1_type)>
static inline this_type FromMember(T* object) {
return this_type(object, &MemberStub<T, F>);
}
template <class T, ret_type (T::*F)(arg1_type) const>
static inline this_type FromConstMember(T const* object) {
return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
}
// In-place assignment to a different function
template <ret_type (*F)(arg1_type)>
inline void SetFree() {
*this = FromFree<F>();
}
template <class T, ret_type (T::*F)(arg1_type)>
inline void SetMember(T* object) {
*this = FromMember<T, F>(object);
}
template <class T, ret_type (T::*F)(arg1_type) const>
inline void SetConstMember(T const* object) {
*this = FromConstMember<T, F>(object);
}
};
template <class ret_type, class arg1_type, class arg2_type>
class Delegate2 {
typedef ret_type (*StubPointer)(void*, arg1_type, arg2_type);
typedef Delegate2<ret_type, arg1_type, arg2_type> this_type;
void* _object;
StubPointer _stub;
inline Delegate2(void* object, StubPointer stub) {
_object = object;
_stub = stub;
}
// Stubs
template <ret_type (*F)(arg1_type, arg2_type)>
static inline ret_type FreeStub(void* /*object*/, arg1_type a1, arg2_type a2) {
return (F)(a1, a2);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type)>
static inline ret_type MemberStub(void* object, arg1_type a1, arg2_type a2) {
T* p = static_cast<T*>(object);
return (p->*F)(a1, a2);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type) const>
static inline ret_type ConstMemberStub(void* object, arg1_type a1, arg2_type a2) {
T* p = static_cast<T*>(object);
return (p->*F)(a1, a2);
}
public:
inline Delegate2() : _object(0), _stub(0) {}
// Function invocation
inline ret_type operator()(arg1_type a1, arg2_type a2) const {
return (*_stub)(_object, a1, a2);
}
// Use stub pointer as a validity flag and equality checker
inline bool operator==(const this_type& rhs) const {
return _object == rhs._object && _stub == rhs._stub;
}
inline bool operator!=(const this_type& rhs) const {
return _object != rhs._object || _stub != rhs._stub;
}
inline bool IsValid() const {
return _stub != 0;
}
inline bool operator!() const {
return _stub == 0;
}
inline void Invalidate() {
_stub = 0;
}
// Delegate creation from a function
template <ret_type (*F)(arg1_type, arg2_type)>
static inline this_type FromFree() {
return this_type(0, &FreeStub<F>);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type)>
static inline this_type FromMember(T* object) {
return this_type(object, &MemberStub<T, F>);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type) const>
static inline this_type FromConstMember(T const* object) {
return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
}
// In-place assignment to a different function
template <ret_type (*F)(arg1_type, arg2_type)>
inline void SetFree() {
*this = FromFree<F>();
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type)>
inline void SetMember(T* object) {
*this = FromMember<T, F>(object);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type) const>
inline void SetConstMember(T const* object) {
*this = FromConstMember<T, F>(object);
}
};
template <class ret_type, class arg1_type, class arg2_type, class arg3_type>
class Delegate3 {
typedef ret_type (*StubPointer)(void*, arg1_type, arg2_type, arg3_type);
typedef Delegate3<ret_type, arg1_type, arg2_type, arg3_type> this_type;
void* _object;
StubPointer _stub;
inline Delegate3(void* object, StubPointer stub) {
_object = object;
_stub = stub;
}
// Stubs
template <ret_type (*F)(arg1_type, arg2_type, arg3_type)>
static inline ret_type FreeStub(void* /*object*/, arg1_type a1, arg2_type a2, arg3_type a3) {
return (F)(a1, a2, a3);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type)>
static inline ret_type MemberStub(void* object, arg1_type a1, arg2_type a2, arg3_type a3) {
T* p = static_cast<T*>(object);
return (p->*F)(a1, a2, a3);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type) const>
static inline ret_type ConstMemberStub(void* object, arg1_type a1, arg2_type a2, arg3_type a3) {
T* p = static_cast<T*>(object);
return (p->*F)(a1, a2, a3);
}
public:
inline Delegate3() : _object(0), _stub(0) {}
// Function invocation
inline ret_type operator()(arg1_type a1, arg2_type a2, arg3_type a3) const {
return (*_stub)(_object, a1, a2, a3);
}
// Use stub pointer as a validity flag and equality checker
inline bool operator==(const this_type& rhs) const {
return _object == rhs._object && _stub == rhs._stub;
}
inline bool operator!=(const this_type& rhs) const {
return _object != rhs._object || _stub != rhs._stub;
}
inline bool IsValid() const {
return _stub != 0;
}
inline bool operator!() const {
return _stub == 0;
}
inline void Invalidate() {
_stub = 0;
}
// Delegate creation from a function
template <ret_type (*F)(arg1_type, arg2_type, arg3_type)>
static inline this_type FromFree() {
return this_type(0, &FreeStub<F>);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type)>
static inline this_type FromMember(T* object) {
return this_type(object, &MemberStub<T, F>);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type) const>
static inline this_type FromConstMember(T const* object) {
return this_type(const_cast<T*>(object), &ConstMemberStub<T, F>);
}
// In-place assignment to a different function
template <ret_type (*F)(arg1_type, arg2_type, arg3_type)>
inline void SetFree() {
*this = FromFree<F>();
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type)>
inline void SetMember(T* object) {
*this = FromMember<T, F>(object);
}
template <class T, ret_type (T::*F)(arg1_type, arg2_type, arg3_type) const>
inline void SetConstMember(T const* object) {
*this = FromConstMember<T, F>(object);
}
};
// Add more here if needed, but keep in mind that a short, simple interface
// is rewarded by making the delegates faster...
} // namespace OVR
#endif // OVR_Delegates_h

View File

@ -0,0 +1,287 @@
/************************************************************************************
Filename : OVR_Deque.h
Content : Deque container
Created : Nov. 15, 2013
Authors : Dov Katz
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#pragma once
#include "OVR_ContainerAllocator.h"
namespace OVR {
template <class Elem, class Allocator = ContainerAllocator<Elem>>
class Deque {
public:
enum { DefaultCapacity = 500 };
Deque(int capacity = DefaultCapacity);
virtual ~Deque(void);
virtual void PushBack(const Elem& Item); // Adds Item to the end
virtual void PushFront(const Elem& Item); // Adds Item to the beginning
virtual Elem PopBack(void); // Removes Item from the end
virtual Elem PopFront(void); // Removes Item from the beginning
virtual const Elem& PeekBack(int count = 0) const; // Returns count-th Item from the end
virtual const Elem& PeekFront(int count = 0) const; // Returns count-th Item from the beginning
virtual inline size_t GetSize(void) const; // Returns Number of Elements
OVR_FORCE_INLINE int GetSizeI(void) const {
return (int)GetSize();
}
virtual inline size_t GetCapacity(void) const; // Returns the maximum possible number of elements
virtual void Clear(void); // Remove all elements
virtual inline bool IsEmpty() const;
virtual inline bool IsFull() const;
protected:
Elem* Data; // The actual Data array
const int Capacity; // Deque capacity
int Beginning; // Index of the first element
int End; // Index of the next after last element
// Instead of calculating the number of elements, using this variable
// is much more convenient.
int ElemCount;
private:
OVR_NON_COPYABLE(Deque);
};
template <class Elem, class Allocator = ContainerAllocator<Elem>>
class InPlaceMutableDeque : public Deque<Elem, Allocator> {
typedef Deque<Elem, Allocator> BaseType;
public:
InPlaceMutableDeque(int capacity = BaseType::DefaultCapacity) : BaseType(capacity) {}
virtual ~InPlaceMutableDeque(){};
using BaseType::PeekBack;
using BaseType::PeekFront;
virtual Elem& PeekBack(int count = 0); // Returns count-th Item from the end
virtual Elem& PeekFront(int count = 0); // Returns count-th Item from the beginning
};
// Same as Deque, but allows to write more elements than maximum capacity
// Old elements are lost as they are overwritten with the new ones
template <class Elem, class Allocator = ContainerAllocator<Elem>>
class CircularBuffer : public InPlaceMutableDeque<Elem, Allocator> {
typedef InPlaceMutableDeque<Elem, Allocator> BaseType;
public:
CircularBuffer(int MaxSize = BaseType::DefaultCapacity) : BaseType(MaxSize){};
virtual ~CircularBuffer() {}
// The following methods are inline as a workaround for a VS bug causing erroneous C4505 warnings
// See: http://stackoverflow.com/questions/3051992/compiler-warning-at-c-template-base-class
inline virtual void PushBack(const Elem& Item); // Adds Item to the end, overwriting the oldest
// element at the beginning if necessary
inline virtual void PushFront(const Elem& Item); // Adds Item to the beginning, overwriting the
// oldest element at the end if necessary
};
//----------------------------------------------------------------------------------
// Deque Constructor function
template <class Elem, class Allocator>
Deque<Elem, Allocator>::Deque(int capacity)
: Capacity(capacity), Beginning(0), End(0), ElemCount(0) {
Data = (Elem*)Allocator::Alloc(Capacity * sizeof(Elem));
}
// Deque Destructor function
template <class Elem, class Allocator>
Deque<Elem, Allocator>::~Deque(void) {
Clear();
Allocator::Free(Data);
}
template <class Elem, class Allocator>
void Deque<Elem, Allocator>::Clear() {
if (!IsEmpty()) {
if (Beginning < End) {
// no wrap-around
Allocator::DestructArray(Data + Beginning, End - Beginning);
} else {
// wrap-around
Allocator::DestructArray(Data + Beginning, Capacity - Beginning);
Allocator::DestructArray(Data, End);
}
}
Beginning = 0;
End = 0;
ElemCount = 0;
}
// Push functions
template <class Elem, class Allocator>
void Deque<Elem, Allocator>::PushBack(const Elem& Item) {
// Error Check: Make sure we aren't
// exceeding our maximum storage space
OVR_ASSERT(ElemCount < Capacity);
Allocator::Construct(Data + End, Item);
++End;
++ElemCount;
// Check for wrap-around
if (End >= Capacity)
End -= Capacity;
}
template <class Elem, class Allocator>
void Deque<Elem, Allocator>::PushFront(const Elem& Item) {
// Error Check: Make sure we aren't
// exceeding our maximum storage space
OVR_ASSERT(ElemCount < Capacity);
--Beginning;
// Check for wrap-around
if (Beginning < 0)
Beginning += Capacity;
Allocator::Construct(Data + Beginning, Item);
++ElemCount;
}
// Pop functions
template <class Elem, class Allocator>
Elem Deque<Elem, Allocator>::PopFront(void) {
// Error Check: Make sure we aren't reading from an empty Deque
OVR_ASSERT(ElemCount > 0);
Elem ReturnValue = Data[Beginning];
Allocator::Destruct(Data + Beginning);
++Beginning;
--ElemCount;
// Check for wrap-around
if (Beginning >= Capacity)
Beginning -= Capacity;
return ReturnValue;
}
template <class Elem, class Allocator>
Elem Deque<Elem, Allocator>::PopBack(void) {
// Error Check: Make sure we aren't reading from an empty Deque
OVR_ASSERT(ElemCount > 0);
--End;
--ElemCount;
// Check for wrap-around
if (End < 0)
End += Capacity;
Elem ReturnValue = Data[End];
Allocator::Destruct(Data + End);
return ReturnValue;
}
// Peek functions
template <class Elem, class Allocator>
const Elem& Deque<Elem, Allocator>::PeekFront(int count) const {
// Error Check: Make sure we aren't reading from an empty Deque
OVR_ASSERT(ElemCount > count);
int idx = Beginning + count;
if (idx >= Capacity)
idx -= Capacity;
return Data[idx];
}
template <class Elem, class Allocator>
const Elem& Deque<Elem, Allocator>::PeekBack(int count) const {
// Error Check: Make sure we aren't reading from an empty Deque
OVR_ASSERT(ElemCount > count);
int idx = End - count - 1;
if (idx < 0)
idx += Capacity;
return Data[idx];
}
// Mutable Peek functions
template <class Elem, class Allocator>
Elem& InPlaceMutableDeque<Elem, Allocator>::PeekFront(int count) {
// Error Check: Make sure we aren't reading from an empty Deque
OVR_ASSERT(BaseType::ElemCount > count);
int idx = BaseType::Beginning + count;
if (idx >= BaseType::Capacity)
idx -= BaseType::Capacity;
return BaseType::Data[idx];
}
template <class Elem, class Allocator>
Elem& InPlaceMutableDeque<Elem, Allocator>::PeekBack(int count) {
// Error Check: Make sure we aren't reading from an empty Deque
OVR_ASSERT(BaseType::ElemCount > count);
int idx = BaseType::End - count - 1;
if (idx < 0)
idx += BaseType::Capacity;
return BaseType::Data[idx];
}
template <class Elem, class Allocator>
inline size_t Deque<Elem, Allocator>::GetCapacity(void) const {
return Capacity;
}
template <class Elem, class Allocator>
inline size_t Deque<Elem, Allocator>::GetSize(void) const {
return ElemCount;
}
template <class Elem, class Allocator>
inline bool Deque<Elem, Allocator>::IsEmpty(void) const {
return ElemCount == 0;
}
template <class Elem, class Allocator>
inline bool Deque<Elem, Allocator>::IsFull(void) const {
return ElemCount == Capacity;
}
// ******* CircularBuffer<Elem> *******
// Push functions
template <class Elem, class Allocator>
void CircularBuffer<Elem, Allocator>::PushBack(const Elem& Item) {
if (this->IsFull())
this->PopFront();
BaseType::PushBack(Item);
}
template <class Elem, class Allocator>
void CircularBuffer<Elem, Allocator>::PushFront(const Elem& Item) {
if (this->IsFull())
this->PopBack();
BaseType::PushFront(Item);
}
} // namespace OVR

View File

@ -0,0 +1,878 @@
/************************************************************************************
PublicHeader: None
Filename : OVR_Error.cpp
Content : Structs and functions for handling OVRErrorInfos
Created : February 15, 2015
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Error.h"
#include "OVR_Types.h"
#include "OVR_Std.h"
#include "OVR_String.h"
#include "OVR_Timer.h"
#include "OVR_DebugHelp.h"
#include "OVR_Atomic.h"
#include "OVR_UTF8Util.h"
#include "OVR_Threads.h"
#include "OVR_Win32_IncludeWindows.h"
#include "Logging/Logging_Library.h"
OVR_DISABLE_ALL_MSVC_WARNINGS()
OVR_DISABLE_MSVC_WARNING(4265)
#if defined(OVR_OS_WIN32)
#include <winerror.h>
#include <dxgitype.h>
#endif
#include <utility>
#include <algorithm>
#include <chrono>
#include <ctime>
#include <stdarg.h>
#include <cstdio>
#include <mutex>
#include <unordered_map>
OVR_RESTORE_MSVC_WARNING()
OVR_RESTORE_ALL_MSVC_WARNINGS()
OVR_DEFINE_SINGLETON(OVR::LastErrorTLS);
OVR_DISABLE_MSVC_WARNING(4996) // 'localtime': This function or variable may be unsafe.
static ovrlog::Channel Logger("Kernel:Error");
// -----------------------------------------------------------------------------
// ***** OVR_ERROR_ENABLE_BACKTRACES
//
// If defined then we record backtraces in Errors.
//
#if !defined(OVR_ERROR_ENABLE_BACKTRACES)
#if defined(OVR_BUILD_DEBUG)
#define OVR_ERROR_ENABLE_BACKTRACES 1
#endif
#endif
namespace OVR {
//-----------------------------------------------------------------------------
// LastErrorTLS
static SymbolLookup Symbols;
LastErrorTLS::LastErrorTLS() {
Symbols.Initialize();
// Must be at end of function
PushDestroyCallbacks();
}
LastErrorTLS::~LastErrorTLS() {
Symbols.Shutdown();
}
void LastErrorTLS::OnSystemDestroy() {
delete this;
}
// This is an accessor which auto-allocates and initializes the return value if needed.
OVRError& LastErrorTLS::LastError() {
Lock::Locker autoLock(&TheLock);
ThreadId threadId = GetCurrentThreadId();
auto i = TLSDictionary.Find(threadId);
if (i == TLSDictionary.End()) {
TLSDictionary.Add(threadId, OVRError::Success());
i = TLSDictionary.Find(threadId);
}
return (*i).Second;
}
// ****** OVRFormatDateTime
//
// Prints a date/time like so:
// Y-M-d H:M:S [ms:us:ns]
// Example output:
// 2016-12-25 8:15:01 [392:445:23]
//
// SysClockTime is of type std::chrono::time_point<std::chrono::system_clock>.
//
// To consider: Move SysClockTime and OVRFormatDateTime to OVRKernel.
//
static void OVRFormatDateTime(SysClockTime sysClockTime, OVR::String& dateTimeString) {
// Get the basic Date and HMS time.
char buffer[128];
struct tm tmResult;
const time_t cTime = std::chrono::system_clock::to_time_t(sysClockTime);
#if defined(_MSC_VER)
localtime_s(&tmResult, &cTime);
#else
localtime_r(&cTime, &tmResult);
#endif
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tmResult);
// Append milli:micro:nano time.
std::chrono::system_clock::duration timeSinceEpoch = sysClockTime.time_since_epoch();
std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
timeSinceEpoch -= s;
std::chrono::milliseconds ms =
std::chrono::duration_cast<std::chrono::milliseconds>(timeSinceEpoch);
timeSinceEpoch -= ms;
std::chrono::microseconds us =
std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch);
timeSinceEpoch -= us;
std::chrono::nanoseconds ns =
std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch);
char buffer2[384];
sprintf(buffer2, "%s [%d:%d:%d]", buffer, (int)ms.count(), (int)us.count(), (int)ns.count());
dateTimeString = buffer2;
}
static void OVRRemoveTrailingNewlines(String& s) {
while (!s.IsEmpty() && ((s.Back() == '\n') || (s.Back() == '\r')))
s.PopBack();
}
OVR_SELECTANY const int64_t OVRError::kLogLineUnset;
OVRError::OVRError() {
Reset();
}
OVRError::OVRError(ovrResult code) : OVRError() {
Code = code;
}
OVRError::OVRError(ovrResult code, const char* pFormat, ...) : OVRError(code) {
va_list argList;
va_start(argList, pFormat);
StringBuffer strbuff;
strbuff.AppendFormatV(pFormat, argList);
SetDescription(strbuff.ToCStr());
va_end(argList);
}
OVRError::OVRError(const OVRError& ovrError) {
operator=(ovrError);
}
OVRError::OVRError(OVRError&& ovrError) {
operator=(std::move(ovrError));
}
OVRError::~OVRError() {
// Empty
}
OVRError& OVRError::operator=(const OVRError& ovrError) {
ErrorString = ovrError.ErrorString;
Code = ovrError.Code;
SysCodeType = ovrError.SysCodeType;
SysCode = ovrError.SysCode;
strcpy(SysCodeStr, ovrError.SysCodeStr);
Description = ovrError.Description;
Context = ovrError.Context;
OVRTime = ovrError.OVRTime;
ClockTime = ovrError.ClockTime;
LogLine = ovrError.LogLine;
SourceFilePath = ovrError.SourceFilePath;
SourceFileLine = ovrError.SourceFileLine;
Backtrace = ovrError.Backtrace;
AlreadyLogged = ovrError.AlreadyLogged;
return *this;
}
OVRError& OVRError::operator=(OVRError&& ovrError) {
ErrorString = ovrError.ErrorString;
Code = ovrError.Code;
SysCodeType = ovrError.SysCodeType;
SysCode = ovrError.SysCode;
strcpy(SysCodeStr, ovrError.SysCodeStr);
Description = std::move(ovrError.Description);
Context = std::move(ovrError.Context);
OVRTime = ovrError.OVRTime;
ClockTime = ovrError.ClockTime;
LogLine = ovrError.LogLine;
SourceFilePath = std::move(ovrError.SourceFilePath);
SourceFileLine = ovrError.SourceFileLine;
Backtrace = std::move(ovrError.Backtrace);
AlreadyLogged = ovrError.AlreadyLogged;
return *this;
}
void OVRError::SetCurrentValues() {
OVRTime = Timer::GetSeconds(); // It would be better if we called ovr_GetTimeInSeconds, but that
// doesn't have a constant header to use.
ClockTime = std::chrono::system_clock::now();
#if defined(OVR_ERROR_ENABLE_BACKTRACES)
if (Symbols.IsInitialized()) {
void* addressArray[32];
size_t n = Symbols.GetBacktrace(
addressArray, OVR_ARRAY_COUNT(addressArray), 2, nullptr, OVR_THREADSYSID_INVALID);
Backtrace.Clear();
Backtrace.Append(addressArray, n);
}
#endif
}
void OVRError::Reset() {
ErrorString.Clear();
Code = ovrSuccess;
SysCodeType = ovrSysErrorCodeType::None;
SysCode = ovrSysErrorCodeSuccess;
SysCodeStr[0] = '\0';
Description.Clear();
Context.Clear();
OVRTime = 0;
ClockTime = SysClockTime();
LogLine = kLogLineUnset;
SourceFilePath.Clear();
SourceFileLine = 0;
Backtrace.ClearAndRelease();
AlreadyLogged = false;
}
String OVRError::GetErrorString() const {
if (ErrorString.GetSize() == 0) { // If not already cached...
ErrorString.AppendString("OVR Error:\n");
// Code
OVR::String errorCodeString;
GetErrorCodeString(Code, false, errorCodeString);
ErrorString.AppendFormat(" Code: %d -- %s\n", Code, errorCodeString.ToCStr());
// SysCode
if (SysCode != ovrSysErrorCodeSuccess) {
if (SysCodeStr[0]) { // If the sys error was previously set to a custom value...
ErrorString.AppendFormat(
" System error: %d (%x) -- %s\n", (int)SysCode, (int)SysCode, SysCodeStr);
} else { // Else just build it with the system error code.
OVR::String sysErrorString;
GetSysErrorCodeString(SysCodeType, SysCode, false, sysErrorString);
OVRRemoveTrailingNewlines(sysErrorString);
ErrorString.AppendFormat(
" System error: %d (%x) -- %s\n", (int)SysCode, (int)SysCode, sysErrorString.ToCStr());
}
}
// Description
if (Description.GetLength()) {
ErrorString.AppendFormat(" Description: %s\n", Description.ToCStr());
}
// OVRTime
ErrorString.AppendFormat(" OVRTime: %f\n", OVRTime);
// SysClockTime
OVR::String sysClockTimeString;
OVRFormatDateTime(ClockTime, sysClockTimeString);
ErrorString.AppendFormat(" Time: %s\n", sysClockTimeString.ToCStr());
// Context
if (Context.GetLength())
ErrorString.AppendFormat(" Context: %s\n", Context.ToCStr());
// If LogLine is set,
if (LogLine != kLogLineUnset)
ErrorString.AppendFormat(" LogLine: %lld\n", LogLine);
// FILE/LINE
if (SourceFilePath.GetLength())
ErrorString.AppendFormat(" File/Line: %s:%d\n", SourceFilePath.ToCStr(), SourceFileLine);
// Backtrace
if (Backtrace.GetSize()) {
// We can trace symbols in a debug build here or we can trace just addresses. See other code
// for examples of how to trace symbols.
ErrorString.AppendFormat(" Backtrace: ");
for (size_t i = 0, iEnd = Backtrace.GetSize(); i != iEnd; ++i)
ErrorString.AppendFormat(" %p", Backtrace[i]);
ErrorString.AppendChar('\n');
}
}
return OVR::String(ErrorString.ToCStr(), ErrorString.GetSize());
}
void OVRError::SetCode(ovrResult code) {
Code = code;
}
ovrResult OVRError::GetCode() const {
return Code;
}
void OVRError::SetSysCode(
ovrSysErrorCodeType sysCodeType,
ovrSysErrorCode sysCode,
const char* sysCodeString) {
SysCodeType = sysCodeType;
SysCode = sysCode;
if (sysCodeString)
OVR_strlcpy(SysCodeStr, sysCodeString, sizeof(SysCodeStr));
}
ovrSysErrorCodeType OVRError::GetSysCodeType() const {
return SysCodeType;
}
ovrSysErrorCode OVRError::GetSysCode() const {
return SysCode;
}
void OVRError::SetDescription(const char* pDescription) {
if (pDescription) {
Description = pDescription;
OVRRemoveTrailingNewlines(Description); // Users sometimes send text with trailing newlines,
// which have no purpose in the error report.
} else
Description.Clear();
}
String OVRError::GetDescription() const {
return Description;
}
void OVRError::SetContext(const char* pContext) {
if (pContext) {
Context = pContext;
OVRRemoveTrailingNewlines(Context);
} else
Context.Clear();
}
String OVRError::GetContext() const {
return Context;
}
void OVRError::SetOVRTime(double ovrTime) {
OVRTime = ovrTime;
}
double OVRError::GetOVRTime() const {
return OVRTime;
}
void OVRError::SetSysClockTime(const SysClockTime& clockTime) {
ClockTime = clockTime;
}
SysClockTime OVRError::GetSysClockTime() const {
return ClockTime;
}
void OVRError::SetLogLine(int64_t logLine) {
LogLine = logLine;
}
int64_t OVRError::GetLogLine() const {
return LogLine;
}
void OVRError::SetSource(const char* pSourceFilePath, int sourceFileLine) {
if (pSourceFilePath)
SourceFilePath = pSourceFilePath;
else
SourceFilePath.Clear();
SourceFileLine = sourceFileLine;
}
std::pair<OVR::String, int> OVRError::GetSource() const {
return std::make_pair(SourceFilePath, SourceFileLine);
}
OVRError::AddressArray OVRError::GetBacktrace() const {
return Backtrace;
}
void LogError(OVRError& ovrError) {
// If not already logged,
if (!ovrError.IsAlreadyLogged()) {
Logger.LogError(ovrError.GetErrorString());
ovrError.SetAlreadyLogged();
}
}
void SetError(OVRError& ovrError) {
// Record that the current thread's last error is this error. If we wanted to support
// chaining of errors such that multiple OVRErrors could be concurrent in a thread
// (e.g. one that occurred deep in the call chain and a higher level version of it higher
// in the call chain), we could handle that here.
LastErrorTLS::GetInstance()->LastError() = ovrError;
}
static OVRErrorCallback ErrorCallback;
void SetErrorCallback(OVRErrorCallback callback) {
ErrorCallback = callback;
}
OVRError MakeError(
ovrResult errorCode,
ovrSysErrorCodeType sysCodeType,
ovrSysErrorCode sysCode,
const char* sysCodeString, // Optional pre-generated sys error code string.
const char* pSourceFile,
int sourceLine,
bool logError,
bool assertError,
const char* pContext,
const char* pDescriptionFormat,
...) {
OVRError ovrError(errorCode);
ovrError.SetCurrentValues(); // Sets the current time, etc.
ovrError.SetSysCode(sysCodeType, sysCode, sysCodeString);
va_list argList;
va_start(argList, pDescriptionFormat);
StringBuffer strbuff;
strbuff.AppendFormatV(pDescriptionFormat, argList);
va_end(argList);
ovrError.SetDescription(strbuff.ToCStr());
ovrError.SetContext(pContext);
ovrError.SetSource(pSourceFile, sourceLine);
// Set the TLS last error.
LastErrorTLS::GetInstance()->LastError() = ovrError;
int silencerOptions = ovrlog::ErrorSilencer::GetSilenceOptions();
if (silencerOptions & ovrlog::ErrorSilencer::CompletelySilenceLogs)
logError = false;
if (silencerOptions & ovrlog::ErrorSilencer::PreventErrorAsserts)
assertError = false;
if (logError)
Logger.LogError(ovrError.GetErrorString().ToCStr());
if (assertError) {
// Assert in debug mode to alert unit tester/developer of the error as it occurs.
OVR_FAIL_M(ovrError.GetErrorString().ToCStr());
}
// ErrorCallback should be called after LastErrorTLS is set.
// ErrorCallback could choose to save LastErrorTLS if desired.
if (ErrorCallback) {
const bool quiet = !logError && !assertError;
ErrorCallback(ovrError, quiet);
}
return ovrError;
}
typedef std::unordered_map<ovrResult, const char*> ResultNameMap;
static ResultNameMap& GetResultNameMap() {
static ResultNameMap resultNameMap;
return resultNameMap;
}
const char* GetErrorCodeName(ovrResult errorCode) {
if (errorCode == ovrSuccess) {
return "ovrSuccess"; // Speed up a common case
}
const ResultNameMap& resultNameMap = GetResultNameMap();
ResultNameMap::const_iterator it = resultNameMap.find(errorCode);
if (it == resultNameMap.end()) {
OVR_FAIL_M("Unknown result code. It should have been registered");
return "UnknownResultCode";
}
return it->second;
}
void OVRError::RegisterResultCodeName(ovrResult number, const char* name) {
ResultNameMap& resultNameMap = GetResultNameMap();
if (resultNameMap.find(number) != resultNameMap.end()) {
OVR_FAIL_M("Duplicate result code registered");
return;
}
resultNameMap[number] = name;
}
bool GetErrorCodeString(ovrResult resultIn, bool prefixErrorCode, OVR::String& sResult) {
char codeBuffer[256];
const char* errorCodeName = GetErrorCodeName(resultIn);
if (prefixErrorCode) {
snprintf(
codeBuffer,
OVR_ARRAY_COUNT(codeBuffer),
"0x%llx (%lld) %s",
(uint64_t)resultIn,
(int64_t)resultIn,
errorCodeName);
} else {
snprintf(codeBuffer, OVR_ARRAY_COUNT(codeBuffer), "%s", errorCodeName);
}
sResult = codeBuffer;
return true;
}
#if defined(OVR_OS_WIN32)
static const wchar_t* OVR_DXGetErrorStringW(HRESULT dwDXGIErrorCode) {
switch (dwDXGIErrorCode) {
case DXGI_ERROR_DEVICE_HUNG:
return L"DXGI_ERROR_DEVICE_HUNG"; // The application's device failed due to badly formed
// commands sent by the application. This is an design-time
// issue that should be investigated and fixed.
case DXGI_ERROR_DEVICE_REMOVED:
return L"DXGI_ERROR_DEVICE_REMOVED"; // The video card has been physically removed from the
// system, or a driver upgrade for the video card has
// occurred. The application should destroy and recreate
// the device. For help debugging the problem, call
// ID3D10Device::GetDeviceRemovedReason.
case DXGI_ERROR_DEVICE_RESET:
return L"DXGI_ERROR_DEVICE_RESET"; // The device failed due to a badly formed command. This is
// a run-time issue; The application should destroy and
// recreate the device.
case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
return L"DXGI_ERROR_DRIVER_INTERNAL_ERROR"; // The driver encountered a problem and was put
// into the device removed state.
case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:
return L"DXGI_ERROR_FRAME_STATISTICS_DISJOINT"; // An event (for example, a power cycle)
// interrupted the gathering of presentation
// statistics.
case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:
return L"DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE"; // The application attempted to acquire
// exclusive ownership of an output, but
// failed because some other application
// (or device within the application)
// already acquired ownership.
case DXGI_ERROR_INVALID_CALL:
return L"DXGI_ERROR_INVALID_CALL"; // The application provided invalid parameter data; this
// must be debugged and fixed before the application is
// released.
case DXGI_ERROR_MORE_DATA:
return L"DXGI_ERROR_MORE_DATA"; // The buffer supplied by the application is not big enough to
// hold the requested data.
case DXGI_ERROR_NONEXCLUSIVE:
return L"DXGI_ERROR_NONEXCLUSIVE"; // A global counter resource is in use, and the Direct3D
// device can't currently use the counter resource.
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
return L"DXGI_ERROR_NOT_CURRENTLY_AVAILABLE"; // The resource or request is not currently
// available, but it might become available
// later.
case DXGI_ERROR_NOT_FOUND:
return L"DXGI_ERROR_NOT_FOUND"; // When calling IDXGIObject::GetPrivateData, the GUID passed
// in is not recognized as one previously passed to
// IDXGIObject::SetPrivateData or
// IDXGIObject::SetPrivateDataInterface. When calling
// IDXGIFactory::EnumAdapters or IDXGIAdapter::EnumOutputs,
// the enumerated ordinal is out of range.
case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:
return L"DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED"; // Reserved
case DXGI_ERROR_REMOTE_OUTOFMEMORY:
return L"DXGI_ERROR_REMOTE_OUTOFMEMORY"; // Reserved
case DXGI_ERROR_WAS_STILL_DRAWING:
return L"DXGI_ERROR_WAS_STILL_DRAWING"; // The GPU was busy at the moment when a call was made
// to perform an operation, and did not execute or
// schedule the operation.
case DXGI_ERROR_UNSUPPORTED:
return L"DXGI_ERROR_UNSUPPORTED"; // The requested functionality is not supported by the
// device or the driver.
case DXGI_ERROR_ACCESS_LOST:
return L"DXGI_ERROR_ACCESS_LOST"; // The desktop duplication interface is invalid. The desktop
// duplication interface typically becomes invalid when a
// different type of image is displayed on the desktop.
case DXGI_ERROR_WAIT_TIMEOUT:
return L"DXGI_ERROR_WAIT_TIMEOUT"; // The time-out interval elapsed before the next desktop
// frame was available.
case DXGI_ERROR_SESSION_DISCONNECTED:
return L"DXGI_ERROR_SESSION_DISCONNECTED"; // The Remote Desktop Services session is currently
// disconnected.
case DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE:
return L"DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE"; // The DXGI output (monitor) to which the swap
// chain content was restricted is now
// disconnected or changed.
case DXGI_ERROR_CANNOT_PROTECT_CONTENT:
return L"DXGI_ERROR_CANNOT_PROTECT_CONTENT"; // DXGI can't provide content protection on the
// swap chain. This error is typically caused by
// an older driver, or when you use a swap chain
// that is incompatible with content protection.
case DXGI_ERROR_ACCESS_DENIED:
return L"DXGI_ERROR_ACCESS_DENIED"; // You tried to use a resource to which you did not have
// the required access privileges. This error is most
// typically caused when you write to a shared resource
// with read-only access.
case DXGI_ERROR_NAME_ALREADY_EXISTS:
return L"DXGI_ERROR_NAME_ALREADY_EXISTS"; // The supplied name of a resource in a call to
// IDXGIResource1::CreateSharedHandle is already
// associated with some other resource.
case DXGI_ERROR_SDK_COMPONENT_MISSING:
return L"DXGI_ERROR_SDK_COMPONENT_MISSING"; // The operation depends on an SDK component that
// is missing or mismatched.
}
return nullptr;
}
#endif
bool GetSysErrorCodeString(
ovrSysErrorCodeType sysErrorCodeType,
ovrSysErrorCode sysErrorCode,
bool prefixErrorCode,
OVR::String& sResult) {
char errorBuffer[1024];
errorBuffer[0] = '\0';
if (prefixErrorCode) {
char prefixBuffer[64];
snprintf(
prefixBuffer,
OVR_ARRAY_COUNT(prefixBuffer),
"0x%llx (%lld): ",
(uint64_t)sysErrorCode,
(int64_t)sysErrorCode);
sResult = prefixBuffer;
} else {
sResult.Clear();
}
if (sysErrorCodeType == ovrSysErrorCodeType::Vulkan) {
// We have a problem here in that we can't #include <vulkan.h> because this source is
// distributed as part of a public SDK, and we don't want to create that dependency.
// However, vulkan.h errors are stable, so we can repeat them here.
char buffer[32];
itoa(sysErrorCode, buffer, 10);
const char* vkResultStr = buffer; // Default to the numerical value.
switch (static_cast<int32_t>(sysErrorCode)) {
case 0:
vkResultStr = "VK_SUCESS";
break;
case 1:
vkResultStr = "VK_NOT_READY";
break;
case 2:
vkResultStr = "VK_TIMEOUT";
break;
case 3:
vkResultStr = "VK_EVENT_SET";
break;
case 4:
vkResultStr = "VK_EVENT_RESET";
break;
case 5:
vkResultStr = "VK_INCOMPLETE";
break;
case -1:
vkResultStr = "VK_ERROR_OUT_OF_HOST_MEMORY";
break;
case -2:
vkResultStr = "VK_ERROR_OUT_OF_DEVICE_MEMORY";
break;
case -3:
vkResultStr = "VK_ERROR_INITIALIZATION_FAILED";
break;
case -4:
vkResultStr = "VK_ERROR_DEVICE_LOST";
break;
case -5:
vkResultStr = "VK_ERROR_MEMORY_MAP_FAILED";
break;
case -6:
vkResultStr = "VK_ERROR_LAYER_NOT_PRESENT";
break;
case -7:
vkResultStr = "VK_ERROR_EXTENSION_NOT_PRESENT";
break;
case -8:
vkResultStr = "VK_ERROR_FEATURE_NOT_PRESENT";
break;
case -9:
vkResultStr = "VK_ERROR_INCOMPATIBLE_DRIVER";
break;
case -10:
vkResultStr = "VK_ERROR_TOO_MANY_OBJECTS";
break;
case -11:
vkResultStr = "VK_ERROR_FORMAT_NOT_SUPPORTED";
break;
case -12:
vkResultStr = "VK_ERROR_FRAGMENTED_POOL";
break;
case -1000000000:
vkResultStr = "VK_ERROR_SURFACE_LOST_KHR";
break;
case -1000000001:
vkResultStr = "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
break;
case 1000001003:
vkResultStr = "VK_SUBOPTIMAL_KHR";
break;
case -1000001004:
vkResultStr = "VK_ERROR_OUT_OF_DATE_KHR";
break;
case -1000003001:
vkResultStr = "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
break;
case -1000011001:
vkResultStr = "VK_ERROR_VALIDATION_FAILED_EXT";
break;
case -1000012000:
vkResultStr = "VK_ERROR_INVALID_SHADER_NV";
break;
case -1000069000:
vkResultStr = "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
break;
case -1000072003:
vkResultStr = "VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR";
break;
case -1000174001:
vkResultStr = "VK_ERROR_NOT_PERMITTED_EXT";
break;
default:
// Use the numerical default above.
break;
}
OVR::OVR_strlcat(errorBuffer, vkResultStr, sizeof(errorBuffer));
} else if (sysErrorCodeType == ovrSysErrorCodeType::OpenGL) {
char buffer[32];
itoa(sysErrorCode, buffer, 10);
const char* glResultStr = buffer; // Default to the numerical value.
switch (sysErrorCode) {
case 0:
glResultStr = "GL_NO_ERROR";
break;
case 0x0500:
glResultStr = "GL_INVALID_ENUM";
break;
case 0x0501:
glResultStr = "GL_INVALID_VALUE";
break;
case 0x0502:
glResultStr = "GL_INVALID_OPERATION";
break;
case 0x0503:
glResultStr = "GL_STACK_OVERFLOW";
break;
case 0x0504:
glResultStr = "GL_STACK_UNDERFLOW";
break;
case 0x0505:
glResultStr = "GL_OUT_OF_MEMORY";
break;
default:
// Use the numerical default above.
break;
}
OVR::OVR_strlcat(errorBuffer, glResultStr, sizeof(errorBuffer));
} else if (sysErrorCodeType == ovrSysErrorCodeType::OS) {
#if defined(OVR_OS_WIN32)
// Note: It may be useful to use FORMAT_MESSAGE_FROM_HMODULE here to get a module-specific error
// string if our source of errors ends up including more than just system-native errors. For
// example, a third party module with custom errors defined in it.
WCHAR errorBufferW[1024];
DWORD errorBufferWCapacity = OVR_ARRAY_COUNT(errorBufferW);
DWORD length = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
(DWORD)sysErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
errorBufferW,
errorBufferWCapacity,
nullptr);
if (!length) // If failed...
{
if (HRESULT_FACILITY(sysErrorCode) == _FACDXGI) // If it is a DXGI error...
{
// This situation occurs on Windows 7. You can't use FORMAT_MESSAGE_FROM_HMODULE to solve it
// either. We can only use DXGetErrorString or manually handle it.
const wchar_t* pStr = OVR_DXGetErrorStringW(sysErrorCode);
if (pStr) {
wcscpy_s(errorBufferW, OVR_ARRAY_COUNT(errorBufferW), pStr);
length = (DWORD)wcslen(errorBufferW);
}
}
}
if (length) // If errorBufferW contains what we are looking for...
{
// Need to convert WCHAR errorBuffer to UTF8 char sResult;
const auto requiredUTF8Length =
OVR::UTF8Util::Strlcpy(errorBuffer, OVR_ARRAY_COUNT(errorBuffer), errorBufferW);
if (requiredUTF8Length >=
OVR_ARRAY_COUNT(errorBuffer)) // Zero out if too big (XXX truncate instead?)
errorBuffer[0] = '\0';
// Else fall through
} // Else fall through
#else
#if (((_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && !_GNU_SOURCE) || \
defined(__APPLE__) || defined(__BSD__)
const int result = strerror_r((int)sysErrorCode, errorBuffer, OVR_ARRAY_COUNT(errorBuffer));
if (result != 0) // If failed... [result is 0 upon success; result will be EINVAL if the code is
// not recognized; ERANGE if buffer didn't have enough capacity.]
errorBuffer[0] = '\0'; // re-null-terminate, in case strerror_r left it in an invalid state.
#else
const char* result = strerror_r((int)sysErrorCode, errorBuffer, OVR_ARRAY_COUNT(errorBuffer));
if (result ==
nullptr) // Implementations in practice seem to always return a pointer, though the
// behavior isn't formally standardized.
errorBuffer[0] = '\0'; // re-null-terminate, in case strerror_r left it in an invalid state.
#endif
#endif
}
// Fall through functionality of simply printing the value as an integer.
if (errorBuffer[0]) // If errorBuffer was successfully written above...
{
sResult += errorBuffer;
return true;
}
sResult += "(unknown)"; // This is not localized. Question: Is there a way to get the error
// formatting functions above to print this themselves in a localized way?
return false;
}
} // namespace OVR

View File

@ -0,0 +1,802 @@
/************************************************************************************
Filename : OVR_Error.h
Content : Structs and functions for handling OVRErrors
Created : February 15, 2015
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#pragma once
#include "Kernel/OVR_Log.h"
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_Array.h"
#include "Kernel/OVR_System.h"
#include "Kernel/OVR_Threads.h"
#include "Kernel/OVR_Hash.h"
#include <chrono>
#include <utility>
#include <functional>
OVR_DISABLE_MSVC_WARNING(4351) // new behavior: elements of array will be default initialized
#ifndef OVR_RESULT_DEFINED
#define OVR_RESULT_DEFINED ///< Allows ovrResult to be independently defined.
/// API call results are represented at the highest level by a single ovrResult.
typedef int32_t ovrResult;
#endif
/// \brief Indicates if an ovrResult indicates success.
///
/// Some functions return additional successful values other than ovrSucces and
/// require usage of this macro to indicate successs.
///
#if !defined(OVR_SUCCESS)
#define OVR_SUCCESS(result) (result >= 0)
#endif
/// \brief Indicates if an ovrResult indicates an unqualified success.
///
/// This is useful for indicating that the code intentionally wants to
/// check for result == ovrSuccess as opposed to OVR_SUCCESS(), which
/// checks for result >= ovrSuccess.
///
#if !defined(OVR_UNQUALIFIED_SUCCESS)
#define OVR_UNQUALIFIED_SUCCESS(result) (result == ovrSuccess)
#endif
/// \brief Indicates if an ovrResult indicates failure.
///
#if !defined(OVR_FAILURE)
#define OVR_FAILURE(result) (!OVR_SUCCESS(result))
#endif
// Success is a value greater or equal to 0, while all error types are negative values.
#ifndef OVR_SUCCESS_DEFINED
#define OVR_SUCCESS_DEFINED ///< Allows ovrResult to be independently defined.
typedef enum ovrSuccessType_ {
/// This is a general success result. Use OVR_SUCCESS to test for success.
ovrSuccess = 0,
} ovrSuccessType;
#endif
/// -----------------------------------------------------------------------------
/// ***** OVR_FILE / OVR_LINE
///
#if !defined(OVR_FILE)
#if defined(OVR_BUILD_DEBUG)
#define OVR_FILE __FILE__
#define OVR_LINE __LINE__
#else
#define OVR_FILE nullptr
#define OVR_LINE 0
#endif
#endif
// LOG_VAARG_ATTRIBUTE macro, enforces printf-style formatting 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
namespace OVR {
/// -----------------------------------------------------------------------------
/// ***** OVR_MAKE_ERROR, OVR_MAKE_ERROR_F, OVR_MAKE_SYS_ERROR, OVR_MAKE_SYS_ERROR_F
///
/// Declaration:
/// OVRError OVR_MAKE_ERROR(ovrResult r, const char* description);
/// OVRError OVR_MAKE_ERROR_F(ovrResult r, const char* format, ...);
///
/// OVRError OVR_MAKE_SYS_ERROR(ovrResult r, ovrSysErrorCode sysCode, const char* description);
/// OVRError OVR_MAKE_SYS_ERROR_F(ovrResult r, ovrSysErrorCode sysCode, const char* format, ...);
///
/// Example usage:
/// OVRError InitGraphics()
/// {
/// if(!GraphicsCardPresent())
/// {
/// return OVR_MAKE_ERROR(ovrError_GraphicsInit, "Failed to init graphics; graphics
/// support absent.");
/// }
///
/// HRESULT hr = pDevice->CreateTexture2D(&dsDesc, nullptr, &Texture);
/// if(FAILED(hr))
/// {
/// return OVR_MAKE_SYS_ERROR_F(ovrError_GraphicsInit, hr, "Failed to create texture of
/// size %u x %u", dsDesc.Width, dsDesc.Height);
/// }
/// or:
/// OVR_HR_CHECK_RET_ERROR(ovrError_GraphicsInit, hr, "Failed to create texture of size
/// %u x %u", dsDesc.Width, dsDesc.Height);
///
/// return ovrSuccess; // Converts to an OVRError instance that has no error.
/// }
///
#define OVR_MAKE_ERROR(errorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::None, \
OVR::ovrSysErrorCodeSuccess, \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_ERROR_F(errorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::None, \
OVR::ovrSysErrorCodeSuccess, \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
__VA_ARGS__)
#define OVR_MAKE_SYS_ERROR(errorCode, sysErrorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OS, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_SYS_ERROR_F(errorCode, sysErrorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OS, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
__VA_ARGS__)
#define OVR_MAKE_VULKAN_ERROR(errorCode, sysErrorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::Vulkan, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_VULKAN_ERROR_F(errorCode, sysErrorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::Vulkan, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
__VA_ARGS__)
#define OVR_MAKE_OPENGL_ERROR(errorCode, sysErrorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OpenGL, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_OPENGL_ERROR_F(errorCode, sysErrorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OpenGL, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
true, \
nullptr, \
__VA_ARGS__)
// Consider using OVR_MAKE_QUIET_ERROR() instead of OVR_MAKE_ERROR() where the error
// should not automatically log/assert. If the error is normal (HMD unplugged) or
// repetitive then please use OVR_MAKE_QUIET_ERROR().
#define OVR_MAKE_QUIET_ERROR(errorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::None, \
OVR::ovrSysErrorCodeSuccess, \
nullptr, \
OVR_FILE, \
OVR_LINE, \
false, \
false, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_QUIET_ERROR_F(errorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::None, \
OVR::ovrSysErrorCodeSuccess, \
nullptr, \
OVR_FILE, \
OVR_LINE, \
false, \
false, \
nullptr, \
__VA_ARGS__)
#define OVR_MAKE_QUIET_SYS_ERROR(errorCode, sysErrorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OS, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
false, \
false, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_QUIET_SYS_ERROR_F(errorCode, sysErrorCode, ...) \
OVR::MakeError( \
(errorCode), \
ovrSysErrorCodeType::OS, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
false, \
false, \
nullptr, \
__VA_ARGS__)
// Consider using OVR_MAKE_NOASSERT_ERROR() instead of OVR_MAKE_ERROR() where the error
// should not automatically log/assert. If the error is normal (HMD unplugged) or
// repetitive then please use OVR_MAKE_NOASSERT_ERROR().
#define OVR_MAKE_NOASSERT_ERROR(errorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::None, \
OVR::ovrSysErrorCodeSuccess, \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
false, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_NOASSERT_ERROR_F(errorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::None, \
OVR::ovrSysErrorCodeSuccess, \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
false, \
nullptr, \
__VA_ARGS__)
#define OVR_MAKE_NOASSERT_SYS_ERROR(errorCode, sysErrorCode, pDescription) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OS, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
false, \
nullptr, \
"%s", \
(pDescription))
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_MAKE_NOASSERT_SYS_ERROR_F(errorCode, sysErrorCode, ...) \
OVR::MakeError( \
(errorCode), \
OVR::ovrSysErrorCodeType::OS, \
(sysErrorCode), \
nullptr, \
OVR_FILE, \
OVR_LINE, \
true, \
false, \
nullptr, \
__VA_ARGS__)
// Set the TLS last error:
#define OVR_SET_ERROR(ovrError) OVR::SetError(ovrError)
// Log the error if it has not already been logged:
#define OVR_LOG_ERROR(ovrError) OVR::LogError(ovrError)
// Check an HRESULT error code and assert/log on failure:
#define OVR_HR_CHECK_RET_ERROR(errorCode, sysErrorCode, pDescription) \
if (FAILED(sysErrorCode)) { \
return OVR_MAKE_SYS_ERROR_F((errorCode), (sysErrorCode), "%s", (pDescription)); \
}
// Note: The format string is the first part of the .../__VA_ARGS__ as per the C99-C++11 Standards.
#define OVR_HR_CHECK_RET_ERROR_F(errorCode, sysErrorCode, ...) \
if (FAILED(sysErrorCode)) { \
return OVR_MAKE_SYS_ERROR_F((errorCode), (sysErrorCode), __VA_ARGS__); \
}
/// -----------------------------------------------------------------------------
/// ***** ovrSysErrorCodeType
///
enum class ovrSysErrorCodeType {
None,
OS, /// Windows: HRESULT or DWORD system error code from GetLastError.
ALVR, /// AMD LiquidVR error
NVAPI, /// NVidia API error
NVENC, /// NVidia video encode API error
Vulkan, /// Vulkan graphics API
OpenGL /// OpenGL graphics API
};
/// -----------------------------------------------------------------------------
/// ***** ovrSysErrorCode
///
/// Identifies a platform- or API-specific error identifier.
/// The error space is identified by ovrSysErrorCodeType.
///
typedef uint32_t ovrSysErrorCode;
/// -----------------------------------------------------------------------------
/// ***** ovrSysErrorCodeSuccess
///
/// Identifies a ovrSysErrorCode that's success.
///
const ovrSysErrorCode ovrSysErrorCodeSuccess = 0;
/// -----------------------------------------------------------------------------
/// ***** ovrSysErrorCodeNone
///
/// Identifies a ovrSysErrorCode that's un-set.
///
const ovrSysErrorCode ovrSysErrorCodeNone = 0;
// SysClockTime is a C++11 equivalent to C time_t.
typedef std::chrono::time_point<std::chrono::system_clock> SysClockTime;
/// -----------------------------------------------------------------------------
/// ***** OVRError
///
/// Represents an error and relevant information about it.
/// While you can create error instances directly via this class, it's better if
/// you create them via the OVR_MAKE_ERROR family of macros, or at least via the
/// MakeError function.
///
/// Relevant design analogues:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms723041%28v=vs.85%29.aspx
///
class OVRError {
private:
// Cannot convert boolean to OVRError - It must be done explicitly.
OVRError(bool) {
OVR_ASSERT(false);
}
OVRError(bool, const char*, ...) {
OVR_ASSERT(false);
}
public:
OVRError();
OVRError(ovrResult code); // Intentionally not explicit.
OVRError(ovrResult code, const char* pFormat, ...);
OVRError(const OVRError& OVRError);
OVRError(OVRError&& OVRError);
virtual ~OVRError();
// Construct a success code. Use Succeeded() to check for success.
static OVRError Success() {
return OVRError();
}
OVRError& operator=(const OVRError& OVRError);
OVRError& operator=(OVRError&& OVRError);
// Use this to check if result is a success code
bool Succeeded() const {
return Code >= ovrSuccess;
}
bool Failed() const {
return !Succeeded();
}
// Sets the OVRTime, ClockTime, Backtrace to current values.
void SetCurrentValues(); // To do: Come up with a more appropiate name.
// Clears all members to a newly default-constructed state.
void Reset();
// Get the full error string for this error. May include newlines.
String GetErrorString() const;
// Property accessors
void SetCode(ovrResult code);
ovrResult GetCode() const;
// Example usage:
// ovrResult SomeFunction() {
// OVRError err = SomeOtherFunction();
// return err;
// }
operator ovrResult() const {
return Code;
}
void SetSysCode(
ovrSysErrorCodeType sysCodeType,
ovrSysErrorCode sysCode,
const char* sysCodeString = nullptr);
ovrSysErrorCodeType GetSysCodeType() const;
ovrSysErrorCode GetSysCode() const;
void SetDescription(const char* pDescription);
String GetDescription() const;
void SetContext(const char* pContext);
String GetContext() const;
void SetOVRTime(double ovrTime);
double GetOVRTime() const;
void SetSysClockTime(const SysClockTime& clockTime);
SysClockTime GetSysClockTime() const;
static const int64_t kLogLineUnset = -1;
void SetLogLine(int64_t logLine);
int64_t GetLogLine() const;
bool IsAlreadyLogged() const {
return AlreadyLogged;
}
void SetAlreadyLogged() {
AlreadyLogged = true;
}
void ResetAlreadyLogged() {
AlreadyLogged = false;
}
void SetSource(const char* pSourceFilePath, int sourceFileLine);
std::pair<String, int> GetSource() const;
typedef OVR::Array<void*> AddressArray;
AddressArray GetBacktrace() const;
static void RegisterResultCodeName(ovrResult number, const char* name);
protected:
mutable StringBuffer ErrorString; /// Complete error string describing the variables below.
ovrResult Code; /// The main ovrResult, which is a high level error id.
ovrSysErrorCodeType SysCodeType;
ovrSysErrorCode SysCode; /// ovrSysErrorCodeSuccess means no system error code.
char SysCodeStr[64]; /// String describing just the sys code error.
String Description; /// Unlocalized error description string.
String Context; /// Context string. For example, for a file open failure this is the file path.
double OVRTime; /// Time when the error was generated. Same format as OVR time.
SysClockTime ClockTime; /// Wall clock time.
int64_t LogLine; /// Log line of the error. -1 if not set (not logged).
String SourceFilePath; /// The __FILE__ where the error was first encountered.
int SourceFileLine; /// The __LINE__ where the error was first encountered.
AddressArray Backtrace; /// Backtrace at point of error. May be empty in publicly released builds.
bool AlreadyLogged; /// Error has already been logged to avoid double-printing it.
};
/// -----------------------------------------------------------------------------
/// ***** SetError
///
/// This function sets the error as the last error via the TLS last error class.
///
void SetError(OVRError& ovrError);
/// -----------------------------------------------------------------------------
/// ***** LogError
///
/// Utility function for logging an error based on the Log subsystem.
///
void LogError(OVRError& ovrError);
/// -----------------------------------------------------------------------------
/// ***** MakeError
///
/// Utility function for making an error, logging it, and setting it as the last error for the
/// current thread. It's preferred to instead use the OVR_MAKE_ERROR macro functions, as they handle
/// file/line functionality cleanly between debug and release. The "quiet" parameter will prevent it
/// from automatically logging/asserting.
///
OVRError MakeError(
ovrResult errorCode,
ovrSysErrorCodeType sysCodeType,
ovrSysErrorCode sysCode,
const char* sysCodeString, // Optional pre-generated sys error code string.
const char* pSourceFile,
int sourceLine,
bool logError,
bool assertError,
const char* pContext,
const char* pDescriptionFormat,
...) OVR_LOG_VAARG_ATTRIBUTE(8, 9);
// -----------------------------------------------------------------------------
// ***** LastErrorTLS
//
// We don't use C++11 thread-local storage nor C-style __thread/__declsped(thread)
// to manager thread-local storage, as neither of these provide a means for us
// to control the lifetime of the data. Rather it can be controlled only
// passively by the thread's lifetime. Similarly we don't use pthread_getspecific
// pthread_setspecific (and Windows equivalents) because they too don't let us
// control the lifetime of the data. Our solution is to have a map of threads
// to thread-specific data, and we can clear the entire map on ovrShutdown as-needed.
// this scheme is not as fast as the aforementioned schemes but it doesn't need to
// be fast for our use.
//
// We use pointers below instead of concrete objects because we want to have their
// lifetimes entirely controlled by ovr_Initialize/ovr_Shutdown.
class LastErrorTLS : public NewOverrideBase, public SystemSingletonBase<LastErrorTLS> {
OVR_DECLARE_SINGLETON(LastErrorTLS);
public:
OVRError& LastError();
protected:
// Protect hash from multiple thread access.
Lock TheLock;
// Map thread-id to OVRError objects.
typedef Hash<ThreadId, OVRError> TLSHash;
TLSHash TLSDictionary;
};
/// -----------------------------------------------------------------------------
/// ***** GetErrorCodeName
///
/// Utility function which converts an ovrResult error code to a string which matches
/// errorCode's ovrResult enumeration identifier.
///
const char* GetErrorCodeName(ovrResult errorCode);
/// -----------------------------------------------------------------------------
/// ***** GetErrorCodeString
///
/// Utility function which converts an ovrResult error code to a readable string version.
///
bool GetErrorCodeString(ovrResult errorCode, bool prefixErrorCode, OVR::String& sResult);
/// -----------------------------------------------------------------------------
/// ***** GetSysErrorCodeString
///
/// Utility function which converts a system error to a string. Similar to the Windows FormatMessage
/// function and the Unix strerror_r function.
/// If prefixErrorCode is true then the string is prefixed with "<code>: "
/// Returns true if the sysErrorCode was a known valid code and a string was produced from it.
/// Else the returned string will be empty. The returned string may have tabs or newlines.
/// Users of OVR_MAKE_SYS_ERROR and MakeError don't need to call this function, as it's done
/// automatically internally.
///
bool GetSysErrorCodeString(
ovrSysErrorCodeType sysErrorCodeType,
ovrSysErrorCode sysErrorCode,
bool prefixErrorCode,
OVR::String& sResult);
//-------------------------------------------------------------------------------------
// C++ Exception state
// CPPExceptionInfo contains information about a possible exception state for the current library.
// We cannot safely use the OVRError system for this, as it could fail when we are in an exceptional
// state. It would be a non-trivial effort to make the OVRError system work in a way that doesn't
// allocate memory, for various reasons.
struct CPPExceptionInfo {
CPPExceptionInfo() OVR_NOEXCEPT : ExceptionOccurred(false), Description() {}
void Reset() OVR_NOEXCEPT {
ExceptionOccurred = false;
Description[0] = '\0';
}
void FromStdException(const std::exception& e) OVR_NOEXCEPT {
ExceptionOccurred = true;
OVR_strlcpy(Description, e.what(), sizeof(Description));
}
void FromString(const char* str) OVR_NOEXCEPT {
ExceptionOccurred = true;
OVR_strlcpy(Description, str, sizeof(Description));
}
// If this get set to true then it should never be set back to false
// except by an unloading of the module (i.e. DLL) itself.
bool ExceptionOccurred;
// Describes the exception in a way that makes no external calls
// nor allocates any memory.
char Description[256];
};
extern OVR::CPPExceptionInfo gCPPExceptionInfo;
// Helper macros to make repetitive exception handling tasks non-redundant.
//
// Example usage:
// OVR_PUBLIC_FUNCTION(void) ovr_DoSomething(ovrSession session)
// {
// OVR_CAPI_TRY_VOID
// {
// HMDState* hmds = GetHMDStateFromOvrHmd(session);
// if (hmds)
// hmds->DoSomething();
// }
// OVR_CAPI_CATCH
// }
//
// OVR_PUBLIC_FUNCTION(int) ovr_DoSomething(ovrSession session)
// {
// int result = 0;
//
// OVR_CAPI_TRY_VALUE(result)
// {
// HMDState* hmds = GetHMDStateFromOvrHmd(session);
// if (hmds)
// result = hmds->DoSomething;
// }
// OVR_CAPI_CATCH
//
// return result;
// }
//
// OVR_PUBLIC_FUNCTION(ovrResult) ovr_DoSomething(ovrSession session)
// {
// OVR_CAPI_TRY_OVRRESULT
// {
// HMDState* pState = GetHMDStateFromOvrHmd(session);
// if (!pState)
// {
// OVR_MAKE_ERROR(ovrError_InvalidSession, L"Invalid session.");
// return ovrError_InvalidSession;
// }
// return hmds->DoSomething();
// }
// OVR_CAPI_CATCH
//
// return ovrError_CPPException;
// }
#if defined(OVR_BUILD_DEBUG)
#define OVR_CAPI_TRY_VOID \
do { \
} while (false);
#define OVR_CAPI_TRY_VALUE(exceptionStateReturnValue) \
do { \
} while (false);
#define OVR_CAPI_TRY_OVRRESULT \
do { \
} while (false);
#define OVR_CAPI_CATCH \
do { \
} while (false);
#else // OVR_BUILD_DEBUG
#define OVR_CAPI_TRY_VOID \
if (OVR::gCPPExceptionInfo.ExceptionOccurred) \
return; \
try
#define OVR_CAPI_TRY_VALUE(exceptionStateReturnValue) \
if (OVR::gCPPExceptionInfo.ExceptionOccurred) \
return exceptionStateReturnValue; \
try
#define OVR_CAPI_TRY_OVRRESULT \
if (OVR::gCPPExceptionInfo.ExceptionOccurred) \
return ovrError_RuntimeException; \
try
#define OVR_CAPI_CATCH \
catch (const std::exception& e) { \
OVR::gCPPExceptionInfo.FromStdException(e); \
} \
catch (...) { \
OVR::gCPPExceptionInfo.FromString("Non-standard C++ exception occurred."); \
}
#endif // OVR_BUILD_DEBUG
/// -----------------------------------------------------------------------------
/// ***** OVRErrorCallback
///
/// Identifies a callback error handler.
/// Callbacks were added instead of integrating logic directly in OVRError
/// to make application specific logic easier
///
typedef std::function<void(const OVRError& err, bool quiet)> OVRErrorCallback;
/// -----------------------------------------------------------------------------
/// ***** SetErrorCallback
///
/// Sets callback to be issued whenever an error is encountered.
///
void SetErrorCallback(OVRErrorCallback callback);
} // namespace OVR
#ifndef MICRO_OVR
namespace ovrlog {
template <>
LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const OVR::OVRError& first) {
buffer.Stream << first.GetErrorString().ToCStr();
}
} // namespace ovrlog
#endif

View File

@ -0,0 +1,521 @@
/**************************************************************************
Filename : OVR_File.cpp
Content : File wrapper class implementation (Win32)
Created : April 5, 1999
Authors : Michael Antonov
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**************************************************************************/
#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 = (uint8_t*)OVR_ALLOC(FILEBUFFER_SIZE);
BufferMode = NoBuffer;
FilePos = 0;
Pos = 0;
DataSize = 0;
}
// Takes another file as source
BufferedFile::BufferedFile(File* pfile) : DelegatedFile(pfile) {
pBuffer = (uint8_t*)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;
}
int64_t BufferedFile::LTell() {
if (BufferMode == ReadBuffer)
return FilePos - DataSize + Pos;
int64_t 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;
}
int64_t BufferedFile::LGetLength() {
int64_t len = pFile->LGetLength();
// If writing through buffer, file length may actually be bigger
if ((len != -1) && (BufferMode == WriteBuffer)) {
int64_t 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)
{
int64_t 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 uint8_t* 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(uint8_t* 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_t)offset) < ~(uint64_t)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_t)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);
}
int64_t BufferedFile::LSeek(int64_t 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 int64_t(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 = (int64_t)(FilePos - DataSize + Pos) + offset;
Pos = DataSize = 0;
} else if (origin == Seek_Set) {
if (((uint64_t)offset - (FilePos - DataSize)) <= DataSize) {
Pos = (unsigned)((uint64_t)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 >= int64_t(FilePos - DataSize) && offset <
int64_t(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
uint8_t* buff = new uint8_t[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;
}
delete[] buff;
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) {
size_t len = OVR_strlen(purl);
for (size_t i = len; i > 0; i--)
if (purl[i] == '\\' || purl[i] == '/')
return purl + i + 1;
return purl;
}
} // namespace OVR

View File

@ -0,0 +1,695 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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 {
//-----------------------------------------------------------------------------------
// ***** OVR_MAX_PATH
//
// Max file path length (for most uses).
// To do: move this to OVR_File.
//
#if !defined(OVR_MAX_PATH)
#if defined( \
_WIN32) // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
#define OVR_MAX_PATH \
MAX_PATH // Windows can use paths longer than MAX_PATH in some cases (network paths, UNC paths).
#else
#define OVR_MAX_PATH 1024 // This isn't a strict limit on all Unix-based platforms.
#endif
#endif
// ***** 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 int64_t LTell() = 0;
// File size
virtual int GetLength() = 0;
virtual int64_t 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 uint8_t* 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(uint8_t* 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 int64_t LSeek(int64_t 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_t PRead64() {
uint64_t v = 0;
Read((uint8_t*)&v, 8);
return v;
}
uint32_t PRead32() {
uint32_t v = 0;
Read((uint8_t*)&v, 4);
return v;
}
uint16_t PRead16() {
uint16_t v = 0;
Read((uint8_t*)&v, 2);
return v;
}
uint8_t PRead8() {
uint8_t v = 0;
Read((uint8_t*)&v, 1);
return v;
}
void PWrite64(uint64_t v) {
Write((uint8_t*)&v, 8);
}
void PWrite32(uint32_t v) {
Write((uint8_t*)&v, 4);
}
void PWrite16(uint16_t v) {
Write((uint8_t*)&v, 2);
}
void PWrite8(uint8_t v) {
Write((uint8_t*)&v, 1);
}
public:
// Writing primitive types - Little Endian
inline void WriteUByte(uint8_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteSByte(int8_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteUInt8(uint8_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteSInt8(int8_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteUInt16(uint16_t v) {
PWrite16((uint16_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteSInt16(int16_t v) {
PWrite16((uint16_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteUInt32(uint32_t v) {
PWrite32((uint32_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteSInt32(int32_t v) {
PWrite32((uint32_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteUInt64(uint64_t v) {
PWrite64((uint64_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteSInt64(int64_t v) {
PWrite64((uint64_t)Alg::ByteUtil::SystemToLE(v));
}
inline void WriteFloat(float v) {
v = Alg::ByteUtil::SystemToLE(v);
Write((uint8_t*)&v, 4);
}
inline void WriteDouble(double v) {
v = Alg::ByteUtil::SystemToLE(v);
Write((uint8_t*)&v, 8);
}
// Writing primitive types - Big Endian
inline void WriteUByteBE(uint8_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteSByteBE(int8_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteUInt8BE(uint16_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteSInt8BE(int16_t v) {
PWrite8((uint8_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteUInt16BE(uint16_t v) {
PWrite16((uint16_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteSInt16BE(uint16_t v) {
PWrite16((uint16_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteUInt32BE(uint32_t v) {
PWrite32((uint32_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteSInt32BE(uint32_t v) {
PWrite32((uint32_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteUInt64BE(uint64_t v) {
PWrite64((uint64_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteSInt64BE(uint64_t v) {
PWrite64((uint64_t)Alg::ByteUtil::SystemToBE(v));
}
inline void WriteFloatBE(float v) {
v = Alg::ByteUtil::SystemToBE(v);
Write((uint8_t*)&v, 4);
}
inline void WriteDoubleBE(double v) {
v = Alg::ByteUtil::SystemToBE(v);
Write((uint8_t*)&v, 8);
}
// Reading primitive types - Little Endian
inline uint8_t ReadUByte() {
return (uint8_t)Alg::ByteUtil::LEToSystem(PRead8());
}
inline int8_t ReadSByte() {
return (int8_t)Alg::ByteUtil::LEToSystem(PRead8());
}
inline uint8_t ReadUInt8() {
return (uint8_t)Alg::ByteUtil::LEToSystem(PRead8());
}
inline int8_t ReadSInt8() {
return (int8_t)Alg::ByteUtil::LEToSystem(PRead8());
}
inline uint16_t ReadUInt16() {
return (uint16_t)Alg::ByteUtil::LEToSystem(PRead16());
}
inline int16_t ReadSInt16() {
return (int16_t)Alg::ByteUtil::LEToSystem(PRead16());
}
inline uint32_t ReadUInt32() {
return (uint32_t)Alg::ByteUtil::LEToSystem(PRead32());
}
inline int32_t ReadSInt32() {
return (int32_t)Alg::ByteUtil::LEToSystem(PRead32());
}
inline uint64_t ReadUInt64() {
return (uint64_t)Alg::ByteUtil::LEToSystem(PRead64());
}
inline int64_t ReadSInt64() {
return (int64_t)Alg::ByteUtil::LEToSystem(PRead64());
}
inline float ReadFloat() {
float v = 0.0f;
Read((uint8_t*)&v, 4);
return Alg::ByteUtil::LEToSystem(v);
}
inline double ReadDouble() {
double v = 0.0;
Read((uint8_t*)&v, 8);
return Alg::ByteUtil::LEToSystem(v);
}
// Reading primitive types - Big Endian
inline uint8_t ReadUByteBE() {
return (uint8_t)Alg::ByteUtil::BEToSystem(PRead8());
}
inline int8_t ReadSByteBE() {
return (int8_t)Alg::ByteUtil::BEToSystem(PRead8());
}
inline uint8_t ReadUInt8BE() {
return (uint8_t)Alg::ByteUtil::BEToSystem(PRead8());
}
inline int8_t ReadSInt8BE() {
return (int8_t)Alg::ByteUtil::BEToSystem(PRead8());
}
inline uint16_t ReadUInt16BE() {
return (uint16_t)Alg::ByteUtil::BEToSystem(PRead16());
}
inline int16_t ReadSInt16BE() {
return (int16_t)Alg::ByteUtil::BEToSystem(PRead16());
}
inline uint32_t ReadUInt32BE() {
return (uint32_t)Alg::ByteUtil::BEToSystem(PRead32());
}
inline int32_t ReadSInt32BE() {
return (int32_t)Alg::ByteUtil::BEToSystem(PRead32());
}
inline uint64_t ReadUInt64BE() {
return (uint64_t)Alg::ByteUtil::BEToSystem(PRead64());
}
inline int64_t ReadSInt64BE() {
return (int64_t)Alg::ByteUtil::BEToSystem(PRead64());
}
inline float ReadFloatBE() {
float v = 0.0f;
Read((uint8_t*)&v, 4);
return Alg::ByteUtil::BEToSystem(v);
}
inline double ReadDoubleBE() {
double v = 0.0;
Read((uint8_t*)&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 int64_t LTell() {
return pFile->LTell();
}
virtual int GetLength() {
return pFile->GetLength();
}
virtual int64_t 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 uint8_t* pbuffer, int numBytes) {
return pFile->Write(pbuffer, numBytes);
}
virtual int Read(uint8_t* 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 int64_t LSeek(int64_t 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
uint8_t* pBuffer;
BufferModeType BufferMode;
// Position in buffer
unsigned Pos;
// Data in buffer if reading
unsigned DataSize;
// Underlying file position
uint64_t 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();
BufferedFile(const BufferedFile&)
: DelegatedFile(), pBuffer(NULL), BufferMode(NoBuffer), Pos(0), DataSize(0), FilePos(0) {}
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 int64_t LTell();
virtual int GetLength();
virtual int64_t LGetLength();
// virtual bool Stat(GFileStats *pfs);
virtual int Write(const uint8_t* pbufer, int numBytes);
virtual int Read(uint8_t* pbufer, int numBytes);
virtual int SkipBytes(int numBytes);
virtual int BytesAvailable();
virtual bool Flush();
virtual int Seek(int offset, int origin = Seek_Set);
virtual int64_t LSeek(int64_t 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;
}
int64_t LTell() {
return (int64_t)FileIndex;
}
int GetLength() {
return FileSize;
}
int64_t LGetLength() {
return (int64_t)FileSize;
}
bool Close() {
Valid = false;
return false;
}
int CopyFromStream(File* pstream, int byteSize) {
OVR_UNUSED2(pstream, byteSize);
return 0;
}
int Write(const uint8_t* pbuffer, int numBytes) {
OVR_UNUSED2(pbuffer, numBytes);
return 0;
}
int Read(uint8_t* 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;
}
int64_t LSeek(int64_t offset, int origin = Seek_Set) {
return (int64_t)Seek((int)offset, origin);
}
public:
MemoryFile(const String& fileName, const uint8_t* 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 uint8_t* pBuffer, int buffSize) : FilePath(pfileName) {
FileData = pBuffer;
FileSize = buffSize;
FileIndex = 0;
Valid = (pfileName && pBuffer && buffSize > 0) ? true : false;
}
private:
String FilePath;
const uint8_t* 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);
} // namespace OVR
#endif

View File

@ -0,0 +1,576 @@
/**************************************************************************
Filename : OVR_FileFILE.cpp
Content : File wrapper class implementation (Win32)
Created : April 5, 1999
Authors : Michael Antonov
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**************************************************************************/
#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;
};
#if defined(OVR_CC_MSVC)
#include "share.h"
#endif
#if defined(OVR_OS_WIN32)
#include "OVR_Win32_IncludeWindows.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 = TRUE;
OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS);
} else {
Disabled = 0;
OldMode = 0;
}
}
~SysErrorModeDisabler() {
if (Disabled)
::SetErrorMode(OldMode);
}
};
#else
class SysErrorModeDisabler {
public:
SysErrorModeDisabler(const char* pfileName) {
OVR_UNUSED(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
uint8_t* pFileTestBuffer;
unsigned FileTestLength;
unsigned TestPos; // File pointer position during tests.
#endif
public:
FILEFile()
: FileName(),
Opened(false),
fs(NULL),
OpenFlags(0),
ErrorCode(0),
LastOp(0)
#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
,
pFileTestBuffer(NULL),
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 int64_t LTell();
virtual int GetLength();
virtual int64_t LGetLength();
// virtual bool Stat(FileStats *pfs);
virtual int GetErrorCode();
// ** Stream implementation & I/O
virtual int Write(const uint8_t* pbuffer, int numBytes);
virtual int Read(uint8_t* pbuffer, int numBytes);
virtual int SkipBytes(int numBytes);
virtual int BytesAvailable();
virtual bool Flush();
virtual int Seek(int offset, int origin);
virtual int64_t LSeek(int64_t 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";
#if defined(OVR_OS_MS)
SysErrorModeDisabler disabler(FileName.ToCStr());
#endif
#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
wchar_t womode[16];
auto fileNameLength = (size_t)UTF8Util::GetLength(FileName.ToCStr()) + 1;
wchar_t* pwFileName = (wchar_t*)OVR_ALLOC(fileNameLength * sizeof(pwFileName[0]));
auto requiredUTF8Length = OVR::UTF8Util::Strlcpy(pwFileName, fileNameLength, FileName.ToCStr());
if (requiredUTF8Length < fileNameLength) {
requiredUTF8Length = OVR::UTF8Util::Strlcpy(womode, OVR_ARRAY_COUNT(womode), omode);
OVR_ASSERT(requiredUTF8Length < OVR_ARRAY_COUNT(womode));
fs = _wfsopen(
pwFileName, womode, _SH_DENYWR); // Allow others to read the file when we are writing it.
}
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 = (uint8_t*)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;
}
int64_t FILEFile::LTell() {
int64_t 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;
}
int64_t FILEFile::LGetLength() {
int64_t pos = LTell();
if (pos >= 0) {
LSeek(0, Seek_End);
int64_t size = LTell();
LSeek(pos, Seek_Set);
return size;
}
return -1;
}
int FILEFile::GetErrorCode() {
return ErrorCode;
}
// ** Stream implementation & I/O
int FILEFile::Write(const uint8_t* 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(uint8_t* 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!
uint8_t* 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) {
int64_t pos = LTell();
int64_t 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() {
int64_t pos = LTell();
int64_t 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();
}
int64_t FILEFile::LSeek(int64_t offset, int origin) {
return Seek((int)offset, origin);
}
int FILEFile::CopyFromStream(File* pstream, int byteSize) {
uint8_t* buff = new uint8_t[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;
}
delete[] buff;
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;
}
*/
Ptr<File> FileFILEOpen(const String& path, int flags, int mode) {
Ptr<File> result = *new FILEFile(path, flags, mode);
return result;
}
// Helper function: obtain file information time.
bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) {
#if defined(OVR_OS_MS)
// 64-bit implementation on Windows.
struct __stat64 fileStat {};
auto pathLength = (size_t)UTF8Util::GetLength(path.ToCStr()) + 1;
wchar_t* pwPath = (wchar_t*)OVR_ALLOC(pathLength * sizeof(pwPath[0]));
auto requiredUTF8Length = OVR::UTF8Util::Strlcpy(pwPath, pathLength, path.ToCStr());
int ret = -1; // Stat returns 0 for success.
if (requiredUTF8Length < pathLength)
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;
}
} // Namespace OVR

View File

@ -0,0 +1,228 @@
/************************************************************************************
PublicHeader: None
Filename : OVR_JSON.h
Content : JSON format reader and writer
Created : April 9, 2013
Author : Brant Lewis
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_JSON_h
#define OVR_JSON_h
#include "OVR_RefCount.h"
#include "OVR_String.h"
#include "OVR_List.h"
namespace OVR {
// JSONItemType describes the type of JSON item, specifying the type of
// data that can be obtained from it.
enum JSONItemType {
JSON_None = 0,
JSON_Null = 1,
JSON_Bool = 2,
JSON_Number = 3,
JSON_String = 4,
JSON_Array = 5,
JSON_Object = 6
};
//-----------------------------------------------------------------------------
// ***** JSON
// JSON object represents a JSON node that can be either a root of the JSON tree
// or a child item. Every node has a type that describes what is is.
// New JSON trees are typically loaded JSON::Load or created with JSON::Parse.
class JSON : public RefCountBase<JSON>, public ListNode<JSON> {
protected:
List<JSON> Children;
public:
JSONItemType Type; // Type of this JSON node.
String Name; // Name part of the {Name, Value} pair in a parent object.
String Value;
double dValue;
public:
~JSON();
// *** Creation of NEW JSON objects
static JSON* CreateObject() {
return new JSON(JSON_Object);
}
static JSON* CreateNull() {
return new JSON(JSON_Null);
}
static JSON* CreateArray() {
return new JSON(JSON_Array);
}
static JSON* CreateBool(bool b);
static JSON* CreateNumber(double num);
static JSON* CreateInt(int num);
static JSON* CreateString(const char* s);
// Creates a new JSON object from parsing string.
// Returns null pointer and fills in *perror in case of parse error.
static JSON* Parse(const char* buff, const char** perror = 0);
// This version works for buffers that are not null terminated strings.
static JSON* ParseBuffer(const char* buff, int len, const char** perror = 0);
// Loads and parses a JSON object from a file.
// Returns 0 and assigns perror with error message on fail.
static JSON* Load(const char* path, const char** perror = 0);
// Saves a JSON object to a file.
bool Save(const char* path);
// Return the String representation of a JSON object.
String Stringify(bool fmt);
// *** Object Member Access
// These provide access to child items of the list.
bool HasItems() const {
return Children.IsEmpty();
}
// Returns first/last child item, or null if child list is empty
JSON* GetFirstItem() {
return (!Children.IsEmpty()) ? Children.GetFirst() : 0;
}
JSON* GetLastItem() {
return (!Children.IsEmpty()) ? Children.GetLast() : 0;
}
// Counts the number of items in the object; these methods are inefficient.
unsigned GetItemCount() const;
JSON* GetItemByIndex(unsigned i);
JSON* GetItemByName(const char* name);
// Accessors by name
double GetNumberByName(const char* name, double defValue = 0.0);
int GetIntByName(const char* name, int defValue = 0);
bool GetBoolByName(const char* name, bool defValue = false);
String GetStringByName(const char* name, const String& defValue = "");
template <typename T>
int GetArrayByName(const char* name, T values[], int count, T defaultValue = T(0)) {
// Zero values in case one or more elements not present in JSON
for (int i = 0; i < count; i++)
values[i] = defaultValue;
JSON* array = GetItemByName(name);
if (!array || array->Type != JSON_Array)
return 0;
int i = 0;
for (JSON* child = array->Children.GetFirst(); !array->Children.IsNull(child);
child = array->Children.GetNext(child)) {
if (i >= count)
break;
values[i++] = (T)child->dValue;
}
OVR_ASSERT(i <= count);
return i;
}
// Returns next item in a list of children; 0 if no more items exist.
JSON* GetNextItem(JSON* item) {
return Children.IsLast(item) ? nullptr : item->GetNext();
}
JSON* GetPrevItem(JSON* item) {
return Children.IsFirst(item) ? nullptr : item->GetPrev();
}
// Child item access functions
void AddItem(const char* string, JSON* item);
void AddNullItem(const char* name) {
AddItem(name, CreateNull());
}
void AddBoolItem(const char* name, bool b) {
AddItem(name, CreateBool(b));
}
void AddIntItem(const char* name, int n) {
AddItem(name, CreateInt(n));
}
void AddNumberItem(const char* name, double n) {
AddItem(name, CreateNumber(n));
}
void AddStringItem(const char* name, const char* s) {
AddItem(name, CreateString(s));
}
// void ReplaceItem(unsigned index, JSON* new_item);
// void DeleteItem(unsigned index);
void RemoveLast();
// *** Array Element Access
// Add new elements to the end of array.
void AddArrayElement(JSON* item);
void InsertArrayElement(int index, JSON* item);
void AddArrayNumber(double n) {
AddArrayElement(CreateNumber(n));
}
void AddArrayInt(int n) {
AddArrayElement(CreateInt(n));
}
void AddArrayString(const char* s) {
AddArrayElement(CreateString(s));
}
template <typename T>
void AddNumberArray(const char* name, const T array[], int arraySize) {
JSON* node = JSON::CreateArray();
AddItem(name, node);
for (int i = 0; i < arraySize; i++)
node->AddArrayNumber((double)array[i]);
}
// Accessed array elements; currently inefficient.
int GetArraySize();
double GetArrayNumber(int index);
const char* GetArrayString(int index);
JSON* Copy(); // Create a copy of this object
// Return text value of JSON. Use OVR_FREE when done with return value
char* PrintValue(bool fmt);
protected:
JSON(JSONItemType itemType = JSON_Object);
// JSON Parsing helper functions.
const char* parseValue(const char* buff, const char** perror);
const char* parseNumber(const char* num);
const char* parseArray(const char* value, const char** perror);
const char* parseObject(const char* value, const char** perror);
const char* parseString(const char* str, const char** perror);
char* PrintValue(int depth, bool fmt);
char* PrintObject(int depth, bool fmt);
char* PrintArray(int depth, bool fmt);
};
} // namespace OVR
#endif // OVR_JSON_h

View File

@ -0,0 +1,281 @@
/************************************************************************************
Filename : OVR_KeyCodes.h
Content : Common keyboard constants
Created : September 19, 2012
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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,
// Synthetic mouse wheel state
Key_MouseWheelAwayFromUser, // "forwards" or "up"
Key_MouseWheelTowardUser, // "backwards" or "down"
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_Backtick = 96, // ` and tilda~ when shifted (US keyboard)
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;
};
*/
} // namespace OVR
#endif

View File

@ -0,0 +1,389 @@
/************************************************************************************
PublicHeader: OVR
Filename : OVR_List.h
Content : Template implementation for doubly-connected linked List
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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:
//
// class MyData : ListNode<MyData>
// {
// . . .
// };
template <class T>
class ListNode {
private:
ListNode<T>* pPrev;
ListNode<T>* pNext;
template <class X, class B>
friend class List;
#ifdef OVR_BUILD_DEBUG
bool marker; // Is this a marker node (rather than an actual data node)?
#endif
public:
#ifdef OVR_BUILD_DEBUG
T* GetPrev() const {
if (pPrev && !pPrev->marker) {
return (T*)(pPrev);
} else {
OVR_FAIL_M("Unstable call to ListNode<>::GetPrev() without first checking List<>IsFirst()");
return nullptr;
}
}
T* GetNext() const {
if (pNext && !pNext->marker) {
return (T*)(pNext);
} else {
OVR_FAIL_M("Unstable call to ListNode<>::GetNext() without first checking List<>IsLast()");
return nullptr;
}
}
#else
T* GetPrev() const {
return (T*)(pPrev);
}
T* GetNext() const {
return (T*)(pNext);
}
#endif
ListNode() {
#ifdef OVR_BUILD_DEBUG
marker = false; // Most nodes are data nodes, so that is the default.
#endif
pPrev = nullptr;
pNext = nullptr;
}
bool IsInList() {
return (pNext != nullptr);
}
void RemoveNode() {
pPrev->pNext = pNext;
pNext->pPrev = pPrev;
pPrev = nullptr;
pNext = nullptr;
}
// 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;
pPrev = nullptr;
pNext = nullptr;
}
// 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 = pdest;
pNext->pPrev = pdest;
pPrev = nullptr;
pNext = nullptr;
}
};
//------------------------------------------------------------------------
// ***** 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 of 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 = &Root;
#ifdef OVR_BUILD_DEBUG
Root.marker = true; // This is a marker node.
#endif
}
void Clear() {
Root.pNext = Root.pPrev = &Root;
}
size_t GetSize() const {
size_t n = 0;
for (const ListNode<B>* pNode = Root.pNext; pNode != &Root; pNode = pNode->pNext)
++n;
return n;
}
const ValueType* GetFirst() const {
return IsEmpty() ? nullptr : (const ValueType*)Root.pNext;
}
const ValueType* GetLast() const {
return IsEmpty() ? nullptr : (const ValueType*)Root.pPrev;
}
ValueType* GetFirst() {
return IsEmpty() ? nullptr : (ValueType*)Root.pNext;
}
ValueType* GetLast() {
return IsEmpty() ? nullptr : (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.pNext == &Root;
}
bool IsFirst(const ValueType* p) const {
return p == Root.pNext;
}
bool IsLast(const ValueType* p) const {
return p == Root.pPrev;
}
bool IsNull(const ListNode<B>* p) const {
return p == nullptr || p == &Root;
}
inline const ValueType* GetPrev(const ValueType* p) const {
return IsNull(p->pPrev) ? nullptr : (const ValueType*)p->pPrev;
}
inline const ValueType* GetNext(const ValueType* p) const {
return IsNull(p->pNext) ? nullptr : (const ValueType*)p->pNext;
}
inline ValueType* GetPrev(ValueType* p) {
return IsNull(p->pPrev) ? nullptr : (ValueType*)p->pPrev;
}
inline ValueType* GetNext(ValueType* p) {
return IsNull(p->pNext) ? nullptr : (ValueType*)p->pNext;
}
void PushFront(ValueType* p) {
p->pNext = Root.pNext;
p->pPrev = &Root;
Root.pNext->pPrev = p;
Root.pNext = p;
}
void PushBack(ValueType* p) {
p->pPrev = Root.pPrev;
p->pNext = &Root;
Root.pPrev->pNext = p;
Root.pPrev = p;
}
static void Remove(ValueType* p) {
p->pPrev->pNext = p->pNext;
p->pNext->pPrev = p->pPrev;
p->pPrev = nullptr;
p->pNext = nullptr;
}
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 = &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 = &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 = &src.Root;
src.Root.pPrev = pfirst->pPrev;
// Add the rest of the items to list.
plast->pNext = Root.pNext;
pfirst->pPrev = &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 = &src.Root;
src.Root.pNext = ptail;
// Add the rest of the items to list.
plast->pNext = Root.pNext;
pfirst->pPrev = &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 = &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 = &pdest->Root;
Root.pPrev->pNext = &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();
}
} // namespace OVR
#endif

View File

@ -0,0 +1,202 @@
/************************************************************************************
Filename : OVR_Lockless.h
Content : Lock-less classes for producer/consumer communication
Created : November 9, 2013
Authors : John Carmack
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#ifndef OVR_Lockless_h
#define OVR_Lockless_h
#include <cstring>
using std::memcpy;
#include "OVR_Atomic.h"
// Define this to compile-in Lockless test logic
//#define OVR_LOCKLESS_TEST
namespace OVR {
// ***** LocklessUpdater
// For single producer cases where you only care about the most recent update, not
// necessarily getting every one that happens (vsync timing, SensorFusion updates).
//
// This is multiple consumer safe, but is currently only used with a single consumer.
//
// The SlotType can be the same as T, but should probably be a larger fixed size.
// This allows for forward compatibility when the updater is shared between processes.
template <class T, class SlotType = T>
class LocklessUpdater {
public:
LocklessUpdater() {
OVR_COMPILER_ASSERT(sizeof(T) <= sizeof(SlotType));
}
T GetState() const {
// Copy the state out, then retry with the alternate slot
// if we determine that our copy may have been partially
// stepped on by a new update.
T state;
int begin, end, final;
for (;;) {
// We are adding 0, only using these as atomic memory barriers, so it
// is ok to cast off the const, allowing GetState() to remain const.
end = UpdateEnd.load(std::memory_order_acquire);
state = Slots[end & 1];
begin = UpdateBegin.load(std::memory_order_acquire);
if (begin == end) {
break;
}
// The producer is potentially blocked while only having partially
// written the update, so copy out the other slot.
state = Slots[(begin & 1) ^ 1];
final = UpdateBegin.load(std::memory_order_acquire);
if (final == begin) {
break;
}
// The producer completed the last update and started a new one before
// we got it copied out, so try fetching the current buffer again.
}
return state;
}
void SetState(const T& state) {
const int slot = UpdateBegin.fetch_add(1) & 1;
// Write to (slot ^ 1) because ExchangeAdd returns 'previous' value before add.
Slots[slot ^ 1] = state;
UpdateEnd.fetch_add(1);
}
std::atomic<int> UpdateBegin = {0};
std::atomic<int> UpdateEnd = {0};
SlotType Slots[2];
};
#pragma pack(push, 8)
// Padded out version stored in the updater slots
// Designed to be a larger fixed size to allow the data to grow in the future
// without breaking older compiled code.
OVR_DISABLE_MSVC_WARNING(4351)
template <class Payload, int PaddingSize>
struct LocklessPadding {
uint8_t buffer[PaddingSize];
LocklessPadding() : buffer() {}
LocklessPadding& operator=(const Payload& rhs) {
// if this fires off, then increase PaddingSize
// IMPORTANT: this WILL break backwards compatibility
static_assert(sizeof(buffer) >= sizeof(Payload), "PaddingSize is too small");
memcpy(buffer, &rhs, sizeof(Payload));
return *this;
}
operator Payload() const {
Payload result;
memcpy(&result, buffer, sizeof(Payload));
return result;
}
};
OVR_RESTORE_MSVC_WARNING()
#pragma pack(pop)
// FIXME: Move this somewhere else
// ***** LocklessBuffer
// FIXME: update these comments
// For single producer cases where you only care about the most recent update, not
// necessarily getting every one that happens (vsync timing, SensorFusion updates, external camera
// frames).
//
// The writer writes an incrementing generation # for each write start, and write end
// The reader reads the last written generation number, saves it, does its operations, then
// reads the latest value of the last written generation number. If they match, there was no
// collision, and the work is done. If not, the reader has to loop until it gets a matching
// Last written generation number
//
// This is to update & read a dynamically sized object in shared memory.
// Initial use case is for frame buffers for cameras, which are an unknown size until runtime
#pragma pack(push, 1)
class LocklessBuffer {
public:
LocklessBuffer() {
;
}
void Initialize(unsigned bufferSize) {
BufferSize = bufferSize;
LastWrittenGeneration = 0;
}
char* StartWrite(unsigned offset) {
++LastWrittenGeneration;
return GetDataForWrite(offset);
}
unsigned GetBufferSize() const {
return BufferSize;
}
int EndWrite() {
return ++LastWrittenGeneration;
}
int GetLastWrittenGeneration() const {
return LastWrittenGeneration;
}
const char* GetDataForRead(unsigned offset = 0) const {
return &(Data[offset]);
}
char* GetDataForWrite(unsigned offset = 0) {
return &(Data[offset]);
}
bool DidReadCollide(int lastReadGeneration) const {
return lastReadGeneration != LastWrittenGeneration;
}
private:
unsigned BufferSize = 0;
std::atomic<int> LastWrittenGeneration;
// Data starts here...
char Data[1];
};
#pragma pack(pop)
} // namespace OVR
#endif // OVR_Lockless_h

View File

@ -0,0 +1,144 @@
/************************************************************************************
Filename : OVR_Log.cpp
Content : Logging support
Created : September 19, 2012
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Log.h"
#include "OVR_Allocator.h"
#include "OVR_String.h"
#include "OVR_DebugHelp.h"
#include "Util/Util_SystemGUI.h"
#include <fstream>
namespace OVR {
ovrlog::Channel DefaultChannel("Kernel:Default");
static OVRAssertionHandler sOVRAssertionHandler = OVR::DefaultAssertionHandler;
static intptr_t sOVRAssertionHandlerUserParameter = 0;
OVRAssertionHandler GetAssertionHandler(intptr_t* userParameter) {
if (userParameter)
*userParameter = sOVRAssertionHandlerUserParameter;
return sOVRAssertionHandler;
}
void SetAssertionHandler(OVRAssertionHandler assertionHandler, intptr_t userParameter) {
sOVRAssertionHandler = assertionHandler;
sOVRAssertionHandlerUserParameter = userParameter;
}
intptr_t
DefaultAssertionHandler(intptr_t /*userParameter*/, const char* title, const char* message) {
if (OVRIsDebuggerPresent()) {
OVR_DEBUG_BREAK;
} else {
OVR_UNUSED(title);
OVR_UNUSED(message);
#if defined(OVR_BUILD_DEBUG)
if (Allocator::GetInstance()) // The code below currently depends on having a valid Allocator.
{
// Print a stack trace of all threads.
OVR::String s;
OVR::String threadListOutput;
static OVR::SymbolLookup symbolLookup;
s = "Failure: ";
s += message;
if (symbolLookup.Initialize() &&
symbolLookup.ReportThreadCallstack(threadListOutput, 4)) // This '4' is there to skip our
// internal handling and retrieve
// starting at the assertion
// location (our caller) only.
{
// threadListOutput has newlines that are merely \n, whereas Windows MessageBox wants \r\n
// newlines. So we insert \r in front of all \n.
for (size_t i = 0, iEnd = threadListOutput.GetSize(); i < iEnd; i++) {
if (threadListOutput[i] == '\n') {
threadListOutput.Insert("\r", i, 1);
++i;
++iEnd;
}
}
s += "\r\n\r\n";
s += threadListOutput;
}
OVR::Util::DisplayMessageBox(title, s.ToCStr());
} else {
// See above.
OVR::Util::DisplayMessageBox(title, message);
}
#else
OVR::Util::DisplayMessageBox(title, message);
#endif
}
return 0;
}
// This currently has no multi-thread safety.
intptr_t AssertHandlerDiskFile(intptr_t userParameter, const char* title, const char* message) {
// We don't have a means to keep state, so we need to have a little state machine here to
// help us direct how this works.
static std::ofstream file;
static std::string filePath;
const char* userFilePath = (userParameter ? reinterpret_cast<const char*>(userParameter) : "");
// See if we need to change the file state.
if (filePath != userFilePath) { // If a file change is occurring...
file.close(); // Possibly close any existing open file. OK if the file wasn't open.
filePath = userFilePath;
if (!filePath.empty()) // If there is a file to open...
file.open(filePath.c_str(), std::ios::out | std::ios::trunc); // If this fails, we won't know.
}
// Write the message if there is a file to write to.
if (file.is_open()) {
file.write("Assertion failure\n", strlen("Assertion failure\n"));
file.write(title, strlen(title));
file.write("\n", 1);
file.write(message, strlen(message));
file.write("\n\n", 1);
}
return 0;
}
bool IsAutomationRunning() {
#if defined(OVR_OS_WIN32)
// We use the OS GetEnvironmentVariable function as opposed to getenv, as that
// is process-wide as opposed to being tied to the current C runtime library.
return GetEnvironmentVariableW(L"OvrAutomationRunning", NULL, 0) > 0;
#else
return getenv("OvrAutomationRunning") != nullptr;
#endif
}
} // namespace OVR

View File

@ -0,0 +1,199 @@
/************************************************************************************
Filename : OVR_Log.h
Content : Logging support
Created : September 19, 2012
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Log_h
#define OVR_Log_h
#ifdef MICRO_OVR
namespace OVR {
template <typename... Args>
void LogText(Args&&...) {}
template <typename... Args>
void LogError(Args&&...) {}
template <typename... Args>
void LogDebug(Args&&...) {}
} // namespace OVR
#define OVR_DEBUG_LOG(args) \
do { \
} while (0);
#define OVR_DEBUG_LOG_TEXT(args) \
do { \
} while (0);
#define OVR_ASSERT_LOG(c, args) \
do { \
} while (0)
namespace ovrlog {
struct Channel {
template <typename... Args>
Channel(Args&&...) {}
template <typename... Args>
void LogTrace(Args&&...) {}
template <typename... Args>
void LogWarningF(Args&&...) {}
};
} // namespace ovrlog
#else
#include "OVR_Types.h"
#include "OVR_Std.h"
#include "OVR_String.h"
#include "Logging/Logging_Library.h"
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#pragma clang diagnostic ignored \
"-Wformat-security" // Otherwise the printf usage below generates a warning.
#endif
namespace OVR {
//-----------------------------------------------------------------------------------
// Terrible Legacy Log Wrapper
//
// Try not to use this deprecated code.
#define OVR_SYSLOG_NAME L"OculusVR"
typedef int LogMessageType;
extern ovrlog::Channel DefaultChannel;
template <typename... Args>
inline void LogText(Args&&... args) {
if (DefaultChannel.Active(ovrlog::Level::Info)) {
char buffer[512];
#pragma warning(push)
#pragma warning(disable : 4840)
int written = snprintf(buffer, sizeof(buffer), args...);
#pragma warning(pop)
if (written <= 0 || written >= (int)sizeof(buffer)) {
OVR_ASSERT(false); // This call should be converted to the new log system.
return;
}
// Fix up the newlines
if (buffer[written - 1] == '\n') {
buffer[written - 1] = '\0';
}
DefaultChannel.LogInfo(buffer);
}
}
template <typename... Args>
inline void LogError(Args&&... args) {
if (DefaultChannel.Active(ovrlog::Level::Error)) {
char buffer[512];
#pragma warning(push)
#pragma warning(disable : 4840)
int written = snprintf(buffer, sizeof(buffer), args...);
#pragma warning(pop)
if (written <= 0 || written >= (int)sizeof(buffer)) {
OVR_ASSERT(false); // This call should be converted to the new log system.
return;
}
// Fix up the newlines
if (buffer[written - 1] == '\n') {
buffer[written - 1] = '\0';
}
DefaultChannel.LogError(buffer);
}
}
template <typename... Args>
inline void LogDebug(Args&&... args) {
if (DefaultChannel.Active(ovrlog::Level::Debug)) {
char buffer[512];
int written = snprintf(buffer, sizeof(buffer), args...);
if (written <= 0 || written >= (int)sizeof(buffer)) {
OVR_ASSERT(false); // This call should be converted to the new log system.
return;
}
// Fix up the newlines
if (buffer[written - 1] == '\n') {
buffer[written - 1] = '\0';
}
DefaultChannel.LogDebug(buffer);
}
}
// This acts as a replacement for the assertion dialog box, primarily for the purpose of assisting
// in testing and automation. Instead of presenting a dialong box, this will write assertions to
// a disk file. userParameter is a pointer to a file path to use, or 0 to drop assertion failures.
// This handler creates the file if not found, and initially clears the file if found.
// Calling SetAssertionHandler to set a file with a path, then later calling SetAssertionHandler
// with another path or 0 causes the closing of the initial file.
//
// Example usage:
// SetAssertionHandler(AssertHandlerDiskFile, (intptr_t)"C:\\SomeDir\SomeFile.txt");
// SetAssertionHandler(AssertHandlerDiskFile, 0); // Write assertions to nowhere.
//
intptr_t AssertHandlerDiskFile(intptr_t userParameter, const char* title, const char* message);
#define OVR_DEBUG_LOG(args) \
do { \
OVR::LogDebug args; \
} while (0);
#define OVR_DEBUG_LOG_TEXT(args) \
do { \
OVR::LogDebug args; \
} while (0);
#define OVR_ERROR_LOG(args) \
do { \
OVR::LogError args; \
} while (0);
// Conditional logging & asserting. It asserts/logs when the condition 'c' is NOT true.
#define OVR_ASSERT_LOG(c, args) \
do { \
if (!(c)) { \
OVR::LogError args; \
OVR_DEBUG_BREAK; \
} \
} while (0)
} // namespace OVR
namespace ovrlog {
template <>
LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const OVR::String& first) {
buffer.Stream << first.ToCStr();
}
} // namespace ovrlog
#endif // MICRO_OVR
#endif // OVR_Log_h

View File

@ -0,0 +1,206 @@
/************************************************************************************
Filename : OVR_NewOverride.inl
Content : New override for LibOVR Allocator
Created : April 7, 2015
Authors : Paul Pedriana, Chris Taylor
Copyright : Copyright 2015-2016 Oculus VR, LLC All Rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_NewOverride_inl
#define OVR_NewOverride_inl
#include "OVR_Allocator.h"
#include <new>
#if defined(_MSC_VER)
#pragma warning(push, 0)
#include <malloc.h>
#include <crtdbg.h>
#include <math.h> // Work around VS header bug by #including math.h then intrin.h.
#if (_MSC_VER >= 1500)
#include <intrin.h>
#endif
#pragma warning(pop)
#if (_MSC_VER >= 1600) // VS2010+
#pragma warning(disable : 4986) // 'operator delete[]': exception specification does not match previous declaration.
#endif
#endif
#if defined(_MSC_VER)
#if (_MSC_VER <= 1900)
#define OVR_THROW_SPEC_NEW(X) __pragma(warning(push)) __pragma(warning(disable: 4290 4987)) _THROW(,X) __pragma(warning(pop))
#define OVR_THROW_SPEC_NEW_NONE() _THROW(,)
#define OVR_THROW_SPEC_DELETE_NONE() _THROW(,)
#else
#define OVR_THROW_SPEC_NEW(x) __pragma(warning(push)) __pragma(warning(disable: 4290 4987)) noexcept(false) __pragma(warning(pop))
#define OVR_THROW_SPEC_NEW_NONE() noexcept
#define OVR_THROW_SPEC_DELETE_NONE() noexcept
#endif
#else
#define OVR_THROW_SPEC_NEW(x) throw(x)
#define OVR_THROW_SPEC_NEW_NONE() throw()
#define OVR_THROW_SPEC_DELETE_NONE() throw()
#endif
// Add common decorators here as neeeded.
#define OVR_NEW_OVERRIDE_INLINE
OVR_NEW_OVERRIDE_INLINE void* operator new(size_t size) OVR_THROW_SPEC_NEW(std::bad_alloc)
{
void* p = OVR_ALLOC(size);
#if !defined(OVR_CPP_NO_EXCEPTIONS)
if(!p)
throw std::bad_alloc();
#endif
return p;
}
OVR_NEW_OVERRIDE_INLINE void* operator new[](size_t size) OVR_THROW_SPEC_NEW(std::bad_alloc)
{
void* p = OVR_ALLOC(size);
#if !defined(OVR_CPP_NO_EXCEPTIONS)
if(!p)
throw std::bad_alloc();
#endif
return p;
}
OVR_NEW_OVERRIDE_INLINE void* operator new(size_t size, std::nothrow_t&) OVR_THROW_SPEC_NEW_NONE()
{
return OVR_ALLOC(size);
}
OVR_NEW_OVERRIDE_INLINE void* operator new[](size_t size, std::nothrow_t&) OVR_THROW_SPEC_NEW_NONE()
{
return OVR_ALLOC(size);
}
OVR_NEW_OVERRIDE_INLINE void* operator new(size_t size, const std::nothrow_t&) OVR_THROW_SPEC_NEW_NONE()
{
return OVR_ALLOC(size);
}
OVR_NEW_OVERRIDE_INLINE void* operator new[](size_t size, const std::nothrow_t&) OVR_THROW_SPEC_NEW_NONE()
{
return OVR_ALLOC(size);
}
OVR_NEW_OVERRIDE_INLINE void operator delete(void* p) OVR_THROW_SPEC_DELETE_NONE()
{
OVR_FREE(p);
}
OVR_NEW_OVERRIDE_INLINE void operator delete[](void* p) OVR_THROW_SPEC_DELETE_NONE()
{
OVR_FREE(p);
}
OVR_NEW_OVERRIDE_INLINE void operator delete(void* p, std::nothrow_t&) OVR_THROW_SPEC_DELETE_NONE()
{
OVR_FREE(p);
}
OVR_NEW_OVERRIDE_INLINE void operator delete[](void* p, std::nothrow_t&) OVR_THROW_SPEC_DELETE_NONE()
{
OVR_FREE(p);
}
OVR_NEW_OVERRIDE_INLINE void operator delete(void* p, const std::nothrow_t&) OVR_THROW_SPEC_DELETE_NONE()
{
OVR_FREE(p);
}
OVR_NEW_OVERRIDE_INLINE void operator delete[](void* p, const std::nothrow_t&) OVR_THROW_SPEC_DELETE_NONE()
{
OVR_FREE(p);
}
// The following new/delete overrides are required under VC++ because it defines the following operator new versions of its own.
#if defined(_MSC_VER)
OVR_NEW_OVERRIDE_INLINE void* operator new(size_t n, const char* /*fileName*/, int /*line*/)
{
return ::operator new(n);
}
OVR_NEW_OVERRIDE_INLINE void* operator new[](size_t n, const char* /*fileName*/, int /*line*/)
{
return ::operator new[](n);
}
OVR_NEW_OVERRIDE_INLINE void operator delete(void* p, const char* /*fileName*/, int /*line*/)
{
::operator delete(p);
}
OVR_NEW_OVERRIDE_INLINE void operator delete[](void* p, const char* /*fileName*/, int /*line*/)
{
::operator delete[](p);
}
OVR_NEW_OVERRIDE_INLINE void* operator new(size_t n, int /*debug*/, const char* /*fileName*/, int /*line*/)
{
return ::operator new (n);
}
OVR_NEW_OVERRIDE_INLINE void* operator new[](size_t n, int /*debug*/, const char* /*fileName*/, int /*line*/)
{
return ::operator new[](n);
}
OVR_NEW_OVERRIDE_INLINE void __CRTDECL
operator delete(void* p, int /*debug*/, const char* /*fileName*/, int /*line*/)
OVR_THROW_SPEC_DELETE_NONE() {
::operator delete(p);
}
OVR_NEW_OVERRIDE_INLINE void __CRTDECL
operator delete[](void* p, int /*debug*/, const char* /*fileName*/, int /*line*/)
OVR_THROW_SPEC_DELETE_NONE() {
::operator delete[](p);
}
#endif
#endif // OVR_NewOverride_inl

View File

@ -0,0 +1,159 @@
/************************************************************************************
Filename : OVR_Nullptr.h
Content : Implements C++11 nullptr for the case that the compiler doesn't.
Created : June 19, 2014
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Nullptr_h
#define OVR_Nullptr_h
#pragma once
#include "OVR_Types.h"
//-----------------------------------------------------------------------------------
// ***** OVR_HAVE_std_nullptr_t
//
// Identifies if <cstddef.h> includes std::nullptr_t.
//
#if !defined(OVR_HAVE_std_nullptr_t) && defined(OVR_CPP11_ENABLED)
#if defined(OVR_STDLIB_LIBCPP)
#define OVR_HAVE_std_nullptr_t 1
#elif defined(OVR_STDLIB_LIBSTDCPP)
#if (__GLIBCXX__ >= 20110325) && (__GLIBCXX__ != 20110428) && (__GLIBCXX__ != 20120702)
#define OVR_HAVE_std_nullptr_t 1
#endif
#elif defined(_MSC_VER) && (_MSC_VER >= 1600) // VS2010+
#define OVR_HAVE_std_nullptr_t 1
#elif defined(__clang__)
#define OVR_HAVE_std_nullptr_t 1
#elif defined(OVR_CPP_GNUC) && (OVR_CC_VERSION >= 406) // GCC 4.6+
#define OVR_HAVE_std_nullptr_t 1
#endif
#endif
//-----------------------------------------------------------------------------------
// ***** nullptr / std::nullptr_t
//
// Declares and defines nullptr and related types.
//
#if defined(OVR_CPP_NO_NULLPTR)
namespace std {
class nullptr_t {
public:
template <typename T>
operator T*() const {
return 0;
}
template <typename C, typename T>
operator T C::*() const {
return 0;
}
#if OVR_CPP_NO_EXPLICIT_CONVERSION_OPERATORS
typedef void* (nullptr_t::*bool_)()
const; // 4.12,p1. We can't portably use operator bool(){ return false; } because bool
operator bool_() const // is convertable to int which breaks other required functionality.
{
return false;
}
#else
operator bool() const {
return false;
}
#endif
private:
void operator&() const; // 5.2.10,p9
};
inline nullptr_t nullptr_get() {
nullptr_t n = {};
return n;
}
#if !defined(nullptr)
#define nullptr nullptr_get()
#endif
} // namespace std
// 5.9,p2 p4
// 13.6, p13
template <typename T>
inline bool operator==(T* pT, const std::nullptr_t) {
return pT == 0;
}
template <typename T>
inline bool operator==(const std::nullptr_t, T* pT) {
return pT == 0;
}
template <typename T, typename U>
inline bool operator==(const std::nullptr_t, T U::*pU) {
return pU == 0;
}
template <typename T, typename U>
inline bool operator==(T U::*pTU, const std::nullptr_t) {
return pTU == 0;
}
inline bool operator==(const std::nullptr_t, const std::nullptr_t) {
return true;
}
inline bool operator!=(const std::nullptr_t, const std::nullptr_t) {
return false;
}
inline bool operator<(const std::nullptr_t, const std::nullptr_t) {
return false;
}
inline bool operator<=(const std::nullptr_t, const std::nullptr_t) {
return true;
}
inline bool operator>(const std::nullptr_t, const std::nullptr_t) {
return false;
}
inline bool operator>=(const std::nullptr_t, const std::nullptr_t) {
return true;
}
using std::nullptr_get;
using std::nullptr_t;
// Some compilers natively support C++11 nullptr but the standard library being used
// doesn't declare std::nullptr_t, in which case we provide one ourselves.
#elif !defined(OVR_HAVE_std_nullptr_t) && !defined(OVR_CPP_NO_DECLTYPE)
namespace std {
typedef decltype(nullptr) nullptr_t;
}
#endif
#endif

View File

@ -0,0 +1,91 @@
/************************************************************************************
Filename : OVR_RefCount.cpp
Content : Reference counting implementation
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_RefCount.h"
#include "OVR_Atomic.h"
#include "OVR_Log.h"
namespace OVR {
// ***** 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() {
RefCount.fetch_add(1, std::memory_order_relaxed);
}
void RefCountImpl::Release() {
if (RefCount.fetch_add(-1, std::memory_order_relaxed) - 1 == 0)
delete this;
}
// *** Thread-Safe RefCountVImpl w/virtual AddRef/Release
void RefCountVImpl::AddRef() {
RefCount.fetch_add(1, std::memory_order_relaxed);
}
void RefCountVImpl::Release() {
if (RefCount.fetch_add(-1, std::memory_order_relaxed) - 1 == 0)
delete this;
}
// *** NON-Thread-Safe RefCountImpl
void RefCountNTSImpl::Release() const {
RefCount--;
if (RefCount == 0)
delete this;
}
} // namespace OVR

View File

@ -0,0 +1,495 @@
/************************************************************************************
PublicHeader: Kernel
Filename : OVR_RefCount.h
Content : Reference counting implementation headers
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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:
std::atomic<int> RefCount = {1};
public:
// RefCountImpl constructor always initializes RefCount to 1 by default.
OVR_FORCE_INLINE RefCountImplCore() {}
// 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 : virtual 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)
#undef OVR_REFCOUNTALLOC_CHECK_DELETE
#ifdef OVR_DEFINE_NEW
#define new OVR_DEFINE_NEW
#endif
// OVR_BUILD_DEFINE_NEW
// DOM-IGNORE-END
};
template <class Base>
class RefCountBaseStatVImpl : virtual public Base {
public:
RefCountBaseStatVImpl() {}
// *** Override New and Delete
// DOM-IGNORE-BEGIN
// Undef new temporarily if it is being redefined
#ifdef OVR_DEFINE_NEW
#undef new
#endif
#define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p)
// Redefine all new & delete operators.
OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE)
#undef 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 : virtual public RefCountBaseStatVImpl<RefCountVImpl> {
public:
// Constructor.
OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatVImpl<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>() {}
};
//-----------------------------------------------------------------------------------
// ***** Ref-Counted template pointer
//
// Automatically AddRefs and Releases interfaces
//
// Note: Some of the member functions take C& as opposed to C* arguments:
// Ptr(C&)
// const Ptr<C>& operator= (C&)
// Ptr<C>& SetPtr(C&)
// These functions do not AddRef the assigned C& value, unlike the C* assignment
// functions. Thus the purpose of these functions is for the Ptr instance to
// assume ownership of a C reference count. Example usage:
// Ptr<Widget> w = *new Widget; // Calls the Ptr(C&) constructor. Note that the Widget
// constructor sets initial refcount to 1.
//
template <class C>
class Ptr {
protected:
C* pObject;
public:
// Constructors
OVR_FORCE_INLINE Ptr() : pObject(0) {}
// This constructor adopts the object's existing reference count rather than increment it.
OVR_FORCE_INLINE Ptr(C& robj) : pObject(&robj) {}
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;
}
// 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) {
// By design we don't check for src == pObject, as we don't expect that to be the case the large
// majority of the time.
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;
}
// This operator adopts the object's existing reference count rather than increment it.
OVR_FORCE_INLINE const Ptr<C>& operator=(C& src) {
if (pObject)
pObject->Release();
pObject = &src;
return *this;
}
// 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;
}
// This function adopts the object's existing reference count rather than increment it.
OVR_FORCE_INLINE Ptr<C>& SetPtr(C& src) {
if (pObject)
pObject->Release();
pObject = &src;
return *this;
}
// 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;
}
};
// LockedPtr
//
// Helper class to simplify thread-safety of the TrackingManager.
// It wraps the Ptr<> object it contains in a Lock.
template <class T>
class LockedPtr {
public:
LockedPtr(Lock* lock = nullptr) : TheLock(lock) {}
void Set(T* value) {
OVR_ASSERT(TheLock);
TheLock->DoLock();
Ptr<T> oldPtr = ThePtr; // Keep a reference to the old ptr
ThePtr = value; // Change/decrement the old ptr (cannot die here due to oldPtr)
TheLock->Unlock();
// Release the old Ptr reference here, outside of the lock
// so that the object will not die while TheLock is held.
}
template <class S>
void Get(Ptr<S>& outputPtr) const {
OVR_ASSERT(TheLock);
TheLock->DoLock();
Ptr<T> retval = ThePtr;
TheLock->Unlock();
outputPtr = retval;
}
protected:
mutable Lock* TheLock;
Ptr<T> ThePtr;
};
} // namespace OVR
#endif

View File

@ -0,0 +1,687 @@
/************************************************************************************
Filename : OVR_SharedMemory.cpp
Content : Inter-process shared memory subsystem
Created : June 1, 2014
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_SharedMemory.h"
#include "OVR_Atomic.h"
#include "OVR_Log.h"
#include "OVR_String.h"
#include "OVR_Array.h"
#if defined(OVR_OS_WIN32)
#include <Sddl.h> // ConvertStringSecurityDescriptorToSecurityDescriptor
#endif // OVR_OS_WIN32
#if defined(OVR_OS_LINUX) || defined(OVR_OS_MAC)
#include <sys/mman.h> // shm_open(), mmap()
#include <errno.h> // error results for mmap
#include <sys/stat.h> // mode constants
#include <fcntl.h> // O_ constants
#include <unistd.h> // close()
#endif // OVR_OS_LINUX
OVR_DEFINE_SINGLETON(OVR::SharedMemoryFactory);
namespace OVR {
static ovrlog::Channel Logger("SharedMemory");
//-----------------------------------------------------------------------------
// SharedMemoryInternalBase
class SharedMemoryInternalBase : public NewOverrideBase {
public:
SharedMemoryInternalBase() {}
virtual ~SharedMemoryInternalBase() {}
virtual void* GetFileView() = 0;
};
//-----------------------------------------------------------------------------
// FakeMemoryBlock
class FakeMemoryBlock : public RefCountBase<FakeMemoryBlock> {
String Name;
std::unique_ptr<char[]> Data;
int SizeBytes;
int References;
public:
FakeMemoryBlock(const String& name, int size)
: Name(name), Data(new char[size]), SizeBytes(size), References(1) {}
bool IsNamed(const String& name) {
return Name.CompareNoCase(name) == 0;
}
void* GetData() {
return Data.get();
}
int GetSizeI() {
return SizeBytes;
}
void IncrementReferences() {
++References;
}
bool DecrementReferences() {
return --References <= 0;
}
};
class FakeMemoryInternal : public SharedMemoryInternalBase {
public:
void* FileView;
Ptr<FakeMemoryBlock> Block;
FakeMemoryInternal(FakeMemoryBlock* block);
~FakeMemoryInternal();
virtual void* GetFileView() override {
return FileView;
}
};
//-----------------------------------------------------------------------------
// FakeMemoryManager
class FakeMemoryManager : public NewOverrideBase, public SystemSingletonBase<FakeMemoryManager> {
OVR_DECLARE_SINGLETON(FakeMemoryManager);
Lock FakeLock;
Array<Ptr<FakeMemoryBlock>> FakeArray;
public:
FakeMemoryInternal* Open(const char* name, int bytes, bool openOnly) {
Lock::Locker locker(&FakeLock);
const int count = FakeArray.GetSizeI();
for (int ii = 0; ii < count; ++ii) {
if (FakeArray[ii]->IsNamed(name)) {
FakeArray[ii]->IncrementReferences();
return new FakeMemoryInternal(FakeArray[ii]);
}
}
if (openOnly) {
return NULL;
}
Ptr<FakeMemoryBlock> data = *new FakeMemoryBlock(name, bytes);
FakeArray.PushBack(data);
return new FakeMemoryInternal(data);
}
void Free(FakeMemoryBlock* block) {
Lock::Locker locker(&FakeLock);
const int count = FakeArray.GetSizeI();
for (int ii = 0; ii < count; ++ii) {
if (FakeArray[ii].GetPtr() == block) {
// If the reference count hit zero,
if (FakeArray[ii]->DecrementReferences()) {
// Toast
FakeArray.RemoveAtUnordered(ii);
}
break;
}
}
}
};
FakeMemoryManager::FakeMemoryManager() {
// Must be at end of function
PushDestroyCallbacks();
}
FakeMemoryManager::~FakeMemoryManager() {
// If this assertion trips it is because we have not cleanly released shared memory resources.
OVR_ASSERT(FakeArray.GetSizeI() == 0);
}
void FakeMemoryManager::OnSystemDestroy() {
delete this;
}
FakeMemoryInternal::FakeMemoryInternal(FakeMemoryBlock* block) : Block(block) {
FileView = Block->GetData();
}
FakeMemoryInternal::~FakeMemoryInternal() {
FakeMemoryManager::GetInstance()->Free(Block);
Block.Clear();
}
} // namespace OVR
OVR_DEFINE_SINGLETON(FakeMemoryManager);
namespace OVR {
static SharedMemoryInternalBase* CreateFakeSharedMemory(
const SharedMemory::OpenParameters& params) {
return FakeMemoryManager::GetInstance()->Open(
params.globalName, params.minSizeBytes, params.openMode == SharedMemory::OpenMode_OpenOnly);
}
//// Windows version
#if defined(OVR_OS_WIN32)
#pragma comment(lib, "advapi32.lib")
// Hidden implementation class for OS-specific behavior
class SharedMemoryInternal : public SharedMemoryInternalBase {
public:
HANDLE FileMapping;
void* FileView;
SharedMemoryInternal(HANDLE fileMapping, void* fileView)
: FileMapping(fileMapping), FileView(fileView) {}
~SharedMemoryInternal() {
// If file view is set,
if (FileView) {
UnmapViewOfFile(FileView);
FileView = NULL;
}
// If file mapping is set,
if (FileMapping != NULL) {
CloseHandle(FileMapping);
FileMapping = NULL;
}
}
virtual void* GetFileView() override {
return FileView;
}
};
static SharedMemoryInternal*
DoFileMap(HANDLE hFileMapping, const char* fileName, bool openReadOnly, int minSize) {
// Interpret the access mode as a map desired access code
DWORD mapDesiredAccess = openReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE;
// Map view of the file to this process
void* pFileView = MapViewOfFile(hFileMapping, mapDesiredAccess, 0, 0, minSize);
// If mapping could not be created,
if (!pFileView) {
CloseHandle(hFileMapping);
Logger.LogDebugF(
"FAILURE: Unable to map view of file for %s error code = %d", fileName, GetLastError());
OVR_UNUSED(fileName);
return NULL;
}
// Create internal representation
SharedMemoryInternal* pimple = new SharedMemoryInternal(hFileMapping, pFileView);
// If memory allocation fails,
if (!pimple) {
UnmapViewOfFile(pFileView);
CloseHandle(hFileMapping);
Logger.LogDebugF("FAILURE: Out of memory");
return NULL;
}
return pimple;
}
static SharedMemoryInternal*
AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly) {
// Interpret the access mode as a map desired access code
DWORD mapDesiredAccess = openReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE;
// Open file mapping
std::wstring wFileName = UTF8StringToUCSString(fileName);
HANDLE hFileMapping = OpenFileMappingW(mapDesiredAccess, TRUE, wFileName.c_str());
// If file was mapped unsuccessfully,
if (NULL == hFileMapping) {
Logger.LogTraceF(
"WARNING: Unable to open file mapping for %s error code = %d (not necessarily bad)",
fileName,
GetLastError());
return NULL;
}
// Map the file
return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
}
static SharedMemoryInternal* AttemptCreateSharedMemory(
const char* fileName,
int minSize,
bool openReadOnly,
bool allowRemoteWrite) {
// Prepare a SECURITY_ATTRIBUTES object
SECURITY_ATTRIBUTES security;
ZeroMemory(&security, sizeof(security));
security.nLength = sizeof(security);
// Security descriptor by DACL strings:
// ACE strings grant Allow(A), Object/Contains Inheritance (OICI) of:
// + Grant All (GA) to System (SY)
// + Grant All (GA) to Built-in Administrators (BA)
// + Grant Read-Only (GR) or Read-Write (GWGR) to Interactive Users (IU) - ie. games
static const wchar_t* DACLString_ReadOnly =
L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GR;;;IU)"
L"(A;OICI;GR;;;S-1-15-3-191680118-1275207936-1426169489-1705429854)";
static const wchar_t* DACLString_ReadWrite =
L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GWGR;;;IU)"
L"(A;OICI;GWGR;;;S-1-15-3-191680118-1275207936-1426169489-1705429854)";
// HACK: https://our.intern.facebook.com/intern/tasks/?t=10698852
// The extra SIDs above give access to our named pipe to UWP apps
// Select the remote process access mode
const wchar_t* remoteAccessString = allowRemoteWrite ? DACLString_ReadWrite : DACLString_ReadOnly;
// Attempt to convert access string to security attributes
// Note: This will allocate the security descriptor with LocalAlloc() and must be freed later
BOOL bConvertOkay = ConvertStringSecurityDescriptorToSecurityDescriptorW(
remoteAccessString, SDDL_REVISION_1, &security.lpSecurityDescriptor, NULL);
// If conversion fails,
if (!bConvertOkay) {
Logger.LogDebugF("FAILURE: Unable to convert access string, error code = %d", GetLastError());
return NULL;
}
// Interpret the access mode as a page protection code
int pageProtectCode = openReadOnly ? PAGE_READONLY : PAGE_READWRITE;
std::wstring wFileName = UTF8StringToUCSString(fileName);
// Attempt to create a file mapping
HANDLE hFileMapping = CreateFileMappingW(
INVALID_HANDLE_VALUE, // From page file
&security, // Security attributes
pageProtectCode, // Read-only?
0, // High word for size = 0
minSize, // Low word for size
wFileName.c_str()); // Name of global shared memory file
// Free the security descriptor buffer
LocalFree(security.lpSecurityDescriptor);
// If mapping could not be created,
if (NULL == hFileMapping) {
Logger.LogDebugF(
"FAILURE: Unable to create file mapping for %s error code = %d", fileName, GetLastError());
return NULL;
}
#ifndef OVR_ALLOW_CREATE_FILE_MAPPING_IF_EXISTS
// If the file mapping already exists,
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(hFileMapping);
Logger.LogDebugF("FAILURE: File mapping at %s already exists", fileName);
return NULL;
}
#endif
// Map the file
return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
}
static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params) {
SharedMemoryInternal* retval = NULL;
// Construct the file mapping name in a Windows-specific way
OVR::String fileMappingName = params.globalName;
const char* fileName = fileMappingName.ToCStr();
// Is being opened read-only?
const bool openReadOnly = (params.accessMode == SharedMemory::AccessMode_ReadOnly);
// Try up to 3 times to reduce low-probability failures:
static const int ATTEMPTS_MAX = 3;
for (int attempts = 0; attempts < ATTEMPTS_MAX; ++attempts) {
// If opening should be attempted first,
if (params.openMode != SharedMemory::OpenMode_CreateOnly) {
// Attempt to open a shared memory map
retval = AttemptOpenSharedMemory(fileName, params.minSizeBytes, openReadOnly);
// If successful,
if (retval) {
// Done!
break;
}
}
// If creating the shared memory is also acceptable,
if (params.openMode != SharedMemory::OpenMode_OpenOnly) {
// Interpret create mode
const bool allowRemoteWrite = (params.remoteMode == SharedMemory::RemoteMode_ReadWrite);
// Attempt to create a shared memory map
retval =
AttemptCreateSharedMemory(fileName, params.minSizeBytes, openReadOnly, allowRemoteWrite);
// If successful,
if (retval) {
// Done!
break;
}
}
} // Re-attempt create/open
// Note: On Windows the initial contents of the region are guaranteed to be zero.
return retval;
}
#endif // OVR_OS_WIN32
#if (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC))
// Hidden implementation class for OS-specific behavior
class SharedMemoryInternal : public SharedMemoryInternalBase {
public:
int FileMapping;
void* FileView;
int FileSize;
SharedMemoryInternal(int fileMapping, void* fileView, int fileSize)
: FileMapping(fileMapping), FileView(fileView), FileSize(fileSize) {}
virtual ~SharedMemoryInternal() {
// If file view is set,
if (FileView) {
munmap(FileView, FileSize);
FileView = MAP_FAILED;
}
// If file mapping is set,
if (FileMapping >= 0) {
close(FileMapping);
FileMapping = -1;
}
}
virtual void* GetFileView() override {
return FileView;
}
};
static SharedMemoryInternal*
DoFileMap(int hFileMapping, const char* fileName, bool openReadOnly, int minSize) {
// Calculate the required flags based on read/write mode
int prot = openReadOnly ? PROT_READ : (PROT_READ | PROT_WRITE);
// Map the file view
void* pFileView = mmap(NULL, minSize, prot, MAP_SHARED, hFileMapping, 0);
if (pFileView == MAP_FAILED) {
close(hFileMapping);
Logger.LogDebugF("FAILURE: Unable to map view of file for %s error code = %d", fileName, errno);
OVR_UNUSED(fileName);
return NULL;
}
// Create internal representation
SharedMemoryInternal* pimple = new SharedMemoryInternal(hFileMapping, pFileView, minSize);
// If memory allocation fails,
if (!pimple) {
munmap(pFileView, minSize);
close(hFileMapping);
Logger.LogDebugF("FAILURE: Out of memory");
return NULL;
}
return pimple;
}
static SharedMemoryInternal*
AttemptOpenSharedMemory(const char* fileName, int minSize, bool openReadOnly) {
// Calculate permissions and flags based on read/write mode
int flags = openReadOnly ? O_RDONLY : O_RDWR;
int perms = openReadOnly ? S_IRUSR : (S_IRUSR | S_IWUSR);
// Attempt to open the shared memory file
int hFileMapping = shm_open(fileName, flags, perms);
// If file was not opened successfully,
if (hFileMapping < 0) {
Logger.LogDebugF(
"WARNING: Unable to open file mapping for %s error code = %d (not necessarily bad)",
fileName,
errno);
return NULL;
}
// Map the file
return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
}
static SharedMemoryInternal* AttemptCreateSharedMemory(
const char* fileName,
int minSize,
bool openReadOnly,
bool allowRemoteWrite) {
// Create mode
// Note: Cannot create the shared memory file read-only because then ftruncate() will fail.
int flags = O_CREAT | O_RDWR;
#ifndef OVR_ALLOW_CREATE_FILE_MAPPING_IF_EXISTS
// Require exclusive access when creating (seems like a good idea without trying it yet..)
if (shm_unlink(fileName) < 0) {
Logger.LogDebugF(
"WARNING: Unable to unlink shared memory file %s error code = %d", fileName, errno);
}
flags |= O_EXCL;
#endif
// Set own read/write permissions
int perms = openReadOnly ? S_IRUSR : (S_IRUSR | S_IWUSR);
// Allow other users to read/write the shared memory file
perms |= allowRemoteWrite ? (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH) : (S_IRGRP | S_IROTH);
// Attempt to open the shared memory file
int hFileMapping = shm_open(fileName, flags, perms);
// If file was not opened successfully,
if (hFileMapping < 0) {
Logger.LogDebugF(
"FAILURE: Unable to create file mapping for %s error code = %d", fileName, errno);
return NULL;
}
int truncRes = ftruncate(hFileMapping, minSize);
// If file was not opened successfully,
if (truncRes < 0) {
close(hFileMapping);
Logger.LogDebugF(
"FAILURE: Unable to truncate file for %s to %d error code = %d", fileName, minSize, errno);
return NULL;
}
// Map the file
return DoFileMap(hFileMapping, fileName, openReadOnly, minSize);
}
static SharedMemoryInternal* CreateSharedMemory(const SharedMemory::OpenParameters& params) {
SharedMemoryInternal* retval = NULL;
// Construct the file mapping name in a Linux-specific way
OVR::String fileMappingName = "/";
fileMappingName += params.globalName;
const char* fileName = fileMappingName.ToCStr();
// Is being opened read-only?
const bool openReadOnly = (params.accessMode == SharedMemory::AccessMode_ReadOnly);
// Try up to 3 times to reduce low-probability failures:
static const int ATTEMPTS_MAX = 3;
for (int attempts = 0; attempts < ATTEMPTS_MAX; ++attempts) {
// If opening should be attempted first,
if (params.openMode != SharedMemory::OpenMode_CreateOnly) {
// Attempt to open a shared memory map
retval = AttemptOpenSharedMemory(fileName, params.minSizeBytes, openReadOnly);
// If successful,
if (retval) {
// Done!
break;
}
}
// If creating the shared memory is also acceptable,
if (params.openMode != SharedMemory::OpenMode_OpenOnly) {
// Interpret create mode
const bool allowRemoteWrite = (params.remoteMode == SharedMemory::RemoteMode_ReadWrite);
// Attempt to create a shared memory map
retval =
AttemptCreateSharedMemory(fileName, params.minSizeBytes, openReadOnly, allowRemoteWrite);
// If successful,
if (retval) {
// Done!
break;
}
}
} // Re-attempt create/open
// Note: On Windows the initial contents of the region are guaranteed to be zero.
return retval;
}
#endif // OVR_OS_LINUX
//-----------------------------------------------------------------------------
// SharedMemory
static bool FakingSharedMemory = false;
void SharedMemory::SetFakeSharedMemory(bool enabled) {
FakingSharedMemory = enabled;
}
bool SharedMemory::IsFakingSharedMemory() {
return FakingSharedMemory;
}
SharedMemory::SharedMemory(
int size,
void* data,
const String& name,
SharedMemoryInternalBase* pInternal)
: Size(size), Data(data), Name(name), Internal(pInternal) {}
SharedMemory::~SharedMemory() {
// Call close when it goes out of scope
Close();
delete Internal;
}
void SharedMemory::Close() {
if (Internal) {
delete Internal;
Internal = NULL;
}
}
//-----------------------------------------------------------------------------
// SharedMemoryFactory
Ptr<SharedMemory> SharedMemoryFactory::Open(const SharedMemory::OpenParameters& params) {
Ptr<SharedMemory> retval;
// Return if no name specified or invalid size requested
// 0 is a valid option, it means 'map the whole region'
// In this case, the client must know the valid size of the region
if (!params.globalName || (params.minSizeBytes < 0)) {
Logger.LogDebug("FAILURE: Invalid parameters to Create()");
return NULL;
}
#ifdef OVR_BUILD_DEBUG
if (Logger.GetMinimumOutputLevel() <= ovrlog::Level::Trace) {
const char* OpType = "{Unknown}";
switch (params.openMode) {
case SharedMemory::OpenMode_CreateOnly:
OpType = "Creating";
break;
case SharedMemory::OpenMode_CreateOrOpen:
OpType = "Creating/Opening";
break;
case SharedMemory::OpenMode_OpenOnly:
OpType = "Opening";
break;
default:
OVR_ASSERT(false);
break;
}
Logger.LogTraceF(
"%s shared memory region: %s > %d bytes", OpType, params.globalName, params.minSizeBytes);
}
#endif
// Attempt to create a shared memory region from the parameters
SharedMemoryInternalBase* pInternal;
if (SharedMemory::IsFakingSharedMemory()) {
pInternal = CreateFakeSharedMemory(params);
} else {
pInternal = CreateSharedMemory(params);
}
if (pInternal) {
// Create the wrapper object
retval = *new SharedMemory(
params.minSizeBytes, pInternal->GetFileView(), params.globalName, pInternal);
}
return retval;
}
SharedMemoryFactory::SharedMemoryFactory() {
Logger.LogDebug("Creating factory");
FakeMemoryManager::GetInstance(); // Make sure the fake memory manager is destroyed at the right
// time
// Must be at end of function
PushDestroyCallbacks();
}
SharedMemoryFactory::~SharedMemoryFactory() {
Logger.LogDebug("Destroying factory");
}
void SharedMemoryFactory::OnSystemDestroy() {
delete this;
}
} // namespace OVR

View File

@ -0,0 +1,229 @@
/************************************************************************************
PublicHeader: OVR
Filename : OVR_SharedMemory.h
Content : Inter-process shared memory subsystem
Created : June 1, 2014
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_SharedMemory_h
#define OVR_SharedMemory_h
#include "OVR_Types.h"
#include "OVR_RefCount.h"
#include "OVR_Allocator.h"
#include "OVR_System.h"
#include "OVR_String.h"
namespace OVR {
class SharedMemoryInternalBase; // Opaque
// SharedMemory
// Note: Safe when used between 32-bit and 64-bit processes
class SharedMemory : public RefCountBase<SharedMemory> {
friend class SharedMemoryFactory;
OVR_NON_COPYABLE(SharedMemory);
public:
// Only constructed by the SharedMemory Factory
SharedMemory(int size, void* data, const String& name, SharedMemoryInternalBase* pInternal);
// Call close when it goes out of scope
~SharedMemory();
// Modes for opening a new shared memory region
enum OpenMode {
// Note: On Windows, Create* requires Administrator priviledges or running as a Service.
OpenMode_CreateOnly, // Must not already exist
OpenMode_OpenOnly, // Must already exist
OpenMode_CreateOrOpen // May exist or not
};
// Local access restrictions
enum AccessMode {
AccessMode_ReadOnly, // Acquire read-only access
AccessMode_ReadWrite, // Acquire read or write access
};
// Remote access restrictions
enum RemoteMode {
RemoteMode_ReadOnly, // Other processes will need to open in read-only mode
RemoteMode_ReadWrite // Other processes can open in read-write mode
};
// Modes for opening a new shared memory region
struct OpenParameters {
OpenParameters()
: globalName(NULL),
minSizeBytes(0),
openMode(SharedMemory::OpenMode_CreateOrOpen),
remoteMode(SharedMemory::RemoteMode_ReadWrite),
accessMode(SharedMemory::AccessMode_ReadWrite) {}
// Creation parameters
const char* globalName; // Name of the shared memory region
int minSizeBytes; // Minimum number of bytes to request
SharedMemory::OpenMode openMode; // Creating the file or opening the file?
SharedMemory::RemoteMode remoteMode; // When creating, what access should other processes get?
SharedMemory::AccessMode
accessMode; // When opening/creating, what access should this process get?
};
public:
// Returns the size of the shared memory region
int GetSizeI() const {
return Size;
}
// Returns the process-local pointer to the shared memory region
// Note: This may be different on different processes
void* GetData() const {
return Data;
}
// Returns the name of the shared memory region
String GetName() {
return Name;
}
protected:
int Size; // How many shared bytes are shared at the pointer address?
void* Data; // Pointer to the shared memory region.
String Name; // Name that can be used to access this region
// Hidden implementation class for OS-specific behavior
SharedMemoryInternalBase* Internal;
// Close and cleanup the shared memory region
// Note: This is called on destruction
void Close();
public:
static void SetFakeSharedMemory(bool enabled);
static bool IsFakingSharedMemory();
};
// SharedMemoryFactory
class SharedMemoryFactory : public NewOverrideBase,
public SystemSingletonBase<SharedMemoryFactory> {
OVR_DECLARE_SINGLETON(SharedMemoryFactory);
public:
// Construct a SharedMemory object.
// Note: The new object is reference-counted so it should be stored with Ptr<>. Initial reference
// count is 1.
Ptr<SharedMemory> Open(const SharedMemory::OpenParameters&);
};
// A shared object
// Its constructor will be called when creating a writer
// Its destructor will not be called
template <class SharedType>
class ISharedObject : public RefCountBase<ISharedObject<SharedType>> {
public:
static const int RegionSize = (int)sizeof(SharedType);
protected:
Ptr<SharedMemory> pSharedMemory;
bool Open(const char* name, bool readOnly) {
// Configure open parameters based on read-only mode
SharedMemory::OpenParameters params;
// FIXME: This is a hack. We currently need to allow clients to open this for read-write even
// though they only need read-only access. This is because in the first 0.4 release the
// LocklessUpdater class technically writes to it (increments by 0) to read from the space.
// This was quickly corrected in 0.4.1 and we are waiting for the right time to disallow write
// access when everyone upgrades to 0.4.1+.
// params.remoteMode = SharedMemory::RemoteMode_ReadOnly;
params.remoteMode = SharedMemory::RemoteMode_ReadWrite;
params.globalName = name;
params.accessMode =
readOnly ? SharedMemory::AccessMode_ReadOnly : SharedMemory::AccessMode_ReadWrite;
params.minSizeBytes = RegionSize;
params.openMode =
readOnly ? SharedMemory::OpenMode_OpenOnly : SharedMemory::OpenMode_CreateOrOpen;
// Attempt to open the shared memory file
pSharedMemory = SharedMemoryFactory::GetInstance()->Open(params);
// If it was not able to be opened,
if (pSharedMemory && pSharedMemory->GetSizeI() >= RegionSize && pSharedMemory->GetData()) {
// If writing,
if (!readOnly) {
// Construct the object also
Construct<SharedType>(pSharedMemory->GetData());
}
return true;
}
return false;
}
SharedType* Get() const {
if (!pSharedMemory) {
return NULL;
}
void* data = pSharedMemory->GetData();
if (!data) {
return NULL;
}
return reinterpret_cast<SharedType*>(data);
}
public:
String GetName() const {
return pSharedMemory ? pSharedMemory->GetName() : "";
}
};
// Writer specialized shared object: Ctor will be called on Open()
template <class SharedType>
class SharedObjectWriter : public ISharedObject<SharedType> {
public:
OVR_FORCE_INLINE bool Open(const char* name) {
return ISharedObject<SharedType>::Open(name, false);
}
OVR_FORCE_INLINE SharedType* Get() {
return ISharedObject<SharedType>::Get();
}
};
// Reader specialized shared object: Ctor will not be called
template <class SharedType>
class SharedObjectReader : public ISharedObject<SharedType> {
public:
OVR_FORCE_INLINE bool Open(const char* name) {
return ISharedObject<SharedType>::Open(name, true);
}
OVR_FORCE_INLINE const SharedType* Get() const {
return ISharedObject<SharedType>::Get();
}
};
} // namespace OVR
#endif // OVR_SharedMemory_h

View File

@ -0,0 +1,523 @@
/************************************************************************************
Filename : OVR_Std.h
Content : Standard C function interface
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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_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 {
// Has the same behavior as itoa aside from also having a dest size argument.
// Return value: Pointer to the resulting null-terminated string, same as parameter str.
#if defined(OVR_OS_MS)
inline char* OVR_CDECL OVR_itoa(int val, char* dest, size_t 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_MS
inline char* OVR_itoa(int val, char* dest, size_t len, int radix) {
if (val == 0) {
if (len > 1) {
dest[0] = '0';
dest[1] = '\0';
} else if (len > 0)
dest[0] = '\0';
return dest;
}
// FIXME: Fix the following code to avoid memory write overruns when len is in sufficient.
int cur = val;
size_t i = 0;
size_t 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 (size_t 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 size_t OVR_CDECL OVR_strlen(const char* str) {
return strlen(str);
}
inline size_t OVR_CDECL OVR_strlen(const wchar_t* str) {
return wcslen(str);
}
inline char* OVR_CDECL OVR_strcpy(char* dest, size_t destsize, const char* src) {
#if defined(OVR_MSVC_SAFESTRING)
// Using strncpy_s() instead of strcpy_s() since strcpy_s will invoke the
// invalid parameter exception handler (now in google breakpad) and will
// cause the server to crash, and even if it returns the data may not be
// copied truncated. The intent of all users surveyed has been to truncate
// so we specify strncpy_s with the truncate option instead.
strncpy_s(dest, destsize, src, _TRUNCATE);
return dest;
#else
// FIXME: This should be a safer implementation
OVR_UNUSED(destsize);
return strcpy(dest, src);
#endif
}
inline wchar_t* OVR_CDECL OVR_strcpy(wchar_t* dest, size_t destsize, const wchar_t* src) {
#if defined(OVR_MSVC_SAFESTRING)
wcscpy_s(dest, destsize, src);
return dest;
#else
// FIXME: This should be a safer implementation
OVR_UNUSED(destsize);
return wcscpy(dest, src);
#endif
}
// Acts the same as the strlcpy function.
// Copies src to dest, 0-terminating even if it involves truncating the write.
// Returns the required strlen of dest (which is one less than the required size of dest).
// strlcpy is a safer alternative to strcpy and strncpy and provides size information.
// However, it still may result in an incomplete copy.
//
// Example usage:
// char buffer[256];
// if(OVR_strlcpy(buffer, "hello world", sizeof(buffer)) < sizeof(buffer))
// { there was enough space }
// else
// { need a larger buffer }
//
size_t OVR_CDECL OVR_strlcpy(char* dest, const char* src, size_t destsize);
size_t OVR_CDECL OVR_strlcpy(wchar_t* dest, const wchar_t* src, size_t destsize);
// Acts the same as the strlcat function.
// Appends src to dest, 0-terminating even if it involves an incomplete write.
// Doesn't 0-terminate in the case that destsize is 0.
// Returns the required strlen of dest (which is one less than the required size of dest).
// The terminating 0 char of dest is overwritten by the first
// character of src, and a new 0 char is appended to dest. The required capacity
// of the destination is (strlen(src) + strlen(dest) + 1).
// strlcat is a safer alternative to strcat and provides size information.
// However, it still may result in an incomplete copy.
//
// Example usage:
// char buffer[256] = "hello ";
// if(OVR_strlcat(buffer, "world", sizeof(buffer)) < sizeof(buffer))
// { there was enough space }
// else
// { need a larger buffer }
//
size_t OVR_CDECL OVR_strlcat(char* dest, const char* src, size_t destsize);
size_t OVR_CDECL OVR_strlcat(wchar_t* dest, const wchar_t* src, size_t destsize);
inline char* OVR_CDECL OVR_strncpy(char* dest, size_t destsize, const char* src, size_t count) {
#if defined(OVR_MSVC_SAFESTRING)
strncpy_s(dest, destsize, src, count);
return dest;
#else
// FIXME: This should be a safer implementation
OVR_UNUSED(destsize);
return strncpy(dest, src, count);
#endif
}
inline char* OVR_CDECL OVR_strcat(char* dest, size_t destsize, const char* src) {
#if defined(OVR_MSVC_SAFESTRING)
strcat_s(dest, destsize, src);
return dest;
#else
// FIXME: This should be a safer implementation
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);
}
const char* OVR_CDECL OVR_strrchr(const char* pString, int c);
inline char* OVR_CDECL OVR_strrchr(char* pString, int c) {
return (char*)OVR_strrchr((const char*)pString, c);
}
// Supports ASCII strings only, by calling tolower on each element.
char* OVR_CDECL OVR_stristr(const char* s1, const char* s2);
// Converts each element via towlower.
wchar_t* OVR_CDECL OVR_stristr(const wchar_t* s1, const wchar_t* s2);
inline const uint8_t* OVR_CDECL OVR_memrchr(const uint8_t* str, size_t size, uint8_t c) {
for (intptr_t i = (intptr_t)size - 1; 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 unsigned 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, size_t size) {
return strncmp(ws1, ws2, size);
}
inline uint64_t OVR_CDECL OVR_strtouq(const char* nptr, char** endptr, int base) {
#if defined(OVR_CC_MSVC)
return _strtoui64(nptr, endptr, base);
#else
return strtoull(nptr, endptr, base);
#endif
}
inline int64_t OVR_CDECL OVR_strtoq(const char* nptr, char** endptr, int base) {
#if defined(OVR_CC_MSVC)
return _strtoi64(nptr, endptr, base);
#else
return strtoll(nptr, endptr, base);
#endif
}
inline int64_t OVR_CDECL OVR_atoq(const char* string) {
#if defined(OVR_CC_MSVC)
return _atoi64(string);
#else
return atoll(string);
#endif
}
inline uint64_t OVR_CDECL OVR_atouq(const char* string) {
return OVR_strtouq(string, NULL, 10);
}
// Implemented in OVR_Std.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, size_t count);
// This is like vsprintf but with a destination buffer size argument. However, the behavior is
// different
// from vsnprintf in that the return value semantics are like vsprintf (which returns -1 on capacity
// overflow) and
// not like vsnprintf (which returns intended strlen on capacity overflow).
// Return value:
// On success, the total number of characters written is returned.
// On failure, a negative number is returned.
inline size_t OVR_CDECL
OVR_vsprintf(char* dest, size_t destsize, const char* format, va_list argList) {
size_t 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 = (size_t)rv;
#else
OVR_UNUSED(destsize);
int rv = _vsnprintf(dest, destsize - 1, format, argList);
OVR_ASSERT(rv != -1);
ret = (size_t)rv;
dest[destsize - 1] = 0;
#endif
#else
// FIXME: This should be a safer implementation
OVR_UNUSED(destsize);
ret = (size_t)vsprintf(dest, format, argList);
OVR_ASSERT(ret < destsize);
#endif
return ret;
}
// Returns the strlen of the resulting formatted string, or a negative value if the format is
// invalid.
// Note: If you are planning on printing a string then it's more efficient to just use vsnprintf and
// look at the return value and handle the uncommon case that there wasn't enough space.
inline int OVR_CDECL OVR_vscprintf(const char* format, va_list argList) {
int ret;
#if defined(OVR_CC_MSVC)
ret = _vscprintf(format, argList);
#else
ret = vsnprintf(NULL, 0, format, argList);
#endif
return ret;
}
wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, size_t destsize, const wchar_t* src);
wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, size_t destsize, const wchar_t* src, size_t count);
wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, size_t destsize, const wchar_t* src);
size_t 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_MS)
#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_MS) || 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_t* 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_t UnicodeAlnumBits[];
extern const uint16_t UnicodeAlphaBits[];
extern const uint16_t UnicodeDigitBits[];
extern const uint16_t UnicodeSpaceBits[];
extern const uint16_t UnicodeXDigitBits[];
// Uncomment if necessary
// extern const uint16_t UnicodeCntrlBits[];
// extern const uint16_t UnicodeGraphBits[];
// extern const uint16_t UnicodeLowerBits[];
// extern const uint16_t UnicodePrintBits[];
// extern const uint16_t UnicodePunctBits[];
// extern const uint16_t 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;
size_t max = OVR_wcslen(string);
if (max > 63)
max = 63;
unsigned char c = 0;
for (size_t 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;
size_t max = OVR_wcslen(string);
if (max > 63)
max = 63;
unsigned char c = 0;
for (size_t 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
}
} // namespace OVR
#endif // OVR_Std_h

View File

@ -0,0 +1,370 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_String.h"
#include <stdlib.h>
#include <ctype.h>
#include <atomic>
namespace OVR {
// Return the byte size of the UTF-8 string corresponding to pchar (not including null termination)
static size_t GetEncodeStringSize(const wchar_t* pchar, size_t length = StringIsNullTerminated) {
size_t len = 0;
if (length == StringIsNullTerminated) {
for (size_t i = 0; pchar[i] != 0; ++i) {
len += UTF8Util::GetEncodeCharSize(pchar[i]);
}
} else {
for (size_t i = 0; i < length; ++i) {
len += UTF8Util::GetEncodeCharSize(pchar[i]);
}
}
return len;
}
static size_t StringStrlcpy(
char* pDestUTF8,
size_t destCharCountNotIncludingNull,
const wchar_t* pSrcUCS,
size_t sourceLength = StringIsNullTerminated) {
// String Data[] buffers always have one extra character for null-termination
return UTF8Util::Strlcpy(pDestUTF8, destCharCountNotIncludingNull + 1, pSrcUCS, sourceLength);
}
static size_t StringStrlcpy(
wchar_t* pDestUCS,
size_t destCharCountNotIncludingNull,
const char* pSrcUTF8,
size_t sourceLength = StringIsNullTerminated) {
// String Data[] buffers always have one extra character for null-termination
return UTF8Util::Strlcpy(pDestUCS, destCharCountNotIncludingNull + 1, pSrcUTF8, sourceLength);
}
// ***** 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(size_t 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) {
AppendString(data);
}
StringBuffer::StringBuffer(const char* data, size_t 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(0),
GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE),
LengthIsSize(false) {
AppendString(src.ToCStr(), src.GetSize());
}
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(size_t growSize) {
if (growSize <= 16)
GrowSize = 16;
else {
uint8_t bits = Alg::UpperBit(uint32_t(growSize - 1));
size_t size = (size_t)1 << bits;
GrowSize = size == growSize ? growSize : size;
}
}
size_t StringBuffer::GetLength() const {
size_t length, size = GetSize();
if (LengthIsSize)
return size;
length = (size_t)UTF8Util::GetLength(pData, (size_t)GetSize());
if (length == GetSize())
LengthIsSize = true;
return length;
}
void StringBuffer::Reserve(size_t _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(size_t _size) {
Reserve(_size);
LengthIsSize = false;
Size = _size;
if (pData)
pData[Size] = 0;
}
void StringBuffer::Clear() {
Resize(0);
}
// Appends a character
void StringBuffer::AppendChar(uint32_t ch) {
char buff[8];
size_t 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.
intptr_t srcSize = 0;
UTF8Util::EncodeChar(buff, &srcSize, ch);
OVR_ASSERT(srcSize >= 0);
size_t size = origSize + srcSize;
Resize(size);
OVR_ASSERT(pData != NULL);
memcpy(pData + origSize, buff, srcSize);
}
// Append a string
void StringBuffer::AppendString(const wchar_t* pstr, size_t len) {
if (!pstr || !len)
return;
size_t srcSize = GetEncodeStringSize(pstr, len);
size_t origSize = GetSize();
size_t size = srcSize + origSize;
Resize(size);
OVR_ASSERT(pData != NULL);
StringStrlcpy(pData + origSize, srcSize, pstr, len);
}
void StringBuffer::AppendString(const char* putf8str, size_t utf8StrSz) {
if (!putf8str || !utf8StrSz)
return;
if (utf8StrSz == StringIsNullTerminated)
utf8StrSz = OVR_strlen(putf8str);
size_t origSize = GetSize();
size_t size = utf8StrSz + origSize;
Resize(size);
OVR_ASSERT(pData != NULL);
memcpy(pData + origSize, putf8str, utf8StrSz);
}
// If pstr is NULL then the StringBuffer is cleared.
void StringBuffer::operator=(const char* pstr) {
pstr = pstr ? pstr : "";
size_t size = OVR_strlen(pstr);
Resize(size);
OVR_ASSERT((pData != NULL) || (size == 0));
memcpy(pData, pstr, size);
}
// If pstr is NULL then the StringBuffer is cleared.
void StringBuffer::operator=(const wchar_t* pstr) {
pstr = pstr ? pstr : L"";
size_t size = GetEncodeStringSize(pstr);
Resize(size);
OVR_ASSERT((pData != NULL) || (size == 0));
StringStrlcpy(pData, size, pstr);
}
void StringBuffer::operator=(const String& src) {
const size_t size = src.GetSize();
Resize(size);
OVR_ASSERT((pData != NULL) || (size == 0));
memcpy(pData, src.ToCStr(), size);
}
void StringBuffer::operator=(const StringBuffer& src) {
Clear();
AppendString(src.ToCStr(), src.GetSize());
}
// Inserts substr at posAt
void StringBuffer::Insert(const char* substr, size_t posAt, size_t len) {
size_t oldSize = Size;
size_t insertSize = (len == StringIsNullTerminated) ? OVR_strlen(substr) : len;
size_t byteIndex =
LengthIsSize ? posAt : (size_t)UTF8Util::GetByteIndex(posAt, pData, (intptr_t)Size);
OVR_ASSERT(byteIndex <= oldSize);
Reserve(oldSize + insertSize);
OVR_ASSERT(pData != NULL); // pData is unilaterally written to below.
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
size_t StringBuffer::InsertCharAt(uint32_t c, size_t posAt) {
char buf[8];
intptr_t len = 0;
UTF8Util::EncodeChar(buf, &len, c);
OVR_ASSERT(len >= 0);
buf[(size_t)len] = 0;
Insert(buf, posAt, len);
return (size_t)len;
}
std::string StringVsprintf(const char* format, va_list args) {
char buffer[512]; // We first try writing into this buffer. If it's not enough then use a string.
va_list tmp_args;
va_copy(tmp_args, args);
const int requiredStrlen = vsnprintf(buffer, sizeof(buffer), format, tmp_args);
va_end(tmp_args);
if (requiredStrlen <
static_cast<int>(sizeof(buffer))) { // If the entire result fits into the buffer.
return std::string(buffer, requiredStrlen);
}
std::string result(requiredStrlen, '\0');
std::vsnprintf(&result[0], result.size(), format, args);
return result;
}
std::string& AppendSprintf(std::string& s, const char* format, ...) {
va_list args;
va_start(args, format);
s += StringVsprintf(format, args);
va_end(args);
return s;
}
std::wstring UTF8StringToUCSString(const char* pUTF8, size_t length) {
if (length == StringIsNullTerminated)
length = OVR_strlen(pUTF8);
std::wstring returnValue(length, wchar_t(0)); // We'll possibly trim this value below.
// Note that Strlcpy doesn't handle UTF8 encoding errors.
size_t decodedLength = StringStrlcpy(&returnValue[0], length, pUTF8, length);
OVR_ASSERT(decodedLength <= length);
returnValue.resize(decodedLength);
return returnValue;
}
std::wstring UTF8StringToUCSString(const std::string& sUTF8) {
return UTF8StringToUCSString(sUTF8.data(), sUTF8.size());
}
std::wstring OVRStringToUCSString(const String& sOVRUTF8) {
return UTF8StringToUCSString(sOVRUTF8.ToCStr(), sOVRUTF8.GetSize());
}
std::string UCSStringToUTF8String(const wchar_t* pUCS, size_t length) {
if (length == StringIsNullTerminated)
length = wcslen(pUCS);
std::string sUTF8;
size_t size = GetEncodeStringSize(pUCS, length);
sUTF8.resize(size);
StringStrlcpy(&sUTF8[0], size, pUCS, length);
return sUTF8;
}
std::string UCSStringToUTF8String(const std::wstring& sUCS) {
return UCSStringToUTF8String(sUCS.data(), sUCS.size());
}
String UCSStringToOVRString(const wchar_t* pUCS, size_t length) {
if (length == StringIsNullTerminated)
length = wcslen(pUCS);
// We use a std::string intermediate because String doesn't support resize or assignment without
// preallocated data.
const std::string sUTF8 = UCSStringToUTF8String(pUCS, length);
const String sOVRUTF8(sUTF8.data(), sUTF8.size());
return sOVRUTF8;
}
String UCSStringToOVRString(const std::wstring& sUCS) {
return UCSStringToOVRString(sUCS.data(), sUCS.length());
}
} // namespace OVR

View File

@ -0,0 +1,597 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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"
#include <string>
#include <stdarg.h>
namespace OVR {
class String;
class StringBuffer;
// Special/default null-terminated length argument
const size_t StringIsNullTerminated = size_t(-1);
//-----------------------------------------------------------------------------------
// ***** String Class
// String is UTF8 based string class with copy-on-write implementation
// for assignment.
class String : public std::string {
public:
typedef std::string inherited;
// Constructors / Destructors.
String() {}
String(const char* data) {
if (data)
inherited::assign(data);
}
String(const char* data1, const char* pdata2, const char* pdata3 = nullptr) {
if (data1)
inherited::append(data1);
if (pdata2)
inherited::append(pdata2);
if (pdata3)
inherited::append(pdata3);
}
String(const char* data, size_t buflen) {
if (data)
inherited::assign(data, buflen);
}
String(const String& src) {
inherited::assign(src.data(), src.length());
}
String(const std::string& src) {
inherited::assign(src.data(), src.length());
}
explicit String(const wchar_t* data) {
if (data)
String::operator=(data); // Need to do UCS2->UTF8 conversion
}
void Clear() {
inherited::clear();
}
operator const char*() const {
return inherited::c_str();
}
const char* ToCStr() const {
return inherited::c_str();
}
// Byte length.
size_t GetSize() const {
return inherited::size();
}
bool IsEmpty() const {
return inherited::empty();
}
// Returns number of Unicode codepoints (not byte length).
size_t GetLength() const {
return (size_t)UTF8Util::GetLength(inherited::data(), inherited::size());
}
// Return Unicode codepoint count.
int GetLengthI() const {
return (int)UTF8Util::GetLength(inherited::data(), inherited::size());
}
// Return Unicode codepoint at the specified codepoint index.
uint32_t GetCharAt(size_t index) const {
return UTF8Util::GetCharAt(index, inherited::data(), inherited::size());
}
// Get first Unicode codepoint.
uint32_t Front() const {
return UTF8Util::GetCharAt(0, inherited::data(), inherited::size());
}
// Get last Unicode codepoint.
uint32_t Back() const {
OVR_ASSERT(!inherited::empty());
return GetCharAt(GetSize() - 1);
}
// Append a Unicode codepoint.
void AppendChar(uint32_t ch) {
char buff[8]; // Max possible UTF8 sequence is 6 bytes.
intptr_t encodeSize = 0;
UTF8Util::EncodeChar(buff, &encodeSize, ch);
inherited::append(buff, encodeSize);
}
// Append Unicode codepoints
void AppendString(const wchar_t* pstr, size_t len = StringIsNullTerminated) {
if (pstr) {
if (len == StringIsNullTerminated)
len = wcslen(pstr);
while (len-- > 0)
AppendChar(*pstr++);
}
}
// Append UTF8 stirng of a given byte size.
void AppendString(const char* putf8str, size_t utf8StrSz = StringIsNullTerminated) {
if (putf8str) {
if (utf8StrSz == StringIsNullTerminated)
inherited::append(putf8str);
else
inherited::append(putf8str, utf8StrSz);
}
}
// Assigns string with known byte size.
void AssignString(const char* putf8str, size_t size) {
if (putf8str || (size == 0))
inherited::assign(putf8str, size);
}
// Remove 'removeLength' Unicode codepoints starting at the 'posAt' Unicode codepoint.
void Remove(size_t posAt, size_t removeLength = 1) {
size_t length = GetLength(); // Unicode size.
if (posAt < length) {
size_t oldSize = inherited::size(); // Byte size.
if ((posAt + removeLength) > length)
removeLength = (length - posAt);
intptr_t bytePos = UTF8Util::GetByteIndex(posAt, inherited::data(), oldSize);
intptr_t removeSize =
UTF8Util::GetByteIndex(removeLength, inherited::data() + bytePos, oldSize - bytePos);
inherited::erase(bytePos, removeSize);
}
}
// Removes the last Unicode codepoint.
void PopBack() {
if (!inherited::empty())
Remove(GetLength() - 1, 1);
}
// Returns a String that's a substring of this.
// start is the index of the first Unicode codepoint you want to include.
// end is the index one past the last Unicode codepoint you want to include.
String Substring(size_t start, size_t end) const {
size_t length = GetLength();
if ((start >= length) || (start >= end))
return String();
if (end > length)
end = length;
// Get position of starting character and size
intptr_t byteStart = UTF8Util::GetByteIndex(start, inherited::data(), inherited::size());
intptr_t byteSize = UTF8Util::GetByteIndex(
end - start, inherited::data() + byteStart, inherited::size() - byteStart);
return String(inherited::data() + byteStart, (size_t)byteSize);
}
// Insert a UTF8 string at the Unicode codepoint posAt.
String& Insert(const char* substr, size_t posAt, size_t strSize = StringIsNullTerminated) {
if (substr) {
if (strSize == StringIsNullTerminated)
strSize = strlen(substr);
size_t byteIndex = UTF8Util::GetByteIndex(posAt, inherited::c_str(), inherited::size());
if (byteIndex > inherited::size())
byteIndex = inherited::size();
inherited::insert(byteIndex, substr, strSize);
}
return *this;
}
// UTF8 compare
static int CompareNoCase(const char* a, const char* b) {
return OVR_stricmp(a, b);
}
// UTF8 compare
static int CompareNoCase(const char* a, const char* b, size_t byteSize) {
return OVR_strnicmp(a, b, byteSize);
}
// Hash function, case-insensitive
static size_t BernsteinHashFunctionCIS(const void* pdataIn, size_t size, size_t seed = 5381) {
const uint8_t* pdata = (const uint8_t*)pdataIn;
size_t h = seed;
while (size > 0) {
size--;
h = ((h << 5) + h) ^ OVR_tolower(pdata[size]);
}
return h;
}
// Hash function, case-sensitive
static size_t BernsteinHashFunction(const void* pdataIn, size_t size, size_t seed = 5381) {
const uint8_t* pdata = (const uint8_t*)pdataIn;
size_t h = seed;
while (size > 0) {
size--;
h = ((h << 5) + h) ^ (unsigned)pdata[size];
}
return h;
}
// 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(inherited::c_str());
}
bool HasExtension() const {
return HasExtension(inherited::c_str());
}
bool HasProtocol() const {
return HasProtocol(inherited::c_str());
}
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.
void operator=(const char* str) {
if (str)
inherited::assign(str);
else
inherited::clear();
}
void operator=(const wchar_t* str) {
inherited::clear();
while (str && *str)
AppendChar(*str++);
}
void operator=(const String& src) {
inherited::assign(src.data(), src.size());
}
void operator+=(const String& src) {
inherited::append(src.data(), src.size());
}
void operator+=(const char* psrc) {
if (psrc)
inherited::append(psrc);
}
void operator+=(const wchar_t* psrc) {
if (psrc)
AppendString(psrc);
}
// Append a Unicode codepoint.
// Note that this function seems amiss by taking char as an argument instead of uint32_t or
// wchar_t.
void operator+=(char ch) {
AppendChar(ch);
}
String operator+(const char* str) const {
String temp(*this);
if (str)
temp += str;
return temp;
}
String operator+(const String& src) const {
String temp(*this);
temp += src;
return temp;
}
bool operator==(const String& str) const {
return (OVR_strcmp(inherited::c_str(), str.c_str()) == 0);
}
bool operator!=(const String& str) const {
return !operator==(str);
}
bool operator==(const char* str) const {
return OVR_strcmp(inherited::c_str(), (str ? str : "")) == 0;
}
bool operator!=(const char* str) const {
return !operator==(str);
}
bool operator<(const char* pstr) const {
return OVR_strcmp(inherited::c_str(), (pstr ? pstr : "")) < 0;
}
bool operator<(const String& str) const {
return *this < str.c_str();
}
bool operator>(const char* pstr) const {
return OVR_strcmp(inherited::c_str(), (pstr ? pstr : "")) > 0;
}
bool operator>(const String& str) const {
return *this > str.c_str();
}
int CompareNoCase(const char* pstr) const {
return CompareNoCase(inherited::c_str(), (pstr ? pstr : ""));
}
int CompareNoCase(const String& str) const {
return CompareNoCase(inherited::c_str(), str.c_str());
}
int CompareNoCaseStartsWith(const String& str) const {
// Problem: the original version of this used GetLength, which seems like a bug because
// CompareNoCase takes a byte length. Need to look back in the OVR_String.h history to see if
// the bug has always been there.
return CompareNoCase(inherited::c_str(), str.c_str(), str.size());
}
// Accesses raw bytes
const char& operator[](int index) const {
OVR_ASSERT(index >= 0 && (size_t)index < inherited::size());
return inherited::operator[](index);
}
const char& operator[](size_t index) const {
OVR_ASSERT(index < inherited::size());
return inherited::operator[](index);
}
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 {
size_t operator()(const String& str) const {
return String::BernsteinHashFunction(str.data(), str.size());
}
};
// Case-insensitive hash functor used for strings. Supports additional
// lookup based on NoCaseKey.
struct NoCaseHashFunctor {
size_t operator()(const String& str) const {
return String::BernsteinHashFunctionCIS(str.data(), str.size());
}
size_t operator()(const NoCaseKey& key) const {
return String::BernsteinHashFunctionCIS(key.pStr->c_str(), key.pStr->size());
}
};
};
//-----------------------------------------------------------------------------------
// ***** String Buffer used for Building Strings
class StringBuffer {
char* pData;
size_t Size;
size_t BufferSize;
size_t GrowSize;
mutable bool LengthIsSize;
public:
// Constructors / Destructor.
StringBuffer();
explicit StringBuffer(size_t growSize);
StringBuffer(const char* data);
StringBuffer(const char* data, size_t 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.
size_t GetGrowSize() const {
return GrowSize;
}
void SetGrowSize(size_t 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.
size_t GetSize() const {
return Size;
}
// Tells whether or not the string is empty.
bool IsEmpty() const {
return GetSize() == 0;
}
// Returns number of characters
size_t GetLength() const;
// Returns character at the specified index
uint32_t GetCharAt(size_t index) const;
uint32_t GetFirstCharAt(size_t index, const char** offset) const;
uint32_t GetNextChar(const char** offset) const;
uint32_t Front() const {
return GetCharAt(0);
}
uint32_t Back() const {
return GetCharAt(GetSize() - 1);
}
// Resize the string to the new size
void Resize(size_t _size);
void Reserve(size_t _size);
// Appends a character
void AppendChar(uint32_t ch);
// Append a string
void AppendString(const wchar_t* pstr, size_t len = StringIsNullTerminated);
void AppendString(const char* putf8str, size_t utf8StrSz = StringIsNullTerminated);
void AppendFormatV(const char* format, va_list argList);
void AppendFormat(const char* format, ...);
// Assigned a string with dynamic data (copied through initializer).
// void AssignString(const InitStruct& src, size_t size);
// Inserts substr at posAt
void Insert(const char* substr, size_t posAt, size_t len = StringIsNullTerminated);
// Inserts character at posAt
size_t InsertCharAt(uint32_t c, size_t posAt);
// 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) {
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[](size_t index) {
OVR_ASSERT(index < GetSize());
return pData[index];
}
const char& operator[](size_t index) const {
OVR_ASSERT(index < GetSize());
return pData[index];
}
};
// Returns a std::string that was initialized via printf-style formatting.
// The behavior is undefined if the specified format or arguments are invalid.
// Example usage:
// std::string s = StringVsprintf("Hello %s", "world");
std::string StringVsprintf(const char* format, va_list args);
// Returns a std::string that was appended to via printf-style formatting.
// The behavior is undefined if the specified format or arguments are invalid.
// Example usage:
// AppendSprintf(s, "appended %s", "hello world");
std::string& AppendSprintf(std::string& s, const char* format, ...);
// Convert a UTF8 String object to a wchar_t UCS (Unicode) std::basic_string object.
// The C++11 Standard Library has similar functionality, but it's not supported by earlier
// versions of Visual Studio. To consider: Add support for this when available.
// length is the strlen of pUTF8. If not specified then it is calculated automatically.
// Returns an empty string in the case that the UTF8 is malformed.
std::wstring UTF8StringToUCSString(const char* pUTF8, size_t length = StringIsNullTerminated);
std::wstring UTF8StringToUCSString(const std::string& sUTF8);
std::wstring OVRStringToUCSString(const String& sOVRUTF8);
// Convert a wchar_t UCS (Unicode) std::basic_string object to a UTF8 std::basic_string object.
// The C++11 Standard Library has similar functionality, but it's not supported by earlier
// versions of Visual Studio. To consider: Add support for this when available.
// length is the strlen of pUCS. If not specified then it is calculated automatically.
// Returns an empty string in the case that the UTF8 is malformed.
std::string UCSStringToUTF8String(const wchar_t* pUCS, size_t length = StringIsNullTerminated);
std::string UCSStringToUTF8String(const std::wstring& sUCS);
String UCSStringToOVRString(const wchar_t* pUCS, size_t length = StringIsNullTerminated);
String UCSStringToOVRString(const std::wstring& sUCS);
} // namespace OVR
#endif

View File

@ -0,0 +1,91 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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);
}
}
};
} // namespace OVR
#endif

View File

@ -0,0 +1,78 @@
/************************************************************************************
Filename : OVR_String_FormatUtil.cpp
Content : String format functions.
Created : February 27, 2013
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_String.h"
#include "OVR_Log.h"
#include <stdarg.h>
namespace OVR {
void StringBuffer::AppendFormatV(const char* format, va_list argList) {
char buffer[512];
char* bufferUsed = buffer;
char* bufferAllocated = NULL;
va_list argListSaved;
va_copy(argListSaved, argList);
int requiredStrlen = vsnprintf(
bufferUsed,
OVR_ARRAY_COUNT(buffer),
format,
argListSaved); // The large majority of the time this will succeed.
if (requiredStrlen >= (int)sizeof(buffer)) // If the initial capacity wasn't enough...
{
bufferAllocated = (char*)OVR_ALLOC(sizeof(char) * (requiredStrlen + 1));
bufferUsed = bufferAllocated;
if (bufferAllocated) {
va_end(argListSaved);
va_copy(argListSaved, argList);
requiredStrlen = vsnprintf(bufferAllocated, (requiredStrlen + 1), format, argListSaved);
}
}
if (requiredStrlen < 0) // If there was a printf format error...
{
bufferUsed = NULL;
}
va_end(argListSaved);
if (bufferUsed)
AppendString(bufferUsed);
if (bufferAllocated)
OVR_FREE(bufferAllocated);
}
void StringBuffer::AppendFormat(const char* format, ...) {
va_list argList;
va_start(argList, format);
AppendFormatV(format, argList);
va_end(argList);
}
} // namespace OVR

View File

@ -0,0 +1,183 @@
/************************************************************************************
Filename : OVR_String_PathUtil.cpp
Content : String filename/url helper function
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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_t 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) {
if (filename)
*pfilename = filename;
else
*pfilename = urlStart;
}
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_t charVal = UTF8Util::DecodeNextChar(&url);
uint32_t 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_t 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));
}
} // namespace OVR

View File

@ -0,0 +1,159 @@
/**************************************************************************
Filename : OVR_SysFile.cpp
Content : File wrapper class implementation (Win32)
Created : April 5, 1999
Authors : Michael Antonov
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**************************************************************************/
#define GFILE_CXX
// Standard C library (Captain Obvious guarantees!)
#include <stdio.h>
#include "OVR_SysFile.h"
#include "OVR_File.h"
#include "OVR_Log.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 int64_t LTell() {
return 0;
}
virtual int GetLength() {
return 0;
}
virtual int64_t LGetLength() {
return 0;
}
// virtual bool Stat(FileStats *pfs) { return 0; }
virtual int GetErrorCode() {
return Error_FileNotFound;
}
// ** Stream implementation & I/O
virtual int Write(const uint8_t* /*pbuffer*/, int /*numBytes*/) {
return -1;
}
virtual int Read(uint8_t* /*pbuffer*/, int /*numBytes*/) {
return -1;
}
virtual int SkipBytes(int /*numBytes*/) {
return 0;
}
virtual int BytesAvailable() {
return 0;
}
virtual bool Flush() {
return 0;
}
virtual int Seek(int /*offset*/, int /*origin*/) {
return -1;
}
virtual int64_t LSeek(int64_t /*offset*/, int /*origin*/) {
return -1;
}
virtual int CopyFromStream(File* /*pstream*/, int /*byteSize*/) {
return -1;
}
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;
}
Ptr<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;
OVR_DEBUG_LOG(("Failed to open file: %s", path.ToCStr()));
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;
}
} // namespace OVR

View File

@ -0,0 +1,105 @@
/************************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#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
int64_t ModifyTime;
int64_t AccessTime;
int64_t 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 OVR, 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();
};
} // Namespace OVR
#endif

View File

@ -0,0 +1,214 @@
/************************************************************************************
Filename : OVR_System.cpp
Content : General kernel initialization/cleanup, including that
of the memory allocator.
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_System.h"
#include "OVR_Threads.h"
#include "OVR_Timer.h"
#include "OVR_DebugHelp.h"
#include "OVR_Log.h"
#include <new>
#if defined(_MSC_VER)
#include <new.h>
#else
#include <new>
#endif
#ifdef OVR_OS_MS
#pragma warning(push, 0)
#include "OVR_Win32_IncludeWindows.h" // GetModuleHandleEx
#pragma warning(pop)
#endif
static ovrlog::Channel Logger("Kernel:System");
namespace OVR {
//-----------------------------------------------------------------------------
// Initialization/Shutdown state
// If true, then Destroy() was called and is in the process of executing
// Added to fix race condition if thread is started after the call to System::Destroy()
static bool ShuttingDown = false;
//-----------------------------------------------------------------------------
// Initialization/Shutdown Callbacks
static SystemSingletonInternal* SystemShutdownListenerList =
nullptr; // Points to the most recent SystemSingletonInternal added to the list.
static Lock& GetSSILock() { // Put listLock in a function so that it can be constructed on-demand.
static Lock listLock; // Will construct on the first usage. However, the guarding of this
// construction is not thread-safe
return listLock; // under all compilers. However, since we are initially calling this on startup
// before other threads
} // could possibly exist, the first usage of this will be before other threads exist.
void SystemSingletonInternal::RegisterDestroyCallback() {
GetSSILock().DoLock();
if (ShuttingDown) {
GetSSILock().Unlock();
OnThreadDestroy();
} else {
GetSSILock().Unlock();
// Insert the listener at the front of the list (top of the stack). This is an analogue of a C++
// forward_list::push_front or stack::push.
NextShutdownSingleton = SystemShutdownListenerList;
SystemShutdownListenerList = this;
}
}
//-----------------------------------------------------------------------------
// System
static int System_Init_Count = 0;
#if defined(_MSC_VER)
// This allows us to throw OVR::bad_alloc instead of std::bad_alloc, which provides less
// information.
int OVRNewFailureHandler(size_t /*size*/) {
throw OVR::bad_alloc();
// Disabled because otherwise a compiler warning is generated regarding unreachable code.
// return 0; // A return value of 0 tells the Standard Library to not retry the allocation.
}
#else
// This allows us to throw OVR::bad_alloc instead of std::bad_alloc, which provides less
// information.
void OVRNewFailureHandler() {
throw OVR::bad_alloc();
}
#endif
// Initializes System core, installing allocator.
void System::Init() {
// Restart logging if we shut down before
ovrlog::RestartLogging();
#if defined(_MSC_VER)
// Make it so that failure of the C malloc family of functions results in the same behavior as C++
// operator new failure.
// This allows us to throw exceptions for malloc usage the same as for operator new bad_alloc.
_set_new_mode(1);
// Tells the standard library to direct new (and malloc) failures to us. Normally we wouldn't need
// to do this, as the
// C++ Standard Library already throws std::bad_alloc on operator new failure. The problem is that
// the Standard Library doesn't
// throw std::bad_alloc upon malloc failure, and we can only intercept malloc failure via this
// means. _set_new_handler specifies
// a global handler for the current running Standard Library. If the Standard Library is being
// dynamically linked instead
// of statically linked, then this is a problem because a call to _set_new_handler would override
// anything the application
// has already set.
_set_new_handler(OVRNewFailureHandler);
#else
// This allows us to throw OVR::bad_alloc instead of std::bad_alloc, which provides less
// information.
// Question: Does this set the handler for all threads or just the current thread? The C++
// Standard doesn't
// explicitly state this, though it may be implied from other parts of the Standard.
std::set_new_handler(OVRNewFailureHandler);
#endif
if (++System_Init_Count == 1) {
Timer::initializeTimerSystem();
} else {
Logger.LogError("Init recursively called; depth = ", System_Init_Count);
// XXX Should this just exit?
}
}
void System::Stop() {
GetSSILock().DoLock();
ShuttingDown = true;
GetSSILock().Unlock();
if (--System_Init_Count == 0) {
Logger.LogInfo("Graceful shutdown: OnThreadDestroy");
// Invoke all of the post-finish callbacks (normal case)
for (SystemSingletonInternal* listener = SystemShutdownListenerList; listener;
listener = listener->NextShutdownSingleton) {
listener->OnThreadDestroy();
}
} else {
Logger.LogError("Stop recursively called; depth = ", System_Init_Count);
}
}
void System::Destroy() {
if (!ShuttingDown) {
Logger.LogWarning("Destroy called before Stop");
System::Stop();
}
if (System_Init_Count == 0) {
Logger.LogInfo("Graceful shutdown: OnSystemDestroy");
// Invoke all of the post-finish callbacks (normal case)
for (SystemSingletonInternal *next, *listener = SystemShutdownListenerList; listener;
listener = next) {
next = listener->NextShutdownSingleton;
listener->OnSystemDestroy();
}
SystemShutdownListenerList = nullptr;
Timer::shutdownTimerSystem();
} else {
Logger.LogError("Destroy recursively called; depth = ", System_Init_Count);
}
GetSSILock().DoLock();
ShuttingDown = false;
GetSSILock().Unlock();
Logger.LogInfo("Graceful shutdown: Stopping logger");
// Prevent memory leak reports
ovrlog::ShutdownLogging();
}
// Returns 'true' if system was properly initialized.
bool System::IsInitialized() {
return System_Init_Count > 0;
}
// Dump any leaked memory
void System::CheckForAllocatorLeaks() {
if (Allocator::IsTrackingLeaks()) {
int ovrLeakCount = Allocator::DumpMemory();
(void)ovrLeakCount;
OVR_ASSERT(ovrLeakCount == 0);
}
}
} // namespace OVR

View File

@ -0,0 +1,181 @@
/************************************************************************************
PublicHeader: OVR
Filename : OVR_System.h
Content : General kernel initialization/cleanup, including that
of the memory allocator.
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_System_h
#define OVR_System_h
#include "OVR_Allocator.h"
#include "OVR_Atomic.h"
namespace OVR {
//-----------------------------------------------------------------------------
// SystemSingleton
// Subsystems are implemented using the Singleton pattern.
// To avoid code duplication in all the places where Singletons are defined,
// The pattern is defined once here and used everywhere.
class SystemSingletonInternal {
friend class System;
// Allows for including this class in the shutdown list.
SystemSingletonInternal* NextShutdownSingleton;
// No copying allowed
OVR_NON_COPYABLE(SystemSingletonInternal);
public:
// Call this to register for a call to OnThreadDestroy and OnSystemDestroy before the
// Kernel is shut down. OnThreadDestroy is called before any remaining existing threads
// are exited. Registered callbacks are called in the reverse order they were registered.
// You would typically call this at the end of your SystemSingletonInternal subclass' constructor.
// The registered objects are not deleted on shutdown; they would have to do that themselves
// within
// OnSystemDestroy or via a compiler-generated static destruction.
void RegisterDestroyCallback();
void PushDestroyCallbacks() {
RegisterDestroyCallback();
} // For backward compatibility.
protected:
SystemSingletonInternal() : NextShutdownSingleton(nullptr) {}
virtual ~SystemSingletonInternal() {}
// Initializes the SystemSingletonInternal.
// You can register for an automatic call to this function by calling PushInitCallbacks.
// The registration of an automatic call to this is not required, but may be useful if
// you need to postpone your initialization until after Kernel is initialized.
// You cannot call PushInitCallbacks or PushDestroyCallbacks while within this function.
virtual void OnSystemInit() {}
// Called just before waiting for threads to exit.
// Listeners are called in the opposite order they were registered.
// This function is useful for terminating threads at the right time before the rest of the system
// is shut down.
// Note: The singleton must not delete itself here, as OnSystemDestroy will subsequently be called
// for it.
virtual void OnThreadDestroy() {}
// Shuts down the SystemSingletonInternal.
// You can register for an automatic call to this function by calling PushDestroyCallbacks.
// The registration of an automatic call to this is not required, but may be useful if
// you need to delay your shutdown until application exit time.
// You cannot call PushInitCallbacks or PushDestroyCallbacks while within this function.
// This function may delete this.
virtual void OnSystemDestroy() {}
};
// Singletons derive from this class
template <class T>
class SystemSingletonBase : public SystemSingletonInternal {
static std::atomic<T*> SingletonInstance;
static T* SlowGetInstance();
struct ZeroInitializer {
ZeroInitializer() {
SingletonInstance = nullptr;
}
};
ZeroInitializer zeroInitializer;
protected:
~SystemSingletonBase() {
// Make sure the instance gets set to zero on dtor
if (SingletonInstance.load() == this)
SingletonInstance = nullptr;
}
public:
static OVR_FORCE_INLINE T* GetInstance() {
// Fast version
// Note: The singleton instance is stored in an std::atomic<> to allow it to be accessed
// atomically from multiple threads without locks.
T* instance = SingletonInstance;
return instance ? instance : SlowGetInstance();
}
};
// For reference, see N3337 14.5.1.3 (Static data members of class templates):
template <class T>
std::atomic<T*> OVR::SystemSingletonBase<T>::SingletonInstance;
// Place this in the singleton class in the header file
#define OVR_DECLARE_SINGLETON(T) \
friend class OVR::SystemSingletonBase<T>; \
\
private: \
T(); \
virtual ~T(); \
virtual void OnSystemDestroy() override;
// Place this in the singleton class source file
#define OVR_DEFINE_SINGLETON(T) \
namespace OVR { \
template <> \
T* SystemSingletonBase<T>::SlowGetInstance() { \
static OVR::Lock lock; \
OVR::Lock::Locker locker(&lock); \
if (!SingletonInstance.load()) \
SingletonInstance = new T; \
return SingletonInstance; \
} \
}
// ***** 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.
class System {
public:
// 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();
// Halt all system threads (call before Destroy).
static void OVR_CDECL Stop();
// De-initializes System more, finalizing the threading system and destroying
// the global memory allocator.
static void OVR_CDECL Destroy();
// Dump any leaked allocations
static void OVR_CDECL CheckForAllocatorLeaks();
};
} // namespace OVR
#endif

View File

@ -0,0 +1,217 @@
/************************************************************************************
PublicHeader: None
Filename : OVR_Threads.h
Content : Contains thread-related (safe) functionality
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Threads_h
#define OVR_Threads_h
#include "OVR_Types.h"
#include "OVR_Atomic.h"
#include "OVR_RefCount.h"
#include "OVR_Array.h"
#include <memory>
#include <mutex>
#include <condition_variable>
#if !defined(_WIN32)
#include <unistd.h>
#endif
// 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 Event;
// Implementation forward declarations
class MutexImpl;
//-----------------------------------------------------------------------------------
// ***** 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 MutexImpl;
std::unique_ptr<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(const std::unique_ptr<Mutex>& pmutex) : Locker(pmutex.get()) {}
~Locker() {
pMutex->Unlock();
}
};
};
//-----------------------------------------------------------------------------------
// ***** 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 std::mutex StateMutex;
std::condition_variable 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
// ThreadHandle is a handle to a thread, which on some platforms (e.g. Windows) is
// different from ThreadId. On Unix platforms, a ThreadHandle is the same as a
// ThreadId and is pthread_t.
typedef void* ThreadHandle;
// ThreadId uniquely identifies a thread; returned by Windows GetCurrentThreadId(),
// Unix pthread_self() 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
namespace Thread {
// *** Sleep
// Sleep msecs milliseconds
bool MSleep(unsigned msecs);
// *** Debugging functionality
void SetCurrentThreadName(const char* name);
void GetCurrentThreadName(char* name, size_t nameCapacity);
}; // namespace Thread
// Returns the unique Id of a thread it is called on, intended for
// comparison purposes.
ThreadId GetCurrentThreadId();
// Returns the unique Id of the current running process.
#if !defined(OVR_OS_MS)
#define GetCurrentProcessId getpid
#endif
//-----------------------------------------------------------------------------------
// ***** OVR_THREAD_LOCAL
//
// Example usage:
// #if defined(OVR_THREAD_LOCAL)
// OVR_THREAD_LOCAL int n = 0; // OK
// extern OVR_THREAD_LOCAL struct Data s; // OK
// static OVR_THREAD_LOCAL char* p; // OK
// OVR_THREAD_LOCAL int i = sizeof(i); // OK.
// OVR_THREAD_LOCAL std::string s("hello"); // Can't be used for initialized
// objects.
// OVR_THREAD_LOCAL int Function(); // Can't be used as return value.
// void Function(){ OVR_THREAD_LOCAL int i = 0; } // Can't be used in function.
// void Function(OVR_THREAD_LOCAL int i){ } // can't be used as argument.
// extern int i; OVR_THREAD_LOCAL int i; // Declarations differ.
// int OVR_THREAD_LOCAL i; // Can't be used as a type modifier.
// OVR_THREAD_LOCAL int i = i; // Can't reference self before
// initialization.
// #endif
#if !defined(_MSC_VER) || \
(_MSC_VER >= 1900) // VC++ doesn't support C++11 thread_local storage until VS2015.
#define OVR_THREAD_LOCAL thread_local
#else
#define OVR_THREAD_LOCAL __declspec(thread)
#endif
} // namespace OVR
#endif // OVR_ENABLE_THREADS
#endif // OVR_Threads_h

View File

@ -0,0 +1,236 @@
/************************************************************************************
Filename : OVR_ThreadsPthread.cpp
Content :
Created :
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#if !defined(_WIN32) // Skip the entire file under Windows
#include "OVR_Threads.h"
#ifdef OVR_ENABLE_THREADS
#include "OVR_Timer.h"
#include "OVR_Log.h"
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#if defined(OVR_OS_MAC) || defined(OVR_OS_BSD)
#include <sys/sysctl.h>
#include <sys/param.h>
#if !defined(OVR_OS_MAC)
#include <pthread_np.h>
#endif
#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;
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) {
OVR_UNUSED(pmutex);
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_UNUSED(pmutex);
OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
// unsigned lockCount;
LockCount--;
// lockCount = LockCount;
pthread_mutex_unlock(&SMutex);
}
bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) {
OVR_UNUSED(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
// NOTE: RefCount mode already thread-safe for all waitables.
Mutex::Mutex(bool recursive) : pImpl(new MutexImpl(this, recursive)) {}
Mutex::~Mutex() {}
// 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) {
std::unique_lock<std::mutex> locker(StateMutex);
// Do the correct amount of waiting
if (delay == OVR_WAIT_INFINITE) {
while (!State)
StateWaitCondition.wait(locker);
} else if (delay) {
if (!State)
StateWaitCondition.wait_for(locker, std::chrono::milliseconds(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) {
std::unique_lock<std::mutex> lock(StateMutex);
State = newState;
Temporary = newTemp;
if (mustNotify) {
lock.unlock();
StateWaitCondition.notify_all();
}
}
ThreadId GetCurrentThreadId() {
return (void*)pthread_self();
}
// *** Sleep functions
/* static */
bool Thread::MSleep(unsigned msecs) {
usleep(msecs * 1000);
return 1;
}
void Thread::SetCurrentThreadName(const char* name) {
#if defined(OVR_OS_APPLE)
pthread_setname_np(name);
#else
pthread_setname_np(pthread_self(), name);
#endif
}
void Thread::GetCurrentThreadName(char* name, size_t nameCapacity) {
name[0] = 0;
#if !defined(OVR_OS_ANDROID)
// Android does not have pthread_getname_np (or an equivalent)
pthread_getname_np(pthread_self(), name, nameCapacity);
#endif
}
} // namespace OVR
#endif // OVR_ENABLE_THREADS
#endif // _WIN32

View File

@ -0,0 +1,319 @@
/************************************************************************************
Filename : OVR_ThreadsWinAPI.cpp
Platform : WinAPI
Content : Windows specific thread-related (safe) functionality
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Threads.h"
#include "OVR_Log.h"
#include "OVR_Timer.h"
#ifdef OVR_ENABLE_THREADS
// For _beginthreadex / _endtheadex
#include <process.h>
#ifdef _WIN32
#include "OVR_DLLHelper.h"
#include <processthreadsapi.h>
#include <sdkddkver.h>
#if !defined(NTDDI_WIN10_RS1) // RS1 SDK introduced these functions.
WINBASEAPI HRESULT WINAPI SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription);
WINBASEAPI HRESULT WINAPI GetThreadDescription(HANDLE hThread, PWSTR* ppszThreadDescription);
#endif
// Example usage:
// Kernel32API kernel32API;
//
// if (kernel32API.getThreadDescription) {
// wchar_t* pStr;
//
// if (SUCCEEDED(getThreadDescription(hThread, &pStr)) {
// <use pStr>
// Localfree(pStr);
// }
// }
//
// if (kernel32API.setThreadDescription) {
// HRESULT hr = setThreadDescription(GetCurrentThread(), L"thread name");
// ...
// }
//
class Kernel32API {
public:
Kernel32API()
: dllHelper{"Kernel32.dll"},
getThreadDescription(dllHelper.Load("GetThreadDescription")),
setThreadDescription(dllHelper.Load("SetThreadDescription")) {}
protected:
OVR::DllHelper dllHelper; // Declared first because the members below depend on the ctor order.
public:
// Recognition of GetThreadDescription requires the Windows 10 SDK v10.14393 (a.k.a build 1607,
// a.k.a. Anniversary Update, a.k.a. RS1, a.k.a. Redstone 1) or later.
// GetThreadDescription requires thread to have at least THREAD_QUERY_LIMITED_INFORMATION access.
decltype(GetThreadDescription)* getThreadDescription;
// Required THREAD_SET_LIMITED_INFORMATION access.
decltype(SetThreadDescription)* setThreadDescription;
};
Kernel32API kernel32API;
#endif // _WIN32
namespace OVR {
//-----------------------------------------------------------------------------------
// *** Internal Mutex implementation class
class MutexImpl : public NewOverrideBase {
// System mutex or semaphore
HANDLE hMutexOrSemaphore;
bool Recursive;
volatile unsigned LockCount;
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;
#if defined(OVR_OS_WIN32) // Older versions of Windows don't support CreateSemaphoreEx, so stick
// with CreateSemaphore for portability.
hMutexOrSemaphore = Recursive ? CreateMutexW(NULL, 0, NULL) : CreateSemaphoreW(NULL, 1, 1, NULL);
#else
// No CreateSemaphore() call, so emulate it.
hMutexOrSemaphore = Recursive ? CreateMutexW(NULL, 0, NULL)
: CreateSemaphoreExW(NULL, 1, 1, NULL, 0, SEMAPHORE_ALL_ACCESS);
#endif
}
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() {}
// 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) {
std::unique_lock<std::mutex> locker(StateMutex);
// Do the correct amount of waiting
if (delay == OVR_WAIT_INFINITE) {
while (!State)
StateWaitCondition.wait(locker);
} else if (delay) {
if (!State)
StateWaitCondition.wait_for(locker, std::chrono::milliseconds(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) {
{
std::lock_guard<std::mutex> lock(StateMutex);
State = newState;
Temporary = newTemp;
}
// NOTE: The lock does not need to be held when calling notify_all(),
// and holding it is in fact a pessimization.
if (mustNotify)
StateWaitCondition.notify_all();
}
//-----------------------------------------------------------------------------------
// ***** Thread Namespace
// *** Sleep functions
// static
bool Thread::MSleep(unsigned msecs) {
::Sleep(msecs);
return 1;
}
static OVR_THREAD_LOCAL char ThreadLocaThreadlName[32] = {};
// Older method of informing the debugger about thread names.
// This needs to be in its own function body due to the use of __try.
static void SetCurrentThreadNameViaException(const char* name) {
#if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING)
// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
#pragma pack(push, 8)
struct THREADNAME_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
};
union TNIUnion {
THREADNAME_INFO tni;
ULONG_PTR upArray[4];
};
#pragma pack(pop)
TNIUnion tniUnion = {{0x1000, name, ::GetCurrentThreadId(), 0}};
__try {
RaiseException(0x406D1388, 0, OVR_ARRAY_COUNT(tniUnion.upArray), tniUnion.upArray);
} __except (
GetExceptionCode() == 0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) {
return;
}
#endif // OVR_BUILD_SHIPPING
}
void Thread::SetCurrentThreadName(const char* name) {
OVR_strlcpy(ThreadLocaThreadlName, name, sizeof(ThreadLocaThreadlName));
// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreaddescription
if (kernel32API.setThreadDescription) {
std::wstring strW = OVR::UTF8StringToUCSString(name);
kernel32API.setThreadDescription(GetCurrentThread(), strW.c_str());
} else {
SetCurrentThreadNameViaException(name);
}
}
void Thread::GetCurrentThreadName(char* name, size_t nameCapacity) {
if (kernel32API.getThreadDescription) {
wchar_t* pStrW;
if (SUCCEEDED(kernel32API.getThreadDescription(GetCurrentThread(), &pStrW))) {
std::string str = OVR::UCSStringToUTF8String(pStrW);
LocalFree(pStrW);
OVR_strlcpy(name, str.c_str(), nameCapacity);
return;
}
}
// Fall back to seeing if we set it above in SetCurrentThreadName.
OVR_strlcpy(name, ThreadLocaThreadlName, nameCapacity);
}
// Returns the unique Id of a thread it is called on, intended for
// comparison purposes.
ThreadId GetCurrentThreadId() {
union {
intptr_t id;
ThreadId threadId;
};
id = ::GetCurrentThreadId();
return threadId;
}
} // namespace OVR
#endif

View File

@ -0,0 +1,458 @@
/************************************************************************************
Filename : OVR_Timer.cpp
Content : Provides static functions for precise timing
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_Timer.h"
#include "OVR_Log.h"
#if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE)
#include "OVR_Win32_IncludeWindows.h"
#include <MMSystem.h>
#pragma comment(lib, "winmm.lib")
#elif defined(OVR_OS_ANDROID)
#include <time.h>
#include <android/log.h>
#else
#include <chrono>
#endif
#if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32)
typedef NTSTATUS(
NTAPI* NtQueryTimerResolutionType)(PULONG MaximumTime, PULONG MinimumTime, PULONG CurrentTime);
static NtQueryTimerResolutionType pNtQueryTimerResolution;
#endif
#if defined(OVR_OS_MS) && !defined(OVR_OS_WIN32) // Non-desktop Microsoft platforms...
// Add this alias here because we're not going to include OVR_CAPI.cpp
extern "C" {
double ovr_GetTimeInSeconds() {
return Timer::GetSeconds();
}
}
#endif
namespace OVR {
// For recorded data playback
bool Timer::useVirtualSeconds = false;
double Timer::VirtualSeconds = 0.0;
//------------------------------------------------------------------------
// *** Android Specific Timer
#if defined( \
OVR_OS_ANDROID) // To consider: This implementation can also work on most Linux distributions
//------------------------------------------------------------------------
// *** Timer - Platform Independent functions
// Returns global high-resolution application timer in seconds.
double Timer::GetSeconds() {
if (useVirtualSeconds)
return VirtualSeconds;
// Choreographer vsync timestamp is based on.
struct timespec tp;
const int status = clock_gettime(CLOCK_MONOTONIC, &tp);
#ifdef OVR_BUILD_DEBUG
if (status != 0) {
OVR_DEBUG_LOG(("clock_gettime status=%i", status));
}
#else
OVR_UNUSED(status);
#endif
return (double)tp.tv_sec;
}
uint64_t Timer::GetTicksNanos() {
if (useVirtualSeconds)
return (uint64_t)(VirtualSeconds * NanosPerSecond);
// Choreographer vsync timestamp is based on.
struct timespec tp;
const int status = clock_gettime(CLOCK_MONOTONIC, &tp);
#ifdef OVR_BUILD_DEBUG
if (status != 0) {
OVR_DEBUG_LOG(("clock_gettime status=%i", status));
}
#else
OVR_UNUSED(status);
#endif
const uint64_t result =
(uint64_t)tp.tv_sec * (uint64_t)(1000 * 1000 * 1000) + uint64_t(tp.tv_nsec);
return result;
}
void Timer::initializeTimerSystem() {
// Empty for this platform.
}
void Timer::shutdownTimerSystem() {
// Empty for this platform.
}
//------------------------------------------------------------------------
// *** Win32 Specific Timer
#elif defined(OVR_OS_MS)
// This helper class implements high-resolution wrapper that combines timeGetTime() output
// with QueryPerformanceCounter. timeGetTime() is lower precision but drives the high bits,
// as it's tied to the system clock.
struct PerformanceTimer {
PerformanceTimer()
: UsingVistaOrLater(false),
TimeCS(),
OldMMTimeMs(0),
MMTimeWrapCounter(0),
PerfFrequency(0),
PerfFrequencyInverse(0),
PerfFrequencyInverseNanos(0),
PerfMinusTicksDeltaNanos(0),
LastResultNanos(0) {}
enum { MMTimerResolutionNanos = 1000000 };
void Initialize();
void Shutdown();
uint64_t GetTimeSeconds();
double GetTimeSecondsDouble();
uint64_t GetTimeNanos();
UINT64 getFrequency() {
if (PerfFrequency == 0) {
LARGE_INTEGER freq;
::QueryPerformanceFrequency(&freq);
PerfFrequency = freq.QuadPart;
PerfFrequencyInverse = 1.0 / (double)PerfFrequency;
PerfFrequencyInverseNanos = 1000000000.0 / (double)PerfFrequency;
}
return PerfFrequency;
}
double GetFrequencyInverse() {
OVR_ASSERT(PerfFrequencyInverse != 0.0); // Assert that the frequency has been initialized.
return PerfFrequencyInverse;
}
// In Vista+ we are able to use QPC exclusively.
bool UsingVistaOrLater;
CRITICAL_SECTION TimeCS;
// timeGetTime() support with wrap.
uint32_t OldMMTimeMs;
uint32_t MMTimeWrapCounter;
// Cached performance frequency result.
uint64_t PerfFrequency; // cycles per second, typically a large value like 3000000, but usually
// not the same as the CPU clock rate.
double PerfFrequencyInverse; // seconds per cycle (will be a small fractional value).
double PerfFrequencyInverseNanos; // nanoseconds per cycle.
// Computed as (perfCounterNanos - ticksCounterNanos) initially,
// and used to adjust timing.
uint64_t PerfMinusTicksDeltaNanos;
// Last returned value in nanoseconds, to ensure we don't back-step in time.
uint64_t LastResultNanos;
};
static PerformanceTimer Win32_PerfTimer;
void PerformanceTimer::Initialize() {
::InitializeCriticalSection(&TimeCS);
MMTimeWrapCounter = 0;
getFrequency();
#if defined(OVR_OS_WIN32) // Desktop Windows only
// Set Vista flag. On Vista, we can just use QPC() without all the extra work
OSVERSIONINFOEXW ver;
ZeroMemory(&ver, sizeof(OSVERSIONINFOEXW));
ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
ver.dwMajorVersion = 6; // Vista+
DWORDLONG condMask = 0;
VER_SET_CONDITION(condMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
// VerifyVersionInfo returns true if the OS meets the conditions set above
UsingVistaOrLater = ::VerifyVersionInfoW(&ver, VER_MAJORVERSION, condMask) != 0;
#else
UsingVistaOrLater = true;
#endif
if (!UsingVistaOrLater) {
#if defined(OVR_OS_WIN32) // Desktop Windows only
// The following has the effect of setting the NT timer resolution (NtSetTimerResolution) to 1
// millisecond.
MMRESULT mmr = timeBeginPeriod(1);
OVR_ASSERT(TIMERR_NOERROR == mmr);
OVR_UNUSED(mmr);
#endif
#if defined(OVR_BUILD_DEBUG) && defined(OVR_OS_WIN32)
HMODULE hNtDll = ::LoadLibraryW(L"NtDll.dll");
if (hNtDll) {
pNtQueryTimerResolution =
(NtQueryTimerResolutionType)::GetProcAddress(hNtDll, "NtQueryTimerResolution");
// pNtSetTimerResolution = (NtSetTimerResolutionType)::GetProcAddress(hNtDll,
// "NtSetTimerResolution");
if (pNtQueryTimerResolution) {
ULONG MinimumResolution; // in 100-ns units
ULONG MaximumResolution;
ULONG ActualResolution;
pNtQueryTimerResolution(&MinimumResolution, &MaximumResolution, &ActualResolution);
OVR_DEBUG_LOG(
("NtQueryTimerResolution = Min %ld us, Max %ld us, Current %ld us",
MinimumResolution / 10,
MaximumResolution / 10,
ActualResolution / 10));
}
::FreeLibrary(hNtDll);
}
#endif
}
}
void PerformanceTimer::Shutdown() {
::DeleteCriticalSection(&TimeCS);
if (!UsingVistaOrLater) {
#if defined(OVR_OS_WIN32) // Desktop Windows only
MMRESULT mmr = timeEndPeriod(1);
OVR_ASSERT(TIMERR_NOERROR == mmr);
OVR_UNUSED(mmr);
#endif
}
}
uint64_t PerformanceTimer::GetTimeSeconds() {
if (UsingVistaOrLater) {
LARGE_INTEGER li;
::QueryPerformanceCounter(&li);
OVR_ASSERT(PerfFrequencyInverse != 0); // Initialize should have been called earlier.
return (uint64_t)(li.QuadPart * PerfFrequencyInverse);
}
return (uint64_t)(GetTimeNanos() * .0000000001);
}
double PerformanceTimer::GetTimeSecondsDouble() {
if (UsingVistaOrLater) {
LARGE_INTEGER li;
::QueryPerformanceCounter(&li);
OVR_ASSERT(PerfFrequencyInverse != 0);
return (li.QuadPart * PerfFrequencyInverse);
}
return (GetTimeNanos() * .0000000001);
}
uint64_t PerformanceTimer::GetTimeNanos() {
uint64_t resultNanos;
LARGE_INTEGER li;
OVR_ASSERT(PerfFrequencyInverseNanos != 0); // Initialize should have been called earlier.
if (UsingVistaOrLater) // Includes non-desktop platforms
{
// Then we can use QPC() directly without all that extra work
::QueryPerformanceCounter(&li);
resultNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos);
return resultNanos;
}
// Pre-Vista computers:
// Note that the Oculus SDK does not run on PCs before Windows 7 SP1
// so this code path should never be taken in practice. We keep it here
// since this is a nice reusable timing library that can be useful for
// other projects.
// On Win32 QueryPerformanceFrequency is unreliable due to SMP and
// performance levels, so use this logic to detect wrapping and track
// high bits.
::EnterCriticalSection(&TimeCS);
// Get raw value and perf counter "At the same time".
::QueryPerformanceCounter(&li);
DWORD mmTimeMs = timeGetTime();
if (OldMMTimeMs > mmTimeMs)
MMTimeWrapCounter++;
OldMMTimeMs = mmTimeMs;
// Normalize to nanoseconds.
uint64_t perfCounterNanos = (uint64_t)(li.QuadPart * PerfFrequencyInverseNanos);
uint64_t mmCounterNanos = ((uint64_t(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000;
if (PerfMinusTicksDeltaNanos == 0)
PerfMinusTicksDeltaNanos = perfCounterNanos - mmCounterNanos;
// Compute result before snapping.
//
// On first call, this evaluates to:
// resultNanos = mmCounterNanos.
// Next call, assuming no wrap:
// resultNanos = prev_mmCounterNanos + (perfCounterNanos - prev_perfCounterNanos).
// After wrap, this would be:
// resultNanos = snapped(prev_mmCounterNanos +/- 1ms) + (perfCounterNanos -
// prev_perfCounterNanos).
//
resultNanos = perfCounterNanos - PerfMinusTicksDeltaNanos;
// Snap the range so that resultNanos never moves further apart then its target resolution.
// It's better to allow more slack on the high side as timeGetTime() may be updated at
// sporadically
// larger then 1 ms intervals even when 1 ms resolution is requested.
if (resultNanos > (mmCounterNanos + MMTimerResolutionNanos * 2)) {
resultNanos = mmCounterNanos + MMTimerResolutionNanos * 2;
if (resultNanos < LastResultNanos)
resultNanos = LastResultNanos;
PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos;
} else if (resultNanos < (mmCounterNanos - MMTimerResolutionNanos)) {
resultNanos = mmCounterNanos - MMTimerResolutionNanos;
if (resultNanos < LastResultNanos)
resultNanos = LastResultNanos;
PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos;
}
LastResultNanos = resultNanos;
::LeaveCriticalSection(&TimeCS);
return resultNanos;
}
//------------------------------------------------------------------------
// *** Timer - Platform Independent functions
// Returns global high-resolution application timer in seconds.
double Timer::GetSeconds() {
return Win32_PerfTimer.GetTimeSecondsDouble();
}
double Timer::GetVirtualSeconds() {
if (useVirtualSeconds)
return VirtualSeconds;
return Win32_PerfTimer.GetTimeSecondsDouble();
}
// Delegate to PerformanceTimer.
uint64_t Timer::GetVirtualTicksNanos() {
if (useVirtualSeconds)
return (uint64_t)(VirtualSeconds * NanosPerSecond);
return Win32_PerfTimer.GetTimeNanos();
}
uint64_t Timer::GetTicksNanos() {
return Win32_PerfTimer.GetTimeNanos();
}
// Windows version also provides the performance frequency inverse.
double Timer::GetPerfFrequencyInverse() {
return Win32_PerfTimer.GetFrequencyInverse();
}
double Timer::GetPerfFrequency() {
return double(Win32_PerfTimer.getFrequency());
}
void Timer::initializeTimerSystem() {
Win32_PerfTimer.Initialize();
}
void Timer::shutdownTimerSystem() {
Win32_PerfTimer.Shutdown();
}
#else // C++11 standard compliant platforms
double Timer::GetSeconds() {
if (useVirtualSeconds)
return VirtualSeconds;
using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>;
auto now = std::chrono::high_resolution_clock::now();
return FpSeconds(now.time_since_epoch()).count();
}
double Timer::GetVirtualSeconds() {
if (useVirtualSeconds)
return VirtualSeconds;
using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>;
auto now = std::chrono::high_resolution_clock::now();
return FpSeconds(now.time_since_epoch()).count();
}
uint64_t Timer::GetTicksNanos() {
if (useVirtualSeconds)
return (uint64_t)(VirtualSeconds * NanosPerSecond);
using Uint64Nanoseconds = std::chrono::duration<uint64_t, std::chrono::nanoseconds::period>;
auto now = std::chrono::high_resolution_clock::now();
return Uint64Nanoseconds(now.time_since_epoch()).count();
}
void Timer::initializeTimerSystem() {}
void Timer::shutdownTimerSystem() {}
#endif // OS-specific
CountdownTimer::CountdownTimer(size_t countdownTimeMs, bool start)
: CountdownTime(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::milliseconds(countdownTimeMs))) {
if (start)
Restart();
}
std::chrono::steady_clock::time_point CountdownTimer::CurrentTime() const {
return std::chrono::steady_clock::now();
}
bool CountdownTimer::IsTimeUp() const {
return (CurrentTime() > DoneTime);
}
void CountdownTimer::Restart() {
DoneTime = (CurrentTime() + CountdownTime);
}
void CountdownTimer::Restart(size_t countdownTimeMs) {
CountdownTime = std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::milliseconds(countdownTimeMs));
Restart();
};
} // namespace OVR

View File

@ -0,0 +1,143 @@
/************************************************************************************
PublicHeader: OVR
Filename : OVR_Timer.h
Content : Provides static functions for precise timing
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Timer_h
#define OVR_Timer_h
#include "OVR_Types.h"
#include <chrono>
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.
MksPerSecond = 1000 * 1000, // Microseconds in one second.
NanosPerSecond = 1000 * 1000 * 1000, // Nanoseconds in one second.
};
// ***** Timing APIs for Application
// These APIs should be used to guide animation and other program functions
// that require precision.
// Returns global high-resolution application timer in seconds.
static double OVR_STDCALL GetSeconds();
// This may return a recorded time if Replaying a recording
static double OVR_STDCALL GetVirtualSeconds();
// Returns time in Nanoseconds, using highest possible system resolution.
static uint64_t OVR_STDCALL GetTicksNanos();
// This may return a recorded time if Replaying a recording
static uint64_t OVR_STDCALL GetVirtualTicksNanos();
#ifdef OVR_OS_MS
static double OVR_STDCALL GetPerfFrequencyInverse();
static double OVR_STDCALL GetPerfFrequency();
#endif
// Kept for compatibility.
// 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.
static uint32_t OVR_STDCALL GetTicksMs() {
return uint32_t(GetTicksNanos() / 1000000);
}
// This may return a recorded time if Replaying a recording
static uint32_t OVR_STDCALL GetVirtualTicksMs() {
return uint32_t(GetVirtualTicksNanos() / 1000000);
}
// for recorded data playback
static void SetVirtualSeconds(double virtualSeconds, bool enable = true) {
VirtualSeconds = virtualSeconds;
useVirtualSeconds = enable;
}
private:
friend class System;
// System called during program startup/shutdown.
static void initializeTimerSystem();
static void shutdownTimerSystem();
// for recorded data playback.
static double VirtualSeconds;
static bool useVirtualSeconds;
#if defined(OVR_OS_ANDROID)
// Android-specific data
#elif defined(OVR_OS_MS)
// Microsoft-specific data
#endif
}; // class Timer
//-----------------------------------------------------------------------------
// CountdownTimer
//
// Acts like a kitchen timer. Implemented using std::chrono::steady_clock.
// Input resolution is in milliseconds.
// Under the hood, it uses the native resolution of a std::chrono::steady_clock.
//
// Exmample usage:
// CountdownTimer timer(5000, true);
//
// if(timer.IsTimeUp())
// {
// DoSomething();
// timer.Restart();
// }
//
struct CountdownTimer {
std::chrono::steady_clock::time_point DoneTime;
std::chrono::steady_clock::duration CountdownTime;
CountdownTimer(size_t countdownTimeMs = 0, bool start = false);
std::chrono::steady_clock::time_point CurrentTime() const;
bool IsTimeUp() const;
void Restart();
void Restart(size_t countdownTimeMs);
};
} // namespace OVR
// This version of ovr_GetTimeInSeconds should be called internally with Oculus service,
// as public APIs are not available.
inline double ovr_GetTimeInSeconds_Internal() {
return OVR::Timer::GetSeconds();
}
#endif

View File

@ -0,0 +1,390 @@
/**************************************************************************
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 (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "OVR_UTF8Util.h"
#include <wchar.h>
#include <string.h>
// sizeof(wchar_t) in preprocessor-accessible form.
#ifndef OVR_WCHAR_SIZE
#if defined(__WCHAR_MAX__)
#if (__WCHAR_MAX__ == 127) || (__WCHAR_MAX__ == 255)
#define OVR_WCHAR_SIZE 1
#elif (__WCHAR_MAX__ == 32767) || (__WCHAR_MAX__ == 65535)
#define OVR_WCHAR_SIZE 2
#else
#define OVR_WCHAR_SIZE 4
#endif
#elif defined(OVR_OS_UNIX)
#define OVR_WCHAR_SIZE 4
#else
#define OVR_WCHAR_SIZE 2
#endif
#endif
namespace OVR {
namespace UTF8Util {
size_t Strlcpy(char* pDestUTF8, size_t destCharCount, const wchar_t* pSrcUCS, size_t sourceLength) {
if (sourceLength == (size_t)-1)
sourceLength = wcslen(pSrcUCS);
size_t destLength = 0;
size_t i;
for (i = 0; i < sourceLength; ++i) {
char buff[6]; // longest utf8 encoding just to be safe
intptr_t count = 0;
EncodeChar(buff, &count, pSrcUCS[i]);
// If this character occupies more than the remaining space (leaving room for the trailing
// '\0'), truncate here
if ((destLength + count) >= destCharCount)
break;
memcpy(pDestUTF8 + destLength, buff, count);
destLength += (size_t)count;
}
// Should be true for all cases other than destCharCount == 0.
if (destLength < destCharCount)
pDestUTF8[destLength] = '\0';
// Compute the required destination size for any remaining source characters
// Note a very long wchar string with lots of multibyte encodings can overflow destLength
// This should be okay since we'll just treat those cases (likely to be originating
// from an attacker) as shorter strings
while (i < sourceLength)
destLength += GetEncodeCharSize(pSrcUCS[i++]);
// Return the intended strlen of pDestUTF8.
return destLength;
}
size_t Strlcpy(wchar_t* pDestUCS, size_t destCharCount, const char* pSrcUTF8, size_t sourceLength) {
if (sourceLength == (size_t)-1)
sourceLength = strlen(pSrcUTF8);
size_t destLength = 0, requiredLength = 0;
for (const char* pSrcUTF8End = (pSrcUTF8 + sourceLength); pSrcUTF8 < pSrcUTF8End;) {
uint32_t c = DecodeNextChar_Advance0(&pSrcUTF8);
OVR_ASSERT_M(
pSrcUTF8 <= (pSrcUTF8 + sourceLength), "Strlcpy sourceLength was not on a UTF8 boundary.");
#if (OVR_WCHAR_SIZE == 2)
if (c >= 0x0000FFFF)
c = 0x0000FFFD;
#endif
if ((destLength + 1) < destCharCount) // If there is enough space to append a wchar_t (leaving
// room for a trailing '\0')...
{
pDestUCS[destLength] = wchar_t(c);
destLength++;
}
requiredLength++;
}
if (destLength < destCharCount)
pDestUCS[destLength] = L'\0';
return requiredLength; // Return the intended wcslen of pDestUCS.
}
intptr_t GetLength(const char* buf, intptr_t buflen) {
const char* p = buf;
intptr_t 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_t GetCharAt(intptr_t index, const char* putf8str, intptr_t length) {
const char* buf = putf8str;
uint32_t 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;
}
intptr_t GetByteIndex(intptr_t index, const char* putf8str, intptr_t byteLength) {
const char* buf = putf8str;
if (byteLength >= 0) {
const char* lastValid = putf8str;
while ((buf - putf8str) < byteLength && index > 0) {
lastValid = buf;
// XXX this may read up to 5 bytes past byteLength
UTF8Util::DecodeNextChar_Advance0(&buf);
index--;
}
// check for UTF8 characters which ran past the end of the buffer
if (buf - putf8str > byteLength)
return lastValid - putf8str;
return buf - putf8str;
}
while (index > 0) {
uint32_t c = UTF8Util::DecodeNextChar(&buf);
index--;
// okay to index past null-terminator, DecodeNextChar will not advance past it
if (c == 0)
break;
}
return buf - putf8str;
}
intptr_t GetByteIndexChecked(intptr_t index, const char* putf8str, intptr_t byteLength) {
const char* buf = putf8str;
// before start of string?
if (index < 0)
return -1;
if (byteLength >= 0) {
while ((buf - putf8str) < byteLength && index > 0) {
// XXX this may read up to 5 bytes past byteLength
UTF8Util::DecodeNextChar_Advance0(&buf);
index--;
}
// check for UTF8 characters which ran past the end of the buffer
if ((buf - putf8str) > byteLength)
return -1;
} else {
while (index > 0) {
uint32_t c = UTF8Util::DecodeNextChar_Advance0(&buf);
index--;
// ran past null terminator
if (c == 0)
return -1;
}
}
// ran off end of string
if (index != 0)
return -1;
// at the valid index'th character-boundary offset in the string
return buf - putf8str;
}
int GetEncodeCharSize(uint32_t 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_t DecodeNextChar_Advance0(const char** putf8Buffer) {
uint32_t 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_t)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 EncodeChar(char* pbuffer, intptr_t* pindex, uint32_t 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.
}
}
} // namespace UTF8Util
} // namespace OVR

View File

@ -0,0 +1,130 @@
/************************************************************************************
Filename : OVR_UTF8Util.h
Content : UTF8 Unicode character encoding/decoding support
Created : September 19, 2012
Notes :
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_UTF8Util_h
#define OVR_UTF8Util_h
#include "OVR_Types.h"
namespace OVR {
namespace UTF8Util {
// *** wchar_t / UTF8 Unicode string conversion
// Same behavior as strlcpy except it does an encoding conversion while doing the copy.
// length refers to wcslen(pSrcUCS).
// Returns the intended strlen of the destination.
//
// Example usage:
// wchar_t* strW = L"abcdefgh";
// char buffer[4];
// size_t requiredStrlen = Strlcpy(buffer, OVR_ARRAY_COUNT(buffer), strW);
//
// if(requiredStrlen >= OVR_ARRAY_COUNT(buffer)) // If not enough space...
// {
// char* pBuffer = new char[requiredStrlen + 1];
// Strlcpy(pBuffer, OVR_ARRAY_COUNT(requiredStrlen + 1), strW);
// ...
// }
//
size_t Strlcpy(
char* pDestUTF8,
size_t destCharCount,
const wchar_t* pSrcUCS,
size_t sourceLength = (size_t)-1);
// Same behavior as strlcpy except it does an encoding conversion while doing the copy.
// length refers to strlen(pSrcUTF8).
// Returns the intended wcslen of the destination.
//
// Example usage:
// char* str8 = "abcdefgh";
// wchar_t buffer[4];
// size_t requiredStrlen = Strlcpy(buffer, OVR_ARRAY_COUNT(buffer), str8);
//
// if(requiredStrlen >= OVR_ARRAY_COUNT(buffer)) // If not enough space...
// {
// wchar_t* pBuffer = new wchar_t[requiredStrlen + 1];
// Strlcpy(pBuffer, OVR_ARRAY_COUNT(requiredStrlen + 1), str8);
// ...
// }
//
size_t Strlcpy(
wchar_t* pDestUCS,
size_t destCharCount,
const char* pSrcUTF8,
size_t sourceLength = (size_t)-1);
// *** 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.
intptr_t GetLength(const char* putf8str, intptr_t 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_t GetCharAt(intptr_t index, const char* putf8str, intptr_t length = -1);
// Converts UTF8 character index into byte offset.
// A valid offset is always returned, either to the start of the string if index < 0,
// or to the terminating null character/end of string if the index'th character is past byteLength.
// Characters which straddle byteLength are truncated.
intptr_t GetByteIndex(intptr_t index, const char* putf8str, intptr_t byteLength = -1);
// Converts UTF8 character index into byte offset.
// -1 is returned if index was out of bounds or if the final character straddles byteLength.
intptr_t GetByteIndexChecked(intptr_t index, const char* putf8str, intptr_t byteLength = -1);
// *** Individual character Encoding/Decoding.
// Determined the number of bytes necessary to encode a UCS character.
int GetEncodeCharSize(uint32_t 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 EncodeChar(char* pbuffer, intptr_t* poffset, uint32_t 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_t DecodeNextChar_Advance0(const char** putf8Buffer);
// Safer version of DecodeNextChar, which doesn't advance pointer if
// null character is hit.
inline uint32_t DecodeNextChar(const char** putf8Buffer) {
uint32_t ch = DecodeNextChar_Advance0(putf8Buffer);
if (ch == 0)
(*putf8Buffer)--;
return ch;
}
} // namespace UTF8Util
} // namespace OVR
#endif

View File

@ -0,0 +1,223 @@
/************************************************************************************
Filename : OVR_Win32_IncludeWindows.h
Content : Small helper header to include Windows.h properly
Created : Oct 16, 2014
Authors : Chris Taylor, Scott Bassett
Copyright : Copyright 2014 Oculus, Inc. All Rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#ifndef OVR_Win32_IncludeWindows_h
#define OVR_Win32_IncludeWindows_h
#include "OVR_Types.h"
// Automatically avoid including the Windows header on non-Windows platforms.
#ifdef OVR_OS_MS
// It is common practice to define WIN32_LEAN_AND_MEAN to reduce compile times.
// However this then requires us to define our own NTSTATUS data type and other
// irritations throughout our code-base.
#ifdef WIN32_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#endif
// Prevents <Windows.h> from #including <Winsock.h>, as we use <Winsock2.h> instead.
#ifndef _WINSOCKAPI_
#define DID_DEFINE_WINSOCKAPI
#define _WINSOCKAPI_
#endif
// Prevents <Windows.h> from defining min() and max() macro symbols.
#ifndef NOMINMAX
#define NOMINMAX
#endif
OVR_DISABLE_ALL_MSVC_WARNINGS()
// d3dkmthk.h requires an NTSTATUS type, but WIN32_LEAN_AND_MEAN will prevent.
#define WIN32_NO_STATUS
#include <Windows.h>
#undef WIN32_NO_STATUS
#include <ntstatus.h>
OVR_RESTORE_ALL_MSVC_WARNINGS()
#ifdef DID_DEFINE_WINSOCKAPI
#undef _WINSOCKAPI_
#undef DID_DEFINE_WINSOCKAPI
#endif
namespace OVR {
//-----------------------------------------------------------------------------
// ScopedHANDLE
//
// MSVC 2010, and MSVC 2012 do not support the <wrl.h> utility header.
// So we provide our own version of HandleRAII, which is an incredibly
// useful pattern for working with Windows HANDLE values.
//
// HANDLEs have two invalid values in Windows, either nullptr or
// INVALID_HANDLE_VALUE. The invalid value that is correct for the usage must
// be provided as the template argument.
struct ScopedHANDLE_NullTraits {
// We cannot make this a static member variable as it is not an integral type.
inline static HANDLE InvalidValue() {
return nullptr;
}
};
struct ScopedHANDLE_InvalidTraits {
inline static HANDLE InvalidValue() {
return INVALID_HANDLE_VALUE;
}
};
template <typename Traits>
class ScopedHANDLE {
HANDLE hAttachedHandle;
public:
ScopedHANDLE(HANDLE handle) : hAttachedHandle(handle) {}
ScopedHANDLE() {
hAttachedHandle = Traits::InvalidValue();
}
ScopedHANDLE& operator=(HANDLE handle) {
Close();
hAttachedHandle = handle;
return *this;
}
~ScopedHANDLE() {
Close();
}
bool IsValid() const {
return hAttachedHandle != Traits::InvalidValue();
}
#if !(defined(OVR_CPP_NO_EXPLICIT_CONVERSION_OPERATORS) && OVR_CPP_NO_EXPLICIT_CONVERSION_OPERATORS)
operator bool() const {
return IsValid();
}
operator HANDLE() const {
return hAttachedHandle;
}
#endif
HANDLE Get() const {
return hAttachedHandle;
}
HANDLE& GetRawRef() {
return hAttachedHandle;
}
void Attach(HANDLE handle) {
Close();
hAttachedHandle = handle;
}
void Detach() {
// Do not close handle
hAttachedHandle = Traits::InvalidValue();
}
bool Copy() {
Close();
if (DuplicateHandle(
GetCurrentProcess(),
cpuEvent,
GetCurrentProcess() serverProcess.Get(),
&hAttachedHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS))
return true;
hAttachedHandle = Traits::InvalidValue();
return false;
}
bool Close() {
bool success = true;
if (hAttachedHandle != Traits::InvalidValue()) {
if (::CloseHandle(hAttachedHandle) != TRUE) {
success = false;
}
hAttachedHandle = Traits::InvalidValue();
}
return success;
}
};
// Different Windows API functions have different invalid values.
// These types are provided to improve readability.
typedef ScopedHANDLE<ScopedHANDLE_NullTraits> ScopedMutexHANDLE;
typedef ScopedHANDLE<ScopedHANDLE_NullTraits> ScopedEventHANDLE;
typedef ScopedHANDLE<ScopedHANDLE_InvalidTraits> ScopedFileHANDLE;
typedef ScopedHANDLE<ScopedHANDLE_NullTraits> ScopedProcessHANDLE;
typedef ScopedHANDLE<ScopedHANDLE_NullTraits> ScopedThreadHANDLE;
typedef ScopedHANDLE<ScopedHANDLE_NullTraits> ScopedSemaphoreHANDLE;
// Scoped registry keys
class ScopedHKEY {
HKEY hAttachedHandle;
public:
ScopedHKEY(HKEY handle) : hAttachedHandle(handle) {}
ScopedHKEY() {
hAttachedHandle = nullptr;
}
ScopedHKEY& operator=(HKEY handle) {
Close();
hAttachedHandle = handle;
return *this;
}
~ScopedHKEY() {
Close();
}
bool IsValid() {
return hAttachedHandle != nullptr;
}
HKEY Get() {
return hAttachedHandle;
}
HKEY& GetRawRef() {
return hAttachedHandle;
}
void Attach(HKEY handle) {
Close();
hAttachedHandle = handle;
}
void Detach() {
// Do not close handle
hAttachedHandle = nullptr;
}
bool Close() {
bool success = true;
if (hAttachedHandle != nullptr) {
if (::RegCloseKey(hAttachedHandle) == ERROR_SUCCESS) {
success = false;
}
hAttachedHandle = nullptr;
}
return success;
}
};
} // namespace OVR
#endif // OVR_OS_MS
#endif // OVR_Win32_IncludeWindows_h

View File

@ -0,0 +1,269 @@
#ifndef _mach_exc_user_
#define _mach_exc_user_
/* Module mach_exc */
#include <string.h>
#include <mach/ndr.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/notify.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/port.h>
#ifdef AUTOTEST
#ifndef FUNCTION_PTR_T
#define FUNCTION_PTR_T
typedef void (*function_ptr_t)(mach_port_t, char*, mach_msg_type_number_t);
typedef struct {
char* name;
function_ptr_t function;
} function_table_entry;
typedef function_table_entry* function_table_t;
#endif /* FUNCTION_PTR_T */
#endif /* AUTOTEST */
#ifndef mach_exc_MSG_COUNT
#define mach_exc_MSG_COUNT 3
#endif /* mach_exc_MSG_COUNT */
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#ifdef __BeforeMigUserHeader
__BeforeMigUserHeader
#endif /* __BeforeMigUserHeader */
#include <sys/cdefs.h>
__BEGIN_DECLS
#if defined(__cplusplus)
extern "C" {
#endif
/* Routine mach_exception_raise_OVR */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t
mach_exception_raise_OVR(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt);
/* Routine mach_exception_raise_state_OVR */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t
mach_exception_raise_state_OVR(
mach_port_t exception_port,
exception_type_t exception,
const mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
int* flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t* new_stateCnt);
/* Routine mach_exception_raise_state_identity_OVR */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t
mach_exception_raise_state_identity_OVR(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
int* flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t* new_stateCnt);
__END_DECLS
/********************** Caution **************************/
/* The following data types should be used to calculate */
/* maximum message sizes only. The actual message may be */
/* smaller, and the position of the arguments within the */
/* message layout may vary from what is presented here. */
/* For example, if any of the arguments are variable- */
/* sized, and less than the maximum is sent, the data */
/* will be packed tight in the actual message to reduce */
/* the presence of holes. */
/********************** Caution **************************/
/* typedefs for all requests */
#ifndef __Request__mach_exc_subsystem__defined
#define __Request__mach_exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
} __Request__mach_exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} __Request__mach_exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} __Request__mach_exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Request__mach_exc_subsystem__defined */
/* union of all requests */
#ifndef __RequestUnion__mach_exc_subsystem__defined
#define __RequestUnion__mach_exc_subsystem__defined
union __RequestUnion__mach_exc_subsystem {
__Request__mach_exception_raise_t Request_mach_exception_raise;
__Request__mach_exception_raise_state_t Request_mach_exception_raise_state;
__Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity;
};
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
/* typedefs for all replies */
#ifndef __Reply__mach_exc_subsystem__defined
#define __Reply__mach_exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} __Reply__mach_exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[144];
} __Reply__mach_exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[144];
} __Reply__mach_exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Reply__mach_exc_subsystem__defined */
/* union of all replies */
#ifndef __ReplyUnion__mach_exc_subsystem__defined
#define __ReplyUnion__mach_exc_subsystem__defined
union __ReplyUnion__mach_exc_subsystem {
__Reply__mach_exception_raise_t Reply_mach_exception_raise;
__Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state;
__Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity;
};
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
#ifndef subsystem_to_name_map_mach_exc
#define subsystem_to_name_map_mach_exc \
{"mach_exception_raise_OVR", 2405}, {"mach_exception_raise_state_OVR", 2406}, { \
"mach_exception_raise_state_identity_OVR", 2407 \
}
#endif
#ifdef __AfterMigUserHeader
__AfterMigUserHeader
#endif /* __AfterMigUserHeader */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
boolean_t mach_exc_server_OVR(mach_msg_header_t * InHeadP, mach_msg_header_t * OutHeadP);
#if defined(__cplusplus)
} // extern"C"
#endif
#endif /* _mach_exc_user_ */

View File

@ -0,0 +1,523 @@
<?xml version="1.0"?>
<instrumentationManifest xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd" xmlns="http://schemas.microsoft.com/win/2004/08/events" xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:trace="http://schemas.microsoft.com/win/2004/08/events/trace">
<instrumentation>
<events>
<provider name="OVR-SDK-LibOVR" guid="{553787FC-D3D7-4F5E-ACB2-1597C7209B3C}" symbol="LibOVRProvider" resourceFileName="res" messageFileName="msg">
<events>
<event symbol="Call" value="0" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Function" opcode="Call" template="FunctionWaypoint" message="$(string.OVR-SDK-LibOVR.event.0.message)"></event>
<event symbol="Return" value="1" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Function" opcode="Return" template="FunctionWaypoint" message="$(string.OVR-SDK-LibOVR.event.1.message)"></event>
<event symbol="Waypoint" value="2" version="0" channel="LibOVR/Debug" level="win:Informational" task="Function" opcode="Waypoint" template="FunctionWaypoint" message="$(string.OVR-SDK-LibOVR.event.2.message)"></event>
<event symbol="DistortionBegin" value="4" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="Begin" template="Distortion" message="$(string.OVR-SDK-LibOVR.event.4.message)"></event>
<event symbol="DistortionWaitGPU" value="5" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="WaitGPU" template="Distortion" message="$(string.OVR-SDK-LibOVR.event.5.message)"></event>
<event symbol="DistortionPresent" value="6" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="Present" template="Distortion" message="$(string.OVR-SDK-LibOVR.event.6.message)"></event>
<event symbol="DistortionEnd" value="7" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="End" template="Distortion" message="$(string.OVR-SDK-LibOVR.event.7.message)"></event>
<event symbol="HmdDesc_v0" value="8" version="0" channel="LibOVR/Analytic" level="win:Informational" task="HmdInfo" opcode="HmdDesc" template="HmdDesc_v0" message="$(string.OVR-SDK-LibOVR.event.8.message)"></event>
<event symbol="HmdDesc" value="8" version="1" channel="LibOVR/Analytic" level="win:Informational" task="HmdInfo" opcode="HmdDesc" template="HmdDesc" message="$(string.OVR-SDK-LibOVR.event.8.message)"></event>
<event symbol="CameraFrameReceived_v0" value="9" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Receive" template="CameraFrameData_v0" message="$(string.OVR-SDK-LibOVR.event.9.message)"></event>
<event symbol="CameraFrameReceived" value="9" version="1" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Receive" template="CameraFrameData" message="$(string.OVR-SDK-LibOVR.event.9.message)"></event>
<event symbol="CameraBeginProcessing_v0" value="10" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Begin" template="CameraFrameData_v0" message="$(string.OVR-SDK-LibOVR.event.10.message)"></event>
<event symbol="CameraBeginProcessing" value="10" version="1" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Begin" template="CameraFrameData" message="$(string.OVR-SDK-LibOVR.event.10.message)"></event>
<event symbol="CameraFrameRequest" value="11" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Request" template="CameraFrameRequest" message="$(string.OVR-SDK-LibOVR.event.11.message)"></event>
<event symbol="CameraEndProcessing_v0" value="12" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="End" template="CameraFrameData_v0" message="$(string.OVR-SDK-LibOVR.event.12.message)"></event>
<event symbol="CameraEndProcessing" value="12" version="1" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="End" template="CameraFrameData" message="$(string.OVR-SDK-LibOVR.event.12.message)"></event>
<event symbol="CameraSkippedFrames_v0" value="13" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Waypoint" template="CameraFrameRequest" message="$(string.OVR-SDK-LibOVR.event.13.message)"></event>
<event symbol="CameraSkippedFrames" value="13" version="1" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Waypoint" template="CameraGetFrame" message="$(string.OVR-SDK-LibOVR.event.13.message)"></event>
<event symbol="JSONChunk" value="14" version="0" channel="LibOVR/Analytic" level="win:Informational" task="HmdInfo" opcode="Waypoint" template="JSONChunk" message="$(string.OVR-SDK-LibOVR.event.14.message)"></event>
<event symbol="LogDebugMessage" value="15" version="0" channel="LibOVR/Debug" level="win:Verbose" task="Log" opcode="Waypoint" template="Log" message="$(string.OVR-SDK-LibOVR.event.15.message)"></event>
<event symbol="LogInfoMessage" value="16" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="Log" message="$(string.OVR-SDK-LibOVR.event.16.message)"></event>
<event symbol="LogErrorMessage" value="17" version="0" channel="LibOVR/Error" level="win:Error" task="Log" opcode="Waypoint" template="Log" message="$(string.OVR-SDK-LibOVR.event.17.message)"></event>
<event symbol="HmdTrackingState" value="18" version="0" channel="LibOVR/Analytic" level="win:Informational" task="HmdInfo" opcode="Waypoint" template="HmdTrackingState" message="$(string.OVR-SDK-LibOVR.event.18.message)"></event>
<event symbol="CameraBlobs_v0" value="19" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Waypoint" template="CameraBlobs_v0" message="$(string.OVR-SDK-LibOVR.event.19.message)"></event>
<event symbol="CameraBlobs" value="19" version="1" channel="LibOVR/Analytic" level="win:Informational" task="Camera" opcode="Waypoint" template="CameraBlobs" message="$(string.OVR-SDK-LibOVR.event.19.message)"></event>
<event symbol="PoseLatchCPUWrite" value="30" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="Begin" template="PoseLLCPUWrite" message="$(string.OVR-SDK-LibOVR.event.30.message)"></event>
<event symbol="PoseLatchGPULatchReadback" value="31" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="End" template="PoseLLGPUReadback" message="$(string.OVR-SDK-LibOVR.event.31.message)"></event>
<event symbol="QueueAheadDelayBegin" value="32" version="0" channel="LibOVR/Analytic" level="win:Informational" task="SubmitFrame" opcode="Begin" template="QueueAhead" message="$(string.OVR-SDK-LibOVR.event.32.message)"></event>
<event symbol="QueueAheadDelayEnd" value="33" version="0" channel="LibOVR/Analytic" level="win:Informational" task="SubmitFrame" opcode="End" template="QueueAhead" message="$(string.OVR-SDK-LibOVR.event.33.message)"></event>
<event symbol="HmdDisplay" value="34" version="0" channel="LibOVR/Analytic" level="win:Informational" task="HmdInfo" opcode="HmdDesc" template="HmdDisplay" message="$(string.OVR-SDK-LibOVR.event.34.message)"></event>
<event symbol="PhaseSyncBegin" value="35" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="Begin" template="PhaseSyncBegin" message="$(string.OVR-SDK-LibOVR.event.35.message)"></event>
<event symbol="PhaseSyncEnd" value="36" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="End" template="PhaseSyncEnd" message="$(string.OVR-SDK-LibOVR.event.36.message)"></event>
<event symbol="VSync" value="37" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Distort" opcode="Present" template="RecordedVSync" message="$(string.OVR-SDK-LibOVR.event.37.message)"></event>
<event symbol="AppCompositorFocus" value="38" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="AppEvent" message="$(string.OVR-SDK-LibOVR.event.38.message)"></event>
<event symbol="AppConnect" value="39" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="AppEvent" message="$(string.OVR-SDK-LibOVR.event.39.message)"></event>
<event symbol="AppDisconnect" value="40" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="AppEvent" message="$(string.OVR-SDK-LibOVR.event.40.message)"></event>
<event symbol="AppNoOp" value="41" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="AppEvent" message="$(string.OVR-SDK-LibOVR.event.41.message)"></event>
<event symbol="PosePrediction" value="42" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Sensor" opcode="Begin" template="PosePrediction" message="$(string.OVR-SDK-LibOVR.event.42.message)"></event>
<event symbol="LatencyTiming" value="43" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="LatencyTiming" message="$(string.OVR-SDK-LibOVR.event.43.message)"></event>
<event symbol="EndFrameAppTiming" value="44" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Waypoint" template="EndFrameAppTiming" message="$(string.OVR-SDK-LibOVR.event.44.message)"></event>
<event symbol="HardwareInfo" value="45" version="0" channel="LibOVR/Analytic" level="win:Informational" task="HmdInfo" opcode="Waypoint" template="HardwareInfoTemplate" message="$(string.OVR-SDK-LibOVR.event.45.message)"></event>
<event symbol="VirtualDisplayPacketTrace" value="46" version="0" channel="LibOVR/Analytic" level="win:Informational" task="VirtualDisplay" opcode="Waypoint" template="VirtualDisplayPacketTemplate" message="$(string.OVR-SDK-LibOVR.event.46.message)"></event>
<event symbol="ClientFrameMissed" value="47" version="0" channel="LibOVR/Analytic" level="win:Informational" task="VirtualDisplay" opcode="Waypoint" template="ClientMissedFrame" message="$(string.OVR-SDK-LibOVR.event.47.message)"></event>
<event symbol="CompositionBegin" value="48" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="Begin" template="CompositionBegin" message="$(string.OVR-SDK-LibOVR.event.48.message)"></event>
<event symbol="CompositionEnd" value="49" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="End" message="$(string.OVR-SDK-LibOVR.event.49.message)"></event>
<event symbol="RenderPacketTrace" value="50" version="0" channel="LibOVR/Analytic" level="win:Informational" task="VirtualDisplay" opcode="Waypoint" template="RenderPacketTemplate" message="$(string.OVR-SDK-LibOVR.event.50.message)"></event>
<event symbol="EndFrameOrigAppTiming" value="51" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Log" opcode="Begin" template="EndFrameAppTiming" message="$(string.OVR-SDK-LibOVR.event.51.message)"></event>
<event symbol="DistortionEndToEndTiming" value="52" version="0" channel="LibOVR/Analytic" level="win:LogAlways" task="CompositorRunLoop" opcode="Call" template="DistortionEndToEndTiming" message="$(string.OVR-SDK-LibOVR.event.52.message)"></event>
<event symbol="CompositionEndSpinWait" value="53" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="End" message="$(string.OVR-SDK-LibOVR.event.53.message)"></event>
<event symbol="CompositionFlushingToGPU" value="54" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="Waypoint" message="$(string.OVR-SDK-LibOVR.event.54.message)"></event>
<event symbol="PhaseSyncGPUCompleted" value="55" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="Waypoint" template="PhaseSyncAppGPUEndTime" message="$(string.OVR-SDK-LibOVR.event.55.message)"></event>
<event symbol="CompositionMissedCompositorFrame" value="56" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="Waypoint" message="$(string.OVR-SDK-LibOVR.event.56.message)"></event>
<event symbol="CompositionGPUStartTime" value="57" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="Waypoint" template="CompositionGPUBeginTime" message="$(string.OVR-SDK-LibOVR.event.57.message)"></event>
<event symbol="NotificationBegin" value="58" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Notification" opcode="Waypoint" template="NotificationBegin" message="$(string.OVR-SDK-LibOVR.event.58.message)"></event>
<event symbol="NotificationEnd" value="59" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Notification" opcode="Waypoint" template="NotificationEnd" message="$(string.OVR-SDK-LibOVR.event.59.message)"></event>
<event symbol="NotificationCompSubmit" value="60" version="0" channel="LibOVR/Analytic" level="win:Informational" task="Notification" opcode="Waypoint" template="NotificationCompSubmit" message="$(string.OVR-SDK-LibOVR.event.60.message)"></event>
<event symbol="MotionEstimationCostStats" value="61" version="0" channel="LibOVR/Analytic" level="win:Informational" task="CompositorRunLoop" opcode="Waypoint" template="MotionEstimationCostStats" message="$(string.OVR-SDK-LibOVR.event.61.message)"></event>
<event symbol="PhaseSyncWaitToBeginFrame" value="62" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="Waypoint" template="PhaseSyncWaitToBeginFrame" message="$(string.OVR-SDK-LibOVR.event.63.message)"></event>
<event symbol="PhaseSyncBeginFrame" value="63" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="Waypoint" template="PhaseSyncBeginFrame" message="$(string.OVR-SDK-LibOVR.event.64.message)"></event>
<event symbol="PhaseSyncEndFrame" value="64" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="Waypoint" template="PhaseSyncEndFrame" message="$(string.OVR-SDK-LibOVR.event.65.message)"></event>
<event symbol="PhaseSyncCompleteFrame" value="65" version="0" channel="LibOVR/Analytic" level="win:Informational" task="PhaseSync" opcode="Waypoint" template="PhaseSyncCompleteFrame" message="$(string.OVR-SDK-LibOVR.event.66.message)"></event>
</events>
<levels></levels>
<tasks>
<task name="Function" symbol="FN_TRACE" value="1" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.FN_TRACE.message)"></task>
<task name="Distort" symbol="DIS_TRACE" value="2" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.DIS_TRACE.message)"></task>
<task name="HmdInfo" symbol="HMD_TRACE" value="3" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.HMD_TRACE.message)"></task>
<task name="Camera" symbol="CAMERA_TRACE" value="4" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.CAMERA_TRACE.message)"></task>
<task name="Log" symbol="LOG_TRACE" value="5" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.LOG_TRACE.message)"></task>
<task name="SubmitFrame" symbol="SUBMITFRAME_TRACE" value="6" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.SUBMITFRAME_TRACE.message)"></task>
<task name="PhaseSync" symbol="PHASESYNC_TRACE" value="7" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.PHASESYNC_TRACE.message)"></task>
<task name="Sensor" symbol="SENSOR_TRACE" value="8" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.SENSOR_TRACE.message)"></task>
<task name="VirtualDisplay" symbol="VIRTUALDISPLAY_TRACE" value="9" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.VIRTUALDISPLAY_TRACE.message)"></task>
<task name="CompositorRunLoop" symbol="COMPOSITOR_RUNLOOP_TRACE" value="10" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.Compositor_RunLoop.message)"></task>
<task name="Notification" symbol="NOTIFICATION_TRACE" value="11" eventGUID="{00000000-0000-0000-0000-000000000000}" message="$(string.OVR-SDK-LibOVR.task.NOTIFICATION_TRACE.message)"></task>
</tasks>
<opcodes>
<opcode name="Call" symbol="FN_CALL" value="10" message="$(string.OVR-SDK-LibOVR.opcode.FN_CALL.message)"></opcode>
<opcode name="Return" symbol="FN_RETURN" value="11" message="$(string.OVR-SDK-LibOVR.opcode.FN_RETURN.message)"></opcode>
<opcode name="Waypoint" symbol="FN_WAYPOINT" value="12" message="$(string.OVR-SDK-LibOVR.opcode.FN_WAYPOINT.message)"></opcode>
<opcode name="Begin" symbol="DIS_BEGIN" value="13" message="$(string.OVR-SDK-LibOVR.opcode.DIS_BEGIN.message)"></opcode>
<opcode name="WaitGPU" symbol="DIS_WAITGPU" value="14" message="$(string.OVR-SDK-LibOVR.opcode.DIS_WAITGPU.message)"></opcode>
<opcode name="Present" symbol="DIS_PRESENT" value="15" message="$(string.OVR-SDK-LibOVR.opcode.DIS_PRESENT.message)"></opcode>
<opcode name="End" symbol="DIS_END" value="16" message="$(string.OVR-SDK-LibOVR.opcode.DIS_END.message)"></opcode>
<opcode name="HmdDesc" symbol="HMD_DESC" value="17" message="$(string.OVR-SDK-LibOVR.opcode.HMD_DESC.message)"></opcode>
<opcode name="Receive" symbol="CAM_RECEIVE" value="18" message="$(string.OVR-SDK-LibOVR.opcode.CAM_RECEIVE.message)"></opcode>
<opcode name="Request" symbol="CAM_REQUEST" value="19" message="$(string.OVR-SDK-LibOVR.opcode.CAM_REQUEST.message)"></opcode>
</opcodes>
<channels>
<channel name="LibOVR/Debug" chid="LibOVR/Debug" symbol="DEBUG_CHANNEL" type="Debug" enabled="false"></channel>
<channel name="LibOVR/Analytic" chid="LibOVR/Analytic" symbol="ANALYTIC_CHANNEL" type="Analytic" enabled="false"></channel>
<channel name="LibOVR/Error" chid="LibOVR/Error" symbol="ERROR_CHANNEL" type="Operational" enabled="false"></channel>
</channels>
<templates>
<template tid="FunctionWaypoint">
<data name="Name" inType="win:UnicodeString" outType="xs:string"></data>
<data name="Line" inType="win:Int32" outType="xs:int"></data>
<data name="FrameID" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="Distortion">
<data name="VidPnTargetId" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="FrameID" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="HmdDesc_v0">
<data name="Type" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="VendorId" inType="win:Int16" outType="xs:short"></data>
<data name="ProductId" inType="win:Int16" outType="xs:short"></data>
<data name="SerialNumber" inType="win:AnsiString" outType="xs:string" length="24"></data>
<data name="FirmwareMajor" inType="win:Int16" outType="xs:short"></data>
<data name="FirmwareMinor" inType="win:Int16" outType="xs:short"></data>
<data name="HmdCaps" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="TrackingCaps" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="DistortionCaps" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ResolutionWidth" inType="win:Int32" outType="xs:int"></data>
<data name="ResolutionHeight" inType="win:Int32" outType="xs:int"></data>
</template>
<template tid="HmdDesc">
<data name="Type" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="VendorId" inType="win:Int16" outType="xs:short"></data>
<data name="ProductId" inType="win:Int16" outType="xs:short"></data>
<data name="SerialNumber" inType="win:AnsiString" outType="xs:string" length="24"></data>
<data name="FirmwareMajor" inType="win:Int16" outType="xs:short"></data>
<data name="FirmwareMinor" inType="win:Int16" outType="xs:short"></data>
<data name="HmdCaps" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="TrackingCaps" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ResolutionWidth" inType="win:Int32" outType="xs:int"></data>
<data name="ResolutionHeight" inType="win:Int32" outType="xs:int"></data>
</template>
<template tid="CameraFrameData_v0">
<data name="FrameRate" inType="win:Float" outType="xs:float"></data>
<data name="FrameNumber" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ArrivalTimeSeconds" inType="win:Double" outType="xs:double"></data>
<data name="CaptureTime" inType="win:Double" outType="xs:double"></data>
<data name="LostFrames" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="CameraFrameData">
<data name="Camera" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="FrameNumber" inType="win:Int32" outType="xs:int"></data>
<data name="HmdFrameNumber" inType="win:Int32" outType="xs:int"></data>
<data name="ArrivalTime" inType="win:Double" outType="xs:double"></data>
<data name="CaptureTime" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="CameraFrameRequest">
<data name="RequestNumber" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="FrameCounter" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="LastFrameNumber" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="CameraGetFrame">
<data name="Camera" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="LastFrameNumber" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="JSONChunk">
<data name="Name" inType="win:UnicodeString" outType="xs:string"></data>
<data name="TotalChunks" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ChunkSequence" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="TotalSize" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ChunkSize" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ChunkOffset" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="Chunk" inType="win:Binary" outType="xs:hexBinary" length="ChunkSize"></data>
</template>
<template tid="Log">
<data name="Message" inType="win:AnsiString" outType="xs:string"></data>
</template>
<template tid="HmdTrackingState">
<data name="TimeInSeconds" inType="win:Double" outType="xs:double"></data>
<data name="HeadPoseQuat" inType="win:Float" outType="xs:float" count="4"></data>
<data name="HeadPoseTranslation" inType="win:Float" outType="xs:float" count="3"></data>
<data name="HeadAngularVelocity" inType="win:Float" outType="xs:float" count="3"></data>
<data name="HeadLinearVelocity" inType="win:Float" outType="xs:float" count="3"></data>
<data name="CameraPoseQuat" inType="win:Float" outType="xs:float" count="4"></data>
<data name="CameraPoseTranslation" inType="win:Float" outType="xs:float" count="3"></data>
<data name="StatusFlags" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="CameraBlobs_v0">
<data name="BlobCount" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="PositionX" inType="win:Double" outType="xs:double" count="BlobCount"></data>
<data name="PositionY" inType="win:Double" outType="xs:double" count="BlobCount"></data>
<data name="Size" inType="win:Int32" outType="xs:int" count="BlobCount"></data>
</template>
<template tid="CameraBlobs">
<data name="Camera" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="FrameNumber" inType="win:Int32" outType="xs:int"></data>
<data name="ArrivalTime" inType="win:Double" outType="xs:double"></data>
<data name="Width" inType="win:Int32" outType="xs:int"></data>
<data name="Height" inType="win:Int32" outType="xs:int"></data>
<data name="BlobCount" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="PositionX" inType="win:Double" outType="xs:double" count="BlobCount"></data>
<data name="PositionY" inType="win:Double" outType="xs:double" count="BlobCount"></data>
<data name="Size" inType="win:Int32" outType="xs:int" count="BlobCount"></data>
</template>
<template tid="PoseLLCPUWrite">
<data name="Sequence" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="Layer" inType="win:Int32" outType="xs:int"></data>
<data name="MotionSensorTime" inType="win:Float" outType="xs:float"></data>
<data name="PredictedScanlineFirst" inType="win:Float" outType="xs:float"></data>
<data name="PredictedScanlineLast" inType="win:Float" outType="xs:float"></data>
<data name="TimeToScanlineFirst" inType="win:Float" outType="xs:float"></data>
<data name="TimeToScanlineLast" inType="win:Float" outType="xs:float"></data>
<data name="StartPosition" inType="win:Float" outType="xs:float" count="3"></data>
<data name="EndPosition" inType="win:Float" outType="xs:float" count="3"></data>
<data name="StartQuat" inType="win:Float" outType="xs:float" count="4"></data>
<data name="EndQuat" inType="win:Float" outType="xs:float" count="4"></data>
</template>
<template tid="QueueAhead">
<data name="QueueAheadSeconds" inType="win:Float" outType="xs:float"></data>
</template>
<template tid="HmdDisplay">
<data name="Extended" inType="win:Boolean" outType="xs:boolean"></data>
<data name="DeviceTypeGuess" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="EdidVendorId" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="EdidModelNumber" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="DisplayID" inType="win:AnsiString" outType="xs:string"></data>
<data name="ModelName" inType="win:AnsiString" outType="xs:string"></data>
<data name="EdidSerialNumber" inType="win:AnsiString" outType="xs:string"></data>
<data name="LogicalResolutionInPixels_w" inType="win:Int32" outType="xs:int"></data>
<data name="LogicalResolutionInPixels_h" inType="win:Int32" outType="xs:int"></data>
<data name="NativeResolutionInPixels_w" inType="win:Int32" outType="xs:int"></data>
<data name="NativeResolutionInPixels_h" inType="win:Int32" outType="xs:int"></data>
<data name="DesktopDisplayOffset_x" inType="win:Int32" outType="xs:int"></data>
<data name="DesktopDisplayOffset_y" inType="win:Int32" outType="xs:int"></data>
<data name="DeviceNumber" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="Rotation" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ApplicationExclusive" inType="win:Boolean" outType="xs:boolean"></data>
</template>
<template tid="PoseLLGPUReadback">
<data name="Sequence" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="Layer" inType="win:Int32" outType="xs:int"></data>
<data name="MotionSensorTime" inType="win:Float" outType="xs:float"></data>
<data name="PredictedScanlineFirst" inType="win:Float" outType="xs:float"></data>
<data name="PredictedScanlineLast" inType="win:Float" outType="xs:float"></data>
<data name="TimeToScanlineFirst" inType="win:Float" outType="xs:float"></data>
<data name="TimeToScanlineLast" inType="win:Float" outType="xs:float"></data>
</template>
<template tid="PhaseSyncBegin">
<data name="LastCompositeTime" inType="win:Double" outType="xs:double"></data>
<data name="LastVSyncTime" inType="win:Double" outType="xs:double"></data>
<data name="FrameIntervalMS" inType="win:Float" outType="xs:float"></data>
<data name="SemaphoreMS" inType="win:Float" outType="xs:float"></data>
<data name="SleepMS" inType="win:Float" outType="xs:float"></data>
<data name="SpinMS" inType="win:Float" outType="xs:float"></data>
<data name="PhaseSyncMS" inType="win:Float" outType="xs:float"></data>
<data name="BeginFrameTime" inType="win:Double" outType="xs:double"></data>
<data name="TargetCompletionTime" inType="win:Double" outType="xs:double"></data>
<data name="TargetCompositeTime" inType="win:Double" outType="xs:double"></data>
<data name="TargetVSyncTime" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="PhaseSyncEnd">
<data name="BeginFrameTime" inType="win:Double" outType="xs:double"></data>
<data name="EndFrameTime" inType="win:Double" outType="xs:double"></data>
<data name="CompletionTime" inType="win:Double" outType="xs:double"></data>
<data name="CompositeTime" inType="win:Double" outType="xs:double"></data>
<data name="VSyncTime" inType="win:Double" outType="xs:double"></data>
<data name="FrameTimeMS" inType="win:Float" outType="xs:float"></data>
<data name="FrameTimeCpuMS" inType="win:Float" outType="xs:float"></data>
<data name="FrameTimeVarianceMS" inType="win:Float" outType="xs:float"></data>
<data name="QueueAhead" inType="win:Float" outType="xs:float"></data>
<data name="FramesMissed" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="AvgFrameTimeMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgFrameTimeCpuMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgFrameTimeVarianceMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgQueueAhead" inType="win:Float" outType="xs:float"></data>
<data name="SyncFrameTimeMS" inType="win:Float" outType="xs:float"></data>
<data name="SyncQueueAhead" inType="win:Float" outType="xs:float"></data>
<data name="SyncFramesMissed" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="PhaseSyncMS" inType="win:Float" outType="xs:float"></data>
</template>
<template tid="RecordedVSync">
<data name="Time" inType="win:Double" outType="xs:double"></data>
<data name="FrameIndex" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="TwGpuEndTime" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="AppEvent">
<data name="ProcessID" inType="win:UInt64" outType="xs:unsignedLong"></data>
</template>
<template tid="PosePrediction">
<data name="OriginalPosition" inType="win:Double" outType="xs:double" count="3"></data>
<data name="OriginalOrientation" inType="win:Double" outType="xs:double" count="4"></data>
<data name="PredictedPosition" inType="win:Double" outType="xs:double" count="3"></data>
<data name="PredictedOrientation" inType="win:Double" outType="xs:double" count="4"></data>
<data name="PredictionTimeDeltaSeconds" inType="win:Double" outType="xs:double"></data>
<data name="TimeInSeconds" inType="win:Double" outType="xs:double"></data>
<data name="id" inType="win:AnsiString" outType="xs:string"></data>
</template>
<template tid="LatencyTiming">
<data name="RenderCpuBegin" inType="win:Double" outType="xs:double"></data>
<data name="RenderCpuEnd" inType="win:Double" outType="xs:double"></data>
<data name="RenderImu" inType="win:Double" outType="xs:double"></data>
<data name="TimewarpCpu" inType="win:Double" outType="xs:double"></data>
<data name="TimewarpLatched" inType="win:Double" outType="xs:double"></data>
<data name="TimewarpGpuEnd" inType="win:Double" outType="xs:double"></data>
<data name="PostPresent" inType="win:Double" outType="xs:double"></data>
<data name="ErrorRender" inType="win:Double" outType="xs:double"></data>
<data name="ErrorTimewarp" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="EndFrameAppTiming">
<data name="FrameIndex" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="RenderImuTime" inType="win:Double" outType="xs:double"></data>
<data name="ScanoutStartTime" inType="win:Double" outType="xs:double"></data>
<data name="GpuRenderDuration" inType="win:Double" outType="xs:double"></data>
<data name="BeginRenderingTime" inType="win:Double" outType="xs:double"></data>
<data name="EndRenderingTime" inType="win:Double" outType="xs:double"></data>
<data name="QueueAheadSeconds" inType="win:Double" outType="xs:double"></data>
<data name="RenderCount" inType="win:Int32" outType="xs:int"></data>
</template>
<template tid="HardwareInfoTemplate">
<data name="RequestedBits" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="CollectedBits" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ImuTemp" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="StmTemp" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="NrfTemp" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="VBusVoltage" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="IAD" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="Proximity" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="PanelOnTime" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="UseRolling" inType="win:Boolean" outType="xs:boolean"></data>
<data name="HighBrightness" inType="win:Boolean" outType="xs:boolean"></data>
<data name="DP" inType="win:Boolean" outType="xs:boolean"></data>
<data name="SelfRefresh" inType="win:Boolean" outType="xs:boolean"></data>
<data name="Persistence" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="LightingOffset" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="PixelSettle" inType="win:UInt16" outType="xs:unsignedShort"></data>
<data name="TotalRows" inType="win:UInt16" outType="xs:unsignedShort"></data>
</template>
<template tid="VirtualDisplayPacketTemplate">
<data name="PacketType" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="Stage" inType="win:Int32" outType="xs:int"></data>
<data name="SubmittingProcessID" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="ActiveProcessID" inType="win:UInt64" outType="xs:unsignedLong"></data>
</template>
<template tid="ClientMissedFrame">
<data name="FrameIndex" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="ProcessID" inType="win:UInt64" outType="xs:unsignedLong"></data>
</template>
<template tid="RenderPacketTemplate">
<data name="Stage" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="ClientPID" inType="win:UInt64" outType="xs:unsignedLong"></data>
</template>
<template tid="DistortionEndToEndTiming">
<data name="ElapsedMs" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="CompositionBegin">
<data name="ExpectedCPUStartTimeInSeconds" inType="win:Double" outType="xs:double"></data>
<data name="ActualCPUStartTimeInSeconds" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="CompositionGPUBeginTime">
<data name="DistortionBeginTimeInSeconds" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="PhaseSyncAppGPUEndTime">
<data name="AppGPUEndTimeSeconds" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="NotificationEnd">
<data name="AppFrameIndex" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="CpuBeginToGpuEndSeconds" inType="win:Double" outType="xs:double"></data>
<data name="CpuBeginSeconds" inType="win:Double" outType="xs:double"></data>
<data name="GpuEndSeconds" inType="win:Double" outType="xs:double"></data>
<data name="SleepTimeMilliseconds" inType="win:UInt32" outType="xs:unsignedInt"></data>
</template>
<template tid="NotificationBegin">
<data name="AppFrameIndex" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="CpuBeginToGpuEndSeconds" inType="win:Double" outType="xs:double"></data>
<data name="CompositeTimeSeconds" inType="win:Double" outType="xs:double"></data>
<data name="VSyncTimeSeconds" inType="win:Double" outType="xs:double"></data>
<data name="CompositeDeltaSeconds" inType="win:Double" outType="xs:double"></data>
<data name="VSyncDeltaSeconds" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="NotificationCompSubmit">
<data name="ShouldBeVisible" inType="win:Boolean" outType="xs:boolean"></data>
<data name="DisabledLayer" inType="win:Boolean" outType="xs:boolean"></data>
<data name="FrameIndex" inType="win:UInt64" outType="xs:unsignedLong"></data>
</template>
<template tid="MotionEstimationCostStats">
<data name="Count" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="Average" inType="win:UInt32" outType="xs:unsignedInt"></data>
<data name="Log2Histogram" inType="win:UInt32" outType="xs:unsignedInt" count="33"></data>
</template>
<template tid="PhaseSyncWaitToBeginFrame">
<data name="Frame" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="BeginWaitTime" inType="win:Double" outType="xs:double"></data>
<data name="SemaphoreMS" inType="win:Float" outType="xs:float"></data>
<data name="SleepMS" inType="win:Float" outType="xs:float"></data>
<data name="SpinMS" inType="win:Float" outType="xs:float"></data>
<data name="EndWaitTime" inType="win:Double" outType="xs:double"></data>
<data name="TargetCompletionTime" inType="win:Double" outType="xs:double"></data>
<data name="TargetCompositeTime" inType="win:Double" outType="xs:double"></data>
<data name="TargetVSyncTime" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="PhaseSyncBeginFrame">
<data name="Frame" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="BeginFrameTime" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="PhaseSyncEndFrame">
<data name="Frame" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="EndFrameTime" inType="win:Double" outType="xs:double"></data>
</template>
<template tid="PhaseSyncCompleteFrame">
<data name="Frame" inType="win:UInt64" outType="xs:unsignedLong"></data>
<data name="CompletionTime" inType="win:Double" outType="xs:double"></data>
<data name="CompositeTime" inType="win:Double" outType="xs:double"></data>
<data name="VSyncTime" inType="win:Double" outType="xs:double"></data>
<data name="FrameTimeMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgFrameTimeMS" inType="win:Float" outType="xs:float"></data>
<data name="FrameTimeCpuMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgFrameTimeCpuMS" inType="win:Float" outType="xs:float"></data>
<data name="FrameTimeGpuMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgFrameTimeGpuMS" inType="win:Float" outType="xs:float"></data>
<data name="FrameVarianceMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgFrameVarianceMS" inType="win:Float" outType="xs:float"></data>
<data name="QueueAheadMS" inType="win:Float" outType="xs:float"></data>
<data name="AvgQueueAheadMS" inType="win:Float" outType="xs:float"></data>
<data name="AdaptiveGpuPerformanceScale" inType="win:Float" outType="xs:float"></data>
<data name="AvgAdaptiveGpuPerformanceScale" inType="win:Float" outType="xs:float"></data>
<data name="PhaseSyncDelayMS" inType="win:Float" outType="xs:float"></data>
</template>
</templates>
</provider>
</events>
</instrumentation>
<localization>
<resources culture="en-US">
<stringTable>
<string id="opcode.Info" value="Info"></string>
<string id="level.Verbose" value="Verbose"></string>
<string id="level.LogAlways" value="Log Always"></string>
<string id="level.Informational" value="Information"></string>
<string id="level.Error" value="Error"></string>
<string id="OVR-SDK-LibOVR.task.VIRTUALDISPLAY_TRACE.message" value="VirtualDisplay"></string>
<string id="OVR-SDK-LibOVR.task.SUBMITFRAME_TRACE.message" value="SubmitFrame"></string>
<string id="OVR-SDK-LibOVR.task.SENSOR_TRACE.message" value="Sensor"></string>
<string id="OVR-SDK-LibOVR.task.PHASESYNC_TRACE.message" value="PhaseSync"></string>
<string id="OVR-SDK-LibOVR.task.NOTIFICATION_TRACE.message" value="Notification"></string>
<string id="OVR-SDK-LibOVR.task.LOG_TRACE.message" value="Log"></string>
<string id="OVR-SDK-LibOVR.task.HMD_TRACE.message" value="HmdInfo"></string>
<string id="OVR-SDK-LibOVR.task.FN_TRACE.message" value="Function"></string>
<string id="OVR-SDK-LibOVR.task.DIS_TRACE.message" value="Distort"></string>
<string id="OVR-SDK-LibOVR.task.Compositor_RunLoop.message" value="Compositor run loop (render thread) events."></string>
<string id="OVR-SDK-LibOVR.task.CAMERA_TRACE.message" value="Camera"></string>
<string id="OVR-SDK-LibOVR.opcode.HMD_DESC.message" value="Descriptor"></string>
<string id="OVR-SDK-LibOVR.opcode.FN_WAYPOINT.message" value="Waypoint"></string>
<string id="OVR-SDK-LibOVR.opcode.FN_RETURN.message" value="Return"></string>
<string id="OVR-SDK-LibOVR.opcode.FN_CALL.message" value="Call"></string>
<string id="OVR-SDK-LibOVR.opcode.DIS_WAITGPU.message" value="WaitGPU"></string>
<string id="OVR-SDK-LibOVR.opcode.DIS_PRESENT.message" value="Present"></string>
<string id="OVR-SDK-LibOVR.opcode.DIS_END.message" value="End"></string>
<string id="OVR-SDK-LibOVR.opcode.DIS_BEGIN.message" value="Begin"></string>
<string id="OVR-SDK-LibOVR.opcode.CAM_REQUEST.message" value="Request"></string>
<string id="OVR-SDK-LibOVR.opcode.CAM_RECEIVE.message" value="Receive"></string>
<string id="OVR-SDK-LibOVR.event.9.message" value="Received Frame %2"></string>
<string id="OVR-SDK-LibOVR.event.8.message" value="Hmd Descriptor for %4"></string>
<string id="OVR-SDK-LibOVR.event.7.message" value="End distortion rendering for %1 (frame %2)"></string>
<string id="OVR-SDK-LibOVR.event.66.message" value="CompleteFrame"></string>
<string id="OVR-SDK-LibOVR.event.65.message" value="EndFrame"></string>
<string id="OVR-SDK-LibOVR.event.64.message" value="BeginFrame"></string>
<string id="OVR-SDK-LibOVR.event.63.message" value="WaitToBeginFrame"></string>
<string id="OVR-SDK-LibOVR.event.62.message" value="BeginWait"></string>
<string id="OVR-SDK-LibOVR.event.61.message" value="Motion Estimation Cost Statistics"></string>
<string id="OVR-SDK-LibOVR.event.60.message" value="Submisison of notification to distortion"></string>
<string id="OVR-SDK-LibOVR.event.6.message" value="Present distortion for %1 (frame %2)"></string>
<string id="OVR-SDK-LibOVR.event.59.message" value="Notification end frame"></string>
<string id="OVR-SDK-LibOVR.event.58.message" value="Notification begin frame"></string>
<string id="OVR-SDK-LibOVR.event.57.message" value="Time GPU started work (after preemption)"></string>
<string id="OVR-SDK-LibOVR.event.56.message" value="Compositor missed frame (glitched)"></string>
<string id="OVR-SDK-LibOVR.event.55.message" value="App GPU work completed."></string>
<string id="OVR-SDK-LibOVR.event.54.message" value="Flushing ATW work to GPU"></string>
<string id="OVR-SDK-LibOVR.event.53.message" value="End of spinwait"></string>
<string id="OVR-SDK-LibOVR.event.52.message" value="Distortion end to end timing %1s"></string>
<string id="OVR-SDK-LibOVR.event.51.message" value="App EndFrame %1"></string>
<string id="OVR-SDK-LibOVR.event.50.message" value="RenderPacket stage %1 client pid %2"></string>
<string id="OVR-SDK-LibOVR.event.5.message" value="Wait for distortion renderer GPU for %1 (frame %2)"></string>
<string id="OVR-SDK-LibOVR.event.49.message" value="End ATW Composition"></string>
<string id="OVR-SDK-LibOVR.event.48.message" value="Begin ATW Composition"></string>
<string id="OVR-SDK-LibOVR.event.47.message" value="Client Missed Frame %1"></string>
<string id="OVR-SDK-LibOVR.event.46.message" value="Virtual Display Packet %1"></string>
<string id="OVR-SDK-LibOVR.event.45.message" value="Hardware info"></string>
<string id="OVR-SDK-LibOVR.event.44.message" value="End frame app timing"></string>
<string id="OVR-SDK-LibOVR.event.43.message" value="Latency Timing"></string>
<string id="OVR-SDK-LibOVR.event.42.message" value="PosePrediction"></string>
<string id="OVR-SDK-LibOVR.event.41.message" value="Testing only. Should not see in production."></string>
<string id="OVR-SDK-LibOVR.event.40.message" value="Application Disconnected"></string>
<string id="OVR-SDK-LibOVR.event.4.message" value="Begin distortion rendering for %1 (frame %2)"></string>
<string id="OVR-SDK-LibOVR.event.39.message" value="Application Connected"></string>
<string id="OVR-SDK-LibOVR.event.38.message" value="Application compositor focus"></string>
<string id="OVR-SDK-LibOVR.event.37.message" value="VSync Service QPC"></string>
<string id="OVR-SDK-LibOVR.event.36.message" value="PhaseSyncEnd"></string>
<string id="OVR-SDK-LibOVR.event.35.message" value="PhaseSyncBegin"></string>
<string id="OVR-SDK-LibOVR.event.34.message" value="Hmd Display %4 %5"></string>
<string id="OVR-SDK-LibOVR.event.33.message" value="End of Queue Ahead frame delay"></string>
<string id="OVR-SDK-LibOVR.event.32.message" value="Beginning timing delay for QueueAhead"></string>
<string id="OVR-SDK-LibOVR.event.31.message" value="Pose latch GPU readback"></string>
<string id="OVR-SDK-LibOVR.event.30.message" value="Pose latch CPU write"></string>
<string id="OVR-SDK-LibOVR.event.29.message" value="Camera %1 Segment Blobs for %2"></string>
<string id="OVR-SDK-LibOVR.event.28.message" value="Camera %1 Get Frame %2"></string>
<string id="OVR-SDK-LibOVR.event.27.message" value="Camera %1 Pose Sensor Fusion for %2"></string>
<string id="OVR-SDK-LibOVR.event.26.message" value="Camera %1 Pose Reconstruction for %2"></string>
<string id="OVR-SDK-LibOVR.event.25.message" value="Synced Camera %1 Clock"></string>
<string id="OVR-SDK-LibOVR.event.24.message" value="Camera Pose Change %1"></string>
<string id="OVR-SDK-LibOVR.event.23.message" value="End Camera LED Matching %1 %2 %3 %4"></string>
<string id="OVR-SDK-LibOVR.event.22.message" value="Begin Camera LED Matching %1 %2 %3 %4"></string>
<string id="OVR-SDK-LibOVR.event.21.message" value="End Global Image Aquisition %2 %3"></string>
<string id="OVR-SDK-LibOVR.event.20.message" value="Begin Global Image Aquisition %1"></string>
<string id="OVR-SDK-LibOVR.event.2.message" value="Waypoint %1:%2 (frame %3)"></string>
<string id="OVR-SDK-LibOVR.event.19.message" value="Blobs %1"></string>
<string id="OVR-SDK-LibOVR.event.18.message" value="Tracking State"></string>
<string id="OVR-SDK-LibOVR.event.17.message" value="Error: %1"></string>
<string id="OVR-SDK-LibOVR.event.16.message" value="Info: %1"></string>
<string id="OVR-SDK-LibOVR.event.15.message" value="Debug: %1"></string>
<string id="OVR-SDK-LibOVR.event.14.message" value="JSON chunk %1 (%3 of %2) size %5"></string>
<string id="OVR-SDK-LibOVR.event.13.message" value="Camera %1 Skipped Frames %2"></string>
<string id="OVR-SDK-LibOVR.event.12.message" value="Finished Processing Frame %2"></string>
<string id="OVR-SDK-LibOVR.event.11.message" value="Request Frame %2"></string>
<string id="OVR-SDK-LibOVR.event.10.message" value="Begin Processing Frame %2"></string>
<string id="OVR-SDK-LibOVR.event.1.message" value="Return %1:%2 (frame %3)"></string>
<string id="OVR-SDK-LibOVR.event.0.message" value="Call %1:%2 (frame %3)"></string>
<string id="OVR-SDK-LibOVR.channel.ERROR_CHANNEL.message" value="Error"></string>
</stringTable>
</resources>
</localization>
</instrumentationManifest>

View File

@ -0,0 +1,3 @@
LANGUAGE 0x9,0x1
1 11 "LibOVREvents_MSG00001.bin"
1 WEVT_TEMPLATE "LibOVREventsTEMP.BIN"

View File

@ -0,0 +1,55 @@
#Setup
If you want stack walking to work on x64:
> reg add "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f
Add USERS Read & Execute privileges to the folder (or one of its parents) containing the LibOVREvents.man file:
> icacls . /grant BUILTIN\Users:(OI)(CI)(RX)
To install or reinstall the ETW manifest after building LibOVR run `install.cmd` as Administrator:
> install
Note: the install script will also attempt to install the manifests for the driver and runtime. Also note that the install
script installs the manifest from the newest version of LibOVR.dll, which might not be the version you are debugging in
Visual Studio (this will only matter if the two versions have specified different events). To be safe make sure your build
is up-to-date.
#Adding trace points
See [./Tracing.h] and the examples in [../OVR_CAPI.cpp].
The following macros can be used to trace call/return and progress through a function:
TraceCall(frameIndex)
TraceReturn(frameIndex)
TraceWaypoint(frameIndex)
Try to place the Call/Return instrumentation as close as possible to the function entry/exit points, and don't forget
to instrument all return paths.
Supply a frame index of 0 if a frame index is not applicable/available.
#Adding new trace events
Use the `ECManGen.exe` utility from the Windows 8.1 SDK to edit the `LibOVREvents.man` manifest.
See [http://msdn.microsoft.com/en-us/library/windows/desktop/dd996930%28v=vs.85%29.aspx]
The `F1` help is also useful.
#Rebuilding the ETW headers and resources
Use the `build.cmd` script to regenerate the `LibOVREvents.h`, `LibOVREvents.rc` and `LibOVREvents*.bin` files.
`clean.cmd` will remove all generated files.
Note that the outputs are checked into the repository so you'll need to `p4 edit` them first.
#Capturing ETW traces
See [../../../Tools/XPerf/README.md]
#Viewing ETW traces with GPUView
See [http://msdn.microsoft.com/en-us/library/windows/desktop/jj585574(v=vs.85).aspx]

View File

@ -0,0 +1,436 @@
/************************************************************************************
PublicHeader: n/a
Filename : Tracing.h
Content : Performance tracing
Created : December 4, 2014
Author : Ed Hutchins
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Tracing_h
#define OVR_Tracing_h
//-----------------------------------------------------------------------------------
// ***** OVR_ENABLE_ETW_TRACING definition (XXX default to on for windows builds?)
//
#ifdef OVR_OS_WIN32
#define OVR_ENABLE_ETW_TRACING
#endif
//-----------------------------------------------------------------------------------
// ***** Trace* definitions
//
#ifdef OVR_ENABLE_ETW_TRACING
#define TracingIsEnabled() (OVR_SDK_LibOVREnableBits[0] != 0)
#ifdef TRACE_STATE_CAPTURE_FUNC
// hook in our own state capture callback to record the state of all opened HMDs (supress unused
// parameter warnings with void() casts)
#define MCGEN_PRIVATE_ENABLE_CALLBACK_V2( \
SourceId, ControlCode, Level, MatchAnyKeyword, MatchAllKeywords, FilterData, CallbackContext) \
(void(SourceId), \
void(Level), \
void(MatchAnyKeyword), \
void(MatchAllKeywords), \
void(FilterData), \
void(CallbackContext), \
(((ControlCode) == EVENT_CONTROL_CODE_CAPTURE_STATE) ? (TRACE_STATE_CAPTURE_FUNC) : 0))
#endif
#if !defined(_In_reads_)
// get VS2010 working
#define _In_reads_(x)
#endif
#include "LibOVREvents.h"
// Register/Unregister the OVR_SDK_LibOVR provider with ETW
// (MCGEN_PRIVATE_ENABLE_CALLBACK_V2 hooks in our state capture)
#define TraceInit() \
do { \
ULONG status = EventRegisterOVR_SDK_LibOVR(); \
if (ERROR_SUCCESS != status) { \
LogError("[LibOVR] Failed to register ETW provider (%ul)", status); \
} \
} while (0)
#define TraceFini() EventUnregisterOVR_SDK_LibOVR()
// Trace function call and return for perf, and waypoints for debug
#define TraceCall(frameIndex) EventWriteCall(__FUNCTIONW__, __LINE__, (frameIndex))
#define TraceReturn(frameIndex) EventWriteReturn(__FUNCTIONW__, __LINE__, (frameIndex))
#define TraceWaypoint(frameIndex) EventWriteWaypoint(__FUNCTIONW__, __LINE__, (frameIndex))
// DistortionRenderer events
#define TraceDistortionBegin(id, frameIndex) EventWriteDistortionBegin((id), (frameIndex))
#define TraceDistortionWaitGPU(id, frameIndex) EventWriteDistortionWaitGPU((id), (frameIndex))
#define TraceDistortionPresent(id, frameIndex) EventWriteDistortionPresent((id), (frameIndex))
#define TraceDistortionEnd(id, frameIndex) EventWriteDistortionEnd((id), (frameIndex))
#define TraceDistortionEndToEndTiming(elapsedMs) EventWriteDistortionEndToEndTiming((elapsedMs))
// Tracking Camera events
#define _TraceCameraFrameData(fn, camIdx, img) \
fn((camIdx), \
(uint32_t)(img).FrameNumber, \
(img).HmdFrameNumber, \
(img).ArrivalTime, \
(img).CaptureTime)
#define TraceCameraFrameReceived(img) _TraceCameraFrameData(EventWriteCameraFrameReceived, 0, (img))
#define TraceCameraBeginProcessing(camIdx, img) \
_TraceCameraFrameData(EventWriteCameraBeginProcessing, (camIdx), (img))
#define TraceCameraEndProcessing(camIdx, img) \
_TraceCameraFrameData(EventWriteCameraEndProcessing, (camIdx), (img))
#define TraceCameraFrameRequest(requestNumber, frameCount, lastFrameNumber) \
EventWriteCameraFrameRequest(requestNumber, frameCount, lastFrameNumber)
#define TraceCameraSkippedFrames(camIdx, skippedFrameCount) \
EventWriteCameraSkippedFrames(camIdx, skippedFrameCount)
// Trace the interesting parts of an ovrHmdDesc structure
#define TraceHmdDesc(desc) \
EventWriteHmdDesc( \
(desc).Type, \
(desc).VendorId, \
(desc).ProductId, \
(desc).SerialNumber, \
(desc).FirmwareMajor, \
(desc).FirmwareMinor, \
(desc).AvailableHmdCaps, \
(desc).AvailableTrackingCaps, \
(desc).Resolution.w, \
(desc).Resolution.h)
#define TraceHmdDisplay(dpy) \
EventWriteHmdDisplay( \
(0), \
(0), \
(dpy).Edid.VendorID, \
(dpy).Edid.ModelNumber, \
(dpy).DisplayIdentifier.ToCStr(), \
(dpy).ModelName.ToCStr(), \
(dpy).EdidSerialNumber.ToCStr(), \
(dpy).LogicalResolutionInPixels.w, \
(dpy).LogicalResolutionInPixels.h, \
(dpy).NativeResolutionInPixels.w, \
(dpy).NativeResolutionInPixels.h, \
0, \
0, \
(dpy).DeviceNumber, \
(dpy).Rotation, \
(dpy).ApplicationExclusive)
// Trace part of a JSON string (events have a 64k limit)
#define TraceJSONChunk(Name, TotalChunks, ChunkSequence, TotalSize, ChunkSize, ChunkOffset, Chunk) \
EventWriteJSONChunk(Name, TotalChunks, ChunkSequence, TotalSize, ChunkSize, ChunkOffset, Chunk)
// Trace messages from the public ovr_Trace API and our internal logger
#define TraceLogDebug(message) EventWriteLogDebugMessage(message)
#define TraceLogInfo(message) EventWriteLogInfoMessage(message)
#define TraceLogError(message) EventWriteLogErrorMessage(message)
// Trace an ovrTrackingState
#define TraceTrackingState(ts) \
EventWriteHmdTrackingState( \
(ts).HeadPose.TimeInSeconds, \
&(ts).HeadPose.ThePose.Orientation.x, \
&(ts).HeadPose.ThePose.Position.x, \
&(ts).HeadPose.AngularVelocity.x, \
&(ts).HeadPose.LinearVelocity.x, \
0, \
0, \
(ts).StatusFlags)
#define TraceCameraBlobs(camIdx, frame) \
if (EventEnabledCameraBlobs()) { \
const int max_blobs = 80; \
int count = (frame).Blobs.GetSizeI(); \
double x[max_blobs]; \
double y[max_blobs]; \
int size[max_blobs]; \
if (count > max_blobs) \
count = max_blobs; \
for (int i = 0; i < count; ++i) { \
x[i] = (frame).Blobs[i].DistortedPosition.x; \
y[i] = (frame).Blobs[i].DistortedPosition.y; \
size[i] = (frame).Blobs[i].BlobSize; \
} \
EventWriteCameraBlobs( \
camIdx, \
(uint32_t)(frame).Frame->FrameNumber, \
(frame).Frame->ArrivalTime, \
(frame).Frame->Width, \
(frame).Frame->Height, \
count, \
x, \
y, \
size); \
} else \
((void)0)
#define TracePosePrediction( \
OriginalPose, PredictedPose, PredictionTimeDeltaSeconds, CurrentTimeInSeconds, id) \
EventWritePosePrediction( \
&(OriginalPose).Translation.x, \
&(OriginalPose).Rotation.x, \
&(PredictedPose).Translation.x, \
&(PredictedPose).Rotation.x, \
(PredictionTimeDeltaSeconds), \
(CurrentTimeInSeconds), \
(id))
// Trace PoseLatching CPU pinned memory write
#define TracePoseLatchCPUWrite( \
Sequence, \
Layer, \
MotionSensorTime, \
PredictedScanlineFirst, \
PredictedScanlineLast, \
TimeToScanlineFirst, \
TimeToScanlineLast, \
StartPosition, \
EndPosition, \
StartQuat, \
EndQuat) \
EventWritePoseLatchCPUWrite( \
Sequence, \
Layer, \
MotionSensorTime, \
PredictedScanlineFirst, \
PredictedScanlineLast, \
TimeToScanlineFirst, \
TimeToScanlineLast, \
StartPosition, \
EndPosition, \
StartQuat, \
EndQuat)
// Trace PoseLatching GPU latch
#define TracePoseLatchGPULatchReadback( \
Sequence, \
Layer, \
MotionSensorTime, \
PredictedScanlineFirst, \
PredictedScanlineLast, \
TimeToScanlineFirst, \
TimeToScanlineLast) \
EventWritePoseLatchGPULatchReadback( \
Sequence, \
Layer, \
MotionSensorTime, \
PredictedScanlineFirst, \
PredictedScanlineLast, \
TimeToScanlineFirst, \
TimeToScanlineLast)
#define TraceVSync(VSyncTime, FrameIndex, TWGpuEndTime) \
EventWriteVSync(VSyncTime, FrameIndex, TWGpuEndTime)
#define TraceAppCompositorFocus(Pid) EventWriteAppCompositorFocus(Pid)
#define TraceAppConnect(Pid) EventWriteAppConnect(Pid)
#define TraceAppDisconnect(Pid) EventWriteAppDisconnect(Pid)
#define TraceAppNoOp(Pid) EventWriteAppNoOp(Pid)
#define TraceLatencyTiming(LatencyTiming) \
EventWriteLatencyTiming( \
LatencyTiming.LatencyRenderCpuBegin, \
LatencyTiming.LatencyRenderCpuEnd, \
LatencyTiming.LatencyRenderIMU, \
LatencyTiming.LatencyTimewarpCpu, \
LatencyTiming.LatencyTimewarpLatched, \
LatencyTiming.LatencyTimewarpGpuEnd, \
LatencyTiming.LatencyPostPresent, \
LatencyTiming.ErrorRender, \
LatencyTiming.ErrorTimewarp)
#define TraceEndFrameAppTiming(AppTiming, RenderCount) \
EventWriteEndFrameAppTiming( \
AppTiming.AppFrameIndex, \
AppTiming.AppRenderIMUTime, \
AppTiming.AppVisibleMidpointTime, \
AppTiming.AppGpuRenderDuration, \
AppTiming.AppBeginRenderingTime, \
AppTiming.AppEndRenderingTime, \
AppTiming.QueueAheadSeconds, \
RenderCount)
#define TraceEndFrameOrigAppTiming(AppTiming, RenderCount) \
EventWriteEndFrameOrigAppTiming( \
AppTiming.AppFrameIndex, \
AppTiming.AppRenderIMUTime, \
AppTiming.AppVisibleMidpointTime, \
AppTiming.AppGpuRenderDuration, \
AppTiming.AppBeginRenderingTime, \
AppTiming.AppEndRenderingTime, \
AppTiming.QueueAheadSeconds, \
RenderCount)
// XXX for future reference this could have been done with events with different opcodes and
// identical templates
#define VirtualDisplayPacketTrace_Begin 0
#define VirtualDisplayPacketTrace_End 1
#define VirtualDisplayPacketTrace_Queue 2
#define VirtualDisplayPacketTrace_QueueRelease 3
#define VirtualDisplayPacketTrace_Result 5
#define TraceVirtualDisplayPacket(PacketType, Stage, SubmittingProcessID, ActiveProcessID) \
EventWriteVirtualDisplayPacketTrace(PacketType, Stage, SubmittingProcessID, ActiveProcessID)
#define TraceClientFrameMissed(FrameIndex, ProcessID) \
EventWriteClientFrameMissed(FrameIndex, ProcessID)
#define TraceCompositionBegin(ExpectedCPUStartTimeInSeconds, ActualCPUStartTimeInSeconds) \
EventWriteCompositionBegin(ExpectedCPUStartTimeInSeconds, ActualCPUStartTimeInSeconds)
#define TraceCompositionEnd() EventWriteCompositionEnd()
#define TraceCompositionEndSpinWait() EventWriteCompositionEndSpinWait()
#define TraceCompositionFlushingToGPU() EventWriteCompositionFlushingToGPU()
#define TraceRenderPacket(Stage, ClientPID) EventWriteRenderPacketTrace(Stage, ClientPID)
#define TraceHardwareInfo(data) \
EventWriteHardwareInfo( \
data.RequestedBits, \
data.CollectedBits, \
data.ImuTemp, \
data.StmTemp, \
data.NrfTemp, \
data.VBusVoltage, \
data.IAD, \
data.Proximity, \
data.PanelOnTime, \
data.UseRolling, \
data.HighBrightness, \
data.DP, \
data.SelfRefresh, \
data.Persistence, \
data.LightingOffset, \
data.PixelSettle, \
data.TotalRows)
#define TraceCompositionMissedCompositorFrame() EventWriteCompositionMissedCompositorFrame()
#define TraceCompositionGPUStartTime(Seconds) EventWriteCompositionGPUStartTime(Seconds)
#define TraceNotificationEnd( \
AppFrameIndex, CpuBeginToGpuEndSeconds, CpuBeginSeconds, GpuEndSeconds, SleepMs) \
EventWriteNotificationEnd( \
AppFrameIndex, CpuBeginToGpuEndSeconds, CpuBeginSeconds, GpuEndSeconds, SleepMs)
#define TraceNotificationBegin( \
AppFrameIndex, \
CpuBeginToGpuEndSeconds, \
CompositeTimeSeconds, \
VSyncTimeSeconds, \
CompositeDeltaSeconds, \
VSyncDeltaSeconds) \
EventWriteNotificationBegin( \
AppFrameIndex, \
CpuBeginToGpuEndSeconds, \
CompositeTimeSeconds, \
VSyncTimeSeconds, \
CompositeDeltaSeconds, \
VSyncDeltaSeconds)
#define TraceNotificationCompSubmit(IsEnabled, IsDisabled, FrameIndex) \
EventWriteNotificationCompSubmit(IsEnabled, IsDisabled, FrameIndex)
#define TraceMotionEstimationCostStats(Count, Average, Log2Histogram) \
EventWriteMotionEstimationCostStats(Count, Average, Log2Histogram)
#else // OVR_ENABLE_ETW_TRACING
// Eventually other platforms could support their form of performance tracing
#define TracingIsEnabled() (false)
#define TraceInit() ((void)0)
#define TraceFini() ((void)0)
#define TraceCall(frameIndex) ((void)0)
#define TraceReturn(frameIndex) ((void)0)
#define TraceWaypoint(frameIndex) ((void)0)
#define TraceDistortionBegin(id, frameIndex) ((void)0)
#define TraceDistortionWaitGPU(id, frameIndex) ((void)0)
#define TraceDistortionPresent(id, frameIndex) ((void)0)
#define TraceDistortionEnd(id, frameIndex) ((void)0)
#define TraceDistortionEndToEndTiming(elapsedMs) ((void)0)
#define TraceCameraFrameReceived(cfd) ((void)0)
#define TraceCameraBeginProcessing(camIdx, img) ((void)0)
#define TraceCameraFrameRequest(requestNumber, frameCount, lastFrameNumber) ((void)0)
#define TraceCameraEndProcessing(camIdx, img) ((void)0)
#define TraceCameraSkippedFrames(camIdx, skippedFrameCount) ((void)0)
#define TraceHmdDesc(desc) ((void)0)
#define TraceHmdDisplay(dpy) ((void)0)
#define TraceJSONChunk(Name, TotalChunks, ChunkSequence, TotalSize, ChunkSize, ChunkOffset, Chunk) \
((void)0)
#define TraceLogDebug(message) ((void)0)
#define TraceLogInfo(message) ((void)0)
#define TraceLogError(message) ((void)0)
#define TraceTrackingState(ts) ((void)0)
#define TraceCameraBlobs(camIdx, frame) ((void)0)
#define TracePoseLatchCPUWrite( \
Sequence, \
Layer, \
MotionSensorTime, \
PredictedScanlineFirst, \
PredictedScanlineLast, \
TimeToScanlineFirst, \
TimeToScanlineLast, \
StartPosition, \
EndPosition, \
StartQuat, \
EndQuat) \
((void)0)
#define TracePoseLatchGPULatchReadback( \
Sequence, \
Layer, \
MotionSensorTime, \
PredictedScanlineFirst, \
PredictedScanlineLast, \
TimeToScanlineFirst, \
TimeToScanlineLast) \
((void)0)
#define TraceVSync(VSyncTime, FrameIndex, TWGpuEndTime) ((void)0)
#define TracePosePrediction( \
OriginalPose, PredictedPose, PredictionTimeDeltaSeconds, CurrentTimeInSeconds, id) \
((void)0)
#define TraceAppCompositorFocus(Pid) ((void)0)
#define TraceAppConnect(Pid) ((void)0)
#define TraceAppDisconnect(Pid) ((void)0)
#define TraceAppNoOp(Pid) ((void)0)
#define TraceLatencyTiming(LatencyTiming) ((void)0)
#define TraceEndFrameAppTiming(AppTiming, RenderCount) ((void)0)
#define TraceEndFrameOrigAppTiming(AppTiming, RenderCount) ((void)0)
#define TraceVirtualDisplayPacket(PacketType, Stage, SubmittingProcessID, ActiveProcessID) ((void)0)
#define TraceClientFrameMissed(FrameIndex, ProcessID) ((void)0)
#define TraceCompositionBegin(ExpectedCPUStartTimeInSeconds, ActualCPUStartTimeInSeconds) ((void)0)
#define TraceCompositionEnd() ((void)0)
#define TraceCompositionEndSpinWait() ((void)0)
#define TraceCompositionFlushingToGPU() ((void)0)
#define TraceRenderPacket(Stage, ClientPID) ((void)0)
#define TraceHardwareInfo(data) ((void)0)
#define TraceCompositionMissedCompositorFrame() ((void)0)
#define TraceCompositionGPUStartTime(Seconds) ((void)0)
#define TraceNotificationEnd( \
AppFrameIndex, CpuBeginToGpuEndSeconds, CpuBeginSeconds, GpuEndSeconds, SleepMs) \
((void)0)
#define TraceNotificationBegin( \
AppFrameIndex, \
CpuBeginToGpuEndSeconds, \
CompositeTimeSeconds, \
VSyncTimeSeconds, \
CompositeDeltaSeconds, \
VSyncDeltaSeconds) \
((void)0)
#define TraceNotificationCompSubmit(IsEnabled, IsDisabled, FrameIndex) ((void)0)
#define TraceMotionEstimationCostStats(Count, Average, Log2Histogram) ((void)0)
#endif // OVR_ENABLE_ETW_TRACING
#endif // OVR_Tracing_h

View File

@ -0,0 +1,16 @@
@echo off
REM
REM build.cmd - rebuild generated ETW tracing files from LibOVREvents.man
REM
REM assume mc.exe is in a path relative to xperf.exe
for /f "delims=" %%a in ('where /F Xperf') do set XPERF_PATH=%%~dpa
set OSTYPE=x86
if not "%PROCESSOR_ARCHITECTURE%"=="x86" set OSTYPE=x64
if not "%PROCESSOR_ARCHITEW6432%"=="" set OSTYPE=x64
set MC="%XPERF_PATH%..\bin\%OSTYPE%\mc.exe"
echo Using Manifest Compiler: %MC%
%MC% -v -a -A -n -um .\LibOVREvents.man -h . -z LibOVREvents

View File

@ -0,0 +1,6 @@
@echo off
REM
REM clean.cmd - remove generated ETW tracing files
REM
del LibOVREvents.h LibOVREvents.rc LibOVREvents*.bin *LibOVRRT*.dll

View File

@ -0,0 +1,105 @@
@echo off
setlocal
REM run this script from an Admin shell to set up ETW tracing
set SCRIPTDIR=%~dp0
REM set SDK_MANIFEST_PATH to the SDK install path (e.g. C:\Program Files (x86)\Oculus)
for /f "delims=" %%a in ('reg query "HKLM\SOFTWARE\Wow6432Node\Oculus VR, LLC\Oculus" -v "Base"') do set SDK_INSTALL_PATH=%%a
set SDK_INSTALL_PATH=%SDK_INSTALL_PATH: Base REG_SZ =%
set SDK_MANIFEST_PATH=%SDK_INSTALL_PATH%\oculus-tools\etw
REM Add USERS Read & Execute privileges to the folder
icacls . /grant BUILTIN\Users:(OI)(CI)(RX) >nul
if %errorlevel% equ 0 goto CaclsOk
echo Failed to set cacls, installation may fail
:CaclsOk
for /f "delims=" %%a in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v "ProductName"') do set PRODUCT_NAME=%%a
set PRODUCT_NAME=%PRODUCT_NAME: =%
set PRODUCT_NAME=%PRODUCT_NAME:ProductNameREG_SZ=%
set PRODUCT_NAME=%PRODUCT_NAME:dows=%
set PRODUCT_NAME=%PRODUCT_NAME:Enterprise=%
set PRODUCT_NAME=%PRODUCT_NAME:Professional=%
set SHORT_PRODUCT_NAME=%PRODUCT_NAME:.1=%
echo Installing %PRODUCT_NAME% manifests:
rem we only support x64 oses these days
set OSTYPE=x64
set OCUSBVID_SYS=%windir%\System32\drivers\ocusbvid111.sys
if "%SHORT_PRODUCT_NAME%"=="Win7" set OCUSBVID_SYS=%windir%\System32\drivers\ocusbvid109.sys
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto GotOSTYPE
if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto GotOSTYPE
echo 32-bit OS not supported
exit /b 1
:GotOSTYPE
REM disable paging on x64 systems if stack walks are desired
if %OSTYPE% neq x64 goto SkipRegCheck
for /f "delims=" %%a in ('reg query "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v "DisablePagingExecutive"') do set REG_DPA=%%a
if %REG_DPA:~-3% equ 0x1 goto SkipRegCheck
echo ************************
echo DisablePagingExecutive should be set if you want stack tracing to work on %OSTYPE%
echo To disable paging run the following as Administrator:
echo reg add "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f
echo and reboot
echo ************************
:SkipRegCheck
set RIFTCAMERADRIVER_DIR=%SCRIPTDIR%..\..\..\RiftPTDriver
set USBVID_EVENTS_MAN=%SDK_MANIFEST_PATH%\OVRUSBVidEvents.man
if exist "%RIFTCAMERADRIVER_DIR%\OCUSBVID\OVRUSBVidEvents.man" set USBVID_EVENTS_MAN=%RIFTCAMERADRIVER_DIR%\OCUSBVID\OVRUSBVidEvents.man
if exist "%SCRIPTDIR%OVRUSBVidEvents.man" set USBVID_EVENTS_MAN=%SCRIPTDIR%OVRUSBVidEvents.man
echo Installing %OCUSBVID_SYS% manifest...
REM uninstall any existing manifest first
wevtutil.exe uninstall-manifest "%USBVID_EVENTS_MAN%"
if %errorlevel% neq 0 echo WARNING: This step failed.
wevtutil.exe install-manifest "%USBVID_EVENTS_MAN%" /rf:"%OCUSBVID_SYS%" /mf:"%OCUSBVID_SYS%"
REM make sure it worked
wevtutil get-publisher OVR-USBVid > nul
if %errorlevel% neq 0 echo WARNING: This step failed.
echo Installed %USBVID_EVENTS_MAN%
set LIBOVR_EVENTS_MAN=%SDK_MANIFEST_PATH%\LibOVREvents.man
if exist "%SCRIPTDIR%LibOVREvents.man" set LIBOVR_EVENTS_MAN=%SCRIPTDIR%LibOVREvents.man
REM get rid of stale dll's
del /f /q "%SCRIPTDIR%LibOVRRT*.dll"
set LIBOVR_PATTERN=LibOVRRT*_1.dll
echo Looking for %LIBOVR_PATTERN% dll's
REM this nightmare command copies the newest version of %LIBOVR_PATTERN% into the current directory without prompting...
forfiles /p:"%SDK_INSTALL_PATH%Support\oculus-runtime" /m:%LIBOVR_PATTERN% /c "cmd /c xcopy /y /f /d @path \"%SCRIPTDIR%.\" >nul" >nul 2>nul
if not exist "%SCRIPTDIR%..\..\..\LibOVR\Lib\Windows" goto NoLibOVRSource
forfiles /s /p:"%SCRIPTDIR%..\..\..\LibOVR\Lib\Windows" /m:%LIBOVR_PATTERN% /c "cmd /c xcopy /y /f /d @path \"%SCRIPTDIR%.\" >nul" >nul 2>nul
:NoLibOVRSource
for /f "delims=" %%a in ('dir /b /o:d "%SCRIPTDIR%%LIBOVR_PATTERN%"') do set LIBOVR_DLL=%%a
echo Installing %LIBOVR_DLL% manifest...
REM uninstall any existing manifest first
wevtutil uninstall-manifest "%LIBOVR_EVENTS_MAN%"
if %errorlevel% neq 0 exit /b 1
REM use absolute paths to the RT .dll, otherwise we risk picking up the wrong (e.g. installed) version from %PATH%
echo wevtutil install-manifest "%LIBOVR_EVENTS_MAN%" /rf:"%SCRIPTDIR%%LIBOVR_DLL%" /mf:"%SCRIPTDIR%%LIBOVR_DLL%"
wevtutil install-manifest "%LIBOVR_EVENTS_MAN%" /rf:"%SCRIPTDIR%%LIBOVR_DLL%" /mf:"%SCRIPTDIR%%LIBOVR_DLL%"
REM note we can't do del /f /q "%SCRIPTDIR%%LIBOVR_PATTERN%" here because the binary has to be present for ETW enumeration to work
REM make sure it worked
wevtutil get-publisher OVR-SDK-LibOVR > nul
if %errorlevel% neq 0 exit /b 1
echo Installed %LIBOVR_EVENTS_MAN%
if not exist "%SCRIPTDIR%..\..\..\Tools" exit /b 0
echo You can now start/stop traces with the GUI:
echo cd %SCRIPTDIR%..\..\..\Tools\TraceScript\ovrtap
echo .\startovrtap.cmd
echo or (command-line):
echo cd %SCRIPTDIR%..\..\..\Tools\Xperf
echo ovrlog

View File

@ -0,0 +1,8 @@
Texture2D Input;
sampler Sampler;
float4 main(float4 pos : SV_POSITION,
float2 tex : TEXCOORD) : SV_TARGET
{
return Input.Sample(Sampler, tex);
}

View File

@ -0,0 +1,14 @@
Texture2DMS<float> Input;
sampler Sampler;
float4 main(float4 pos : SV_POSITION,
float2 tex : TEXCOORD) : SV_TARGET
{
uint width, height, sampleCount;
Input.GetDimensions(width, height, sampleCount);
int2 coord = int2(width * tex.x, height * tex.y);
// This is a terrible resolve and shouldn't be used for anything
// where we care to maintain the result
return Input.Load(coord, sampleCount / 2);
}

View File

@ -0,0 +1,8 @@
void main(float2 pos : POSITION,
float2 tex : TEXCOORD,
out float4 oPos : SV_POSITION,
out float2 oTex : TEXCOORD)
{
oPos = float4(pos, 0, 1);
oTex = tex;
}

View File

@ -0,0 +1,9 @@
Texture2D Input;
sampler Sampler;
float4 main(float4 pos : SV_POSITION,
float2 tex : TEXCOORD) : SV_TARGET
{
float4 temp = Input.Sample(Sampler, tex);
return float4(temp.rrr, 1.0f);
}

View File

@ -0,0 +1,686 @@
/************************************************************************************
Filename : Util_D3D11_Blitter.cpp
Content : D3D11 implementation for blitting, supporting scaling & rotation
Created : February 24, 2015
Authors : Reza Nourai
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "Util_D3D11_Blitter.h"
#ifdef OVR_OS_MS
#include "Util_Direct3D.h"
#include "Shaders/Blt_vs.h"
#include "Shaders/Blt_ps.h"
#include "Shaders/Blt_ps_ms2.h"
#include "Shaders/GrayBlt_ps.h"
#include <io.h>
struct MSAAShader {
const BYTE* Data;
SIZE_T Size;
};
static MSAAShader PixelShaderList[] = {{Blt_ps, sizeof(Blt_ps)},
{Blt_ps_ms2, sizeof(Blt_ps_ms2)},
{GrayBlt_ps, sizeof(GrayBlt_ps)}};
namespace OVR {
namespace D3DUtil {
static ovrlog::Channel Log("Blitter");
//-------------------------------------------------------------------------------------
// ***** CAPI::Blitter
Blitter::Blitter(const Ptr<ID3D11Device>& device)
: Device(),
Context1(),
BltState(),
IL(),
VB(),
VS(),
PS(),
Sampler(),
DepthState(),
AlreadyInitialized(false),
SingleChannel(false) {
device->QueryInterface(IID_PPV_ARGS(&Device.GetRawRef()));
OVR_ASSERT(Device);
Device->GetImmediateContext1(&Context1.GetRawRef());
OVR_ASSERT(Context1);
}
Blitter::~Blitter() {}
bool Blitter::Initialize(bool single_channel) {
SingleChannel = single_channel;
if (!Device) {
OVR_ASSERT(false);
return false;
}
OVR_ASSERT(!AlreadyInitialized);
if (AlreadyInitialized) {
return false;
}
OVR_ASSERT(_countof(PixelShaderList) == PixelShaders::ShaderCount);
UINT deviceFlags = Device->GetCreationFlags();
D3D_FEATURE_LEVEL featureLevel = Device->GetFeatureLevel();
// If the device is single threaded, the context state must be too
UINT stateFlags = 0;
if (deviceFlags & D3D11_CREATE_DEVICE_SINGLETHREADED) {
stateFlags |= D3D11_1_CREATE_DEVICE_CONTEXT_STATE_SINGLETHREADED;
}
// TODO: Clean this up with OVR_D3D_CREATE() when we move OVRError to kernel.
OVR_ASSERT(!BltState); // Expected to be null on the way in.
BltState = nullptr; // Prevents a potential leak on the next line.
HRESULT hr = Device->CreateDeviceContextState(
stateFlags,
&featureLevel,
1,
D3D11_SDK_VERSION,
__uuidof(ID3D11Device1),
nullptr,
&BltState.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
OVR_D3D_TAG_OBJECT(BltState);
OVR_ASSERT(!VS); // Expected to be null on the way in.
VS = nullptr; // Prevents a potential leak on the next line.
hr = Device->CreateVertexShader(Blt_vs, sizeof(Blt_vs), nullptr, &VS.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
OVR_D3D_TAG_OBJECT(VS);
for (int i = 0; i < ShaderCount; ++i) {
Ptr<ID3D11PixelShader> ps;
hr = Device->CreatePixelShader(
PixelShaderList[i].Data, PixelShaderList[i].Size, nullptr, &ps.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
OVR_D3D_TAG_OBJECT(ps);
PS[i] = ps;
}
D3D11_INPUT_ELEMENT_DESC elems[2] = {};
elems[0].Format = DXGI_FORMAT_R32G32_FLOAT;
elems[0].SemanticName = "POSITION";
elems[1].AlignedByteOffset = sizeof(float) * 2;
elems[1].Format = DXGI_FORMAT_R32G32_FLOAT;
elems[1].SemanticName = "TEXCOORD";
OVR_ASSERT(!IL); // Expected to be null on the way in.
IL = nullptr; // Prevents a potential leak on the next line.
hr = Device->CreateInputLayout(elems, _countof(elems), Blt_vs, sizeof(Blt_vs), &IL.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
OVR_D3D_TAG_OBJECT(IL);
// Quad with texcoords designed to rotate the source 90deg clockwise
BltVertex vertices[] = {
{-1, 1, 0, 0}, {1, 1, 1, 0}, {1, -1, 1, 1}, {-1, 1, 0, 0}, {1, -1, 1, 1}, {-1, -1, 0, 1}};
D3D11_BUFFER_DESC bd = {};
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.ByteWidth = sizeof(vertices);
bd.StructureByteStride = sizeof(BltVertex);
bd.Usage = D3D11_USAGE_DEFAULT;
D3D11_SUBRESOURCE_DATA init = {};
init.pSysMem = vertices;
init.SysMemPitch = sizeof(vertices);
init.SysMemSlicePitch = init.SysMemPitch;
OVR_ASSERT(!VB); // Expected to be null on the way in.
VB = nullptr; // Prevents a potential leak on the next line.
hr = Device->CreateBuffer(&bd, &init, &VB.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
OVR_D3D_TAG_OBJECT(VB);
D3D11_SAMPLER_DESC ss = {};
ss.AddressU = ss.AddressV = ss.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
ss.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
ss.MaxLOD = 15;
OVR_ASSERT(!Sampler); // Expected to be null on the way in.
Sampler = nullptr; // Prevents a potential leak on the next line.
hr = Device->CreateSamplerState(&ss, &Sampler.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
// OVR_D3D_TAG_OBJECT(); Seems to already have a name.
D3D11_DEPTH_STENCIL_DESC depthDesc{};
OVR_ASSERT(!DepthState); // Expected to be null on the way in.
DepthState = nullptr; // Prevents a potential leak on the next line.
hr = Device->CreateDepthStencilState(&depthDesc, &DepthState.GetRawRef());
OVR_D3D_CHECK_RET_FALSE(hr);
// Swap to our blt state to set it up
Ptr<ID3DDeviceContextState> existingState;
Context1->SwapDeviceContextState(BltState, &existingState.GetRawRef());
Context1->IASetInputLayout(IL);
Context1->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
Context1->VSSetShader(VS, nullptr, 0);
Context1->PSSetSamplers(0, 1, &Sampler.GetRawRef());
Context1->OMSetDepthStencilState(DepthState, 0);
// Swap back
Context1->SwapDeviceContextState(existingState, nullptr);
AlreadyInitialized = true;
return true;
}
bool Blitter::Blt(ID3D11RenderTargetView* dest, ID3D11ShaderResourceView* source) {
Ptr<ID3D11Resource> resource;
dest->GetResource(&resource.GetRawRef());
Ptr<ID3D11Texture2D> texture;
HRESULT hr = resource->QueryInterface(IID_PPV_ARGS(&texture.GetRawRef()));
OVR_D3D_CHECK_RET_FALSE(hr);
D3D11_TEXTURE2D_DESC desc = {};
texture->GetDesc(&desc);
return Blt(dest, source, 0, 0, desc.Width, desc.Height);
}
bool Blitter::Blt(
ID3D11RenderTargetView* dest,
ID3D11ShaderResourceView* source,
uint32_t topLeftX,
uint32_t topLeftY,
uint32_t width,
uint32_t height) {
OVR_ASSERT(AlreadyInitialized);
if (!AlreadyInitialized) {
return false;
}
// Switch to our state
Ptr<ID3DDeviceContextState> existingState;
Context1->SwapDeviceContextState(BltState, &existingState.GetRawRef());
ID3D11RenderTargetView* nullRTVs[] = {nullptr, nullptr, nullptr, nullptr};
ID3D11ShaderResourceView* nullSRVs[] = {nullptr, nullptr, nullptr, nullptr};
Context1->OMSetRenderTargets(_countof(nullRTVs), nullRTVs, nullptr);
Context1->PSSetShaderResources(0, _countof(nullSRVs), nullSRVs);
// Set the mirror as the render target
Context1->OMSetRenderTargets(1, &dest, nullptr);
D3D11_VIEWPORT vp = {};
vp.TopLeftX = (float)topLeftX;
vp.TopLeftY = (float)topLeftY;
vp.Width = (float)width;
vp.Height = (float)height;
vp.MaxDepth = 1.0f;
Context1->RSSetViewports(1, &vp);
Context1->PSSetShaderResources(0, 1, &source);
Ptr<ID3D11Resource> resource;
source->GetResource(&resource.GetRawRef());
Ptr<ID3D11Texture2D> tmpTexture;
HRESULT hr = resource->QueryInterface(IID_PPV_ARGS(&tmpTexture.GetRawRef()));
if (FAILED(hr)) {
OVR_ASSERT(false);
return false;
}
D3D11_TEXTURE2D_DESC texDesc;
tmpTexture->GetDesc(&texDesc);
if (SingleChannel) {
Context1->PSSetShader(PS[PixelShaders::Grayscale], nullptr, 0);
} else if (texDesc.SampleDesc.Count == 1) {
Context1->PSSetShader(PS[PixelShaders::OneMSAA], nullptr, 0);
} else {
Context1->PSSetShader(PS[PixelShaders::TwoOrMoreMSAA], nullptr, 0);
}
static const uint32_t stride = sizeof(BltVertex);
static const uint32_t offset = 0;
Context1->IASetVertexBuffers(0, 1, &VB.GetRawRef(), &stride, &offset);
Context1->Draw(6, 0);
Context1->OMSetRenderTargets(_countof(nullRTVs), nullRTVs, nullptr);
Context1->PSSetShaderResources(0, _countof(nullSRVs), nullSRVs);
// Switch back to app state
Context1->SwapDeviceContextState(existingState, nullptr);
return true;
}
static bool operator!=(const DXGI_SAMPLE_DESC& s1, const DXGI_SAMPLE_DESC& s2) {
return (s1.Count != s2.Count) || (s1.Quality != s2.Quality);
}
static bool operator!=(const D3D11_TEXTURE2D_DESC& d1, const D3D11_TEXTURE2D_DESC& d2) {
return (d1.Width != d2.Width) || (d1.Height != d2.Height) || (d1.MipLevels != d2.MipLevels) ||
(d1.ArraySize != d2.ArraySize) || (d1.Format != d2.Format) ||
(d1.SampleDesc != d2.SampleDesc) || (d1.Usage != d2.Usage) ||
(d1.BindFlags != d2.BindFlags) || (d1.CPUAccessFlags != d2.CPUAccessFlags) ||
(d1.MiscFlags != d2.MiscFlags);
}
D3DTextureWriter::D3DTextureWriter(ID3D11Device* deviceNew)
: device(deviceNew), textureCopy(), pixels() {
memset(&textureCopyDesc, 0, sizeof(textureCopyDesc)); // We use memset instead of ={} because the
// former zeroes filler memory between
// variables, allowing comparison via
// memcmp.
}
void D3DTextureWriter::Shutdown() {
device.Clear();
textureCopy.Clear();
textureCopyDesc = {};
pixels.reset();
}
void D3DTextureWriter::SetDevice(ID3D11Device* deviceNew) {
if (device != deviceNew) {
textureCopy.Clear();
textureCopyDesc = {};
// No need to clear the pixels.
device = deviceNew;
}
}
D3DTextureWriter::Result D3DTextureWriter::SavePixelsToBMP(const wchar_t* path) {
// Create & write the file
ScopedFileHANDLE bmpFile(CreateFileW(
path, FILE_GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
if (!bmpFile.IsValid()) {
return Result::FILE_CREATION_FAILURE;
}
const int BytesPerPixel = 4;
const auto PixelsWidth = pixelsDimentions.first;
const auto PixelsHeight = pixelsDimentions.second;
const auto ImageSize = PixelsWidth * PixelsHeight * BytesPerPixel;
BITMAPFILEHEADER bfh{};
bfh.bfType = 0x4d42;
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + ImageSize;
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bih{};
bih.biSize = sizeof(bih);
bih.biBitCount = 8 * (WORD)BytesPerPixel;
bih.biPlanes = 1;
bih.biWidth = PixelsWidth;
bih.biHeight = PixelsHeight;
bih.biSizeImage = ImageSize;
DWORD bytesWritten = 0;
WriteFile(bmpFile.Get(), &bfh, sizeof(bfh), &bytesWritten, nullptr);
WriteFile(bmpFile.Get(), &bih, sizeof(bih), &bytesWritten, nullptr);
size_t offset = PixelsWidth * (PixelsHeight - 1);
for (uint32_t y = 0; y < PixelsHeight; ++y) {
WriteFile(
bmpFile.Get(), pixels.get() + offset, PixelsWidth * BytesPerPixel, &bytesWritten, nullptr);
offset -= PixelsWidth;
}
return Result::SUCCESS;
}
D3DTextureWriter::Result D3DTextureWriter::GrabPixels(
ID3D11Texture2D* texture,
UINT subresource,
bool copyTexture,
const ovrTimewarpProjectionDesc* depthProj,
const float* linearDepthScale) {
if (texture == nullptr)
return Result::NULL_SURFACE;
if (device == nullptr)
return Result::NULL_DEVICE;
Ptr<ID3D11DeviceContext> deviceContext;
device->GetImmediateContext(&deviceContext.GetRawRef()); // Always succeeds.
Ptr<ID3D11Texture2D>
textureSource; // This will point to either the input texture or to our textureCopy.
// Create textureCopy surface to copy back to CPU.
D3D11_TEXTURE2D_DESC textureDesc{};
texture->GetDesc(&textureDesc);
if (copyTexture) {
textureDesc.BindFlags = 0;
textureDesc.Usage = D3D11_USAGE_STAGING;
textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
textureDesc.MiscFlags = 0;
// We try to use our existing cached textureCopy as an intermediate texture, which will often
// be possible because a typical usage of this class is to keep copying the same texture.
if (textureDesc != textureCopyDesc) // If not equal...
{
textureCopyDesc = textureDesc;
textureCopy.Clear();
HRESULT hr = device->CreateTexture2D(&textureCopyDesc, nullptr, &textureCopy.GetRawRef());
if (FAILED(hr)) {
textureCopy.Clear();
textureCopyDesc = {};
return Result::TEXTURE_CREATION_FAILURE;
}
}
// Copy texture to textureCopy.
deviceContext->CopyResource(textureCopy, texture); // Always succeeds.
textureSource = textureCopy;
} else {
textureSource = texture;
}
// At this point we have a valid D3D device, source texture, and intermediate texture.
// We will write the source texture to the intermediate texture and then copy the intermediate
// texture to a memory buffer while converting to BGRA, then write the memory buffer to disk.
// We don't copy the source texture directly to the memory buffer because that will block for some
// time.
// Map textureSource so we can read its pixels.
D3D11_MAPPED_SUBRESOURCE mapped{};
HRESULT hr = deviceContext->Map(textureSource, subresource, D3D11_MAP_READ, 0, &mapped);
if (FAILED(hr)) {
return Result::TEXTURE_MAP_FAILURE;
}
// Now copy textureSource to pixels, converting textureSource's format as-needed to make pixels be
// BGRA.
if (pixelsDimentions.first != textureDesc.Width ||
pixelsDimentions.second != textureDesc.Height) {
pixels.reset(new uint32_t[textureDesc.Width * textureDesc.Height]);
pixelsDimentions = {textureDesc.Width, textureDesc.Height};
}
if (textureDesc.Format == DXGI_FORMAT_R11G11B10_FLOAT) {
// Convert from R11G11B10_FLOAT to R8G8B8
uint32_t inputPitchInPixels = (mapped.RowPitch / 4);
for (uint32_t y = 0; y < textureDesc.Height; ++y) {
for (uint32_t x = 0; x < textureDesc.Width; ++x) {
#pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union
union XMFLOAT3PK {
union {
uint32_t v;
struct {
uint32_t xm : 6;
uint32_t xe : 5;
uint32_t ym : 6;
uint32_t ye : 5;
uint32_t zm : 5;
uint32_t ze : 5;
};
};
};
XMFLOAT3PK packedFloat{((uint32_t*)mapped.pData)[y * inputPitchInPixels + x]};
float rFloat, gFloat, bFloat;
uint32_t* floatRef = (uint32_t*)&rFloat;
*floatRef = ((packedFloat.xe - 15 + 127) << 23U) | (packedFloat.xm << 17);
floatRef = (uint32_t*)&gFloat;
*floatRef = ((packedFloat.ye - 15 + 127) << 23U) | (packedFloat.ym << 17);
floatRef = (uint32_t*)&bFloat;
*floatRef = ((packedFloat.ze - 15 + 127) << 23U) | (packedFloat.zm << 18);
// This is back-asswards but we're converting out of linear so all
// the other images stored directly match in brightness for comparison.
uint32_t r = (uint32_t)(powf(rFloat, 1.0f / 2.2f) * 255.0f);
uint32_t g = (uint32_t)(powf(gFloat, 1.0f / 2.2f) * 255.0f);
uint32_t b = (uint32_t)(powf(bFloat, 1.0f / 2.2f) * 255.0f);
uint32_t bgra = (255 << 24) | (r << 16) | (g << 8) | b;
pixels[(y * textureDesc.Width) + x] = bgra;
}
}
} else if (textureDesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) {
// Convert from R10G10B10 to R8G8B8
uint32_t inputPitchInPixels = mapped.RowPitch / 4;
for (uint32_t y = 0; y < textureDesc.Height; ++y) {
for (uint32_t x = 0; x < textureDesc.Width; ++x) {
uint32_t wideRGBA = ((uint32_t*)mapped.pData)[(y * inputPitchInPixels) + x];
uint32_t r = (wideRGBA >> 00) & 0x3ff;
uint32_t g = (wideRGBA >> 10) & 0x3ff;
uint32_t b = (wideRGBA >> 20) & 0x3ff;
uint32_t a = (wideRGBA >> 30) & 0x003;
// This is back-asswards but we're converting out of linear so all
// the other images stored directly match in brightness for comparison.
r = (uint32_t)(powf((float)r / 1024.0f, 1.0f / 2.2f) * 255.0f);
g = (uint32_t)(powf((float)g / 1024.0f, 1.0f / 2.2f) * 255.0f);
b = (uint32_t)(powf((float)b / 1024.0f, 1.0f / 2.2f) * 255.0f);
uint32_t bgra = (a << 24) | (r << 16) | (g << 8) | b;
pixels[(y * textureDesc.Width) + x] = bgra;
}
}
} else if (
textureDesc.Format == DXGI_FORMAT_R32_FLOAT ||
textureDesc.Format == DXGI_FORMAT_R32_TYPELESS ||
textureDesc.Format == DXGI_FORMAT_D32_FLOAT ||
textureDesc.Format == DXGI_FORMAT_D24_UNORM_S8_UINT ||
textureDesc.Format == DXGI_FORMAT_R24G8_TYPELESS ||
textureDesc.Format == DXGI_FORMAT_R24G8_TYPELESS) {
if (depthProj == nullptr && linearDepthScale == nullptr) {
OVR_ASSERT(false);
Log.LogError("Tried to save depth image but depth projection is null or invalid");
}
uint32_t inputPitchInPixels = mapped.RowPitch / 4;
for (uint32_t y = 0; y < textureDesc.Height; ++y) {
for (uint32_t x = 0; x < textureDesc.Width; ++x) {
float rValue = 0.0f;
// 32-bit float is just a float
if (textureDesc.Format == DXGI_FORMAT_R32_FLOAT ||
textureDesc.Format == DXGI_FORMAT_R32_TYPELESS ||
textureDesc.Format == DXGI_FORMAT_D32_FLOAT) {
rValue = ((float*)mapped.pData)[y * inputPitchInPixels + x];
} else {
// 24-bit depth is a normalized value
uint32_t temp = ((uint32_t*)mapped.pData)[y * inputPitchInPixels + x];
temp &= 0xffffff;
static const float MAX_24FLOAT = (powf(2.0f, 24.0f) - 1.0f);
rValue = ((float)temp) / (MAX_24FLOAT);
}
// linearDepth = -(Proj.M[2][3]) / ( Proj.M[2][2] - Proj.M[3][2] * nonLinearDepth))
float linearDepth = 0.0f;
if (linearDepthScale) {
linearDepth = rValue * *linearDepthScale;
} else {
linearDepth = -(depthProj->Projection23) /
(depthProj->Projection22 - depthProj->Projection32 * rValue);
}
linearDepth *= -10.0f;
// This is back-asswards but we're converting out of linear so
// all the other images stored directly match in brightness
// for comparision
uint32_t r = (uint32_t)(255.0f - linearDepth);
r = std::min(r, 255u);
uint32_t bgra = 0xff << 24 | r << 16 | r << 8 | r;
pixels[y * textureDesc.Width + x] = bgra;
}
}
} else if (
(textureDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) ||
(textureDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB) ||
(textureDesc.Format == DXGI_FORMAT_B8G8R8A8_TYPELESS)) {
if (mapped.RowPitch != textureDesc.Height * sizeof(uint32_t)) {
// Copy BGRA texture line by line
auto src = static_cast<uint8_t*>(mapped.pData);
auto end = src + mapped.RowPitch * textureDesc.Height;
for (auto dst = pixels.get(); src < end; src += mapped.RowPitch, dst += textureDesc.Width) {
memcpy(dst, src, textureDesc.Width * sizeof(uint32_t));
}
} else {
memcpy(pixels.get(), mapped.pData, mapped.RowPitch * textureDesc.Height);
}
} else if (
(textureDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM) ||
(textureDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) ||
(textureDesc.Format == DXGI_FORMAT_R8G8B8A8_UINT) ||
(textureDesc.Format == DXGI_FORMAT_R8G8B8A8_TYPELESS)) {
// Convert from RGBA to BGRA.
auto dst = pixels.get();
auto src = static_cast<uint8_t*>(mapped.pData);
auto end = src + mapped.RowPitch * textureDesc.Height;
for (; src < end; src += mapped.RowPitch) {
dst = ConvertRGBA2BGRA(reinterpret_cast<uint32_t*>(src), dst, textureDesc.Width);
}
} else {
// DXGI_FORMAT_NV12, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UINT, possibly others.
uint32_t inputPitchInBytes = mapped.RowPitch;
for (uint32_t y = 0; y < textureDesc.Height; ++y) {
for (uint32_t x = 0; x < textureDesc.Width; ++x) {
uint8_t c = ((uint8_t*)mapped.pData)[(y * inputPitchInBytes) + x];
uint32_t bgra = (0xff << 24) | (c << 16) | (c << 8) | c; // Write as an RGB-based gray.
pixels[(y * textureDesc.Width) + x] = bgra;
}
}
}
deviceContext->Unmap(textureSource, 0); // Always succeeds.
return Result::SUCCESS;
}
uint32_t*
D3DTextureWriter::ConvertRGBA2BGRA(const uint32_t* src, uint32_t* dst, unsigned pixelCount) {
#ifdef OVR_64BIT_POINTERS
auto src2 = reinterpret_cast<const uint64_t*>(src);
auto dst2 = reinterpret_cast<uint64_t*>(dst);
auto lineEnd = src2 + (pixelCount >> 1);
// Convert 2 pixels at a time on 64-bit platforms
while (src2 < lineEnd) {
auto pixel = *src2++;
*dst2++ = (pixel & 0xff00ff00ff00ff00LL) | ((pixel & 0xff000000ffLL) << 16) |
((pixel >> 16) & 0xff000000ffLL);
}
if ((pixelCount & 1) == 0) {
return reinterpret_cast<uint32_t*>(dst2);
}
// Convert remaining odd pixel
auto pixel = *reinterpret_cast<const uint32_t*>(src2);
dst = reinterpret_cast<uint32_t*>(dst2);
*dst++ = (pixel & 0xff00ff00) | ((pixel & 0xff) << 16) | ((pixel >> 16) & 0xff);
#else
auto lineEnd = src + pixelCount;
while (src < lineEnd) {
auto pixel = *src++;
*dst++ = (pixel & 0xff00ff00) | ((pixel & 0xff) << 16) | ((pixel >> 16) & 0xff);
}
#endif
return dst;
}
char* D3DTextureWriter::ConvertBGRA2RGB(const uint32_t* src, char* byteDst, unsigned pixelCount) {
// Convert a stream of 16 byte quadruplets B1G1R1A1|B2R2G2A2|B3G3R3A3|B4G4R4A4
// into 12 byte triplets R1G1B1R2|G2B2R3G3|B3R4G4B4
#ifdef OVR_CPU_SSE
const auto* lineSrc = reinterpret_cast<const __m128i*>(src);
const auto* lineEnd = reinterpret_cast<const __m128i*>(src + (pixelCount & ~3));
auto shuffle = _mm_set_epi8(-1, -1, -1, -1, 12, 13, 14, 8, 9, 10, 4, 5, 6, 0, 1, 2);
auto storeMask = _mm_set_epi8(0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
while (lineSrc < lineEnd) {
__m128i invec = _mm_lddqu_si128(lineSrc++);
invec = _mm_shuffle_epi8(invec, shuffle);
_mm_maskmoveu_si128(invec, storeMask, byteDst);
byteDst += 12;
}
#else
auto dst = reinterpret_cast<uint32_t*>(byteDst);
auto quadEnd = reinterpret_cast<uint32_t*>(byteDst + 3 * (pixelCount & ~3));
while (dst < quadEnd) {
auto pixel1 = *src++;
auto pixel2 = *src++;
auto pixel3 = *src++;
auto pixel4 = *src++;
*dst++ = ((pixel1 >> 16) & 0xff) | (pixel1 & 0x0000ff00) | ((pixel1 << 16) & 0x00ff0000) |
((pixel2 << 8) & 0xff000000);
*dst++ = ((pixel2 >> 8) & 0xff) | ((pixel2 << 8) & 0x0000ff00) | (pixel3 & 0x00ff0000) |
((pixel3 << 16) & 0xff000000);
*dst++ = (pixel3 & 0xff) | ((pixel4 >> 8) & 0x0000ff00) | ((pixel4 << 8) & 0x00ff0000) |
((pixel4 << 24) & 0xff000000);
}
byteDst = reinterpret_cast<char*>(dst);
#endif
// Convert the reminder
for (UINT x = (pixelCount & ~3); x < pixelCount; ++x) {
auto pixel = *src++;
*byteDst++ = (pixel >> 16) & 0xff;
*byteDst++ = (pixel >> 8) & 0xff;
*byteDst++ = pixel & 0xff;
}
return byteDst;
}
D3DTextureWriter::Result D3DTextureWriter::SaveTexture(
ID3D11Texture2D* texture,
UINT subresource,
bool copyTexture,
const wchar_t* path,
const ovrTimewarpProjectionDesc* depthProj,
const float* linearDepthScale) {
auto rc = GrabPixels(texture, subresource, copyTexture, depthProj, linearDepthScale);
if (rc != Result::SUCCESS) {
return rc;
}
return SavePixelsToBMP(path);
}
} // namespace D3DUtil
} // namespace OVR
#endif // OVR_OS_MS

View File

@ -0,0 +1,144 @@
/************************************************************************************
Filename : Util_D3D11_Blitter.h
Content : D3D11 implementation for blitting, supporting scaling
Created : February 24, 2015
Authors : Reza Nourai
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Util_D3D11_Blitter_h
#define OVR_Util_D3D11_Blitter_h
#include "Kernel/OVR_RefCount.h"
#include "Kernel/OVR_Log.h"
#ifdef OVR_OS_MS
#include <d3d11_1.h>
#include "OVR_CAPI.h"
namespace OVR {
namespace D3DUtil {
//-------------------------------------------------------------------------------------
// ***** CAPI::Blitter
// D3D11 implementation of blitter
class Blitter : public RefCountBase<Blitter> {
public:
Blitter(const Ptr<ID3D11Device>& device);
~Blitter();
bool Initialize(bool single_channel = false);
bool Blt(ID3D11RenderTargetView* dest, ID3D11ShaderResourceView* source);
bool Blt(
ID3D11RenderTargetView* dest,
ID3D11ShaderResourceView* source,
uint32_t topLeftX,
uint32_t topLeftY,
uint32_t width,
uint32_t height);
private:
enum PixelShaders { OneMSAA = 0, TwoOrMoreMSAA = 1, Grayscale = 2, ShaderCount = 3 };
Ptr<ID3D11Device1> Device;
Ptr<ID3D11DeviceContext1> Context1;
Ptr<ID3DDeviceContextState> BltState;
Ptr<ID3D11InputLayout> IL;
Ptr<ID3D11Buffer> VB;
Ptr<ID3D11VertexShader> VS;
std::array<Ptr<ID3D11PixelShader>, PixelShaders::ShaderCount> PS;
Ptr<ID3D11SamplerState> Sampler;
Ptr<ID3D11DepthStencilState> DepthState;
bool AlreadyInitialized;
bool SingleChannel;
struct BltVertex {
float x, y;
float u, v;
};
};
// Writes a D3D texture to a file path.
class D3DTextureWriter {
public:
D3DTextureWriter(ID3D11Device* deviceNew = nullptr);
void Shutdown();
void SetDevice(ID3D11Device* deviceNew);
enum class Result {
SUCCESS,
NULL_SURFACE,
NULL_DEVICE,
TEXTURE_CREATION_FAILURE,
TEXTURE_MAP_FAILURE,
FILE_CREATION_FAILURE,
};
// Beware that if the texture being saved is one that is a render target then the rendering to
// that
// texture will need to be complete for this to work properly. You may need to flush the device or
// command buffer to achieve this.
// If copyTexture is true then we make a copy of the input texture before writing it to disk.
// If texture is mapped for writing then you may want to use copyTexture because reading from it
// will be slow.
Result GrabPixels(
ID3D11Texture2D* texture,
UINT subresource,
bool copyTexture,
const ovrTimewarpProjectionDesc* depthProj,
const float* linearDepthScale);
Result SavePixelsToBMP(const wchar_t* path);
// Simple composition of GrabPixels() and SavePixelsToBMP() functions
Result SaveTexture(
ID3D11Texture2D* texture,
UINT subresource,
bool copyTexture,
const wchar_t* path,
const ovrTimewarpProjectionDesc* depthProj,
const float* linearDepthScale);
static uint32_t* ConvertRGBA2BGRA(const uint32_t* src, uint32_t* dst, unsigned pixelCount);
static char* ConvertBGRA2RGB(const uint32_t* src, char* dst, unsigned pixelCount);
protected:
Ptr<ID3D11Device> device; // D3D11Device we use. Must match the textures we work with.
Ptr<ID3D11Texture2D> textureCopy; // The last texture we used. Cached for future use.
D3D11_TEXTURE2D_DESC textureCopyDesc; // This is a D3D11_TEXTURE2D_DESC. The description of
// textureCopy, which allows us to know if we need to free
// it and reallocate it anew.
std::pair<UINT, UINT> pixelsDimentions = {0, 0};
std::unique_ptr<uint32_t[]> pixels; // Windows RGB .bmp files are actually in BGRA or BGR format.
};
} // namespace D3DUtil
} // namespace OVR
#endif // OVR_OS_MS
#endif // OVR_Util_D3D11_Blitter_h

View File

@ -0,0 +1,93 @@
/************************************************************************************
Filename : Util_Direct3D.cpp
Content : Shared code for Direct3D
Created : Oct 14, 2014
Authors : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "Util_Direct3D.h"
#include "Kernel/OVR_Log.h"
namespace OVR {
namespace D3DUtil {
bool VerifyHRESULT(const char* file, int line, HRESULT hr) {
if (FAILED(hr)) {
LogError(
"D3D function returned fail HRESULT at %s on line %d : %s",
file,
line,
D3DUtil::GetWindowsErrorString(hr).ToCStr());
OVR_ASSERT(false);
return false;
}
return true;
}
String GetWindowsErrorString(HRESULT hr) {
wchar_t* errorText = nullptr;
DWORD slen = FormatMessageW(
// use system message tables to retrieve error text
FORMAT_MESSAGE_FROM_SYSTEM
// allocate buffer on local heap for error text
| FORMAT_MESSAGE_ALLOCATE_BUFFER
// Important! will fail otherwise, since we're not
// (and CANNOT) pass insertion parameters
| FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&errorText, // output, allocated via LocalAlloc (free with LocalFree)
256, // minimum size for output buffer
nullptr); // arguments - see note
char formatStr[512];
snprintf(formatStr, sizeof(formatStr), "[Code=%lx = %ld]", hr, hr);
String retStr = formatStr;
if (slen > 0 && errorText) {
retStr += " ";
retStr += errorText;
// release memory allocated by FormatMessage()
LocalFree(errorText);
}
return retStr;
}
void LogD3DCompileError(HRESULT hr, ID3DBlob* blob) {
if (FAILED(hr)) {
char* errStr = (char*)blob->GetBufferPointer();
SIZE_T len = blob->GetBufferSize();
if (errStr && len > 0) {
LogError("Error compiling shader: %s", errStr);
}
}
}
} // namespace D3DUtil
} // namespace OVR

View File

@ -0,0 +1,161 @@
/************************************************************************************
Filename : Util_Direct3D.h
Content : Shared code for Direct3D
Created : Oct 14, 2014
Authors : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Util_Direct3D_h
#define OVR_Util_Direct3D_h
#if defined(_WIN32)
// Include Windows correctly first before implicitly including it below
#include "Kernel/OVR_Win32_IncludeWindows.h"
#include "Kernel/OVR_String.h"
// Direct3D 11
#include <D3D11Shader.h>
#include <d3dcompiler.h>
#if _MSC_VER >= 1800
// Visual Studio 2013+ support newer D3D/DXGI headers.
#define OVR_D3D11_VER 2
#include <d3d11_2.h>
#define OVR_DXGI_VER 3
#include <dxgi1_3.h> // Used in place of 1.2 for IDXGIFactory2 debug version (when available)
#elif _MSC_VER >= 1700
// Visual Studio 2012+ only supports older D3D/DXGI headers.
#define OVR_D3D11_VER 1
#include <d3d11_1.h>
#else
// Visual Studio 2010+ only supports original D3D/DXGI headers.
#define OVR_D3D11_VER 1
#include <d3d11.h>
#endif
namespace OVR {
namespace D3DUtil {
String GetWindowsErrorString(HRESULT hr);
//-----------------------------------------------------------------------------
// Helper macros for verifying HRESULT values from Direct3D API calls
//
// These will assert on failure in debug mode, and in release or debug mode
// these functions will report the file and line where the error occurred,
// and what the error code was to the log at Error-level.
// Assert on HRESULT failure
bool VerifyHRESULT(const char* file, int line, HRESULT hr);
#define OVR_D3D_CHECK(hr) OVR::D3DUtil::VerifyHRESULT(__FILE__, __LINE__, hr)
// Internal implementation of the OVR_D3D_CHECK_RET family of functions.
#define OVR_D3D_CHECK_RET_IMPL(hr, returnExpression) \
{ \
if (!OVR_D3D_CHECK(hr)) { \
returnExpression \
} \
}
// Returns provided value on failure.
// Example usage:
// size_t Func() {
// . . .
// HRESULT hr = Device->QueryInterface(__uuidof(IDXGIDevice), (void
// **)&pDXGIDevice.GetRawRef());
// OVR_D3D_CHECK_RET_VAL(hr, 0);
// . . .
// }
#define OVR_D3D_CHECK_RET_VAL(hr, failureValue) OVR_D3D_CHECK_RET_IMPL(hr, return failureValue;)
// Returns void on failure
// Example usage:
// void Func() {
// . . .
// HRESULT hr = Device->QueryInterface(__uuidof(IDXGIDevice), (void
// **)&pDXGIDevice.GetRawRef());
// OVR_D3D_CHECK_RET(hr);
// . . .
// }
#define OVR_D3D_CHECK_RET(hr) OVR_D3D_CHECK_RET_IMPL(hr, return;)
// Returns false on failure
// Example usage:
// bool Func() {
// . . .
// HRESULT hr = Device->QueryInterface(__uuidof(IDXGIDevice), (void
// **)&pDXGIDevice.GetRawRef());
// OVR_D3D_CHECK_RET_FALSE(hr);
// . . .
// }
#define OVR_D3D_CHECK_RET_FALSE(hr) OVR_D3D_CHECK_RET_IMPL(hr, return false;)
// Returns nullptr on failure
// Example usage:
// void* Func() {
// . . .
// HRESULT hr = Device->QueryInterface(__uuidof(IDXGIDevice), (void
// **)&pDXGIDevice.GetRawRef());
// OVR_D3D_CHECK_RET_NULL(hr);
// . . .
// }
#define OVR_D3D_CHECK_RET_NULL(hr) OVR_D3D_CHECK_RET_IMPL(hr, return nullptr;)
// If the result is a failure, it will write the exact compile error to the error log
void LogD3DCompileError(HRESULT hr, ID3DBlob* errorBlob);
#if defined(OVR_BUILD_DEBUG)
// Enable this to track down double-tagging object warnings from D3D.
#define OVR_D3D_TRACK_DOUBLE_TAGGING
#if defined(OVR_D3D_TRACK_DOUBLE_TAGGING)
#define OVR_D3D_CHECK_REUSE(child) \
char tagReuseBuffer[1024]; \
UINT reuseSize = (UINT)sizeof(tagReuseBuffer); \
OVR_ASSERT(FAILED(child->GetPrivateData(WKPDID_D3DDebugObjectName, &reuseSize, tagReuseBuffer)));
#else
#define OVR_D3D_CHECK_REUSE(child) (void(0))
#endif
#define OVR_D3D_TAG_OBJECT(child) \
if (child) { \
OVR_D3D_CHECK_REUSE(child); \
const char* tagName = OVR_STRINGIZE(child) " " __FILE__ "(" OVR_STRINGIZE(__LINE__) ")"; \
UINT tagDataSize = (UINT)strlen(tagName); \
HRESULT tagHR = child->SetPrivateData(WKPDID_D3DDebugObjectName, tagDataSize, tagName); \
OVR_D3D_CHECK(tagHR); \
}
#else // !Debug:
#define OVR_D3D_TAG_OBJECT(child) (void(0))
#endif // !Debug
} // namespace D3DUtil
} // namespace OVR
#endif // _WIN32
#endif // OVR_Util_Direct3D_h

View File

@ -0,0 +1,269 @@
/************************************************************************************
Filename : Util_GL_Blitter.cpp
Content : GL implementation for blitting, supporting scaling & rotation
Created : February 24, 2015
Authors : Reza Nourai
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#include "Util_GL_Blitter.h"
namespace OVR {
namespace GLUtil {
#define AssertOnGLError() \
{ \
GLuint err = glGetError(); \
OVR_ASSERT_AND_UNUSED(!err, err); \
}
//-------------------------------------------------------------------------------------
// ***** CAPI::Blitter
Blitter::Blitter() : ReadFBO(0) {}
Blitter::~Blitter() {
glDeleteFramebuffers(1, &ReadFBO);
ReadFBO = 0;
}
bool Blitter::Initialize() {
glGenFramebuffers(1, &ReadFBO);
AssertOnGLError();
return true;
}
bool Blitter::Blt(GLuint sourceTexId) {
GLenum status = 0;
GLint currentTex2D = 0;
GLint sourceWidth = 0, sourceHeight = 0;
// Store off currently selected tex2d
glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTex2D);
// Get source texture dimensions
glBindTexture(GL_TEXTURE_2D, sourceTexId);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &sourceWidth);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &sourceHeight);
// Put old texture back
glBindTexture(GL_TEXTURE_2D, currentTex2D);
// Save off the current FBOs
GLint currentReadFB;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFB);
AssertOnGLError();
// setup draw buffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
// setup read buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, ReadFBO);
AssertOnGLError();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexId, 0);
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
AssertOnGLError();
status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
OVR_ASSERT_AND_UNUSED(status == GL_FRAMEBUFFER_COMPLETE, status);
// Do the blt
glBlitFramebuffer(
0,
sourceHeight,
sourceWidth,
0,
0,
0,
sourceWidth,
sourceHeight,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
AssertOnGLError();
// Restore the previous FBOs
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFB);
AssertOnGLError();
return true;
}
bool Blitter::Blt(
GLuint sourceTexId,
uint32_t topLeftX,
uint32_t topLeftY,
uint32_t width,
uint32_t height) {
GLenum status = 0;
GLint currentTex2D = 0;
GLint sourceWidth = 0, sourceHeight = 0;
// Store off currently selected tex2d
glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTex2D);
// Get source texture dimensions
glBindTexture(GL_TEXTURE_2D, sourceTexId);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &sourceWidth);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &sourceHeight);
// Put old texture back
glBindTexture(GL_TEXTURE_2D, currentTex2D);
// Save off the current FBOs
GLint currentReadFB;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFB);
AssertOnGLError();
// setup draw buffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
// Get the default framebuffer size :
GLint dims[4] = {0};
glGetIntegerv(GL_VIEWPORT, dims);
GLint fbHeight = dims[3];
// setup read buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, ReadFBO);
AssertOnGLError();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexId, 0);
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
AssertOnGLError();
status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
OVR_ASSERT_AND_UNUSED(status == GL_FRAMEBUFFER_COMPLETE, status);
// Do the blt
glBlitFramebuffer(
0,
sourceHeight,
sourceWidth,
0,
(GLint)topLeftX,
fbHeight - (GLint)topLeftY - (GLint)height,
(GLint)topLeftX + (GLint)width,
fbHeight - (GLint)topLeftY,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
AssertOnGLError();
// Restore the previous FBOs
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFB);
AssertOnGLError();
return true;
}
bool Blitter::BltCubemap(GLuint sourceTexId, GLuint tempTexId, uint32_t cubeMapSize) {
GLenum status = 0;
GLint currentTex2D = 0;
GLint size = (GLint)cubeMapSize;
const int numFaces = 6;
// Store off currently selected tex2d
glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTex2D);
// Save off the current FBOs
GLint currentReadFB;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFB);
AssertOnGLError();
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
// Create framebuffers
GLuint drawCubeFBO;
glGenFramebuffers(1, &drawCubeFBO);
AssertOnGLError();
GLuint readCubeFBO;
glGenFramebuffers(1, &readCubeFBO);
AssertOnGLError();
GLuint drawFinalCubeFBO;
glGenFramebuffers(1, &drawFinalCubeFBO);
AssertOnGLError();
// Setup draw buffer with temp texture to hold flipped face
// tempTexId holds a Texture2D the size of one cubemap face (cubeMapSize)
// It is used to temporarily hold a flipped face before blitting it to the original cubemap
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawCubeFBO);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tempTexId, 0);
// Setup read buffer with original cubemap texture
glBindFramebuffer(GL_READ_FRAMEBUFFER, readCubeFBO);
AssertOnGLError();
glFramebufferTexture2D(
GL_READ_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
sourceTexId,
0);
AssertOnGLError();
status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
OVR_ASSERT_AND_UNUSED(status == GL_FRAMEBUFFER_COMPLETE, status);
// Do the blt
glBlitFramebuffer(0, size, size, 0, 0, size, size, 0, GL_COLOR_BUFFER_BIT, GL_LINEAR);
AssertOnGLError();
// Setup final draw buffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFinalCubeFBO);
glFramebufferTexture2D(
GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
sourceTexId,
0);
// Setup read buffer to be the previous draw buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, drawCubeFBO); // flipped FBO
AssertOnGLError();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tempTexId, 0);
AssertOnGLError();
status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
OVR_ASSERT_AND_UNUSED(status == GL_FRAMEBUFFER_COMPLETE, status);
// Do the blt again
glBlitFramebuffer(0, size, size, 0, 0, 0, size, size, GL_COLOR_BUFFER_BIT, GL_LINEAR);
AssertOnGLError();
// Delete framebuffers
glDeleteFramebuffers(1, &drawCubeFBO);
drawCubeFBO = 0;
glDeleteFramebuffers(1, &readCubeFBO);
readCubeFBO = 0;
glDeleteFramebuffers(1, &drawFinalCubeFBO);
drawFinalCubeFBO = 0;
}
// Restore the previous FBOs
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFB);
AssertOnGLError();
return true;
}
} // namespace GLUtil
} // namespace OVR

View File

@ -0,0 +1,70 @@
/************************************************************************************
Filename : Util_GL_Blitter.h
Content : GL implementation for blitting, supporting scaling & rotation
Created : February 24, 2015
Authors : Reza Nourai
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Util_GL_Blitter_h
#define OVR_Util_GL_Blitter_h
#include "Kernel/OVR_RefCount.h"
#include "Kernel/OVR_Win32_IncludeWindows.h"
#include "GL/CAPI_GLE.h"
#if defined(OVR_OS_WIN32)
#include <gl/GL.h>
#elif defined(__APPLE__)
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
namespace OVR {
namespace GLUtil {
//-------------------------------------------------------------------------------------
// ***** CAPI::Blitter
// D3D11 implementation of blitter
class Blitter : public RefCountBase<Blitter> {
public:
Blitter();
~Blitter();
bool Initialize();
// Blit sourceTexture to the active frame buffer
bool Blt(GLuint sourceTexId);
bool
Blt(GLuint sourceTexId, uint32_t topLeftX, uint32_t topLeftY, uint32_t width, uint32_t height);
bool BltCubemap(GLuint sourceTexId, GLuint tempTexId, uint32_t cubeMapSize);
private:
GLuint ReadFBO;
};
} // namespace GLUtil
} // namespace OVR
#endif // OVR_Util_GL_Blitter_h

View File

@ -0,0 +1,537 @@
/************************************************************************************
Filename : Util_ImageWindow.cpp
Content : An output object for windows that can display raw images for testing
Created : March 13, 2014
Authors : Dean Beeler
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#include "Kernel/OVR_Types.h"
OVR_DISABLE_ALL_MSVC_WARNINGS()
#include "Kernel/OVR_Allocator.h"
#include "Kernel/OVR_RefCount.h"
#include "Kernel/OVR_Log.h"
#include "Kernel/OVR_System.h"
#include "Kernel/OVR_Nullptr.h"
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_Array.h"
#include "Kernel/OVR_Timer.h"
#include "Util_ImageWindow.h"
#if defined(OVR_OS_WIN32)
#include "Kernel/OVR_Win32_IncludeWindows.h"
#include "DWrite.h"
#endif
OVR_RESTORE_ALL_MSVC_WARNINGS()
#if defined(OVR_OS_WIN32)
typedef HRESULT(WINAPI* D2D1CreateFactoryFn)(
_In_ D2D1_FACTORY_TYPE,
_In_ REFIID,
_In_opt_ const D2D1_FACTORY_OPTIONS*,
_Out_ ID2D1Factory**);
typedef HRESULT(WINAPI* DWriteCreateFactoryFn)(
_In_ DWRITE_FACTORY_TYPE factoryType,
_In_ REFIID iid,
_Out_ IUnknown** factory);
namespace OVR {
namespace Util {
ID2D1Factory* ImageWindow::pD2DFactory = NULL;
IDWriteFactory* ImageWindow::pDWriteFactory = NULL;
HINSTANCE ImageWindow::hInstD2d1 = NULL;
HINSTANCE ImageWindow::hInstDwrite = NULL;
// TODO(review): This appears to be (at present) necessary, the global list is accessed by the
// render loop in Samples. In the current version, windows will just be lost when windowCount
// exceeds MaxWindows; I've left that in place, since this is unfamiliar code. I'm not sure what
// thread-safety guarantees this portion of the code needs to satisfy, so I don't want to
// change it to a list or whatever. Asserts added to catch the error.
ImageWindow* ImageWindow::globalWindow[ImageWindow::MaxWindows];
int ImageWindow::windowCount = 0;
LRESULT CALLBACK MainWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
return 0;
case WM_PAINT: {
LONG_PTR ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (ptr) {
ImageWindow* iw = (ImageWindow*)ptr;
iw->OnPaint();
}
}
return 0;
case WM_SIZE:
// Set the size and position of the window.
return 0;
case WM_DESTROY:
// Clean up window-specific data objects.
return 0;
//
// Process other messages.
//
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
// return 0;
}
ImageWindow::ImageWindow(uint32_t width, uint32_t height)
: hWindow(NULL),
pRT(NULL),
// resolution(),
frontBufferMutex(new Mutex()),
frames(),
greyBitmap(NULL),
colorBitmap(NULL) {
D2D1CreateFactoryFn createFactory = NULL;
DWriteCreateFactoryFn writeFactory = NULL;
if (!hInstD2d1) {
hInstD2d1 = LoadLibraryW(L"d2d1.dll");
}
if (!hInstD2d1) {
hInstD2d1 = LoadLibraryW(L"Dwrite.dll");
}
if (hInstD2d1) {
createFactory = (D2D1CreateFactoryFn)GetProcAddress(hInstD2d1, "D2D1CreateFactory");
}
if (hInstDwrite) {
writeFactory = (DWriteCreateFactoryFn)GetProcAddress(hInstDwrite, "DWriteCreateFactory");
}
// TODO: see note where globalWindow is declared.
globalWindow[windowCount++ % MaxWindows] = this;
OVR_ASSERT(windowCount < MaxWindows);
if (pD2DFactory == NULL && createFactory && writeFactory) {
// Create a Direct2D factory.
HRESULT hResult = createFactory(
D2D1_FACTORY_TYPE_MULTI_THREADED,
__uuidof(ID2D1Factory),
NULL,
&pD2DFactory // This will be AddRef'd for us.
);
OVR_ASSERT_AND_UNUSED(hResult == S_OK, hResult);
// Create a DirectWrite factory.
hResult = writeFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(pDWriteFactory), // This probably should instead be __uuidof(IDWriteFactory)
reinterpret_cast<IUnknown**>(&pDWriteFactory) // This will be AddRef'd for us.
);
OVR_ASSERT_AND_UNUSED(hResult == S_OK, hResult);
}
resolution = D2D1::SizeU(width, height);
if (hWindow) {
SetWindowLongPtrW(hWindow, GWLP_USERDATA, (LONG_PTR)this);
}
}
ImageWindow::~ImageWindow() {
for (int i = 0; i < MaxWindows; ++i) {
if (globalWindow[i] == this) {
globalWindow[i] = NULL;
break;
}
}
if (greyBitmap)
greyBitmap->Release();
if (colorBitmap)
colorBitmap->Release();
if (pRT)
pRT->Release();
{
Mutex::Locker locker(frontBufferMutex);
while (frames.GetSize()) {
Ptr<Frame> aFrame = frames.PopBack();
}
}
if (hWindow) {
ShowWindow(hWindow, SW_HIDE);
DestroyWindow(hWindow);
}
if (pD2DFactory) {
pD2DFactory->Release();
pD2DFactory = NULL;
}
if (pDWriteFactory) {
pDWriteFactory->Release();
pDWriteFactory = NULL;
}
if (hInstD2d1) {
FreeLibrary(hInstD2d1);
hInstD2d1 = NULL;
}
if (hInstDwrite) {
FreeLibrary(hInstDwrite);
hInstDwrite = NULL;
}
}
void ImageWindow::AssociateSurface(void* surface) {
if (pD2DFactory) {
// Assume an IUnknown
IUnknown* unknown = (IUnknown*)surface;
IDXGISurface* pDxgiSurface = NULL;
HRESULT hr = unknown->QueryInterface(&pDxgiSurface);
if (hr == S_OK) {
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
96,
96);
pRT = NULL;
ID2D1RenderTarget* tmpTarget;
hr = pD2DFactory->CreateDxgiSurfaceRenderTarget(pDxgiSurface, &props, &tmpTarget);
if (hr == S_OK) {
DXGI_SURFACE_DESC desc{};
pDxgiSurface->GetDesc(&desc);
int width = desc.Width;
int height = desc.Height;
D2D1_SIZE_U size = D2D1::SizeU(width, height);
D2D1_PIXEL_FORMAT pixelFormat =
D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
D2D1_PIXEL_FORMAT colorPixelFormat =
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
D2D1_BITMAP_PROPERTIES bitmapProps;
bitmapProps.dpiX = 96;
bitmapProps.dpiY = 96;
bitmapProps.pixelFormat = pixelFormat;
D2D1_BITMAP_PROPERTIES colorBitmapProps;
colorBitmapProps.dpiX = 96;
colorBitmapProps.dpiY = 96;
colorBitmapProps.pixelFormat = colorPixelFormat;
HRESULT result = tmpTarget->CreateBitmap(size, bitmapProps, &greyBitmap);
if (result != S_OK) {
tmpTarget->Release();
tmpTarget = NULL;
}
if (tmpTarget) {
result = tmpTarget->CreateBitmap(size, colorBitmapProps, &colorBitmap);
if (result != S_OK) {
tmpTarget->Release();
tmpTarget = NULL;
}
}
pRT = tmpTarget;
}
}
}
}
void ImageWindow::Process() {
if (pRT && greyBitmap) {
OnPaint();
pRT->Flush();
}
}
void ImageWindow::Complete() {
Mutex::Locker locker(frontBufferMutex);
if (frames.IsEmpty())
return;
if (frames.PeekBack(0)->ready)
return;
Ptr<Frame> frame = frames.PeekBack(0);
frame->ready = true;
}
void ImageWindow::OnPaint() {
Mutex::Locker locker(frontBufferMutex);
// Nothing to do
if (frames.IsEmpty())
return;
if (!frames.PeekFront(0)->ready)
return;
Ptr<Frame> currentFrame = frames.PopFront();
Ptr<Frame> nextFrame = NULL;
if (!frames.IsEmpty())
nextFrame = frames.PeekFront(0);
while (nextFrame && nextFrame->ready) {
// Free up the current frame since it's been removed from the deque
currentFrame = frames.PopFront();
if (frames.IsEmpty())
break;
nextFrame = frames.PeekFront(0);
}
if (currentFrame->imageData)
greyBitmap->CopyFromMemory(NULL, currentFrame->imageData, currentFrame->width);
if (currentFrame->colorImageData)
colorBitmap->CopyFromMemory(NULL, currentFrame->colorImageData, currentFrame->colorPitch);
pRT->BeginDraw();
pRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));
// This will mirror our image
D2D1_MATRIX_3X2_F m;
m._11 = -1;
m._12 = 0;
m._21 = 0;
m._22 = 1;
m._31 = 0;
m._32 = 0;
pRT->SetTransform(m);
ID2D1SolidColorBrush* whiteBrush;
pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White, 1.0f), &whiteBrush);
if (currentFrame->imageData) {
pRT->FillOpacityMask(
greyBitmap,
whiteBrush,
D2D1_OPACITY_MASK_CONTENT_TEXT_NATURAL,
D2D1::RectF(-(FLOAT)resolution.width, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height),
// D2D1::RectF( 0.0f, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ),
D2D1::RectF(0.0f, 0.0f, (FLOAT)resolution.width, (FLOAT)resolution.height));
} else if (currentFrame->colorImageData) {
pRT->DrawBitmap(
colorBitmap,
D2D1::RectF(-(FLOAT)resolution.width, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height));
}
pRT->SetTransform(D2D1::Matrix3x2F::Identity());
whiteBrush->Release();
Array<CirclePlot>::Iterator it;
for (it = currentFrame->plots.Begin(); it != currentFrame->plots.End(); ++it) {
ID2D1SolidColorBrush* aBrush;
pRT->CreateSolidColorBrush(D2D1::ColorF(it->r, it->g, it->b), &aBrush);
D2D1_ELLIPSE ellipse;
ellipse.point.x = it->x;
ellipse.point.y = it->y;
ellipse.radiusX = it->radius;
ellipse.radiusY = it->radius;
if (it->fill)
pRT->FillEllipse(&ellipse, aBrush);
else
pRT->DrawEllipse(&ellipse, aBrush);
aBrush->Release();
}
static const WCHAR msc_fontName[] = L"Verdana";
static const FLOAT msc_fontSize = 20;
IDWriteTextFormat* textFormat = NULL;
// Create a DirectWrite text format object.
pDWriteFactory->CreateTextFormat(
msc_fontName,
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
msc_fontSize,
L"", // locale
&textFormat);
D2D1_SIZE_F renderTargetSize = pRT->GetSize();
Array<TextPlot>::Iterator textIt;
for (textIt = currentFrame->textLines.Begin(); textIt != currentFrame->textLines.End();
++textIt) {
ID2D1SolidColorBrush* aBrush;
pRT->CreateSolidColorBrush(D2D1::ColorF(textIt->r, textIt->g, textIt->b), &aBrush);
WCHAR* tmpString = (WCHAR*)calloc(textIt->text.GetLength(), sizeof(WCHAR));
for (unsigned i = 0; i < textIt->text.GetLength(); ++i) {
tmpString[i] = (WCHAR)textIt->text.GetCharAt(i);
}
pRT->DrawText(
tmpString,
(UINT32)textIt->text.GetLength(),
textFormat,
D2D1::RectF(textIt->x, textIt->y, renderTargetSize.width, renderTargetSize.height),
aBrush);
free(tmpString);
aBrush->Release();
}
if (textFormat)
textFormat->Release();
pRT->EndDraw();
pRT->Flush();
}
Ptr<Frame> ImageWindow::lastUnreadyFrame() {
static int framenumber = 0;
if (frames.GetSize() && !frames.PeekBack(0)->ready)
return frames.PeekBack(0);
// Create a new frame if an unready one doesn't already exist
Ptr<Frame> tmpFrame = *new Frame(framenumber);
frames.PushBack(tmpFrame);
++framenumber;
return tmpFrame;
}
void ImageWindow::UpdateImageBW(const uint8_t* imageData, uint32_t width, uint32_t height) {
if (pRT && greyBitmap) {
Mutex::Locker locker(frontBufferMutex);
Ptr<Frame> frame = lastUnreadyFrame();
frame->imageData = malloc(width * height);
frame->width = width;
frame->height = height;
memcpy(frame->imageData, imageData, width * height);
}
}
void ImageWindow::UpdateImageRGBA(
const uint8_t* imageData,
uint32_t width,
uint32_t height,
uint32_t pitch) {
if (pRT && colorBitmap) {
Mutex::Locker locker(frontBufferMutex);
Ptr<Frame> frame = lastUnreadyFrame();
frame->colorImageData = malloc(pitch * height);
frame->width = width;
frame->height = height;
frame->colorPitch = pitch;
memcpy(frame->colorImageData, imageData, pitch * height);
}
}
void ImageWindow::addCircle(float x, float y, float radius, float r, float g, float b, bool fill) {
if (pRT) {
CirclePlot cp;
cp.x = x;
cp.y = y;
cp.radius = radius;
cp.r = r;
cp.g = g;
cp.b = b;
cp.fill = fill;
Mutex::Locker locker(frontBufferMutex);
Ptr<Frame> frame = lastUnreadyFrame();
frame->plots.PushBack(cp);
}
}
void ImageWindow::addText(float x, float y, float r, float g, float b, OVR::String text) {
if (pRT) {
TextPlot tp;
tp.x = x;
tp.y = y;
tp.r = r;
tp.g = g;
tp.b = b;
tp.text = text;
Mutex::Locker locker(frontBufferMutex);
Ptr<Frame> frame = lastUnreadyFrame();
frame->textLines.PushBack(tp);
}
}
} // namespace Util
} // namespace OVR
#else // defined(OVR_OS_WIN32)
namespace OVR {
namespace Util {
ImageWindow* ImageWindow::globalWindow[4];
int ImageWindow::windowCount = 0;
} // namespace Util
} // namespace OVR
#endif //#else //defined(OVR_OS_WIN32)

View File

@ -0,0 +1,234 @@
/************************************************************************************
Filename : Util_ImageWindow.h
Content : An output object for windows that can display raw images for testing
Created : March 13, 2014
Authors : Dean Beeler
Copyright : Copyright 2014 Oculus, Inc. All Rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#ifndef OVR_Util_ImageWindow_h
#define OVR_Util_ImageWindow_h
#if defined(OVR_OS_WIN32)
#include "Kernel/OVR_Win32_IncludeWindows.h"
#include <d2d1.h>
#include <dwrite.h>
#endif
#include "Kernel/OVR_Array.h"
#include "Kernel/OVR_Threads.h"
#include "Kernel/OVR_Deque.h"
#include <stdint.h>
namespace OVR {
namespace Util {
typedef struct {
float x;
float y;
float radius;
float r;
float g;
float b;
bool fill;
} CirclePlot;
typedef struct {
float x;
float y;
float r;
float g;
float b;
OVR::String text;
} TextPlot;
class Frame : virtual public RefCountBaseV<Frame> {
public:
Frame(int frame)
: frameNumber(frame),
plots(),
textLines(),
imageData(NULL),
colorImageData(NULL),
width(0),
height(0),
colorPitch(0),
ready(false) {}
~Frame() {
if (imageData)
free(imageData);
if (colorImageData)
free(colorImageData);
plots.ClearAndRelease();
textLines.ClearAndRelease();
}
int frameNumber;
Array<CirclePlot> plots;
Array<TextPlot> textLines;
void* imageData;
void* colorImageData;
int width;
int height;
int colorPitch;
bool ready;
};
#if defined(OVR_OS_WIN32)
class ImageWindow {
HWND hWindow;
ID2D1RenderTarget* pRT;
D2D1_SIZE_U resolution;
std::unique_ptr<Mutex> frontBufferMutex;
InPlaceMutableDeque<Ptr<Frame>> frames;
ID2D1Bitmap* greyBitmap;
ID2D1Bitmap* colorBitmap;
public:
// constructors
ImageWindow();
ImageWindow(uint32_t width, uint32_t height);
virtual ~ImageWindow();
void GetResolution(size_t& width, size_t& height) {
width = resolution.width;
height = resolution.height;
}
void OnPaint(); // Called by Windows when it receives a WM_PAINT message
void UpdateImage(const uint8_t* imageData, uint32_t width, uint32_t height) {
UpdateImageBW(imageData, width, height);
}
void UpdateImageBW(const uint8_t* imageData, uint32_t width, uint32_t height);
void UpdateImageRGBA(const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch);
void Complete(); // Called by drawing thread to submit a frame
void Process(); // Called by rendering thread to do window processing
void AssociateSurface(void* surface);
void addCircle(float x, float y, float radius, float r, float g, float b, bool fill);
void addText(float x, float y, float r, float g, float b, OVR::String text);
static ImageWindow* GlobalWindow(int window) {
return globalWindow[window];
}
static int WindowCount() {
return windowCount;
}
private:
Ptr<Frame> lastUnreadyFrame();
static const int MaxWindows = 4;
static ImageWindow* globalWindow[MaxWindows];
static int windowCount;
static ID2D1Factory* pD2DFactory;
static IDWriteFactory* pDWriteFactory;
static HINSTANCE hInstD2d1;
static HINSTANCE hInstDwrite;
};
#else
class ImageWindow {
public:
// constructors
ImageWindow() {}
ImageWindow(uint32_t width, uint32_t height) {
OVR_UNUSED(width);
OVR_UNUSED(height);
}
virtual ~ImageWindow() {}
void GetResolution(size_t& width, size_t& height) {
width = 0;
height = 0;
}
void OnPaint() {}
void UpdateImage(const uint8_t* imageData, uint32_t width, uint32_t height) {
UpdateImageBW(imageData, width, height);
}
void UpdateImageBW(const uint8_t* imageData, uint32_t width, uint32_t height) {
OVR_UNUSED(imageData);
OVR_UNUSED(width);
OVR_UNUSED(height);
}
void UpdateImageRGBA(const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch) {
OVR_UNUSED(imageData);
OVR_UNUSED(width);
OVR_UNUSED(height);
OVR_UNUSED(pitch);
}
void Complete() {}
void Process() {}
void AssociateSurface(void* surface) {
OVR_UNUSED(surface);
}
void addCircle(float x, float y, float radius, float r, float g, float b, bool fill) {
OVR_UNUSED(x);
OVR_UNUSED(y);
OVR_UNUSED(radius);
OVR_UNUSED(r);
OVR_UNUSED(g);
OVR_UNUSED(b);
OVR_UNUSED(fill);
}
void addText(float x, float y, float r, float g, float b, OVR::String text) {
OVR_UNUSED(x);
OVR_UNUSED(y);
OVR_UNUSED(r);
OVR_UNUSED(g);
OVR_UNUSED(b);
OVR_UNUSED(text);
}
static ImageWindow* GlobalWindow(int window) {
return globalWindow[window];
}
static int WindowCount() {
return windowCount;
}
private:
static const int MaxWindows = 4;
static ImageWindow* globalWindow[4];
static int windowCount;
};
#endif
} // namespace Util
} // namespace OVR
#endif

View File

@ -0,0 +1,86 @@
/************************************************************************************
Filename : Util_LongPollThread.cpp
Content : Allows us to do all long polling tasks from a single thread to minimize deadlock
risk
Created : June 30, 2013
Authors : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#include "Util_LongPollThread.h"
#include "Util_Watchdog.h"
OVR_DEFINE_SINGLETON(OVR::Util::LongPollThread);
namespace OVR {
namespace Util {
void LongPollThread::AddPollFunc(CallbackListener<PollFunc>* func) {
PollSubject.AddListener(func);
}
LongPollThread::LongPollThread() : Terminated(false) {
LongPollThreadHandle = std::make_unique<std::thread>([this] { this->Run(); });
// Must be at end of function
PushDestroyCallbacks();
}
LongPollThread::~LongPollThread() {
OVR_ASSERT(!LongPollThreadHandle->joinable());
}
void LongPollThread::OnThreadDestroy() {
fireTermination();
LongPollThreadHandle->join();
}
void LongPollThread::Wake() {
WakeEvent.SetEvent();
}
void LongPollThread::fireTermination() {
Terminated.store(true, std::memory_order_relaxed);
Wake();
}
void LongPollThread::OnSystemDestroy() {
delete this;
}
void LongPollThread::Run() {
Thread::SetCurrentThreadName("LongPoll");
WatchDog watchdog("LongPoll");
// While not terminated,
do {
watchdog.Feed(10000);
PollSubject.Call();
WakeEvent.Wait(WakeupInterval);
WakeEvent.ResetEvent();
} while (!Terminated.load(std::memory_order_acquire));
}
} // namespace Util
} // namespace OVR

View File

@ -0,0 +1,76 @@
/************************************************************************************
Filename : Util_LongPollThread.h
Content : Allows us to do all long polling tasks from a single thread to minimize deadlock
risk Created : June 30, 2013 Authors : Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#ifndef OVR_Util_LongPollThread_h
#define OVR_Util_LongPollThread_h
#include "Kernel/OVR_Timer.h"
#include "Kernel/OVR_Atomic.h"
#include "Kernel/OVR_Allocator.h"
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_System.h"
#include "Kernel/OVR_Threads.h"
#include "Kernel/OVR_Callbacks.h"
#include <thread>
namespace OVR {
namespace Util {
//-----------------------------------------------------------------------------
// LongPollThread
// This thread runs long-polling subsystems that wake up every second or so
// The motivation is to reduce the number of threads that are running to minimize the risk of
// deadlock
class LongPollThread : public SystemSingletonBase<LongPollThread> {
OVR_DECLARE_SINGLETON(LongPollThread);
virtual void OnThreadDestroy() override;
public:
typedef Delegate0<void> PollFunc;
static const int WakeupInterval = 1000; // milliseconds
void AddPollFunc(CallbackListener<PollFunc>* func);
void Wake();
// debug method for assertion to maintain initialization order for this singleton
static bool IsInitialized();
protected:
CallbackEmitter<PollFunc> PollSubject;
std::atomic<bool> Terminated;
Event WakeEvent;
std::unique_ptr<std::thread> LongPollThreadHandle;
void fireTermination();
void Run();
};
} // namespace Util
} // namespace OVR
#endif // OVR_Util_LongPollThread_h

View File

@ -0,0 +1,288 @@
/************************************************************************************
Filename : Util_SystemGUI.cpp
Content : OS GUI access, usually for diagnostics.
Created : October 20, 2014
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#include "Util_SystemGUI.h"
#include "Kernel/OVR_UTF8Util.h"
#if defined(OVR_OS_WIN32)
#include "Kernel/OVR_Win32_IncludeWindows.h"
#endif // OVR_OS_WIN32
#include "Kernel/OVR_Std.h"
#include "Kernel/OVR_Allocator.h"
#include <stdio.h>
#include <stdarg.h>
#include <memory>
namespace OVR {
namespace Util {
bool DisplayMessageBoxVaList(const char* pTitle, const char* pFormat, va_list argList) {
char buffer[512];
char* pBuffer = buffer;
std::unique_ptr<char[]> pAllocated;
va_list argListSaved;
OVR_VA_COPY(argListSaved, argList);
int result = vsnprintf(
buffer, OVR_ARRAY_COUNT(buffer), pFormat, argList); // Returns the required strlen of buffer.
if (result >= (int)OVR_ARRAY_COUNT(buffer)) // If there was insufficient capacity...
{
pAllocated = std::make_unique<char[]>(result + 1);
pBuffer = pAllocated.get();
va_end(argList); // The caller owns argList and will call va_end on it.
OVR_VA_COPY(argList, argListSaved);
vsnprintf(pBuffer, (size_t)result + 1, pFormat, argList);
}
bool returnValue = DisplayMessageBox(pTitle, pBuffer);
return returnValue;
}
bool DisplayMessageBoxF(const char* pTitle, const char* pFormat, ...) {
va_list argList;
va_start(argList, pFormat);
bool returnValue = DisplayMessageBoxVaList(pTitle, pFormat, argList);
va_end(argList);
return returnValue;
}
#if defined(OVR_OS_MS)
// On Windows we implement a manual dialog message box. The reason for this is that there's no way
// to
// have a message box like this without either using MFC or WinForms or relying on Windows Vista+.
bool DisplayMessageBox(const char* pTitle, const char* pText) {
#define ID_EDIT 100
struct Dialog {
static size_t LineCount(const char* pText) {
size_t count = 0;
while (*pText) {
if (*pText++ == '\n')
count++;
}
return count;
}
static WORD* WordUp(WORD* pIn) {
return (WORD*)((((uintptr_t)pIn + 3) >> 2) << 2);
}
static INT_PTR CALLBACK Proc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) {
switch (iMsg) {
case WM_INITDIALOG: {
HWND hWndEdit = GetDlgItem(hDlg, ID_EDIT);
const wchar_t* pText = (const wchar_t*)lParam;
SetWindowTextW(hWndEdit, pText);
HFONT hFont = CreateFontW(
-11,
0,
0,
0,
FW_DONTCARE,
FALSE,
FALSE,
FALSE,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH,
L"Courier New");
if (hFont)
SendMessageW(hWndEdit, WM_SETFONT, WPARAM(hFont), TRUE);
SendMessageW(hWndEdit, EM_SETSEL, (WPARAM)0, (LPARAM)0);
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_EDIT: {
// Handle messages from the edit control here.
HWND hWndEdit = GetDlgItem(hDlg, ID_EDIT);
SendMessageW(hWndEdit, EM_SETSEL, (WPARAM)0, (LPARAM)0);
return TRUE;
}
case IDOK:
EndDialog(hDlg, 0);
return TRUE;
case IDABORT:
_exit(0); // We don't call abort() because the visual studio runtime
// will capture the signal and throw up another dialog
}
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
return TRUE;
}
return FALSE;
}
};
char dialogTemplateMemory[1024];
memset(dialogTemplateMemory, 0, sizeof(dialogTemplateMemory));
LPDLGTEMPLATEW pDlg = (LPDLGTEMPLATEW)dialogTemplateMemory;
const size_t textLength = strlen(pText);
const size_t textLineCount = Dialog::LineCount(pText);
// Sizes are in Windows dialog units, which are relative to a character size. Depends on the font
// and environment settings. Often the pixel size will be ~3x the dialog unit x size. Often the
// pixel size will be ~3x the dialog unit y size.
const int kGutterSize = 6; // Empty border space around controls within the dialog
const int kButtonWidth = 30;
const int kButtonHeight = 10;
const int kDialogWidth =
((textLength > 1000)
? 600
: ((textLength > 400) ? 300 : 200)); // To do: Clip this against screen bounds.
const int kDialogHeight =
((textLineCount > 100) ? 400
: ((textLineCount > 25) ? 300 : ((textLineCount > 10) ? 200 : 100)));
// Define a dialog box.
pDlg->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
pDlg->cdit = 3; // Control count
pDlg->x = 10; // X position To do: Center the dialog.
pDlg->y = 10;
pDlg->cx = (short)kDialogWidth;
pDlg->cy = (short)kDialogHeight;
WORD* pWord = (WORD*)(pDlg + 1);
*pWord++ = 0; // No menu
*pWord++ = 0; // Default dialog box class
WCHAR* pWchar = (WCHAR*)pWord;
const size_t titleLength = strlen(pTitle);
size_t wcharCount = OVR::UTF8Util::Strlcpy(pWchar, 128, pTitle, titleLength);
pWord += wcharCount + 1;
// Define an OK button.
pWord = Dialog::WordUp(pWord);
PDLGITEMTEMPLATEW pDlgItem = (PDLGITEMTEMPLATEW)pWord;
pDlgItem->x = pDlg->cx - (kGutterSize + kButtonWidth);
pDlgItem->y = pDlg->cy - (kGutterSize + kButtonHeight);
pDlgItem->cx = kButtonWidth;
pDlgItem->cy = kButtonHeight;
pDlgItem->id = IDOK;
pDlgItem->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
pWord = (WORD*)(pDlgItem + 1);
*pWord++ = 0xFFFF;
*pWord++ = 0x0080; // button class
pWchar = (WCHAR*)pWord;
pWchar[0] = 'O';
pWchar[1] = 'K';
pWchar[2] = '\0'; // Not currently localized.
pWord += 3; // OK\0
*pWord++ = 0; // no creation data
// Define a QUIT button
pWord = Dialog::WordUp(pWord);
pDlgItem = (PDLGITEMTEMPLATEW)pWord;
pDlgItem->x = pDlg->cx - ((kGutterSize + kButtonWidth) * 2);
pDlgItem->y = pDlg->cy - (kGutterSize + kButtonHeight);
pDlgItem->cx = kButtonWidth;
pDlgItem->cy = kButtonHeight;
pDlgItem->id = IDABORT;
pDlgItem->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
pWord = (WORD*)(pDlgItem + 1);
*pWord++ = 0xFFFF;
*pWord++ = 0x0080; // button class
pWchar = (WCHAR*)pWord;
pWchar[0] = 'Q';
pWchar[1] = 'U';
pWchar[2] = 'I';
pWchar[3] = 'T';
pWchar[4] = '\0'; // Not currently localized.
pWord += 5; // QUIT\0
*pWord++ = 0; // no creation data
// Define an EDIT contol.
pWord = Dialog::WordUp(pWord);
pDlgItem = (PDLGITEMTEMPLATEW)pWord;
pDlgItem->x = kGutterSize;
pDlgItem->y = kGutterSize;
pDlgItem->cx = pDlg->cx - (kGutterSize + kGutterSize);
pDlgItem->cy = pDlg->cy - (kGutterSize + kButtonHeight + kGutterSize + (kGutterSize / 2));
pDlgItem->id = ID_EDIT;
pDlgItem->style = ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN |
ES_READONLY | WS_VSCROLL | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
pWord = (WORD*)(pDlgItem + 1);
*pWord++ = 0xFFFF;
*pWord++ = 0x0081; // edit class atom
*pWord++ = 0; // no creation data
// We use SafeMMapAlloc instead of malloc/new because we may be in a situation with a broken heap,
// which triggered this message box.
LRESULT ret = 0;
size_t textBuffWCapacity = textLength + 1;
wchar_t* textBuffW = (wchar_t*)OVR::SafeMMapAlloc(textBuffWCapacity * sizeof(wchar_t));
if (textBuffW) {
OVR::UTF8Util::Strlcpy(textBuffW, textBuffWCapacity, pText, textLength);
ret =
DialogBoxIndirectParamW(NULL, (LPDLGTEMPLATEW)pDlg, NULL, Dialog::Proc, (LPARAM)textBuffW);
OVR::SafeMMapFree(textBuffW, textBuffWCapacity * sizeof(wchar_t));
}
return (ret != 0);
}
#elif defined(OVR_OS_MAC)
// For Apple we use the Objective C implementation in Util_GUI.mm
#else
// To do.
bool DisplayMessageBox(const char* pTitle, const char* pText) {
printf("\n\nMessageBox\n%s\n", pTitle);
printf("%s\n\n", pText);
return false;
}
#endif
} // namespace Util
} // namespace OVR

View File

@ -0,0 +1,43 @@
/************************************************************************************
Filename : Util_SystemGUI.h
Content : OS GUI access, usually for diagnostics.
Created : October 20, 2014
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#ifndef OVR_Util_SystemGUI_h
#define OVR_Util_SystemGUI_h
#include <stdarg.h>
namespace OVR {
namespace Util {
// Displays a modal message box on the default GUI display (not on a VR device).
// The message box interface (e.g. OK button) is not localized.
bool DisplayMessageBox(const char* pTitle, const char* pText);
bool DisplayMessageBoxF(const char* pTitle, const char* pFormat, ...);
bool DisplayMessageBoxVaList(const char* pTitle, const char* pFormat, va_list argList);
} // namespace Util
} // namespace OVR
#endif // OVR_Util_SystemGUI_h

View File

@ -0,0 +1,484 @@
/************************************************************************************
Filename : Util_SystemInfo.h
Content : Various operations to get information about the system
Created : September 26, 2014
Author : Kevin Jenkins
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
#ifndef OVR_Util_SystemInfo_h
#define OVR_Util_SystemInfo_h
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_Types.h"
#include "Kernel/OVR_Array.h"
#include "Kernel/OVR_DebugHelp.h"
#include <vector>
#include <set>
#include <string>
#if (__cplusplus >= 201703L) || \
(defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L) && (_MSC_VER >= 1913))
#define OVR_UTIL_STD_FILESYSTEM_AVAILABLE
#include <filesystem>
#endif // (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L) && (_MSC_VER
// >= 1913))
namespace OVR {
namespace Util {
//-----------------------------------------------------------------------------
// Utility functions
//
bool Is64BitWindows();
const char* OSAsString();
String OSVersionAsString();
uint64_t GetGuidInt();
String GetGuidString();
const char* GetProcessInfo();
String GetCameraDriverVersion();
void GetGraphicsCardList(OVR::Array<OVR::String>& gpus);
String GetProcessorInfo();
enum WindowsVersion { Windows7_SP1 = 0, Windows8, Windows8_1, Windows10, Windows10_TH2 };
bool IsAtLeastWindowsVersion(WindowsVersion version);
bool IsAtMostWindowsVersion(WindowsVersion version);
#ifdef OVR_UTIL_STD_FILESYSTEM_AVAILABLE
// Returns the current module (e.g. exe or dll) path. If executed within a DLL, it returns the
// DLL path.
std::filesystem::path GetCurrentModuleDirectoryPath();
#endif // OVR_UTIL_STD_FILESYSTEM_AVAILABLE
// Checks if the computer is currently locked
bool CheckIsComputerLocked();
//-----------------------------------------------------------------------------
// SettingsManager
//
// This is a portable interface replacement for Windows-specific registry key functions.
//
// For all path-based functions:
// - Paths are UTF8-encoded.
// - Paths are case-insensitive.
// - Path follows Unix path conventions (e.g. /a/b/c/d).
//
// Windows platform:
// Applications may use Registry setting paths to read the Windows registry.
// An example Windows path is:
// "/HKEY_LOCAL_MACHINE/Software/Oculus/ForceGPUDriverVersionAcceptance"
// Windows registry path reading for Win32 apps on Win64 OSs does not change
// the path to insert Wow64 paths, though users of this functionality may explicitly
// use "WoW64" in paths.
//
// Example Windows usage with default paths:
// mgr.SetDefaultLocation("/HKEY_LOCAL_MACHINE/Software/Oculus/");
// ...
// mgr.ReadValue("EnableTimeouts", timeoutsEnabled);
// mgr.ReadValue("FileName", test.tga);
// mgr.ReadValue("Graphics/ATWEnabled", aswEnabled);
//
// Example Windows usage with full paths:
// bool result = mgr.ReadValue("/HKEY_LOCAL_MACHINE/Software/Oculus/EnablePTW", ptwEnabled);
// bool result = mgr.WriteValue("/HKEY_LOCAL_MACHINE/Software/Oculus/FileName", "test.tga");
//
class SettingsManager {
public:
SettingsManager() = default;
~SettingsManager() = default;
enum Options {
OptionNone = 0x00,
OptionUse32BitDatabase = 0x01, // Windows: This means to use KEY_WOW64_32KEY
OptionIgnoreAlternativeLocations = 0x02
};
// ReadValue:
// - Return true if value exists and could be read, false if doesn't exist or can't be read.
// - The value is written to only if the setting could be read (true return value).
// - If the value was not originally written as the requested type,
// it is interpreted as the type.
// - If the path is relative path (no initial '/' char) then the default location
// is prefixed to path.
bool ReadValue(const char* path, bool& value, int options = OptionNone);
bool ReadValue(const char* path, int32_t& value, int options = OptionNone);
bool ReadValue(const char* path, uint32_t& value, int options = OptionNone);
bool ReadValue(const char* path, unsigned long& value, int options = OptionNone); // a.k.a. DWORD
bool ReadValue(const char* path, int64_t& value, int options = OptionNone);
bool ReadValue(const char* path, uint64_t& value, int options = OptionNone);
bool ReadValue(const char* path, float& value, int options = OptionNone);
bool ReadValue(const char* path, double& value, int options = OptionNone);
bool ReadValue(const char* path, std::string& value, int options = OptionNone);
bool ReadValue(const char* path, std::wstring& value, int options = OptionNone);
bool ReadValue(const char* path, std::vector<uint8_t>& value, int options = OptionNone);
bool ReadValue(const char* path, uint8_t* data, size_t& dataSize, int options = OptionNone);
// WriteValue:
// - Creates the setting if not already present
// - Return true if the setting could be created (if needed) and written.
// - Writes only the given path and not alternative locations.
// - A value of a given type can be overwritten with a value of a different type.
bool WriteValue(const char* path, bool value, int options = OptionNone);
bool WriteValue(const char* path, int32_t value, int options = OptionNone);
bool WriteValue(const char* path, uint32_t value, int options = OptionNone);
bool WriteValue(const char* path, int64_t value, int options = OptionNone);
bool WriteValue(const char* path, uint64_t value, int options = OptionNone);
bool WriteValue(const char* path, float value, int options = OptionNone);
bool WriteValue(const char* path, double value, int options = OptionNone);
bool WriteValue(const char* path, const char* value, size_t valStrlen, int options = OptionNone);
bool WriteValue(const char* path, const std::string& str, int options = OptionNone);
bool WriteValue(const char* path, const wchar_t* val, size_t valStrlen, int options = OptionNone);
bool WriteValue(const char* path, const std::wstring& str, int options = OptionNone);
bool WriteValue(const char* path, const uint8_t* data, size_t dataSize, int options = OptionNone);
// Returns true if the value can be read and exists.
bool ValueExists(const char* path, int options = OptionNone) const;
// Returns true if the value could be deleted. Returns false if the value was present but deletion
// failed. wasPresent indicates if the value was present before deletion was attempted. Deletion
// affects the path and any registered alternative locations, unless
// OptionIgnoreAlternativeLocations is specified.
bool DeleteValue(const char* path, bool* wasPresent = nullptr, int options = OptionNone);
// Sets the default location for read and written value paths.
// Path must be a "full path", which begins with a path separator.
// Path must end with trailing path separtor.
// Defaults to empty.
// Example usage:
// settingsMgr.SetDefaultLocation("/HKEY_LOCAL_MACHINE/Software/Oculus/");
void SetDefaultLocation(const char* parentPath);
#define OVR_DEFAULT_SETTINGS_LOCATION "/HKEY_LOCAL_MACHINE/Software/Oculus/"
// Returns the default location, which can be set by SetDefaultLocation.
std::string GetDefaultLocation() const;
// Adds an alternative location for value reads.
// Path must end with trailing path separtor.
// This function exists for the purpose of allowing migration of old Oculus settings
// paths to a single unified path.
// This function is not thread safe. It can be called from only a single thread at a time.
// Example usage:
// settingsMgr.AddAlternativeLocation("/HKEY_LOCAL_MACHINE/Software/Oculus2/");
void AddAlternativeLocation(const char* parentPath);
// This function is not thread safe. It can be called from only a single thread at a time.
// Example usage:
// settingsMgr.RemoveAlternativeLocation("/HKEY_LOCAL_MACHINE/Software/Oculus2/");
void RemoveAlternativeLocation(const char* parentPath);
struct InsensitiveCompare {
bool operator()(const std::string& a, const std::string& b) const {
return OVR_stricmp(a.c_str(), b.c_str()) < 0;
}
};
typedef std::set<std::string, InsensitiveCompare> StringSet;
// Gets a set of all currently registered alternative locations.
StringSet GetAlternativeLocations() const;
// Sets Oculus-specific alternative locations.
// This function is not thread safe. It can be called from only a single thread at a time.
void SetDefaultAlternativeLocations();
// Returns the values present at the given parentPath.
// Writes a single line per enumerated value, with each line followed by a newline,
// including the last line.
// The parentPath must begin with '/' and end with '/' or be NULL to indicate usage of the
// default path.
// Example usage:
// str = settingsMgr.EnumerateValues("/HKEY_LOCAL_MACHINE/Software/Oculus/");
// Note:
// When this data is backed by the Windows registry, the enumerated types won't be the
// same as the written types. For example, WriteDouble writes to the Windows registry as
// a binary value, and will be enumerated by EnumerateValues as binary instead of float.
std::string EnumerateValues(const char* parentPath = nullptr, int options = OptionNone) const;
protected:
// This is a parent settings directory which acts as the default if value
// reads and writes do not include a parent path.
// Example Windows value: "/HKEY_LOCAL_MACHINE/Software/Oculus2/"
std::string defaultLocation;
#ifdef _WIN32
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx
struct Win32RegistryAnyValue {
uint32_t type; // Which REG_XXX type is used.
std::vector<uint8_t> binaryData; // REG_BINARY
uint32_t dwordData; // REG_DWORD
uint64_t qwordData; // REG_QWORD
std::string stringData; // REG_SZ or REG_EXPAND_SZ
std::vector<std::string> stringArrayData; // REG_MULTI_SZ
Win32RegistryAnyValue();
};
// Reads the Windows registry at the given path to a Win32RegistryAnyValue.
bool Win32ReadAnyRegistryValue(const char* path, Win32RegistryAnyValue& any, int options) const;
// Reads the Windows registry value (sourceData/sourceDataSize) to a Win32RegistryAnyValue.
bool Win32ReadAnyRegistryValue(
DWORD dwType,
uint8_t* sourceData,
size_t sourceDataSize,
Win32RegistryAnyValue& anyValue) const;
// Oculus unfortunately has a history of divergent Windows registry key storage locations.
// As part of the effort to converge on a single location, we provide backward-compatible
// old locations to check when reading values, so existing users that have registry keys at
// the old locations can still have their settings work. The locations are the parent
// registry key "directory". So for the "/HKEY_LOCAL_MACHINE/Software/Oculus LLC/Test" key,
// the location would be stored here as "/HKEY_LOCAL_MACHINE/Software/Oculus LLC/"
StringSet RegistryAlternativeLocations;
#endif
};
extern SettingsManager DefaultSettingsManager;
#ifdef OVR_OS_MS
//--------------------------------------------------------------------------------------------------
// Windows registry functions
//
// Use these functions only for reading non-Oculus registry data. For Oculus settings, use the
// SettingsManager instead, which supplants direct Windows registry usage.
//--------------------------------------------------------------------------------------------------
enum class RegistryDB {
normal, // KEY_WOW64_64KEY. 64 bit apps use 64 bit database, 32 bit apps use 32 bit database.
wow64_32 // KEY_WOW64_32KEY. Force looking at the 32 bit database even if this is a 64 bit app.
};
enum class RegistryBase {
localMachine, // HKEY_LOCAL_MACHINE
currentUser // HKEY_CURRENT_USER
};
// Returns true if a registry key of the given type is present and can be interpreted as a boolean,
// otherwise
// returns defaultValue. It's not possible to tell from a single call to this function if the given
// registry key
// was present. For Strings, boolean means (atoi(str) != 0). For DWORDs, boolean means (dw != 0).
// If registryDB is RegistryDB::normal then KEY_WOW64_32KEY is used in the registry lookup.
// If registryBase is RegistryBase::currentUser then the HKEY_CURRENT_USER root is used
// instead of HKEY_LOCAL_MACHINE
bool GetRegistryBoolW(
const wchar_t* pSubKey,
const wchar_t* stringName,
bool defaultValue,
RegistryDB registryDB = RegistryDB::normal,
RegistryBase registryBase = RegistryBase::localMachine);
// Returns true if a DWORD-type registry key of the given name is present, else sets out to 0 and
// returns false.
// If registryDB is RegistryDB::normal then KEY_WOW64_32KEY is used in the registry lookup.
// If registryBase is RegistryBase::currentUser then the HKEY_CURRENT_USER root is used
// instead of HKEY_LOCAL_MACHINE
bool GetRegistryDwordW(
const wchar_t* pSubKey,
const wchar_t* stringName,
DWORD& out,
RegistryDB registryDB = RegistryDB::normal,
RegistryBase registryBase = RegistryBase::localMachine);
// Returns true if a string-type registry key of the given name is present, else sets out to empty
// and returns false.
// The output string will always be 0-terminated, but may be empty.
// If registryDB is RegistryDB::normal then KEY_WOW64_32KEY is used in the registry lookup.
// If registryBase is RegistryBase::currentUser then the HKEY_CURRENT_USER root is used
// instead of HKEY_LOCAL_MACHINE
bool GetRegistryStringW(
const wchar_t* pSubKey,
const wchar_t* stringName,
wchar_t out[MAX_PATH],
RegistryDB registryDB = RegistryDB::normal,
RegistryBase registryBase = RegistryBase::localMachine);
// Returns true if the DWORD value could be successfully written to the registry.
// If registryDB is RegistryDB::normal then KEY_WOW64_32KEY is used in the registry lookup.
// If registryBase is RegistryBase::currentUser then the HKEY_CURRENT_USER root is used
// instead of HKEY_LOCAL_MACHINE
bool SetRegistryDwordW(
const wchar_t* pSubKey,
const wchar_t* stringName,
DWORD newValue,
RegistryDB registryDB = RegistryDB::normal,
RegistryBase registryBase = RegistryBase::localMachine);
// Returns true if the value could be successfully deleted from the registry.
// If registryDB is RegistryDB::normal then KEY_WOW64_32KEY is used in the registry lookup.
// If registryBase is RegistryBase::currentUser then the HKEY_CURRENT_USER root is used
// instead of HKEY_LOCAL_MACHINE
bool DeleteRegistryValue(
const wchar_t* pSubKey,
const wchar_t* stringName,
RegistryDB registryDB = RegistryDB::normal,
RegistryBase registryBase = RegistryBase::localMachine);
#endif // OVR_OS_MS
//-----------------------------------------------------------------------------
// Get the path where the Oculus runtime output is written.
//
// GetBaseOVRPath() returns the top level Oculus output path.
// On Windows, this is %LOCALAPPDATA%\Oculus.
//
// GetBaseOVRPathW() has the identical behavior as GetBaseOVRPath(), but
// returns its result in a std::wstring.
//
// GetOVRPath() returns a path that represents a subdirectory from the top-level
// Oculus output path. (The top-level path is what GetBaseOVRPath() returns.)
//
// GetOVRPathW() has the identical behavior as GetOVRPath(), but returns its
// result in a std::wstring.
String GetBaseOVRPath(bool create_dir);
std::wstring GetBaseOVRPathW(bool create_dir);
String GetOVRPath(const wchar_t* subPath, bool create_dir);
std::wstring GetOVRPathW(const wchar_t* subPath, bool create_dir);
//-----------------------------------------------------------------------------
// Process path information
// Returns the file path for the binary associated with the given runtime process id.
// Returns an empty string if the process id is invalid or could not be read.
// If enableErrorResults is true then failure strings will be written to the returned
// value in parentheses. This is useful for logging.
std::string GetProcessPath(pid_t processId, bool fileNameOnly, bool enableErrorResults);
#if defined(_WIN32)
// Returns the file path for the binary associated with the given runtime process id.
// Returns an empty string if the process handle is invalid or could not be read.
// If enableErrorResults is true then failure strings will be written to the returned
// value in parentheses. This is useful for logging.
std::string GetProcessPath(HANDLE processHandle, bool fileNameOnly, bool enableErrorResults);
#endif
// Returns true if the process was loaded outside of our Oculus store directory.
bool IsProcessSideLoaded(const std::string& processPath);
// Same as GetProcessPath, except scrubs the returned process path string if it's something we have
// deemed
// cannot be reported in our log, usually due to privacy measures we have enacted.
std::string GetLoggableProcessPath(pid_t processId, bool fileNameOnly);
#if defined(_WIN32)
// Same as GetProcessPath, except scrubs the returned process path string if it's something we have
// deemed
// cannot be reported in our log, usually due to privacy measures we have enacted.
std::string GetLoggableProcessPath(HANDLE processHandle, bool fileNameOnly);
// Retrives the root of the Oculus install directory
// The returned string is a directory path and does not have a trailing path separator.
// Example return value: L"C:\Program Files// (x86)\Oculus\Support\oculus-runtime"
std::wstring GetOVRRuntimePathW();
#endif
// Returns a string with a comma-separated list of tags for the machine.
// The tags are obtained from the registry.
String GetMachineTags();
#if defined(_WIN32)
String GetFileVersionStringW(const wchar_t filePath[MAX_PATH]);
String GetSystemFileVersionStringW(const wchar_t filePath[MAX_PATH]);
#endif
//-----------------------------------------------------------------------------
// Retrieves memory usage info for the current process.
//
// Do not call this function frequently as it takes hundreds of microseconds to
// execute on typical computers.
struct ProcessMemoryInfo {
uint64_t UsedMemory; // Same as Windows working set size.
// https://msdn.microsoft.com/en-us/library/windows/desktop/cc441804%28v=vs.85%29.aspx
};
ProcessMemoryInfo GetCurrentProcessMemoryInfo();
//-----------------------------------------------------------------------------
// Retrieves memory info for the system (counterpart of GetCurrentProcessMemoryInfo)
//
// Do not call this function frequently as it takes a long time to
// execute on typical computers.
struct SystemMemoryInfo {
double PageFault; // System-wide hard page faults per sec
uint64_t CommittedTotal; // Total number of committed memory (in bytes) on the system
};
SystemMemoryInfo GetSystemMemoryInfo();
enum class CPUInstructionSet {
Unknown,
Basic, // Just basic 32 bit 386.
SEE1,
SSE2,
SSE3,
SSSE3, // Supplementary SSE3
SSE41,
SSE42,
AVX1,
AVX2
};
//-----------------------------------------------------------------------------
// Indicates the minimum instruction set that the CPU + OS support. Note that an older OS may
// not properly support a given CPU instruction set, usually because it doesn't know how to
// preserve its registers on context switch.
CPUInstructionSet GetSupportedCPUInstructionSet(bool* popcntSupported, bool* lzcntSupported);
//-----------------------------------------------------------------------------
// Implements ModuleInfo lookup functionalitity.
// This class is separate from the OVR::SymbolLookup class, which has some
// similar functionality, because that class has extra wiring to support (heavy)
// symbol lookups, which we don't want here and which would put a lot more weight
// on the usage of this functionality (which we want to be able to call frequently
// at runtime).
//
class ModuleInfoLookup {
public:
ModuleInfoLookup() = default;
~ModuleInfoLookup() = default;
const OVR::ModuleInfo* GetModuleInfoForAddress(uint64_t address);
const OVR::ModuleInfo& GetModuleInfoForCurrentModule();
bool GetAddressIsFromCurrentModule(uint64_t address);
// All process ModuleInfo, sorted by address.
std::vector<OVR::ModuleInfo> ModuleInfoArray;
// ModuleInfo for the current module. Not necessarily the main executable module.
OVR::ModuleInfo CurrentModuleInfo;
};
extern ModuleInfoLookup DefaultModuleInfoLookup;
} // namespace Util
} // namespace OVR
#endif // OVR_Util_SystemInfo_h

View File

@ -0,0 +1,335 @@
/************************************************************************************
Filename : Util_Watchdog.cpp
Content : Deadlock reaction
Created : June 27, 2013
Authors : Kevin Jenkins, Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#include "Util_Watchdog.h"
#include <Logging/Logging_Library.h>
#include "Kernel/OVR_DebugHelp.h"
#include "Kernel/OVR_Win32_IncludeWindows.h"
#if defined(OVR_OS_LINUX) || defined(OVR_OS_MAC)
#include <unistd.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#if defined(OVR_OS_LINUX)
#include <sys/wait.h>
#endif
#endif
OVR_DEFINE_SINGLETON(OVR::Util::WatchDogObserver);
namespace OVR {
namespace Util {
// Watchdog class default threshold before announcing a long cycle
const int DefaultThreshholdMsec = 60000; // milliseconds
//-----------------------------------------------------------------------------
// Tools
static uint32_t GetFastMsTime() {
#if defined(OVR_OS_MS)
return ::GetTickCount();
#else
return Timer::GetTicksMs();
#endif
}
static std::string SanitizeString(const char* cstr) {
std::ostringstream ss;
char ch;
while (ch = *cstr++, ch != '\0') {
if (ch < ' ' || ch == '\"' || ch == '\'' || ch == '\\' || ch == '`' || ch > '~')
ch = '_';
ss << ch;
}
return ss.str();
}
//-----------------------------------------------------------------------------
// WatchDogObserver
static ovrlog::Channel Logger("Kernel:Watchdog");
WatchDogObserver::WatchDogObserver()
: ListLock(),
DogList(),
IsReporting(false),
TerminationEvent(),
DeadlockSeen(false),
AutoTerminateOnDeadlock(true),
ApplicationName(),
OrganizationName(),
WriteMiniDump(nullptr),
AddBreakpadInfoClient(nullptr) {
WatchdogThreadHandle = std::make_unique<std::thread>([this] { this->Run(); });
// Must be at end of function
PushDestroyCallbacks();
}
WatchDogObserver::~WatchDogObserver() {
OVR_ASSERT(!WatchdogThreadHandle->joinable());
}
void WatchDogObserver::OnThreadDestroy() {
Logger.LogDebug("Setting TerminationEvent for watchdog thread.");
TerminationEvent.SetEvent();
WatchdogThreadHandle->join();
}
void WatchDogObserver::OnSystemDestroy() {
// NOTE: Explicitly allowed to do this by the top-level comment of OnSystemDestroy()
delete this;
}
void WatchDogObserver::OnDeadlock(const String& deadlockedThreadName) {
// If is reporting deadlock details:
if (IsReporting) {
if (SymbolLookup::Initialize()) {
// Static to avoid putting 32 KB on the stack. This is only called once, so it's safe.
static SymbolLookup symbolLookup;
String threadListOutput, moduleListOutput;
symbolLookup.ReportThreadCallstacks(threadListOutput);
symbolLookup.ReportModuleInformation(moduleListOutput);
Logger.LogWarning(
"---DEADLOCK STATE---\n\n",
threadListOutput.c_str(),
"\n\n",
moduleListOutput.c_str(),
"\n---END OF DEADLOCK STATE---");
}
// For internal builds done by our engineers, we write minidumps as per the settings below. We
// don't generate breakpad minidumps nor upload breakpad crash reports. For non-internal builds
// (typically public builds), we generate breakpad mindumps and will upload them as necessary.
if (WriteMiniDump != nullptr) {
// non-internal builds
OVR_ASSERT(AddBreakpadInfoClient != nullptr);
AddBreakpadInfoClient("is_deadlock_minidump", "true");
// deadlockedThreadName might contain special characters
std::string threadName = SanitizeString(deadlockedThreadName.c_str());
AddBreakpadInfoClient("deadlock_thread_name", threadName.c_str());
WriteMiniDump(nullptr);
} else {
// internal builds
ExceptionHandler::ReportDeadlock(deadlockedThreadName, OrganizationName, ApplicationName);
}
// Disable reporting after the first deadlock report
DisableReporting();
}
DeadlockSeen = true;
Logger.LogError(
"WatchDogObserver::OnDeadlock: Deadlock detected in thread '",
deadlockedThreadName.c_str(),
"'");
if (OVRIsDebuggerPresent()) {
Logger.LogWarning(
"WatchDogObserver::OnDeadlock: Aborting termination since debugger is present. Normally the app would terminate itself here");
} else if (AutoTerminateOnDeadlock) {
Logger.LogError(
"WatchDogObserver::OnDeadlock: Waiting ",
TerminationDelayMsec,
" msec until deadlock termination");
if (!TerminationEvent.Wait(TerminationDelayMsec)) {
#if defined(_WIN32) // To do: Implement a generic OVR library terminate self API. With a clean
// option (exit()) and drastic option (TerminateProcess()).
::TerminateProcess(GetCurrentProcess(), 0xd00ddead);
#else
exit(0xd00ddead);
#endif
}
Logger.LogError(
"WatchDogObserver::OnDeadlock: Deadlock termination aborted - Graceful shutdown");
}
}
int WatchDogObserver::Run() {
Thread::SetCurrentThreadName("WatchDog");
Logger.LogDebug("WatchDogObserver::Run: Starting watchdog thread");
// Milliseconds between checks
static const int kWakeupIntervalMsec = 4000; // 4 seconds
// Number of consecutive long cycles before the watchdog dumps a minidump
static const int kLongCycleTimeLimitMsec = 60000; // 1 minute
static const int kMaxConsecutiveLongCycles = kLongCycleTimeLimitMsec / kWakeupIntervalMsec;
// By counting consecutive long cycles instead of using a timeout, we prevent false positives
// from debugger breakpoints and sleep/resume of the OS.
int ConsecutiveLongCycles = 0; // Number of long cycles seen in a row
// While not requested to terminate:
while (!TerminationEvent.Wait(kWakeupIntervalMsec)) {
bool sawLongCycle = false;
String deadlockedThreadName;
{
Lock::Locker locker(&ListLock);
const uint32_t t1 = GetFastMsTime();
const int count = DogList.GetSizeI();
for (int i = 0; i < count; ++i) {
const WatchDog* dog = DogList[i];
const int threshold = dog->ThreshholdMilliseconds;
const uint32_t t0 = dog->WhenLastFedMilliseconds;
// If threshold exceeded, assume there is thread deadlock of some sort.
int delta = static_cast<int>(t1 - t0);
// Include an upper bound in case the computer went to sleep
if (delta > threshold && (ConsecutiveLongCycles > 0 || delta < threshold * 5)) {
sawLongCycle = true;
deadlockedThreadName = dog->ThreadName;
Logger.LogWarning(
"WatchDogObserver::Run: Long cycle detected ",
ConsecutiveLongCycles + 1,
"x (max=",
kMaxConsecutiveLongCycles,
") in thread '",
deadlockedThreadName.c_str(),
"'");
}
}
}
// If we did not see a long cycle:
if (!sawLongCycle) {
if (ConsecutiveLongCycles > 0) {
// Reset log cycle count
ConsecutiveLongCycles = 0;
Logger.LogWarning("WatchDogObserver::Run: Recovered from long cycles");
}
// Reset deadlock seen flag
DeadlockSeen = false;
} else if (++ConsecutiveLongCycles >= kMaxConsecutiveLongCycles) {
// Since it requires consecutive long cycles, waking up from sleep/resume will not trigger a
// deadlock.
OnDeadlock(deadlockedThreadName);
}
}
Logger.LogDebug("WatchDogObserver::Run: Terminating watchdog thread");
return 0;
}
void WatchDogObserver::Add(WatchDog* dog) {
Lock::Locker locker(&ListLock);
if (!dog->Listed) {
Logger.LogDebugF("WatchDogObserver::Add Watchdog: %s", dog->GetThreadName().c_str());
DogList.PushBack(dog);
dog->Listed = true;
} else
Logger.LogDebugF(
"WatchDogObserver::Add Watchdog: %s already added.", dog->GetThreadName().c_str());
}
void WatchDogObserver::Remove(WatchDog* dog) {
Lock::Locker locker(&ListLock);
bool removed = false;
Logger.LogDebugF("WatchDogObserver::Remove Watchdog: %s", dog->GetThreadName().c_str());
if (dog->Listed) {
for (int i = 0; i < DogList.GetSizeI(); ++i) {
if (DogList[i] == dog) {
DogList.RemoveAt(i--);
removed = true;
break;
}
}
dog->Listed = false;
}
if (!removed)
Logger.LogDebugF(
"WatchDogObserver::Remove Watchdog: %s already removed.", dog->GetThreadName().c_str());
}
void WatchDogObserver::EnableReporting(const String& organization, const String& application) {
OrganizationName = organization;
ApplicationName = application;
IsReporting = true;
}
void WatchDogObserver::DisableReporting() {
IsReporting = false;
}
//-----------------------------------------------------------------------------
// WatchDog
WatchDog::WatchDog(const String& threadName)
: ThreshholdMilliseconds(DefaultThreshholdMsec), ThreadName(threadName), Listed(false) {
WhenLastFedMilliseconds = GetFastMsTime();
OVR_ASSERT(!ThreadName.empty());
}
WatchDog::~WatchDog() {
Disable();
}
String WatchDog::GetThreadName() const {
return ThreadName;
}
void WatchDog::Disable() {
WatchDogObserver::GetInstance()->Remove(this);
}
void WatchDog::Enable() {
WatchDogObserver::GetInstance()->Add(this);
}
void WatchDog::Feed(int threshold) {
WhenLastFedMilliseconds = GetFastMsTime();
ThreshholdMilliseconds = threshold;
if (!Listed) {
Enable();
}
}
} // namespace Util
} // namespace OVR

View File

@ -0,0 +1,146 @@
/************************************************************************************
Filename : Util_Watchdog.h
Content : Deadlock reaction
Created : June 27, 2013
Authors : Kevin Jenkins, Chris Taylor
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Licensed under the Oculus VR Rift SDK License Version 3.3 (the "License");
you may not use the Oculus VR Rift SDK except in compliance with the License,
which is provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
You may obtain a copy of the License at
http://www.oculusvr.com/licenses/LICENSE-3.3
Unless required by applicable law or agreed to in writing, the Oculus VR SDK
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************************/
#ifndef OVR_Util_Watchdog_h
#define OVR_Util_Watchdog_h
#include "Kernel/OVR_Timer.h"
#include "Kernel/OVR_Atomic.h"
#include "Kernel/OVR_Allocator.h"
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_System.h"
#include "Kernel/OVR_Threads.h"
#include <thread>
namespace OVR {
namespace Util {
//-----------------------------------------------------------------------------
// WatchDog
class WatchDog : public NewOverrideBase {
friend class WatchDogObserver;
public:
WatchDog(const String& threadName);
~WatchDog();
String GetThreadName() const;
void Disable();
void Enable();
void Feed(int threshold);
protected:
// Use 32 bit int so assignment and comparison is atomic
std::atomic<uint32_t> WhenLastFedMilliseconds = {0};
std::atomic<int> ThreshholdMilliseconds = {0};
String ThreadName;
bool Listed;
};
//-----------------------------------------------------------------------------
// WatchDogObserver
class WatchDogObserver : public SystemSingletonBase<WatchDogObserver> {
OVR_DECLARE_SINGLETON(WatchDogObserver);
virtual void OnThreadDestroy() override;
friend class WatchDog;
std::unique_ptr<std::thread> WatchdogThreadHandle;
public:
// Uses the exception logger to write deadlock reports
void EnableReporting(const String& organization = String(), const String& application = String());
// Disables deadlock reports
void DisableReporting();
// This is the delay between deciding a deadlock occurred and terminating the process, to allow
// for logging
const int TerminationDelayMsec = 10000; // 10 seconds in msec
// Enable/disable auto-terminate on deadlock report
// IsDeadlocked() will return true, and after TerminationDelayMsec it will exit the process
void SetAutoTerminateOnDeadlock(bool enable = true) {
AutoTerminateOnDeadlock = enable;
}
// Is currently in a deadlock state?
bool IsDeadlocked() const {
return DeadlockSeen;
}
// Sets the callback used to trigger a Breakpad Minidump write.
// Note: Lock does not need to be held here because only accessed on startup before deadlocks are
// reported.
void SetMiniDumpWriteCallback(void (*pWriteMiniDump)(void*)) {
WriteMiniDump = pWriteMiniDump;
}
// Sets the callback used to add additional info to Breakpad client
void SetAddBreakpadInfoClientCallback(
void (*pAddBreakpadInfoClient)(const char* name, const char* value)) {
AddBreakpadInfoClient = pAddBreakpadInfoClient;
}
protected:
Lock ListLock;
Array<WatchDog*> DogList;
// This indicates that EnableReporting() was requested
bool IsReporting = false;
Event TerminationEvent;
// Has a deadlock been seen?
bool DeadlockSeen = false;
bool AutoTerminateOnDeadlock = true;
// On Windows, deadlock logs are stored in %AppData%\OrganizationName\ApplicationName\.
// See ExceptionHandler::ReportDeadlock() for how these are used.
String ApplicationName;
String OrganizationName;
void OnDeadlock(const String& deadlockedThreadName);
// Breakpad is used to write minidump files.
void (*WriteMiniDump)(void* pExceptionPtrs);
void (*AddBreakpadInfoClient)(const char* name, const char* value);
protected:
int Run();
void Add(WatchDog* dog);
void Remove(WatchDog* dog);
};
} // namespace Util
} // namespace OVR
#endif // OVR_Util_Watchdog_h