Initial commit

This commit is contained in:
2025-12-17 16:47:48 +00:00
commit 13813f3363
4964 changed files with 1079753 additions and 0 deletions

103
engine/core/CMakeLists.txt Normal file
View File

@@ -0,0 +1,103 @@
add_library(Core STATIC)
set(SOURCES
src/AyaAssert.cpp
src/AyaAssert.hpp
src/AyaBase.hpp
src/AyaCrash.cpp
src/AyaDbgInfo.cpp
src/AyaDbgInfo.hpp
src/AyaFormat.cpp
src/AyaFormat.hpp
src/AyaPlatform.hpp
src/AyaStrings.hpp
src/BaldPtr.hpp
src/CEvent.cpp
src/CEvent.hpp
src/CPUCount.cpp
src/CPUCount.hpp
src/Coordinator.cpp
src/Coordinator.hpp
src/Crypt.cpp
src/Crypt.hpp
src/Debug.cpp
src/Debug.hpp
src/Declarations.hpp
src/DenseHash.hpp
src/FastLog.cpp
src/FastLog.hpp
src/FastLogStream.cpp
src/FastLogStream.hpp
src/GlobalVectorItem.hpp
src/HardwareInfo.hpp
src/ImGui.cpp
src/ImGui.hpp
src/Log.cpp
src/Log.hpp
src/MathUtil.cpp
src/MathUtil.hpp
src/Memory.cpp
src/Memory.hpp
src/Nil.hpp
src/ProcessPerfCounter.cpp
src/ProcessPerfCounter.hpp
src/Profiler.cpp
src/Profiler.hpp
src/RegistryUtil.hpp
src/RunningAverage.hpp
src/SafeToLower.hpp
src/ScopedSingleton.hpp
src/SelectState.hpp
src/SimpleJSON.cpp
src/SimpleJSON.hpp
src/StreamHelpers.cpp
src/StringConv.cpp
src/StringConv.hpp
src/SystemUtil.cpp
src/SystemUtil.hpp
src/TaskScheduler.Job.cpp
src/TaskScheduler.Job.hpp
src/TaskScheduler.Thread.cpp
src/TaskScheduler.cpp
src/TaskScheduler.hpp
src/ThreadSafe.cpp
src/atomic.hpp
src/boost.cpp
src/callable.hpp
src/format_string.cpp
src/format_string.hpp
src/intrusive_ptr_target.hpp
src/intrusive_set.hpp
src/intrusive_weak_ptr.hpp
src/make_shared.hpp
src/object_pool.hpp
src/signal.cpp
src/signal.hpp
src/stringbuffer.hpp
src/threadsafe.hpp
src/time.cpp
src/time.hpp
src/trie.hpp
src/ublas_ext.hpp
)
# Platform-specific configuration
if(AYA_OS_ANDROID)
list(APPEND SOURCES
src/ifaddrs.cpp
src/ifaddrs.hpp
)
endif()
target_include_directories(Core
PUBLIC
${ENGINE_DIR}/core/src
PRIVATE
${THIRD_PARTY_DIR}/BulletPhysics/src
${ENGINE_DIR}/app/src
${ENGINE_DIR}/3d/src
${ENGINE_DIR}/gfx/src
${ENGINE_DIR}/gfx/src/API/GL/glad/include
)
target_sources(Core PRIVATE ${SOURCES})

View File

@@ -0,0 +1,810 @@
#pragma once
#include "Debug.hpp"
#include "boost/utility.hpp"
#include "boost/type_traits.hpp"
#include <stdlib.h>
#ifdef __ANDROID__
#include <malloc.h>
#endif
namespace Aya
{
//
// ArrayBase
//
template<class T>
class ArrayBase
{
public:
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef T* iterator;
typedef const T* const_iterator;
typedef T* reverse_iterator;
typedef const T* const_reverse_iterator;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
inline T& operator[](size_t i);
inline const T& operator[](size_t i) const;
inline T& at(size_t i);
inline const T& at(size_t i) const;
inline size_t size() const;
inline T& front();
inline const T& front() const;
inline T& back();
inline const T& back() const;
inline T* begin();
inline const T* begin() const;
inline const T* cbegin() const;
inline T* end();
inline const T* end() const;
inline const T* cend() const;
inline T* data();
inline const T* data() const;
inline const T* cdata() const;
inline bool empty() const;
protected:
inline ArrayBase(T* _data, size_t _size);
inline ArrayBase(const ArrayBase<T>& a);
private:
inline ArrayBase<T>& operator=(const ArrayBase<T>& src);
protected:
T* mData;
size_t mSize;
};
//
// ArrayBase Implementation
//
template<class T>
ArrayBase<T>::ArrayBase(T* _data, size_t _size)
: mData(_data)
, mSize(_size)
{
}
template<class T>
ArrayBase<T>::ArrayBase(const ArrayBase<T>& a)
: mData(a.mData)
, mSize(a.mSize)
{
}
template<class T>
inline T& ArrayBase<T>::operator[](size_t i)
{
AYAASSERT_VERY_FAST(i < mSize);
return mData[i];
}
template<class T>
inline const T& ArrayBase<T>::operator[](size_t i) const
{
AYAASSERT_VERY_FAST(i < mSize);
return mData[i];
}
template<class T>
inline T& ArrayBase<T>::at(size_t i)
{
AYAASSERT_VERY_FAST(i < mSize);
return mData[i];
}
template<class T>
inline const T& ArrayBase<T>::at(size_t i) const
{
AYAASSERT_VERY_FAST(i < mSize);
return mData[i];
}
template<class T>
inline size_t ArrayBase<T>::size() const
{
return mSize;
}
template<class T>
inline T& ArrayBase<T>::front()
{
AYAASSERT_VERY_FAST(mSize > 0);
return mData[0];
}
template<class T>
inline const T& ArrayBase<T>::front() const
{
AYAASSERT_VERY_FAST(mSize > 0);
return mData[0];
}
template<class T>
inline T& ArrayBase<T>::back()
{
AYAASSERT_VERY_FAST(mSize > 0);
return mData[mSize - 1];
}
template<class T>
inline const T& ArrayBase<T>::back() const
{
AYAASSERT_VERY_FAST(mSize > 0);
return mData[mSize - 1];
}
template<class T>
inline T* ArrayBase<T>::begin()
{
return mData;
}
template<class T>
inline const T* ArrayBase<T>::begin() const
{
return mData;
}
template<class T>
inline const T* ArrayBase<T>::cbegin() const
{
return mData;
}
template<class T>
inline T* ArrayBase<T>::end()
{
return mData + mSize;
}
template<class T>
inline const T* ArrayBase<T>::end() const
{
return mData + mSize;
}
template<class T>
inline const T* ArrayBase<T>::cend() const
{
return mData + mSize;
}
template<class T>
inline T* ArrayBase<T>::data()
{
return mData;
}
template<class T>
inline const T* ArrayBase<T>::data() const
{
return mData;
}
template<class T>
inline const T* ArrayBase<T>::cdata() const
{
return mData;
}
template<class T>
inline bool ArrayBase<T>::empty() const
{
return mSize == 0;
}
struct ArrayNoInit
{
};
//
// ArrayDynamic
//
template<class T>
class ArrayDynamic : public ArrayBase<T>
{
public:
static const boost::uint32_t defaultAlignment = 16;
typedef ArrayBase<T> Base;
inline ArrayDynamic();
inline explicit ArrayDynamic(size_t _size, ArrayNoInit, boost::uint32_t _align = defaultAlignment);
inline explicit ArrayDynamic(size_t _size, boost::uint32_t _align = defaultAlignment);
inline ArrayDynamic(const ArrayDynamic<T>& a);
inline ArrayDynamic(const ArrayBase<T>& a);
inline ~ArrayDynamic();
inline void clear();
inline ArrayDynamic<T>& operator=(const ArrayDynamic<T>& _a);
inline ArrayDynamic<T>& operator=(const ArrayBase<T>& _a);
inline void reserve(size_t _capacity);
inline void resize(size_t _size);
inline size_t capacity() const;
inline void push_back(const T& _a);
inline void pop_back();
inline void insert(size_t i, const T& val);
inline T* insert(const T* it, const T& val);
template<class InputType>
inline void insert_count(T* it, InputType first, size_t count);
template<class InputType>
inline void insert(T* it, InputType first, InputType last);
void assign(size_t size, const T& value);
private:
template<class InputType>
inline void copyConstruct(void* dst, InputType src, size_t count);
inline void copyConstruct(void* dst, const T* src, size_t count);
inline void increase_capacity(size_t requestedCapacity);
size_t mCapacity;
bool mNoInit;
boost::uint32_t mAlignment;
};
//
// ArrayDynamic implementation
//
namespace array_dynamic_details
{
inline void* aligned_alloc(std::size_t alignment, std::size_t size) BOOST_NOEXCEPT
{
if (!size)
{
return 0;
}
if (alignment < sizeof(void*))
{
alignment = sizeof(void*);
}
#ifdef _WIN32
void* p = _aligned_malloc(size, alignment);
#elif defined(__ANDROID__)
void* p = ::memalign(alignment, size);
#else
void* p;
if (::posix_memalign(&p, alignment, size) != 0)
{
p = 0;
}
#endif
return p;
}
inline void aligned_free(void* ptr) BOOST_NOEXCEPT
{
#ifdef _WIN32
_aligned_free(ptr);
#else
::free(ptr);
#endif
}
//
// Construct
//
template<class T>
static void construct(void* dst, size_t count, const boost::true_type& hasTrivialConstructor)
{
}
template<class T>
static void construct(void* dst, size_t count, const boost::false_type& hasTrivialConstructor)
{
for (size_t i = 0; i < count; i++)
{
new (reinterpret_cast<char*>(dst) + i * sizeof(T)) T();
}
}
template<class T>
static void construct(void* dst, size_t count)
{
construct<T>(dst, count, boost::has_trivial_constructor<T>());
}
//
// Copy
//
template<class T>
static void copyTrivial(void* dst, const T* src, size_t count, const boost::false_type& isFundamentalOrPointer)
{
memcpy(dst, src, count * sizeof(T));
}
template<class T>
static void copyTrivial(void* dst, const T* src, size_t count, const boost::true_type& isFundamentalOrPointer)
{
if (count > 16)
{
copyTrivial(dst, src, count, boost::false_type());
}
else
{
for (size_t i = 0; i < count; i++)
{
new (reinterpret_cast<char*>(dst) + i * sizeof(T)) T(src[i]);
}
}
}
template<bool B>
struct bool_type : boost::integral_constant<bool, B>
{
static const bool value = B;
};
template<class T>
static void copyTrivial(void* dst, const T* src, size_t count)
{
copyTrivial(dst, src, count, bool_type<boost::is_fundamental<T>::value || boost::is_pointer<T>::value>());
}
template<class T>
static void copyConstruct(void* dst, const T* src, size_t count, const boost::true_type& hasTrivialCopyConstruct)
{
copyTrivial(dst, src, count);
}
template<class T>
static void copyConstruct(void* dst, const T* src, size_t count, const boost::false_type& hasTrivialCopyConstruct)
{
for (size_t i = 0; i < count; i++)
{
new (reinterpret_cast<char*>(dst) + i * sizeof(T)) T(src[i]);
}
}
template<class T>
static void copyConstruct(void* dst, const T* src, size_t count)
{
copyConstruct(dst, src, count, boost::has_trivial_copy_constructor<T>());
}
//
// Destroy
//
template<class T>
void destroy(T* src, size_t count, const boost::false_type& hasTrivialDestructor)
{
for (size_t i = 0; i < count; i++)
{
src[i].~T();
}
}
template<class T>
void destroy(T* src, size_t count, const boost::true_type& hasTrivialDestructor)
{
}
template<class T>
void destroy(T* src, size_t count)
{
destroy(src, count, boost::has_trivial_destructor<T>());
}
//
// Shift right
//
template<class T>
static void shiftRightTrivialCopy(T* src, size_t count, size_t offset, const boost::false_type& isFundamentalOrPointer)
{
memmove(static_cast<void*>(src + offset), src, count * sizeof(T));
}
template<class T>
static void shiftRightTrivialCopy(T* src, size_t count, size_t offset, const boost::true_type& isFundamentalOrPointer)
{
if (count < 16)
{
for (size_t i = 0; i < count; i++)
{
new ((T*)src + count + offset - 1 - i) T(src[count - 1 - i]);
}
}
else
{
shiftRightTrivialCopy(src, count, offset, boost::false_type());
}
}
template<class T>
static void shiftRightTrivialCopy(T* src, size_t count, size_t offset)
{
shiftRightTrivialCopy(src, count, offset, boost::integral_constant<bool, boost::is_fundamental<T>::value || boost::is_pointer<T>::value>());
}
template<class T>
static void shiftRight(T* src, size_t count, size_t offset, const boost::true_type& hasTrivialCopy)
{
shiftRightTrivialCopy(src, count, offset, boost::integral_constant<bool, boost::is_fundamental<T>::value || boost::is_pointer<T>::value>());
}
template<class T>
static void shiftRightNonTrivialOverlapping(T* src, size_t count, size_t offset, const boost::false_type& hasTrivialDestructor)
{
for (size_t i = 0; i < count; i++)
{
new ((T*)src + count + offset - 1 - i) T(src[count - 1 - i]);
src[count - 1 - i].~T();
}
}
template<class T>
static void shiftRightNonTrivialOverlapping(T* src, size_t count, size_t offset, const boost::true_type& hasTrivialDestructor)
{
for (size_t i = 0; i < count; i++)
{
new ((T*)src + count + offset - 1 - i) T(src[count - 1 - i]);
}
}
template<class T>
static void shiftRight(T* src, size_t count, size_t offset, const boost::false_type& hasTrivialCopy)
{
shiftRightNonTrivialOverlapping(src, count, offset, boost::has_trivial_destructor<T>());
}
template<class T>
static void shiftRight(T* src, size_t count, size_t offset)
{
shiftRight(src, count, offset, boost::has_trivial_copy<T>());
}
} // namespace array_dynamic_details
template<class T>
template<class InputType>
void ArrayDynamic<T>::copyConstruct(void* dst, InputType src, size_t count)
{
for (size_t i = 0; i < count; i++)
{
new (reinterpret_cast<char*>(dst) + i * sizeof(T)) T(*src);
src++;
}
}
template<class T>
void ArrayDynamic<T>::copyConstruct(void* dst, const T* src, size_t count)
{
if (mNoInit)
{
array_dynamic_details::copyTrivial(dst, src, count);
}
else
{
array_dynamic_details::copyConstruct(dst, src, count);
}
}
template<class T>
ArrayDynamic<T>::ArrayDynamic()
: ArrayBase<T>(NULL, 0)
, mAlignment(16)
, mNoInit(false)
, mCapacity(0)
{
}
template<class T>
ArrayDynamic<T>::ArrayDynamic(size_t _size, boost::uint32_t _align)
: ArrayBase<T>(NULL, 0)
, mAlignment(_align)
, mNoInit(false)
, mCapacity(0)
{
reserve(_size);
Base::mSize = _size;
// Initialize
array_dynamic_details::construct<T>(Base::mData, Base::mSize);
}
template<class T>
ArrayDynamic<T>::ArrayDynamic(size_t _size, ArrayNoInit, boost::uint32_t _align)
: ArrayBase<T>(NULL, 0)
, mAlignment(_align)
, mNoInit(true)
, mCapacity(0)
{
reserve(_size);
Base::mSize = _size;
}
template<class T>
ArrayDynamic<T>::ArrayDynamic(const ArrayDynamic<T>& a)
: ArrayBase<T>(NULL, 0)
, mAlignment(a.mAlignment)
, mNoInit(a.mNoInit)
, mCapacity(0)
{
reserve(a.size());
copyConstruct(Base::mData, a.data(), a.size());
Base::mSize = a.size();
}
template<class T>
ArrayDynamic<T>::ArrayDynamic(const ArrayBase<T>& a)
: ArrayBase<T>(NULL, 0)
, mAlignment(defaultAlignment)
, mNoInit(false)
, mCapacity(0)
{
reserve(a.size());
copyConstruct(Base::mData, a.data(), a.size());
Base::mSize = a.size();
}
template<class T>
ArrayDynamic<T>::~ArrayDynamic()
{
clear();
if (mCapacity > 0)
{
array_dynamic_details::aligned_free(Base::mData);
mCapacity = 0;
Base::mData = NULL;
}
}
template<class T>
void ArrayDynamic<T>::clear()
{
if (!mNoInit)
{
array_dynamic_details::destroy(Base::mData, Base::mSize);
}
Base::mSize = 0;
}
template<class T>
ArrayDynamic<T>& ArrayDynamic<T>::operator=(const ArrayDynamic<T>& _a)
{
clear();
if (mCapacity > 0 && mAlignment != _a.mAlignment)
{
array_dynamic_details::aligned_free(Base::mData);
mCapacity = 0;
Base::mData = NULL;
}
mNoInit = _a.mNoInit;
mAlignment = _a.mAlignment;
reserve(_a.size());
copyConstruct(Base::mData, _a.data(), _a.size());
Base::mSize = _a.size();
return *this;
}
template<class T>
ArrayDynamic<T>& ArrayDynamic<T>::operator=(const ArrayBase<T>& _a)
{
clear();
reserve(_a.size());
copyConstruct(Base::mData, _a.data(), _a.size());
Base::mSize = _a.size();
return *this;
}
template<class T>
void ArrayDynamic<T>::reserve(size_t _capacity)
{
if (_capacity > mCapacity)
{
void* newData = array_dynamic_details::aligned_alloc(mAlignment, _capacity * sizeof(T));
if (mNoInit)
{
array_dynamic_details::copyTrivial(newData, Base::mData, Base::mSize);
}
else
{
array_dynamic_details::copyConstruct(newData, Base::mData, Base::mSize);
array_dynamic_details::destroy(Base::mData, Base::mSize);
}
if (mCapacity > 0)
{
array_dynamic_details::aligned_free(Base::mData);
}
Base::mData = (T*)newData;
mCapacity = _capacity;
}
}
template<class T>
void ArrayDynamic<T>::resize(size_t _size)
{
if (_size <= Base::mSize)
{
if (!mNoInit)
{
array_dynamic_details::destroy(Base::mData + _size, Base::mSize - _size);
}
Base::mSize = _size;
return;
}
if (_size > mCapacity)
{
reserve(_size);
}
if (!mNoInit)
{
array_dynamic_details::construct<T>(Base::mData + Base::mSize, _size - Base::mSize);
}
Base::mSize = _size;
}
template<class T>
inline size_t ArrayDynamic<T>::capacity() const
{
return mCapacity;
}
template<class T>
inline void ArrayDynamic<T>::increase_capacity(size_t requestedCapacity)
{
size_t newCapacity = mCapacity == 0 ? 2 : 2 * mCapacity;
while (newCapacity < requestedCapacity)
newCapacity *= 2;
reserve(newCapacity);
}
template<class T>
inline void ArrayDynamic<T>::push_back(const T& _a)
{
if (mCapacity == Base::mSize)
{
increase_capacity(mCapacity + 1);
}
new (Base::data() + Base::mSize) T(_a);
Base::mSize++;
}
template<class T>
inline void ArrayDynamic<T>::pop_back()
{
AYAASSERT_VERY_FAST(Base::mSize > 0);
Base::mSize--;
if (mNoInit)
{
(Base::data() + Base::mSize)->~T();
}
}
template<class T>
void ArrayDynamic<T>::insert(size_t i, const T& val)
{
AYAASSERT_VERY_FAST(i <= Base::mSize);
if (i == Base::mSize)
{
push_back(val);
return;
}
if (mCapacity == Base::mSize)
{
increase_capacity(mCapacity + 1);
}
if (mNoInit)
{
array_dynamic_details::shiftRightTrivialCopy(Base::data() + i, Base::mSize - i, 1);
}
else
{
array_dynamic_details::shiftRight(Base::data() + i, Base::mSize - i, 1);
}
Base::mSize++;
new (Base::data() + i) T(val);
}
template<class T>
inline T* ArrayDynamic<T>::insert(const T* it, const T& val)
{
AYAASSERT_VERY_FAST(Base::begin() <= it);
AYAASSERT_VERY_FAST(Base::end() >= it);
size_t index = it - Base::begin();
insert(index, val);
return Base::begin() + index;
}
template<class T>
template<class InputType>
void ArrayDynamic<T>::insert_count(T* it, InputType first, size_t count)
{
AYAASSERT_VERY_FAST(Base::begin() <= it);
AYAASSERT_VERY_FAST(Base::end() >= it);
size_t index = it - Base::begin();
if (mCapacity < Base::mSize + count)
{
increase_capacity(Base::mSize + count);
}
it = Base::begin() + index;
if (it < Base::end())
{
if (mNoInit)
{
array_dynamic_details::shiftRightTrivialCopy(it, Base::end() - it, count);
}
else
{
array_dynamic_details::shiftRight(it, Base::end() - it, count);
}
}
copyConstruct(it, first, count);
Base::mSize += count;
}
template<class T>
template<class InputType>
inline void ArrayDynamic<T>::insert(T* it, InputType first, InputType last)
{
size_t count = last - first;
insert_count(it, first, count);
}
template<class T>
void ArrayDynamic<T>::assign(size_t size, const T& value)
{
clear();
reserve(size);
T* it = Base::begin();
for (size_t i = 0; i < size; i++)
{
new (it + i) T(value);
}
Base::mSize = size;
}
//
// ArrayRef
//
template<class T>
class ArrayRef : public ArrayBase<T>
{
public:
typedef ArrayBase<T> Base;
inline ArrayRef(T* _data, size_t _size);
inline ArrayRef(const ArrayRef<T>& a);
inline ArrayRef(const ArrayBase<T>& a);
inline ArrayRef<T>& operator=(const ArrayBase<T>& src);
};
//
// ArrayRef Implementation
//
template<class T>
ArrayRef<T>::ArrayRef(T* _data, size_t _size)
: Base(_data, _size)
{
}
template<class T>
ArrayRef<T>::ArrayRef(const ArrayRef<T>& a)
: Base(a)
{
}
template<class T>
ArrayRef<T>::ArrayRef(const ArrayBase<T>& a)
: Base(a)
{
}
template<class T>
ArrayRef<T>& ArrayRef<T>::operator=(const ArrayBase<T>& src)
{
Base::mData = src.mData;
Base::mSize = src.mSize;
return *this;
}
} // namespace Aya

View File

@@ -0,0 +1,50 @@
/**
@file debugAssert.cpp
Windows implementation of assertion routines.
@maintainer Morgan McGuire, graphics3d.com
@created 2001-08-26
@edited 2006-02-02
*/
#include "AyaPlatform.hpp" // includes <Windows.h>
#include "AyaAssert.hpp"
#include "AyaFormat.hpp"
#include <string>
using namespace std;
namespace Aya
{
namespace _internal
{
AssertionHook _debugHook;
AssertionHook _failureHook;
} // namespace _internal
void setAssertionHook(AssertionHook hook)
{
Aya::_internal::_debugHook = hook;
}
AssertionHook assertionHook()
{
return Aya::_internal::_debugHook;
}
void setFailureHook(AssertionHook hook)
{
Aya::_internal::_failureHook = hook;
}
AssertionHook failureHook()
{
return Aya::_internal::_failureHook;
}
} // namespace Aya

View File

@@ -0,0 +1,62 @@
/**
@file debugAssert.h
debugAssert(expression);
debugAssertM(expression, message);
@cite
John Robbins, Microsoft Systems Journal Bugslayer Column, Feb 1999.
<A HREF="http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm">
http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm</A>
@cite
Douglas Cox, An assert() Replacement, Code of The Day, flipcode, Sept 19, 2000
<A HREF="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1">
http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1</A>
@maintainer Morgan McGuire, matrix@graphics3d.com
@created 2001-08-26
@edited 2006-01-12
Copyright 2000-2006, Morgan McGuire.
All rights reserved.
*/
#ifndef x68BFA40003704acb85BC500AEC18DCA7
#define x68BFA40003704acb85BC500AEC18DCA7
#include <string>
#include "AyaBase.hpp"
namespace Aya
{
typedef bool (*AssertionHook)(const char* _expression, const char* filename, int lineNumber);
/**
Allows customization of the global function invoked when a debugAssert fails.
The initial value is Aya::_internal::_handleDebugAssert_. Aya will invoke
rawBreak if the hook returns true. If NULL, assertions are not handled.
*/
void setAssertionHook(AssertionHook hook);
AssertionHook assertionHook();
/**
Called by alwaysAssertM in case of failure in release mode. If returns
true then the program exits with -1 (you can replace this with your own
version that throws an exception or has other failure modes).
*/
void setFailureHook(AssertionHook hook);
AssertionHook failureHook();
namespace _internal
{
extern AssertionHook _debugHook;
extern AssertionHook _failureHook;
} // namespace _internal
} // namespace Aya
#endif

View File

@@ -0,0 +1,16 @@
#pragma once
#include <stdint.h>
#ifdef _DEBUG
// This will catch some really nasty bugs:
#pragma warning(error : 4702)
#endif
#ifndef _DEBUG
#if _SECURE_SCL != 0
#error Define _SECURE_SCL equal to 0 in release builds!
#endif
#endif

View File

@@ -0,0 +1,18 @@
/*
* AyaCrash.cpp
* MacClient
*
* Created by elebel on 7/20/10.
*/
#include "Debug.hpp"
void AYACRASH()
{
Aya::Debugable::doCrash();
}
void AYACRASH(const char* message)
{
Aya::Debugable::doCrash(message);
}

View File

@@ -0,0 +1,74 @@
#include "AyaDbgInfo.hpp"
#include <string.h>
using namespace Aya;
AyaDbgInfo AyaDbgInfo::s_instance;
AyaDbgInfo::AyaDbgInfo()
{
memset(this, 0, sizeof(AyaDbgInfo));
}
void AyaDbgInfo::AddPlace(long ID)
{
// Shift all places to upper indices
for (int i = PLACE_HISTORY - 1; i > 0; i--)
{
s_instance.PlaceIDs[i] = s_instance.PlaceIDs[i - 1];
}
s_instance.PlaceIDs[0] = ID;
s_instance.PlaceCounter++;
}
void AyaDbgInfo::RemovePlace(long ID)
{
s_instance.PlaceCounter--;
for (int i = 0; i < PLACE_HISTORY; i++)
{
if (s_instance.PlaceIDs[i] == ID)
{
// Shift all places after it to lower indices
for (int j = i; j < PLACE_HISTORY - 1; j++)
{
s_instance.PlaceIDs[j] = s_instance.PlaceIDs[j + 1];
}
s_instance.PlaceIDs[PLACE_HISTORY - 1] = 0;
return;
}
}
}
#pragma warning(push)
#pragma warning(disable : 4996)
void AyaDbgInfo::SetGfxCardName(const char* s)
{
strncpy(s_instance.GfxCardName, s, DBG_STRING_MAX - 1);
s_instance.GfxCardName[DBG_STRING_MAX - 1] = '\0';
}
void AyaDbgInfo::SetGfxCardDriverVersion(const char* s)
{
strncpy(s_instance.GfxCardDriverVersion, s, DBG_STRING_MAX - 1);
s_instance.GfxCardDriverVersion[DBG_STRING_MAX - 1] = '\0';
}
void AyaDbgInfo::SetGfxCardVendor(const char* s)
{
strncpy(s_instance.GfxCardVendorName, s, DBG_STRING_MAX - 1);
s_instance.GfxCardVendorName[DBG_STRING_MAX - 1] = '\0';
}
void AyaDbgInfo::SetCPUName(const char* s)
{
strncpy(s_instance.CPUName, s, DBG_STRING_MAX - 1);
s_instance.CPUName[DBG_STRING_MAX - 1] = '\0';
}
void AyaDbgInfo::SetServerIP(const char* s)
{
strncpy(s_instance.ServerIP, s, DBG_STRING_MAX - 1);
s_instance.ServerIP[DBG_STRING_MAX - 1] = '\0';
}
#pragma warning(pop)

View File

@@ -0,0 +1,70 @@
#pragma once
#include <cstddef>
#define PLACE_HISTORY 4
#define DBG_STRING_MAX 128
namespace Aya
{
// Global struct with the sole purpose of being accessible in minidump
struct AyaDbgInfo
{
static AyaDbgInfo s_instance;
AyaDbgInfo();
size_t cbMaterials;
size_t cbTextures;
size_t cbMeshes;
size_t cbEstFreeTextureMem;
size_t cCommitTotal;
size_t cCommitLimit;
size_t cPhysicalTotal;
size_t cPhysicalAvailable;
size_t cbPageSize;
size_t cKernelPaged;
size_t cKernelNonPaged;
size_t cSystemCache;
size_t HandleCount;
size_t ProcessCount;
size_t ThreadCount;
char GfxCardName[DBG_STRING_MAX];
char GfxCardDriverVersion[DBG_STRING_MAX];
char GfxCardVendorName[DBG_STRING_MAX];
size_t TotalVideoMemory;
char CPUName[DBG_STRING_MAX];
size_t NumCores;
char AudioDeviceName[DBG_STRING_MAX];
char ServerIP[DBG_STRING_MAX];
// Index 0 is always the last place visited
union
{
long PlaceIDs[PLACE_HISTORY];
struct
{
long Place0, Place1, Place2, Place3;
};
};
long PlaceCounter;
long PlayerID;
static void SetGfxCardName(const char* s);
static void SetGfxCardDriverVersion(const char* s);
static void SetGfxCardVendor(const char* s);
static void SetCPUName(const char* s);
static void SetServerIP(const char* s);
static void AddPlace(long ID);
static void RemovePlace(long ID);
};
} // namespace Aya

View File

@@ -0,0 +1,109 @@
#include "AyaFormat.hpp"
#include "AyaPlatform.hpp"
#include "FastLog.hpp"
#include <math.h>
#include "boost/scoped_array.hpp"
#define NEWLINE "\r\n"
#ifdef _MSC_VER
// disable: "C++ exception handler used"
#pragma warning(push)
#pragma warning(disable : 4530)
#endif // _MSC_VER
// If your platform does not have vsnprintf, you can find a
// implementation at http://www.ijs.si/software/snprintf/
namespace Aya
{
std::runtime_error runtime_error(const char* fmt, ...)
{
va_list argList;
va_start(argList, fmt);
std::string result = vformat(fmt, argList);
va_end(argList);
return std::runtime_error(result);
}
std::string format(const char* fmt, ...)
{
va_list argList;
va_start(argList, fmt);
std::string result = vformat(fmt, argList);
va_end(argList);
return result;
}
std::string vformat(const char* fmt, va_list argPtr)
{
if (!fmt)
return "";
// We draw the line at a 1MB string.
const int maxSize = 1000000;
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time.
const int stackBufferSize = 161;
#ifdef _WIN32
int actualSize = _vscprintf(fmt, argPtr);
if (actualSize < stackBufferSize)
{
char stackBuffer[stackBufferSize];
vsnprintf_s(stackBuffer, stackBufferSize, stackBufferSize, fmt, argPtr);
return std::string(stackBuffer);
}
else
{
// Use the heap.
boost::scoped_array<char> heapBuffer;
if (actualSize > maxSize)
actualSize = maxSize;
heapBuffer.reset(new char[actualSize + 1]);
vsnprintf_s(heapBuffer.get(), actualSize + 1, actualSize, fmt, argPtr);
std::string result(heapBuffer.get());
return result;
}
#else
char stackBuffer[stackBufferSize];
int actualSize = vsnprintf(stackBuffer, stackBufferSize, fmt, argPtr);
if (actualSize < stackBufferSize)
return stackBuffer;
#if defined(__linux) || defined(__APPLE__)
// FIXME: THIS IS BAD
// Use the heap.
return stackBuffer;
#else
// Use the heap.
if (actualSize > maxSize)
actualSize = maxSize;
#endif
boost::scoped_array<char> heapBuffer(new char[actualSize + 1]);
vsnprintf(heapBuffer.get(), actualSize + 1, fmt, argPtr);
heapBuffer[actualSize] = '\0';
std::string result(heapBuffer.get());
return result;
#endif
}
} // namespace Aya
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#undef NEWLINE

View File

@@ -0,0 +1,123 @@
#ifndef RBXFORMAT_H
#define RBXFORMAT_H
#include <string>
#include <stdio.h>
#include <cstdarg>
#include <stdexcept>
#include "AyaBase.hpp"
#ifdef __APPLE__
#include <objc/objc.h>
#endif
#ifdef _WIN32
#define strcasecmp stricmp
#else
#define sprintf_s snprintf
#define sscanf_s sscanf
#ifndef DWORD
#define DWORD uint32_t
#endif
#ifndef ARRRAYSIZE
#define ARRAYSIZE(x) (sizeof(x) / (sizeof(((x)[0]))))
#endif
#endif // ifndef _WIN32
#if defined(__GNUC__)
#define AYA_PRINTF_ATTR(string_index, first_to_check) __attribute__((format(printf, string_index, first_to_check)))
#else
#define AYA_PRINTF_ATTR(string_index, first_to_check)
#endif
namespace Aya
{
// Convenience function:
std::runtime_error runtime_error(const char* fmt, ...) AYA_PRINTF_ATTR(1, 2);
#ifdef AYA_PLATFORM_IOS
typedef std::runtime_error base_exception;
class physics_receiver_exception : public base_exception
{
public:
physics_receiver_exception(const std::string& m)
: base_exception(m)
{
}
};
class network_stream_exception : public base_exception
{
public:
network_stream_exception(const std::string& m)
: base_exception(m)
{
}
};
#else
typedef std::exception base_exception;
class physics_receiver_exception : public base_exception
{
const std::string msg;
public:
physics_receiver_exception(const std::string& m)
: msg(m)
{
}
~physics_receiver_exception() throw() {}
const char* what() const throw()
{
return msg.c_str();
}
};
class network_stream_exception : public base_exception
{
const std::string msg;
public:
network_stream_exception(const std::string& m)
: msg(m)
{
}
~network_stream_exception() throw() {}
const char* what() const throw()
{
return msg.c_str();
}
};
#endif
/**
Produces a string from arguments of the style of printf. This avoids
problems with buffer overflows when using sprintf and makes it easy
to use the result functionally. This function is fast when the resulting
string is under 160 characters (not including terminator) and slower
when the string is longer.
*/
std::string format(const char* fmt...) AYA_PRINTF_ATTR(1, 2);
/**
Like format, but can be called with the argument list from a ... function.
*/
std::string vformat(const char* fmt, va_list argPtr);
std::string trim_trailing_slashes(const std::string& path);
}; // namespace Aya
#endif

View File

@@ -0,0 +1,25 @@
// this file meant to include basic OS .h dependencies.
// obviously, it only supports windows at this time.
#pragma once
#ifdef WIN32
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#ifndef STRICT
#define STRICT 1
#endif
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <Windows.h>
#undef NOMINMAX
#undef _G3D_INTERNAL_HIDE_WINSOCK_
#undef _WINSOCKAPI_
#include <intrin.h>
#endif
#include "AyaBase.hpp"

View File

@@ -0,0 +1,48 @@
#pragma once
#ifndef Aya_strcasestr
namespace Aya
{
/* GCC often has strcasestr(); if not, you can use the following */
/* borrowed these definitions from Apache */
#define ap_tolower(c) (tolower(((unsigned char)(c))))
#define ap_toupper(c) (toupper(((unsigned char)(c))))
static const char* Aya_strcasestr(const char* h, const char* n)
{
if (!h || !*h || !n || !*n)
{
return 0;
}
char *a = (char*)h, *e = (char*)n;
while (*a && *e)
{
if (ap_toupper(*a) != ap_toupper(*e))
{
++h;
a = (char*)h;
e = (char*)n;
}
else
{
++a;
++e;
}
}
return (const char*)(*e) ? 0 : h;
}
static inline const char* Aya_strcasestr(const char* h, char* n)
{
return Aya_strcasestr(h, static_cast<const char*>(n));
}
static inline const char* Aya_strcasestr(char* h, const char* n)
{
return Aya_strcasestr(static_cast<const char*>(h), n);
}
} // namespace Aya
#endif // defined Aya_strcasestr

185
engine/core/src/BaldPtr.hpp Normal file
View File

@@ -0,0 +1,185 @@
#pragma once
#include "Debug.hpp"
namespace Aya
{
/**
* Wraps raw pointers with a layer of protection in debug builds.
* Checks for bad pointers on access.
*/
template<class T>
class BaldPtr
{
public:
/**
* Constructor.
* Sets pointer to NULL.
*/
inline BaldPtr()
: mPointer(NULL)
{
}
/**
* Constructor.
* Pointer is also validated.
*
* @param Pointer pointer to wrap, may be NULL
*/
inline BaldPtr(T* Pointer)
: mPointer(Pointer)
{
validate();
}
/**
* Shallow copies pointer.
* Pointer is validated.
*
* @param Pointer pointer to copy, may be NULL
* @return this pointer
*/
inline T*& operator=(T* Pointer)
{
mPointer = Pointer;
validate();
return mPointer;
}
/**
* Dereference operator.
* Checks for NULL and validates.
*
* @return reference to pointer
*/
inline T& operator*() const
{
AYAASSERT_VERY_FAST(mPointer);
validate();
return *mPointer;
}
/**
* Class pointer cast.
* Validates pointer.
*
* @return pointer cast to a class *, may be NULL
*/
inline operator T*() const
{
validate();
return mPointer;
}
/**
* Arrow operator that checks and returns the pointer.
* Checks for NULL and validates.
*
* @return data pointer
*/
inline T* operator->() const
{
AYAASSERT_VERY_FAST(mPointer);
validate();
return mPointer;
}
/**
* Get the raw pointer value.
* Performs no checks.
*
* @return data pointer, may be NULL
*/
inline T* get() const
{
return mPointer;
}
/**
* Validates the pointer.
* Various memory patterns are checked such as fence posts,
* deleted, and allocated memory. Standard CRT patterns are
* checked as well as Ogre patterns.
*
* @return description
*/
inline void validate() const
{
#ifdef __AYA_VERY_FAST_ASSERT
if (!mPointer)
return;
#endif
AYAASSERT_VERY_FAST(
// CRT debug allocator
(unsigned)mPointer != 0xCCCCCCCC && // uninitialized stack memory
(unsigned)mPointer != 0xCDCDCDCD && // uninitialized heap memory
(unsigned)mPointer != 0xFDFDFDFD && // "no man's land" guard bytes before and after allocated heap memory
(unsigned)mPointer != 0xDDDDDDDD && // deleted heap memory
(unsigned)mPointer != 0xFEEEFEEE && // deleted heap memory
// Ogre allocator
(unsigned)mPointer != 0xBAADF00D && // before "no man's land" guard bytes
(unsigned)mPointer != 0xDEADC0DE && // after "no man's land" guard bytes
(unsigned)mPointer != 0xFEEDFACE && // uninitialized memory
(unsigned)mPointer != 0xDEADBEEF); // deleted memory
}
private:
T* mPointer;
};
} // namespace Aya
// struct Foo
// {
// int x;
// };
//
// template<typename Ptr>
// void test()
// {
// Ptr f = new Foo();
// Foo* f2 = f;
// f->x = 23;
// (*f).x = 32;
// void* v = f;
// BaldPtr<Foo> b = f;
// BaldPtr<Foo> b2(f);
// const void* vc = f;
//
// delete f;
// }
//
// template<typename Ptr>
// void testConst()
// {
// Ptr f = new Foo();
// const Foo* f2 = f;
// int x = f->x;
// int y = (*f).x;
// const void* v = f;
// BaldPtr<const Foo> b = f;
// BaldPtr<const Foo> b2(f);
//
// delete f;
// }
//
// int _tmain(int argc, _TCHAR* argv[])
// {
// test<BaldPtr<Foo> const>();
// test<Foo* const>();
// test<BaldPtr<Foo> >();
// test<Foo* >();
//
// testConst<BaldPtr<const Foo> const>();
// testConst<const Foo* const>();
// testConst<BaldPtr<const Foo> >();
// testConst<const Foo* >();
//
// return 0;
// }

116
engine/core/src/CEvent.cpp Normal file
View File

@@ -0,0 +1,116 @@
#include "Debug.hpp"
#include "CEvent.hpp"
#ifndef _WIN32
const int Aya::CEvent::cWAIT_OBJECT_0;
const int Aya::CEvent::cWAIT_TIMEOUT;
const int Aya::CEvent::cINFINITE;
#endif
#ifdef _WIN32
#include <Windows.h>
#endif
void Aya::CEvent::Wait()
{
WaitForSingleObject(*this, cINFINITE);
}
bool Aya::CEvent::Wait(int milliseconds)
{
return WaitForSingleObject(*this, milliseconds) == cWAIT_OBJECT_0;
}
#ifdef AYA_CEVENT_BOOST
Aya::CEvent::~CEvent() throw() {}
Aya::CEvent::CEvent(bool bManualReset)
: isSet(false)
, manualReset(bManualReset)
{
}
void Aya::CEvent::Set() throw()
{
boost::unique_lock<boost::mutex> lock(mut);
if (manualReset)
{
isSet = true;
cond.notify_all();
}
else
{
isSet = true;
cond.notify_one();
}
}
int Aya::CEvent::WaitForSingleObject(CEvent& event, int milliseconds)
{
if (milliseconds == cINFINITE)
{
boost::unique_lock<boost::mutex> lock(event.mut);
if (!event.isSet)
event.cond.wait(lock);
if (!event.manualReset)
event.isSet = false;
return cWAIT_OBJECT_0;
}
else
{
boost::system_time const time = boost::get_system_time() + boost::posix_time::milliseconds(milliseconds);
boost::unique_lock<boost::mutex> lock(event.mut);
bool result = event.isSet || event.cond.timed_wait(lock, time);
if (result && !event.manualReset)
event.isSet = false;
return result ? cWAIT_OBJECT_0 : cWAIT_TIMEOUT;
}
}
#else
static void WINAPI RbxThrowLastWin32()
{
DWORD dwError = ::GetLastError();
HRESULT hr = HRESULT_FROM_WIN32(dwError);
throw Aya::runtime_error("HRESULT = 0x%.8X", hr);
}
Aya::CEvent::~CEvent() throw()
{
if (m_h != NULL)
{
BOOL result = ::CloseHandle(m_h);
AYAASSERT(result != 0);
m_h = NULL;
}
}
Aya::CEvent::CEvent(bool bManualReset)
: m_h(NULL)
{
m_h = ::CreateEvent(NULL, bManualReset ? TRUE : FALSE, FALSE, NULL);
if (!m_h)
RbxThrowLastWin32();
}
void Aya::CEvent::Set() throw()
{
if (m_h && !::SetEvent(m_h))
RbxThrowLastWin32();
}
int Aya::CEvent::WaitForSingleObject(CEvent& event, int milliseconds)
{
return ::WaitForSingleObject(event.m_h, milliseconds);
}
#endif

View File

@@ -0,0 +1,51 @@
#pragma once
#include "intrusive_ptr_target.hpp"
#include "boost/noncopyable.hpp"
#include "AyaFormat.hpp"
#include "time.hpp"
#include <boost/thread.hpp>
namespace Aya
{
#ifndef _WIN32
#define AYA_CEVENT_BOOST
#endif
// TODO: This class is modeled heavily off of ATL::CEvent and should be
// cleaned up. Probably it should be split into 2 classes:
// Manual and Automatic
class CEvent : public boost::noncopyable
{
#ifdef AYA_CEVENT_BOOST
const bool manualReset;
volatile bool isSet;
boost::condition_variable cond;
boost::mutex mut;
#else
#ifdef _WIN32
private:
void* m_h;
#endif
#endif
public:
CEvent(bool bManualReset);
~CEvent() throw();
void Set() throw();
void Wait();
// TODO: Deprecate:
bool Wait(int milliseconds);
bool Wait(Aya::Time::Interval interval)
{
return Wait((int)(1000.0 * interval.seconds()));
}
private:
static const int cWAIT_OBJECT_0 = 0;
static const int cWAIT_TIMEOUT = 258;
static const int cINFINITE = 0xFFFFFFFF;
static int WaitForSingleObject(CEvent& event, int milliseconds);
};
} // namespace Aya

View File

@@ -0,0 +1,9 @@
#include "CPUCount.hpp"
#include <thread>
unsigned int RbxTotalUsableCoreCount(unsigned int defaultValue)
{
unsigned n = std::thread::hardware_concurrency();
return n ? n : defaultValue;
}

View File

@@ -0,0 +1,3 @@
#pragma once
unsigned int RbxTotalUsableCoreCount(unsigned int defaultValue);

View File

@@ -0,0 +1,121 @@
#include "Coordinator.hpp"
using namespace Aya;
using namespace Tasks;
bool Barrier::isInhibited(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
return jobs[job] > counter;
}
void Barrier::onPostStep(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
unsigned int& count(jobs[job]);
// Note: we could almost assert that count==counter, but
// there may be edge cases where a step slips through
// the cracks
if (count == counter)
{
count++;
if (--remainingTasks == 0)
releaseBarrier();
}
}
void Barrier::releaseBarrier()
{
remainingTasks = int(jobs.size());
counter++;
}
void Barrier::onAdded(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
AYAASSERT(jobs.find(job) == jobs.end());
jobs[job] = counter;
remainingTasks++;
AYAASSERT(remainingTasks <= jobs.size());
}
void Barrier::onRemoved(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
AYAASSERT(jobs.find(job) != jobs.end());
if (jobs[job] <= counter)
remainingTasks--;
jobs.erase(job);
AYAASSERT(remainingTasks <= jobs.size());
}
bool SequenceBase::isInhibited(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
if (nextJobIndex < jobs.size())
return jobs[nextJobIndex] != job;
else
return true;
}
void SequenceBase::advance()
{
Aya::mutex::scoped_lock lock(mutex);
if (++nextJobIndex == jobs.size())
nextJobIndex = 0;
}
void SequenceBase::onAdded(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
jobs.push_back(job);
}
void SequenceBase::onRemoved(TaskScheduler::Job* job)
{
Aya::mutex::scoped_lock lock(mutex);
for (size_t i = 0; i < jobs.size(); ++i)
if (jobs[i] == job)
{
jobs.erase(jobs.begin() + i);
if (nextJobIndex > i)
--nextJobIndex;
break;
}
}
bool Exclusive::isInhibited(TaskScheduler::Job* job)
{
AYAASSERT(job != runningJob);
return runningJob != NULL;
}
void Exclusive::onPreStep(TaskScheduler::Job* job)
{
AYAASSERT(runningJob == NULL);
runningJob = job;
}
void Exclusive::onPostStep(TaskScheduler::Job* job)
{
AYAASSERT(runningJob == job);
runningJob = NULL;
}

View File

@@ -0,0 +1,115 @@
#pragma once
#include "TaskScheduler.hpp"
#include <map>
namespace Aya
{
namespace Tasks
{
// Prevents jobs from running according to some coordination logic.
// Generally a Coordinator will affect execution order but not
// affect parallelism. It may be more efficient to enforce resource
// locks by specializing the TaskScheduler. See the DataModel scheduler.
class AyaBaseClass Coordinator
{
public:
// These functions must be written in a thread-safe manner
// However, no 2 threads will call a function with the same job
virtual bool isInhibited(TaskScheduler::Job* job) = 0;
virtual void onPreStep(TaskScheduler::Job* job) {}
virtual void onPostStep(TaskScheduler::Job* job) {}
virtual void onAdded(TaskScheduler::Job* job) {}
virtual void onRemoved(TaskScheduler::Job* job) {}
};
// Prevents jobs from running in parallel
class Exclusive : public Coordinator
{
volatile TaskScheduler::Job* runningJob;
public:
Exclusive()
: runningJob(NULL)
{
}
virtual bool isInhibited(TaskScheduler::Job* job);
virtual void onPreStep(TaskScheduler::Job* job);
virtual void onPostStep(TaskScheduler::Job* job);
virtual void onAdded(TaskScheduler::Job* job) {}
virtual void onRemoved(TaskScheduler::Job* job) {}
};
// Requires that all coordinated jobs finish stepping before any job steps again.
// It is the task-equivalent of a thread barrier.
class Barrier : public Coordinator
{
unsigned int counter;
unsigned int remainingTasks;
Aya::mutex mutex;
std::map<TaskScheduler::Job*, unsigned int> jobs;
void releaseBarrier();
public:
Barrier()
: counter(0)
, remainingTasks(0)
{
}
virtual bool isInhibited(TaskScheduler::Job* job);
virtual void onPostStep(TaskScheduler::Job* job);
virtual void onAdded(TaskScheduler::Job* job);
virtual void onRemoved(TaskScheduler::Job* job);
};
class SequenceBase : public Coordinator
{
private:
unsigned int nextJobIndex;
Aya::mutex mutex;
std::vector<TaskScheduler::Job*> jobs;
protected:
void advance();
public:
SequenceBase()
: nextJobIndex(0)
{
}
virtual bool isInhibited(TaskScheduler::Job* job);
virtual void onAdded(TaskScheduler::Job* job);
virtual void onRemoved(TaskScheduler::Job* job);
};
// Requires that all coordinated jobs execute in sequence.
// Jobs are allowed to run in parallel, but they must start
// execution in the sequence in which they are added to the
// coordinator
class Sequence : public SequenceBase
{
public:
virtual void onPreStep(TaskScheduler::Job* job)
{
advance();
}
};
// Requires that all coordinated jobs execute in sequence.
// Jobs are *not* allowed to run in parallel.
// This is equivalent to Exclusive and Sequence combined
class ExclusiveSequence : public SequenceBase
{
public:
virtual void onPostStep(TaskScheduler::Job* job)
{
advance();
}
};
} // namespace Tasks
} // namespace Aya

View File

@@ -0,0 +1,38 @@
#pragma once
#include "atomic.hpp"
#include "Declarations.hpp"
namespace Aya
{
namespace Diagnostics
{
template<typename T>
class AyaBaseClass Countable
{
static Aya::atomic<int> count;
public:
static long getCount()
{
return count;
}
~Countable()
{
--count;
}
protected:
Countable()
{
++count;
}
};
template<class T>
Aya::atomic<int> Countable<T>::count;
} // namespace Diagnostics
} // namespace Aya

105
engine/core/src/Crypt.cpp Normal file
View File

@@ -0,0 +1,105 @@
#include "Crypt.hpp"
#include "AyaFormat.hpp"
#include <boost/scoped_array.hpp>
#include <iostream>
#include <string>
#include <sstream>
#include <openssl/sha.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
namespace
{
// The stuff between "-----BEGIN PUBLIC KEY-----" is base64 encoded string, the key is also base64 encoded & should work.
// Public/Private Key Pair generated by Ben D from the public/private blobs
// TODO Load dynamically
char publicKey[] = {"-----BEGIN PUBLIC KEY-----\n"
"-----END PUBLIC KEY-----\n"};
// Helper Fn for Base64 Decode, pass out the unsigned char** with the caller responsibility to free out
int unbase64(char* input, unsigned char** out)
{
BIO *b64, *bmem;
// base64 encoding is longer than the original 'string'
int length = strlen(input);
unsigned char* buffer = (unsigned char*)malloc(length);
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(input, length);
bmem = BIO_push(b64, bmem);
length = BIO_read(bmem, buffer, length);
buffer[length] = 0;
BIO_free_all(bmem);
*out = buffer; // caller frees
return length;
}
// verify that the plaintext message is matching signature (base64 encoded)
int verifyMessage(char* message, char* sig_b64)
{
SHA512_CTX sha512_ctx = {0};
unsigned char digest[SHA512_DIGEST_LENGTH];
int rc = 1;
rc = SHA512_Init(&sha512_ctx);
if (1 != rc)
throw Aya::runtime_error("Error during SHA512_Init");
rc = SHA512_Update(&sha512_ctx, (unsigned char*)message, strlen(message));
if (1 != rc)
throw Aya::runtime_error("Error during SHA512_Update");
rc = SHA512_Final(digest, &sha512_ctx);
if (1 != rc)
throw Aya::runtime_error("Error during SHA512_Final");
unsigned char* signature;
int length = unbase64(sig_b64, &signature);
// WARNING: no error checking for brevity
BIO* bio = BIO_new_mem_buf(publicKey, sizeof(publicKey));
RSA* rsa_key = 0;
rsa_key = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
int ret = RSA_verify(NID_sha512, digest, SHA512_DIGEST_LENGTH, signature, length, rsa_key);
ERR_print_errors_fp(stdout);
RSA_free(rsa_key);
BIO_free(bio);
free(signature);
return ret;
}
} // namespace
namespace Aya
{
Crypt::Crypt() {}
Crypt::~Crypt() {}
// Crypt implementation with RSA security using SHA1 algorithm for signature verification.
void Crypt::verifySignatureBase64(std::string messageStr, std::string signatureStr)
{
#ifndef AYA_TEST_BUILD
if (!verifyMessage((char*)messageStr.c_str(), (char*)signatureStr.c_str()))
throw std::runtime_error(""); // message hidden on purpose - prevent reverse engineering
#endif
}
} // namespace Aya

20
engine/core/src/Crypt.hpp Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#if defined(_WIN32) && !defined(AYA_PLATFORM_DURANGO)
#include <Windows.h>
#include <wincrypt.h>
#endif
#include <string>
namespace Aya
{
class Crypt
{
public:
Crypt();
~Crypt();
void verifySignatureBase64(std::string message, std::string signatureBase64);
};
} // namespace Aya

51
engine/core/src/Debug.cpp Normal file
View File

@@ -0,0 +1,51 @@
// d9mz - might be something wrong with my sdk, but this is needed to compile
// shadercompiler - it sucks
#ifndef PTRDIFF_MAX
#define PTRDIFF_MAX 9223372036854775807
#endif
#include "Debug.hpp"
#include "AyaAssert.hpp"
#include "AyaFormat.hpp"
#include <algorithm>
#ifdef _WIN32
#include <Debugapi.h>
#endif
const int CRASHONASSERT = 255;
void ReleaseAssert(int channel, const char* msg)
{
if (channel == CRASHONASSERT)
AYACRASH(msg);
else
FLog::FastLog(channel, msg, 0);
}
namespace Aya
{
// overload this in the debugger to pass by the crash
volatile bool Debugable::doCrashEnabled = true;
void Debugable::doCrash()
{
if (doCrashEnabled)
{
DebugBreak();
}
}
#pragma optimize("", off)
void Debugable::doCrash(const char* message)
{
if (doCrashEnabled)
{
DebugBreak();
}
}
#pragma optimize("", on)
} // namespace Aya

207
engine/core/src/Debug.hpp Normal file
View File

@@ -0,0 +1,207 @@
#pragma once
#include "AyaPlatform.hpp"
#include "AyaAssert.hpp"
#include "AyaFormat.hpp"
#include <set>
#include <ostream>
#include <fstream>
#include <assert.h>
#if (defined(_DEBUG) && defined(_WIN32))
#include <crtdbg.h>
#endif
#ifdef __ANDROID__
#include <typeinfo>
#include <cstdlib>
#endif
#ifdef _WIN32
#undef min
#undef max
#endif
#include "Declarations.hpp"
#include "FastLog.hpp"
#ifndef _WIN32
#define __noop
inline void DebugBreak()
{
#if defined(__i386__)
// gcc on intel
__asm__ __volatile__("int $3");
#else
// some other gcc
::abort();
#endif
}
#endif
LOGGROUP(Asserts)
/* Overview of builds and switches:
AYAASSERT: Standard assert. Should be reasonably fast. Do not do "finds" or complex stuff here. Simple bools,
simple math, a couple levels of pointer indirection, etc. AYAASSERT_VERY_FAST: High fr equency, extremely fast assert. Not in regular debug
build because frequency too high. Mostly inner engine stuff AYAASSERT_SLOW: Put things like "find" here. Will always run
in debug builds AYAASSERT_IF_VALIDATING: Very slow stuff. Only turns on if the "validating debug" switch is turned on in debug or noOpt build
AYAASSERT_FISHING: Usually doesn't go off, should be safe - turn on for engine testing
AYAASSERT() AYAASSERT_VERY_FAST() AYAASSERT_SLOW()
AYAASSERT_IF_VALIDATING() AYAASSERT_FISHING() DEBUG X X
X X - NoOpt
X X - -
- ReleaseAssert X - -
- - Release - -
- - -
*/
#ifdef _DEBUG
#define __AYA_VERY_FAST_ASSERT
#define __AYA_VALIDATE_ASSERT
// #define __AYA_SLOW_ASSERT // TODO: Hire a physics guy to enable them
// #define __AYA_FISHING_ASSERT
#define __AYA_NOT_RELEASE
#endif
#ifdef _NOOPT
#define __AYA_CRASH_ON_ASSERT
#define __AYA_VERY_FAST_ASSERT
#define __AYA_NOT_RELEASE
#endif
namespace Aya
{
// Used for memory leak detection and other stuff
class Debugable
{
public:
// this is here as a last chance way to debug an assert build, force assertions on, but not crash
static volatile bool doCrashEnabled;
static void doCrash();
static void doCrash(const char*);
static void* badMemory()
{
return reinterpret_cast<void*>(0x00000003);
} // set values to this when deleting to check if ever coming back
};
} // namespace Aya
void AYACRASH();
void AYACRASH(const char* message);
void ReleaseAssert(int channel, const char* msg);
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
// macro to convince a compiler a variable is used while not generating instructions (useful for removing warnings)
#define AYA_UNUSED(x) (void)(sizeof((x), 0))
// This macro will cause a crash. Usually you don't call it directly. Use AYAASSERT instead
#define AYA_CRASH_ASSERT(expr) ((void)(!!(expr) || (Aya::Debugable::doCrash(#expr), 0)))
// This macro will just log an assert string, if we will run into crash log with the assert information will be sent to us
#define AYA_LOG_ASSERT(expr)
// LEGACY_ASSERT should be used when we have some assert bogging us and it seems like this guy is a good candidate for removal
// usage just replace AYAASSERT with LEGACY_ASSERT and it will gone by default, but if you need to see it temporary define FIRE_LEGACY_ASSERT
#undef FIRE_LEGACY_ASSERT
#ifdef FIRE_LEGACY_ASSERT
#define LEGACY_ASSERT(expr) AYAASSERT(expr)
#else
#define LEGACY_ASSERT(expr) ((void)0)
#endif
#define AYAASSERTENABLED
// AYAASSERT()
//
#ifdef __AYA_CRASH_ON_ASSERT
#define AYAASSERT AYA_CRASH_ASSERT
#else
#if (defined(_DEBUG) && defined(__APPLE__)) // Apple Debug
#include "TargetConditionals.hpp"
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#define AYAASSERT AYA_LOG_ASSERT // iOS has no way to step over asserts (makes debugging hard)
#else
#define AYAASSERT(expr) assert(expr)
#define AYAASSERTENABLED
#endif
#elif (defined(_DEBUG) && defined(_WIN32)) // Windows Debug
/*
#define AYAASSERT(expr) \
((void) (!!(expr) || \
((Aya::_internal::_debugHook != NULL) && (Aya::_internal::_debugHook(#expr, __FILE__, __LINE__))) || \
(_ASSERTE(expr), 0)))
*/
#define AYAASSERT(expr) assert(expr)
#define AYAASSERTENABLED
#else // All Platform Release
#define AYAASSERT AYA_LOG_ASSERT
#endif
#endif
// AYAASSERT_VERY_FAST()
//
#ifdef __AYA_VERY_FAST_ASSERT
#define AYAASSERT_VERY_FAST(expr) AYAASSERT(expr)
#else
#define AYAASSERT_VERY_FAST(expr) ((void)0)
#endif
// AYAASSERT_SLOW()
//
#ifdef __AYA_SLOW_ASSERT
#define AYAASSERT_SLOW(expr) AYAASSERT(expr)
#else
#define AYAASSERT_SLOW(expr) ((void)0)
#endif
// AYAASSERT_FISHING)
//
#ifdef __AYA_FISHING_ASSERT
#define AYAASSERT_FISHING(expr) AYAASSERT(expr)
#else
#define AYAASSERT_FISHING(expr) ((void)0)
#endif
// AYAASSERT_IF_VALIDATING()
//
#ifdef __AYA_VALIDATE_ASSERT
#define AYAASSERT_IF_VALIDATING(expr) AYAASSERT((expr))
#else
#define AYAASSERT_IF_VALIDATING(expr) ((void)0)
#endif
// AYAASSERT_NOT_RELEASE() make sure this code is not being compiled in release build
#ifdef __AYA_NOT_RELEASE
#define AYAASSERT_NOT_RELEASE() ((void)0)
#else
#define AYAASSERT_NOT_RELEASE() AYACRASH()
#endif
// Same as boost::polymorphic_downcast but with an AYAASSERT
template<class T, class U>
inline T aya_static_cast(U u)
{
AYAASSERT_SLOW(dynamic_cast<T>(u) == u);
return static_cast<T>(u);
}

View File

@@ -0,0 +1,55 @@
#pragma once
/*
http://msdn.microsoft.com/en-us/magazine/cc301398.aspx
These days abstract classes are
not just common, they're ubiquitous. Think: where in Windows® do
abstract classes appear over and over again? That's right, in COM!
A COM interface is an abstract class with only pure virtual functions.
As everything in Windows migrates to COM land, Windows-based programs
have COM interfaces up the wazoo. A typical COM class might implement
a dozen or more interfaces, each with several functions.
Even outside COM, the notion of an interface is quite powerful
and useful, as in the Java language. Each interface implementation might
use several layers of classes, none intended to be used by themselves,
but only as base classes for yet more classes. ATL provides many such
classes using templates, another source of class proliferation. All of
this adds up to lots of initialization code and useless vtables with
NULL entries. The total bloat can become significant, especially when
you're developing small objects that must load over a slow medium like
the Internet.
So __declspec(novtable) was invented to solve the problem. It's a
Microsoft-specific optimization hint that tells the compiler: this class
is never used by itself, but only as a base class for other classes, so
don't bother with all that vtable stuff, thank you.
*/
#ifdef _WIN32
// Decoration to indicate a class is to be treated as an "Interface"
// The class should contain pure virtual functions and maybe a little
// trivial code. Otherwise, use AyaBaseClass.
// !!! You can't define a virtual destructor for a class of this type
#define AyaInterface __declspec(novtable)
// Decoration to indicate a class should not be instantiated directly
// !!! You can't define a virtual destructor for a class of this type
#define AyaBaseClass __declspec(novtable)
/****
Note:
C++ doesn't have a strict "Interface" type. AyaInterface should be used
for classes that declare only pure virtual functions and maybe a constructor
and/or a field. Classes that define non-trivial code should use AyaBaseClass instead.
***/
#else
#define AyaInterface
#define AyaBaseClass
#endif

View File

@@ -0,0 +1,370 @@
#pragma once
#include <boost/functional/hash.hpp>
#include <vector>
#include "Debug.hpp"
namespace Aya
{
// Internal implementation of DenseHashSet and DenseHashMap
namespace detail
{
template<typename Key>
struct DenseHashSetItem
{
Key key;
DenseHashSetItem(const Key& key)
: key(key)
{
}
};
template<typename Key, typename Value>
struct DenseHashMapItem
{
Key key;
Value value;
DenseHashMapItem(const Key& key)
: key(key)
, value()
{
}
};
template<typename Key, typename Item, typename Hash, typename Eq>
class DenseHashTable
{
public:
class const_iterator;
DenseHashTable(const Key& empty_key, size_t buckets = 0)
: data(buckets, Item(empty_key))
, count(0)
, empty_key(empty_key)
{
// buckets has to be power-of-two or zero
AYAASSERT((buckets & (buckets - 1)) == 0);
}
void clear()
{
data.clear();
count = 0;
}
Item* insert(const Key& key)
{
// It is invalid to insert empty_key into the table since it acts as a "entry does not exist" marker
AYAASSERT(!eq(key, empty_key));
if (count >= data.size() * 3 / 4)
{
rehash();
}
size_t hashmod = data.size() - 1;
size_t bucket = hasher(key) & hashmod;
for (size_t probe = 0; probe <= hashmod; ++probe)
{
Item& probe_item = data[bucket];
// Element does not exist, insert here
if (eq(probe_item.key, empty_key))
{
probe_item.key = key;
count++;
return &probe_item;
}
// Element already exists
if (eq(probe_item.key, key))
{
return &probe_item;
}
// Hash collision, quadratic probing
bucket = (bucket + probe + 1) & hashmod;
}
// Hash table is full - this should not happen
AYAASSERT(false);
return NULL;
}
const Item* find(const Key& key) const
{
if (data.empty())
return 0;
if (eq(key, empty_key))
return 0;
size_t hashmod = data.size() - 1;
size_t bucket = hasher(key) & hashmod;
for (size_t probe = 0; probe <= hashmod; ++probe)
{
const Item& probe_item = data[bucket];
// Element exists
if (eq(probe_item.key, key))
return &probe_item;
// Element does not exist
if (eq(probe_item.key, empty_key))
return NULL;
// Hash collision, quadratic probing
bucket = (bucket + probe + 1) & hashmod;
}
// Hash table is full - this should not happen
AYAASSERT(false);
return NULL;
}
const_iterator begin() const
{
size_t start = 0;
while (start < data.size() && eq(data[start].key, empty_key))
start++;
return const_iterator(this, start);
}
const_iterator end() const
{
return const_iterator(this, data.size());
}
size_t size() const
{
return count;
}
size_t bucket_count() const
{
return data.size();
}
class const_iterator
{
public:
const_iterator()
: set(0)
, index(0)
{
}
const_iterator(const DenseHashTable<Key, Item, Hash, Eq>* set, size_t index)
: set(set)
, index(index)
{
}
const Item& getItem() const
{
return set->data[index];
}
const Key& operator*() const
{
return set->data[index].key;
}
const Key* operator->() const
{
return &set->data[index].key;
}
bool operator==(const const_iterator& other) const
{
return set == other.set && index == other.index;
}
bool operator!=(const const_iterator& other) const
{
return set != other.set || index != other.index;
}
const_iterator& operator++()
{
size_t size = set->data.size();
do
{
index++;
} while (index < size && set->eq(set->data[index].key, set->empty_key));
return *this;
}
const_iterator operator++(int)
{
const_iterator res = *this;
++*this;
return res;
}
private:
const DenseHashTable<Key, Item, Hash, Eq>* set;
size_t index;
};
private:
std::vector<Item> data;
size_t count;
Key empty_key;
Hash hasher;
Eq eq;
void rehash()
{
size_t newsize = data.empty() ? 16 : data.size() * 2;
DenseHashTable newtable(empty_key, newsize);
for (size_t i = 0; i < data.size(); ++i)
if (!eq(data[i].key, empty_key))
*newtable.insert(data[i].key) = data[i];
AYAASSERT(count == newtable.count);
data.swap(newtable.data);
}
};
} // namespace detail
// This is a faster alternative of boost::unordered_set, but it does not implement the same interface (i.e. it does not support erasing and has
// contains() instead of find())
template<typename Key, typename Hash = boost::hash<Key>, typename Eq = std::equal_to<Key>>
class DenseHashSet
{
typedef detail::DenseHashTable<Key, detail::DenseHashSetItem<Key>, Hash, Eq> Impl;
Impl impl;
public:
typedef typename Impl::const_iterator const_iterator;
DenseHashSet(const Key& empty_key, size_t buckets = 0)
: impl(empty_key, buckets)
{
}
void clear()
{
impl.clear();
}
void insert(const Key& key)
{
impl.insert(key);
}
bool contains(const Key& key) const
{
return impl.find(key) != 0;
}
size_t size() const
{
return impl.size();
}
bool empty() const
{
return impl.size() == 0;
}
size_t bucket_count() const
{
return impl.bucket_count();
}
const_iterator begin() const
{
return impl.begin();
}
const_iterator end() const
{
return impl.end();
}
};
// This is a faster alternative of boost::unordered_map, but it does not implement the same interface (i.e. it does not support erasing and has
// contains() instead of find())
template<typename Key, typename Value, typename Hash = boost::hash<Key>, typename Eq = std::equal_to<Key>>
class DenseHashMap
{
typedef detail::DenseHashTable<Key, detail::DenseHashMapItem<Key, Value>, Hash, Eq> Impl;
Impl impl;
public:
typedef typename Impl::const_iterator const_iterator;
DenseHashMap(const Key& empty_key, size_t buckets = 0)
: impl(empty_key, buckets)
{
}
void clear()
{
impl.clear();
}
// Note: this reference is invalidated by any insert operation (i.e. operator[])
Value& operator[](const Key& key)
{
return impl.insert(key)->value;
}
// Note: this pointer is invalidated by any insert operation (i.e. operator[])
const Value* find(const Key& key) const
{
const detail::DenseHashMapItem<Key, Value>* result = impl.find(key);
return result ? &result->value : NULL;
}
// Note: this pointer is invalidated by any insert operation (i.e. operator[])
Value* find(const Key& key)
{
const detail::DenseHashMapItem<Key, Value>* result = impl.find(key);
return result ? const_cast<Value*>(&result->value) : NULL;
}
bool contains(const Key& key) const
{
return impl.find(key) != 0;
}
size_t size() const
{
return impl.size();
}
bool empty() const
{
return impl.size() == 0;
}
size_t bucket_count() const
{
return impl.bucket_count();
}
const_iterator begin() const
{
return impl.begin();
}
const_iterator end() const
{
return impl.end();
}
};
} // namespace Aya

816
engine/core/src/FastLog.cpp Normal file
View File

@@ -0,0 +1,816 @@
#include "FastLog.hpp"
#ifdef _WIN32
#include <Windows.h>
#elif __ANDROID__
#include <sys/atomics.h>
#elif defined(__linux) || defined(__APPLE__)
#include <atomic>
#else
#include <libkern/OSAtomic.h>
#endif
#include "signal.hpp"
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include "atomic.hpp"
#include "time.hpp"
#include "boost.hpp"
#include "AyaPlatform.hpp"
#include "AyaFormat.hpp"
#include "Utility/Statistics.hpp"
#include <fstream>
#include <string>
#include <boost/unordered_map.hpp>
#include <iomanip>
#include <algorithm>
#include <set>
#include <deque>
#define FLOAT_MARKER 0xFFFF10AD
#define MAX_LOG_MESSAGE 260
LOGVARIABLE(FastLogValueChanged, 1)
namespace FLog
{
struct IValueGetSet
{
public:
virtual void set(const std::string& valueData, FastVarType _fflagMask) = 0;
virtual std::string get() = 0;
virtual FastVarType getType() = 0;
virtual void setIsSync(bool) = 0;
virtual bool getIsSync() = 0;
virtual bool* getIsSyncAddr() = 0;
};
template<class T>
T convertFromString(const std::string& valueData);
template<>
int convertFromString<int>(const std::string& valueData)
{
return atoi(valueData.c_str());
}
template<>
bool convertFromString<bool>(const std::string& valueData)
{
return (valueData == "true" || valueData == "True") ? true : false;
}
template<>
std::string convertFromString<std::string>(const std::string& valueData)
{
return valueData;
}
template<>
Channel convertFromString<unsigned char>(const std::string& valueData)
{
return (unsigned char)atoi(valueData.c_str());
}
std::string convertToString(bool a)
{
return a ? "true" : "false";
}
std::string convertToString(int a)
{
#pragma warning(push)
#pragma warning(disable : 4996)
char temp[200];
snprintf(temp, 200 - 1, "%d", a);
return temp;
#pragma warning(pop)
}
std::string convertToString(std::string a)
{
return a;
}
template<class T>
class ValueGetSet : public IValueGetSet
{
T* value;
FastVarType fastVarType;
bool isSync;
public:
ValueGetSet(T* value, FastVarType _fastVarType)
: value(value)
, fastVarType(_fastVarType)
, isSync(true)
{
}
void set(const std::string& valueData, FastVarType _fflagMask)
{
if (_fflagMask != FASTVARTYPE_ANY)
{
// assert(fastVarType == _fflagMask);
if (fastVarType != _fflagMask)
return;
}
*value = convertFromString<T>(valueData);
}
std::string get()
{
return convertToString(*value);
}
FastVarType getType()
{
return fastVarType;
}
void setIsSync(bool sync)
{
isSync = sync;
}
bool getIsSync()
{
return isSync;
}
bool* getIsSyncAddr()
{
return &isSync;
}
};
typedef boost::unordered_map<std::string, IValueGetSet*> Variables;
typedef boost::unordered_map<std::string, std::string> UnknownVariables;
Variables* gVariables;
UnknownVariables* gUnknownVariables;
typedef boost::unordered_map<std::string, Channel*> LogGroups;
LogGroups* gLogGroups = 0;
LogGroups* gDynamicLogGroups = 0;
typedef boost::unordered_map<std::string, Channel> UnknownLogGroups;
UnknownLogGroups* gUnknownLogGroups = 0;
typedef boost::unordered_map<std::string, bool*> FastFlags;
FastFlags* gFastFlags = 0;
FastFlags* gDynamicFastFlags = 0;
typedef boost::unordered_map<std::string, bool> UnknownFastFlags;
UnknownFastFlags* gUnknownFastFlags = 0;
#if defined(__aarch64__)
typedef double FASTFLAG_VAR_FLOAT;
#else
typedef float FASTFLAG_VAR_FLOAT;
#endif
double timeDummy()
{
return 0.0;
}
TimeFunc timeF = timeDummy;
ExternalLogFunc logF = NULL;
// Exactly 32 bytes on 32bit platform, add stuff responsibly
struct LogEntry
{
const char* message;
float timestamp;
unsigned threadid;
union Args
{
struct IntArgs
{
const void* arg0;
const void* arg1;
const void* arg2;
const void* arg3;
const void* arg4;
} intArgs;
struct FloatArgs
{
FASTFLAG_VAR_FLOAT arg0f;
FASTFLAG_VAR_FLOAT arg1f;
FASTFLAG_VAR_FLOAT arg2f;
FASTFLAG_VAR_FLOAT arg3f;
FASTFLAG_VAR_FLOAT arg4f;
} floatArgs;
char sarg[sizeof(void*) * 5];
} args;
};
static LogEntry g_FastLog[LOGCHANNELS][LOG_HISTORY];
static Aya::atomic<unsigned int> g_LogCounters[LOGCHANNELS] = {0, 0, 0, 0, 0};
// flf == FastLogFloat
inline void* flf(FASTFLAG_VAR_FLOAT value)
{
return reinterpret_cast<void*(&)>(value);
}
inline float flfBack(const void* v)
{
return static_cast<float>(reinterpret_cast<FASTFLAG_VAR_FLOAT&>(v));
}
void printMessage(char* result, unsigned maxPrint, size_t threadid, float timeStamp, const char* message, const void* arg0, const void* arg1,
const void* arg2, const void* arg3, const void* arg4, float* lastTimeStamp = NULL)
{
#pragma warning(push)
#pragma warning(disable : 4996)
float delta = 0;
if (lastTimeStamp)
{
delta = (*lastTimeStamp - timeStamp) * 1000.0f;
*lastTimeStamp = timeStamp;
}
int printed = snprintf(result, maxPrint - 1, "%.5f %.1f ms %x:", timeStamp, delta, unsigned(threadid));
if (static_cast<int>(reinterpret_cast<long>(arg0)) == FLOAT_MARKER)
snprintf(result + printed, MAX_LOG_MESSAGE - printed - 1, message, flfBack(arg1), flfBack(arg2), flfBack(arg3), flfBack(arg4));
else
snprintf(result + printed, MAX_LOG_MESSAGE - printed - 1, message, arg0, arg1, arg2, arg3, arg4);
#pragma warning(pop)
return;
}
void printMessageFormatted(char* result, unsigned maxPrint, unsigned threadid, float timeStamp, const char* message, float* lastTimeStamp = NULL)
{
#pragma warning(push)
#pragma warning(disable : 4996)
float delta = 0;
if (lastTimeStamp)
{
delta = (*lastTimeStamp - timeStamp) * 1000.0f;
*lastTimeStamp = timeStamp;
}
int printed = snprintf(result, maxPrint - 1, "%.5f %.1f ms %u:", timeStamp, delta, threadid);
strncat(result, message, MAX_LOG_MESSAGE - printed - 1);
#pragma warning(pop)
return;
}
void printMessage(
char* result, unsigned maxPrint, size_t threadid, float timeStamp, const char* message, const char* sarg, float* lastTimeStamp = NULL)
{
#pragma warning(push)
#pragma warning(disable : 4996)
float delta = 0;
if (lastTimeStamp)
{
delta = (*lastTimeStamp - timeStamp) * 1000.0f;
*lastTimeStamp = timeStamp;
}
int printed = snprintf(result, maxPrint - 1, "%.5f %.1f ms %x:", timeStamp, delta, unsigned(threadid));
snprintf(result + printed, MAX_LOG_MESSAGE - printed - 1, message, sarg);
#pragma warning(pop)
return;
}
struct BinaryLogDumper
{
void* context;
Detail::BinaryLogFunc callback;
boost::mutex currentMutex;
LogEntry* current;
long counter;
long history;
boost::mutex writeQueueMutex;
boost::condition_variable writeQueueHasData;
std::deque<LogEntry*> writeQueue;
boost::thread writeThread;
BinaryLogDumper(void* context, Detail::BinaryLogFunc callback)
: context(context)
, callback(callback)
, current(0)
, counter(0)
, history(8192)
{
boost::thread t(boost::bind(&BinaryLogDumper::writeWorker, this));
writeThread.swap(t);
}
static void append(std::vector<char>& s, const void* data, size_t size)
{
s.insert(s.end(), static_cast<const char*>(data), static_cast<const char*>(data) + size);
}
template<typename T>
static void append(std::vector<char>& s, const T& v)
{
append(s, &v, sizeof(v));
}
void writeWorker()
{
std::set<const void*> strings;
std::vector<char> data;
while (true)
{
LogEntry* entry = NULL;
{
boost::unique_lock<boost::mutex> lock(writeQueueMutex);
if (writeQueue.empty())
writeQueueHasData.wait(lock);
if (!writeQueue.empty())
{
entry = writeQueue.front();
writeQueue.pop_front();
}
}
if (entry)
{
int header[2] = {};
data.resize(sizeof(header));
// append string data
for (int i = 0; i < history; ++i)
{
std::pair<std::set<const void*>::iterator, bool> p = strings.insert(entry[i].message);
if (p.second)
{
size_t length = strlen(entry[i].message);
header[0] += 1;
append(data, entry[i].message);
append(data, length);
append(data, entry[i].message, length);
}
}
// append entry data
header[1] = history;
append(data, entry, sizeof(entry[0]) * history);
// write!
memcpy(&data[0], header, sizeof(header));
callback(context, &data[0], data.size());
delete[] entry;
}
}
}
void addEntry(Channel level, const LogEntry& entry)
{
boost::unique_lock<boost::mutex> lock(currentMutex);
if (counter == history)
{
boost::unique_lock<boost::mutex> lock(writeQueueMutex);
writeQueue.push_back(current);
writeQueueHasData.notify_one();
current = 0;
counter = 0;
}
if (!current)
{
current = new LogEntry[history];
}
current[counter++] = entry;
if (IsVerboseLogging())
{
char temp[MAX_LOG_MESSAGE];
memset(temp, 0, sizeof(temp));
printMessage(temp, MAX_LOG_MESSAGE, entry.threadid, entry.timestamp, entry.message, entry.args.intArgs.arg0, entry.args.intArgs.arg1,
entry.args.intArgs.arg2, entry.args.intArgs.arg3, entry.args.intArgs.arg4);
printf("[FLog::%d %f (%d)] %s\n", level, entry.timestamp, entry.threadid, temp);
}
}
};
static BinaryLogDumper* gDumper;
void FastLog(Channel level, const char* message, const void* arg0)
{
FastLog(level, message, arg0, 0, 0, 0, 0);
}
void FastLog(Channel level, const char* message, const void* arg0, const void* arg1, const void* arg2)
{
FastLog(level, message, arg0, arg1, arg2, 0, 0);
}
void FastLogFormatted(Channel level, const char* message, ...)
{
va_list pArgList;
va_start(pArgList, message);
char temp[MAX_LOG_MESSAGE];
#ifdef WIN32
vsnprintf_s(temp, MAX_LOG_MESSAGE, message, pArgList);
#else
vsnprintf(temp, MAX_LOG_MESSAGE, message, pArgList);
#endif
va_end(pArgList);
if (level > LOGCHANNELS)
{
if (logF)
{
char temp2[MAX_LOG_MESSAGE];
memset(temp2, 0, sizeof(temp2));
printMessageFormatted(temp2, MAX_LOG_MESSAGE, (unsigned)GetCurrentThreadId(), (float)timeF(), temp);
logF(level - LOGCHANNELS, temp2);
return;
}
else
level = 1;
}
LogEntry entry;
entry.message = temp;
entry.timestamp = (float)timeF();
entry.threadid = (unsigned)GetCurrentThreadId();
Aya::atomic<unsigned int>& counter = g_LogCounters[level - 1];
unsigned int index = (--counter) % LOG_HISTORY;
g_FastLog[level - 1][index] = entry;
if (gDumper)
gDumper->addEntry(level, entry);
}
void FastLog(Channel level, const char* message, const void* arg0, const void* arg1, const void* arg2, const void* arg3, const void* arg4)
{
if (level > LOGCHANNELS)
{
if (logF)
{
char temp[MAX_LOG_MESSAGE];
memset(temp, 0, sizeof(temp));
printMessage(temp, MAX_LOG_MESSAGE, GetCurrentThreadId(), (float)timeF(), message, arg0, arg1, arg2, arg3, arg4);
logF(level - LOGCHANNELS, temp);
return;
}
else
level = 1;
}
#ifndef NDEBUG
char temp[MAX_LOG_MESSAGE];
memset(temp, 0, sizeof(temp));
printMessage(temp, MAX_LOG_MESSAGE, GetCurrentThreadId(), (float)timeF(), message, arg0, arg1, arg2, arg3, arg4);
// printf("%s\n", temp);
#endif
LogEntry entry;
entry.message = message;
entry.args.intArgs.arg0 = arg0;
entry.args.intArgs.arg1 = arg1;
entry.args.intArgs.arg2 = arg2;
entry.args.intArgs.arg3 = arg3;
entry.args.intArgs.arg4 = arg4;
entry.timestamp = (float)timeF();
entry.threadid = GetCurrentThreadId();
Aya::atomic<unsigned int>& counter = g_LogCounters[level - 1];
unsigned int index = (--counter) % LOG_HISTORY;
g_FastLog[level - 1][index] = entry;
if (gDumper)
gDumper->addEntry(level, entry);
}
void Init(TimeFunc tF)
{
timeF = tF;
}
void SetExternalLogFunc(ExternalLogFunc lF)
{
logF = lF;
}
void FastLogF(Channel channel, const char* message, float arg1, float arg2, float arg3, float arg4)
{
FastLog(channel, message, (void*)FLOAT_MARKER, flf(arg1), flf(arg2), flf(arg3), flf(arg4));
}
void FastLogS(Channel level, const char* message, const char* sarg, int sargsize)
{
if (level > LOGCHANNELS)
{
if (logF)
{
char temp[MAX_LOG_MESSAGE];
memset(temp, 0, sizeof(temp));
printMessage(temp, MAX_LOG_MESSAGE, GetCurrentThreadId(), (float)timeF(), message, sarg);
logF(level - LOGCHANNELS, temp);
return;
}
else
level = 1;
}
LogEntry entry;
entry.message = message;
#pragma warning(push)
#pragma warning(disable : 4996)
if (sarg != NULL)
{
const char* start = &sarg[std::max(0, sargsize - (int)(sizeof(entry.args.sarg) - 1))];
strncpy(entry.args.sarg, start, sizeof(entry.args.sarg));
entry.args.sarg[sizeof(entry.args.sarg) - 1] = '\0';
}
else
strncpy(entry.args.sarg, "NULL", sizeof(entry.args.sarg));
#pragma warning(pop)
entry.timestamp = (float)timeF();
entry.threadid = GetCurrentThreadId();
Aya::atomic<unsigned int>& counter = g_LogCounters[level - 1];
unsigned int index = (--counter) % LOG_HISTORY;
g_FastLog[level - 1][index] = entry;
if (gDumper)
gDumper->addEntry(level, entry);
}
void FastLogS(Channel level, const char* message, const char* sarg)
{
FastLogS(level, message, sarg, sarg ? (int)strlen(sarg) : 0);
}
void FastLogS(Channel level, const char* message, const std::string& sarg)
{
FastLogS(level, message, sarg.c_str(), (int)sarg.size());
}
void WriteFastLogDump(const char* fileName, int numEntries)
{
std::ofstream logFileStream(fileName);
if (logFileStream.fail())
return;
if (numEntries > LOG_HISTORY)
numEntries = LOG_HISTORY;
for (int ii = 0; ii < LOGCHANNELS; ++ii)
{
float lastTimeStamp = 0;
logFileStream << "Level " << ii << "\n";
if (!g_LogCounters[ii])
continue;
unsigned int currentIndex = g_LogCounters[ii] % LOG_HISTORY;
for (int currentCount = 1; currentCount <= numEntries; ++currentCount)
{
if (currentIndex >= LOG_HISTORY)
currentIndex = 0;
LogEntry* entry = &(g_FastLog[ii][currentIndex++]);
if (!entry || !entry->message)
continue;
char buffer[MAX_LOG_MESSAGE];
if (strstr(entry->message, "%s"))
printMessage(buffer, MAX_LOG_MESSAGE, entry->threadid, entry->timestamp, entry->message, entry->args.sarg, &lastTimeStamp);
else
printMessage(buffer, MAX_LOG_MESSAGE, entry->threadid, entry->timestamp, entry->message, entry->args.intArgs.arg0,
entry->args.intArgs.arg1, entry->args.intArgs.arg2, entry->args.intArgs.arg3, entry->args.intArgs.arg4, &lastTimeStamp);
logFileStream << buffer << "\n";
}
logFileStream.flush();
}
logFileStream.close();
}
unsigned short GetNumSynchronizedVariable()
{
unsigned short count = 0;
for (Variables::iterator iter = gVariables->begin(); iter != gVariables->end(); iter++)
{
if ((iter->second->getType() & FASTVARTYPE_SYNC) > 0)
{
count++;
}
}
return count;
}
void ResetSynchronizedVariablesState()
{
for (Variables::iterator iter = gVariables->begin(); iter != gVariables->end(); iter++)
{
if ((iter->second->getType() & FASTVARTYPE_SYNC) > 0)
{
iter->second->setIsSync(false);
}
}
}
int GetFastLogCounter(Channel channel)
{
if (channel > LOGCHANNELS)
channel = 1;
return g_LogCounters[channel - 1];
}
template<class T>
void RegisterVariable(const char* varName, T* value, bool** isSync, FastVarType fastVarType)
{
if (gVariables == NULL)
gVariables = new Variables();
if (gUnknownVariables == NULL)
gUnknownVariables = new UnknownVariables();
std::string name = varName;
assert(gVariables->find(name) == gVariables->end());
ValueGetSet<T>* varValue = new ValueGetSet<T>(value, fastVarType);
gVariables->insert(Variables::value_type(name, varValue));
if (isSync != NULL)
{
*isSync = varValue->getIsSyncAddr();
}
UnknownVariables::iterator itUnknown = gUnknownVariables->find(varName);
if (itUnknown != gUnknownVariables->end())
{
Variables::iterator itVars = gVariables->find(name);
itVars->second->set(itUnknown->second, fastVarType);
gUnknownVariables->erase(itUnknown);
}
}
int RegisterLogGroup(const char* groupName, Channel* groupVar, bool** isSync, FastVarType fastVarType)
{
RegisterVariable(groupName, groupVar, isSync, fastVarType);
return 1;
}
int RegisterString(const char* groupName, std::string* groupVar, bool** isSync, FastVarType fastVarType)
{
RegisterVariable(groupName, groupVar, isSync, fastVarType);
return 1;
}
int RegisterInt(const char* groupName, int* groupVar, bool** isSync, FastVarType fastVarType)
{
RegisterVariable(groupName, groupVar, isSync, fastVarType);
return 1;
}
int RegisterFlag(const char* flagName, bool* flagVar, bool** isSync, FastVarType fastVarType)
{
RegisterVariable(flagName, flagVar, isSync, fastVarType);
return 1;
}
static void visitVariable(Variables::value_type pair, VariableVisitor visitor, void* context)
{
visitor(pair.first.c_str(), pair.second->get(), context);
}
void ForEachVariable(VariableVisitor visitor, void* context, FastVarType flagType)
{
if (flagType == FASTVARTYPE_ANY)
{
std::for_each(gVariables->begin(), gVariables->end(), boost::bind(visitVariable, _1, visitor, context));
}
else
{
for (Variables::iterator iter = gVariables->begin(); iter != gVariables->end(); iter++)
{
if ((iter->second->getType() & flagType) > 0)
{
visitor(iter->first.c_str(), iter->second->get(), context);
}
}
}
}
bool SetValue(const std::string& name, const std::string& value, FastVarType fastVarType, bool loadedFromServer)
{
if (!gVariables)
return false;
if (!gUnknownVariables)
gUnknownVariables = new UnknownVariables();
bool result = false;
Variables::iterator it = gVariables->find(name);
if (it != gVariables->end())
{
it->second->set(value, fastVarType);
if (loadedFromServer)
{
it->second->setIsSync(true);
}
result = true;
}
else
{
(*gUnknownVariables)[name] = value;
}
FASTLOGS(FLog::FastLogValueChanged, "Setting variable %s", name);
FASTLOGS(FLog::FastLogValueChanged, "...to value %s", value);
return result;
}
bool GetValue(const std::string& name, std::string& value, bool alsoCheckUnknown)
{
Variables::iterator it = gVariables->find(name);
if (it != gVariables->end())
{
value = it->second->get();
return true;
}
else
{
if (alsoCheckUnknown)
{
UnknownVariables::const_iterator unknownIt = gUnknownVariables->find(name);
if (unknownIt != gUnknownVariables->end())
{
value = unknownIt->second;
return true;
}
}
return false;
}
}
bool SetValueFromServer(const std::string& name, const std::string& value)
{
return SetValue(name, value, FASTVARTYPE_SYNC, true);
}
double NowFast()
{
return timeF();
}
namespace Detail
{
void SetBinaryLog(void* context, BinaryLogFunc callback)
{
assert(!gDumper);
BinaryLogDumper* dumper = new BinaryLogDumper(context, callback);
gDumper = dumper;
}
} // namespace Detail
} // namespace FLog

462
engine/core/src/FastLog.hpp Normal file
View File

@@ -0,0 +1,462 @@
#pragma once
#include <stddef.h>
#include <string>
#ifdef __ANDROID__
#include <stdlib.h>
#endif
#define FASTLOG(group, message) \
do \
{ \
if (group) \
FLog::FastLog(group, message "", 0); \
} while (0)
#define FASTLOG1(group, message, arg1) \
do \
{ \
if (group) \
FLog::FastLog(group, message "", reinterpret_cast<const void*>(arg1)); \
} while (0)
#define FASTLOG2(group, message, arg1, arg2) \
do \
{ \
if (group) \
FLog::FastLog(group, message "", reinterpret_cast<const void*>(arg1), reinterpret_cast<const void*>(arg2), 0); \
} while (0)
#define FASTLOG3(group, message, arg1, arg2, arg3) \
do \
{ \
if (group) \
FLog::FastLog( \
group, message "", reinterpret_cast<const void*>(arg1), reinterpret_cast<const void*>(arg2), reinterpret_cast<const void*>(arg3)); \
} while (0)
#define FASTLOG4(group, message, arg1, arg2, arg3, arg4) \
do \
{ \
if (group) \
FLog::FastLog(group, message "", reinterpret_cast<const void*>(arg1), reinterpret_cast<const void*>(arg2), \
reinterpret_cast<const void*>(arg3), reinterpret_cast<const void*>(arg4), 0); \
} while (0)
#define FASTLOG5(group, message, arg1, arg2, arg3, arg4, arg5) \
do \
{ \
if (group) \
FLog::FastLog(group, message "", reinterpret_cast<const void*>(arg1), reinterpret_cast<const void*>(arg2), \
reinterpret_cast<const void*>(arg3), reinterpret_cast<const void*>(arg4), reinterpret_cast<const void*>(arg5)); \
} while (0)
#define FASTLOGS(group, message, sarg) \
do \
{ \
if (group) \
FLog::FastLogS(group, message "", sarg); \
} while (0)
#define FASTLOG1F(group, message, arg1) \
do \
{ \
if (group) \
FLog::FastLogF(group, message "", arg1); \
} while (0)
#define FASTLOG2F(group, message, arg1, arg2) \
do \
{ \
if (group) \
FLog::FastLogF(group, message "", arg1, arg2); \
} while (0)
#define FASTLOG3F(group, message, arg1, arg2, arg3) \
do \
{ \
if (group) \
FLog::FastLogF(group, message "", arg1, arg2, arg3); \
} while (0)
#define FASTLOG4F(group, message, arg1, arg2, arg3, arg4) \
do \
{ \
if (group) \
FLog::FastLogF(group, message "", arg1, arg2, arg3, arg4); \
} while (0)
#define FASTLOGNOFILTER(channel, message) FLog::FastLog(channel, message "", 0)
#define FASTLOGNOFILTER2(channel, message, arg1, arg2) \
FLog::FastLog(channel, message "", reinterpret_cast<const void*>(arg1), reinterpret_cast<const void*>(arg2), 0)
#define FASTLOGF(group, message, ...) \
do \
{ \
if (group) \
FLog::FastLogFormatted(group, message "", ##__VA_ARGS__); \
} while (0)
#define LOG_HISTORY 2048 // Has to be a power of two. If changing, modify debugger entries below
#define LOGGROUP(group) \
namespace FLog \
{ \
extern Channel group; \
}
#define LOGVARIABLE(group, defaulton) \
namespace FLog \
{ \
Channel group = defaulton; \
int group##Initer = RegisterLogGroup(#group, &group, NULL, FASTVARTYPE_STATIC); \
}
#define DYNAMIC_LOGGROUP(group) \
namespace DFLog \
{ \
extern FLog::Channel group; \
}
#define DYNAMIC_LOGVARIABLE(group, defaulton) \
namespace DFLog \
{ \
FLog::Channel group = defaulton; \
int group##Initer = FLog::RegisterLogGroup(#group, &group, NULL, FASTVARTYPE_DYNAMIC); \
}
#define SYNCHRONIZED_LOGGROUP(group) \
namespace SFLog \
{ \
extern FLog::Channel get##group(); \
}
#define SYNCHRONIZED_LOGVARIABLE(group, defaulton) \
namespace SFLog \
{ \
FLog::Channel group = defaulton; \
bool* group##IsSync; \
int group##Initer = FLog::RegisterLogGroup(#group, &group, &group##IsSync, FASTVARTYPE_SYNC); \
FLog::Channel get##group() \
{ \
assert(*group##IsSync == true); \
return group; \
} \
}
#define FASTSTRING(var) \
namespace FString \
{ \
extern std::string var; \
}
#define FASTSTRINGVARIABLE(var, defaultvalue) \
namespace FString \
{ \
std::string var = defaultvalue; \
int var##Initer = FLog::RegisterString(#var, &var, NULL, FASTVARTYPE_STATIC); \
}
#define FASTFLAG(var) \
namespace FFlag \
{ \
extern bool var; \
}
#define FASTFLAGVARIABLE(var, defaultvalue) \
FASTSTRINGVARIABLE(PlaceFilter_##var, "") \
namespace FFlag \
{ \
bool var = defaultvalue; \
int var##Initer = FLog::RegisterFlag(#var, &var, NULL, FASTVARTYPE_STATIC); \
}
#define DYNAMIC_FASTFLAG(var) \
namespace DFFlag \
{ \
extern bool var; \
}
#define DYNAMIC_FASTFLAGVARIABLE(var, defaultvalue) \
FASTSTRINGVARIABLE(PlaceFilter_##var, "") \
namespace DFFlag \
{ \
bool var = defaultvalue; \
int var##Initer = FLog::RegisterFlag(#var, &var, NULL, FASTVARTYPE_DYNAMIC); \
}
#ifndef _WIN32
#if defined(__i386__)
#define AYA_DEBUG_BREAK() \
{ \
__asm__ __volatile__("int $3"); \
} \
(void)0
#else
#define AYA_DEBUG_BREAK() \
{ \
::abort(); \
} \
(void)0
#endif
#else
#define AYA_DEBUG_BREAK() \
{ \
__debugbreak(); \
} \
(void)0
#endif
#if defined(_DEBUG) || defined(_NOOPT) || defined(AYA_TEST_BUILD)
#define VALIDATE_SYNCHRONIZED_FLAG(var) \
{ \
if (!var) \
AYA_DEBUG_BREAK(); \
} \
((void)0)
#else
#define VALIDATE_SYNCHRONIZED_FLAG(var) ((void)0)
#endif
#define SYNCHRONIZED_FASTFLAG(var) \
namespace SFFlag \
{ \
extern bool get##var(); \
}
#define SYNCHRONIZED_FASTFLAGVARIABLE(var, defaultvalue) \
FASTSTRINGVARIABLE(PlaceFilter_##var, "") \
namespace SFFlag \
{ \
bool var = defaultvalue; \
bool* var##IsSync; \
int var##Initer = FLog::RegisterFlag(#var, &var, &var##IsSync, FASTVARTYPE_SYNC); \
bool get##var() \
{ \
VALIDATE_SYNCHRONIZED_FLAG(*var##IsSync); \
return var; \
} \
}
#define FASTINT(var) \
namespace FInt \
{ \
extern int var; \
}
#define FASTINTVARIABLE(var, defaultvalue) \
FASTSTRINGVARIABLE(PlaceFilter_##var, "") \
namespace FInt \
{ \
int var = defaultvalue; \
int var##Initer = FLog::RegisterInt(#var, &var, NULL, FASTVARTYPE_STATIC); \
}
#define DYNAMIC_FASTINT(var) \
namespace DFInt \
{ \
extern int var; \
}
#define DYNAMIC_FASTINTVARIABLE(var, defaultvalue) \
FASTSTRINGVARIABLE(PlaceFilter_##var, "") \
namespace DFInt \
{ \
int var = defaultvalue; \
int var##Initer = FLog::RegisterInt(#var, &var, NULL, FASTVARTYPE_DYNAMIC); \
}
#define SYNCHRONIZED_FASTINT(var) \
namespace SFInt \
{ \
extern int get##var(); \
}
#define SYNCHRONIZED_FASTINTVARIABLE(var, defaultvalue) \
FASTSTRINGVARIABLE(PlaceFilter_##var, "") \
namespace SFInt \
{ \
int var = defaultvalue; \
bool* var##IsSync; \
int var##Initer = FLog::RegisterInt(#var, &var, &var##IsSync, FASTVARTYPE_SYNC); \
int get##var() \
{ \
VALIDATE_SYNCHRONIZED_FLAG(*var##IsSync); \
return var; \
} \
}
#define DYNAMIC_FASTSTRING(var) \
namespace DFString \
{ \
extern std::string var; \
}
#define DYNAMIC_FASTSTRINGVARIABLE(var, defaultvalue) \
namespace DFString \
{ \
std::string var = defaultvalue; \
int var##Initer = FLog::RegisterString(#var, &var, NULL, FASTVARTYPE_DYNAMIC); \
}
#define SYNCHRONIZED_FASTSTRING(var) \
namespace SFString \
{ \
extern std::string get##var(); \
}
#define SYNCHRONIZED_FASTSTRINGVARIABLE(var, defaultvalue) \
namespace SFString \
{ \
std::string var = defaultvalue; \
bool* var##IsSync; \
int var##Initer = FLog::RegisterString(#var, &var, &var##IsSync, FASTVARTYPE_SYNC); \
std::string get##var() \
{ \
VALIDATE_SYNCHRONIZED_FLAG(*var##IsSync); \
return var; \
} \
}
#define ABTEST_NEWUSERS(var) \
namespace ABNewUsers \
{ \
extern int var; \
}
#define ABTEST_NEWUSERS_VARIABLE(var) \
namespace ABNewUsers \
{ \
int var = 0; \
int var##Initer = FLog::RegisterInt(#var, &var, NULL, FASTVARTYPE_AB_NEWUSERS); \
}
#define ABTEST_NEWSTUDIOUSERS(var) \
namespace ABNewStudioUsers \
{ \
extern int var; \
}
#define ABTEST_NEWSTUDIOUSERS_VARIABLE(var) \
namespace ABNewStudioUsers \
{ \
int var = 0; \
int var##Initer = FLog::RegisterInt(#var, &var, NULL, FASTVARTYPE_AB_NEWSTUDIOUSERS); \
}
#define ABTEST_ALLUSERS(var) \
namespace ABAllUsers \
{ \
extern int var; \
}
#define ABTEST_ALLUSERS_VARIABLE(var) \
namespace ABAllUsers \
{ \
int var = 0; \
int var##Initer = FLog::RegisterInt(#var, &var, NULL, FASTVARTYPE_AB_ALLUSERS); \
}
#define LOGCHANNELS 5
#define FILECHANNEL(id) (LOGCHANNELS + id)
enum ABTestVariation
{
Control = 0,
Variation1 = 1,
Variation2 = 2,
Variation3 = 3,
Variation4 = 4,
Variation5 = 5
};
enum FastVarType
{
FASTVARTYPE_STATIC = 0,
FASTVARTYPE_DYNAMIC = 1,
FASTVARTYPE_SYNC = 2,
FASTVARTYPE_AB_NEWUSERS = 4,
FASTVARTYPE_AB_NEWSTUDIOUSERS = 8,
FASTVARTYPE_AB_ALLUSERS = 16,
FASTVARTYPE_ANY = 1 + 2 + 4 + 8 + 16
};
namespace FLog
{
// Mini guide:
// ---------------------------------
// Using logs
// ---------------------------------
// Use FASTLOG macros _ONLY_
// Examples:
// Declare a group, then log using this group:
// LOGGROUP(Test);
// Important(!): Add group to v8dataModel/FastLogSettings.cpp
// Just message - only constant strings are allowed (no generated strings), use arguments instead
// FASTLOG(FLog::Test, "Cannot initialize graphics engine");
// Message with arguments like numbers and pointers
// FASTLOG2(FLog::Test, "Sound systems %p has %u effects", system, effectcount);
// Message with floating-point arguments - has to use flf() function:
// FASTLOG1F(FLog::Test, "Average block size %f", avesize);
// Message with string (works for both char* and std::strings) - only one string is allowed
// FASTLOGS(FLog::Test, "Object name: %s", name);
// Channel 1: Non-chatty / important events (Game started, loaded UI script) -- more permanent messages
// Channel 2: Per frame data
// Channel 3-5: User defined / used for debugging / more temporary
// ---------------------------------
// Looking at the logs
// ---------------------------------
// Helpful debugger entries - select and mouse over:
// Channel 1 - {,,Log}g_FastLog[0]+({,,Log}g_LogCounters[0]%2048)
// Channel 2 - {,,Log}g_FastLog[1]+({,,Log}g_LogCounters[1]%2048)
// Channel 3 - {,,Log}g_FastLog[2]+({,,Log}g_LogCounters[2]%2048)
// Channel 4 - {,,Log}g_FastLog[3]+({,,Log}g_LogCounters[3]%2048)
// Channel 5 - {,,Log}g_FastLog[4]+({,,Log}g_LogCounters[4]%2048)
typedef unsigned char Channel;
// Make "too many actual parameters for macro" a error
#pragma warning(error : 4002)
typedef double (*TimeFunc)();
typedef void (*ExternalLogFunc)(Channel channel, const char* message);
void Init(TimeFunc timeF);
void SetExternalLogFunc(ExternalLogFunc logF);
void FastLog(Channel channel, const char* message, const void* arg0);
void FastLog(Channel channel, const char* message, const void* arg0, const void* arg1, const void* arg2);
void FastLog(Channel channel, const char* message, const void* arg0, const void* arg1, const void* arg2, const void* arg3, const void* arg4);
void FastLogS(Channel channel, const char* message, const char* sarg);
void FastLogS(Channel channel, const char* message, const std::string& sarg);
void FastLogF(Channel channel, const char* message, float arg1, float arg2 = 0.0f, float arg3 = 0.0f, float arg4 = 0.0f);
void FastLogFormatted(Channel channel, const char* message, ...);
int GetFastLogCounter(Channel channel);
int RegisterLogGroup(const char* groupName, Channel* groupVar, bool** isSync = NULL, FastVarType fastVarType = FASTVARTYPE_STATIC);
int RegisterFlag(const char* flagName, bool* flagVar, bool** isSync = NULL, FastVarType fastVarType = FASTVARTYPE_STATIC);
int RegisterInt(const char* flagName, int* flagVar, bool** isSync = NULL, FastVarType fastVarType = FASTVARTYPE_STATIC);
int RegisterString(const char* flagName, std::string* flagVar, bool** isSync = NULL, FastVarType fastVarType = FASTVARTYPE_STATIC);
typedef void (*VariableVisitor)(const std::string& name, const std::string& value, void* context);
void ForEachVariable(VariableVisitor visitor, void* context, FastVarType flagType);
bool SetValue(const std::string& name, const std::string& value, FastVarType fastVarType = FASTVARTYPE_ANY, bool loadedFromServer = false);
bool GetValue(const std::string& name, std::string& value, bool alsoCheckUnknownFlags = false);
bool SetValueFromServer(const std::string& name, const std::string& value);
double NowFast();
void WriteFastLogDump(const char* fileName, int numEntries);
unsigned short GetNumSynchronizedVariable();
void ResetSynchronizedVariablesState();
namespace Detail
{
typedef void (*BinaryLogFunc)(void* context, const void* data, size_t size);
void SetBinaryLog(void* context, BinaryLogFunc callback);
} // namespace Detail
} // namespace FLog
LOGGROUP(Legacy)
namespace FLog
{
const Channel Always = 1;
const Channel Error = 1;
const Channel Warning = 1;
}; // namespace FLog

View File

@@ -0,0 +1,144 @@
#include "FastLogStream.hpp"
#include <stdio.h>
#ifdef __ANDROID__
#include <unistd.h>
#elif defined(__linux) || defined(__APPLE__)
#include <unistd.h>
#endif
#ifdef _WIN32
#include <Ws2tcpip.h>
typedef SOCKET socket_t;
#define SOCKET_VALID(s) (s != INVALID_SOCKET)
#define SOCKET_CLOSE(s) closesocket(s)
#else
#include <sys/socket.h>
#include <arpa/inet.h>
typedef int socket_t;
#define SOCKET_VALID(s) (s >= 0)
#define SOCKET_CLOSE(s) close(s)
static int fopen_s(FILE** pFile, const char* filename, const char* mode)
{
(*pFile) = fopen(filename, mode);
return (*pFile) != NULL ? 0 : -1;
}
#endif
namespace
{
void LogStreamFileWrite(void* context, const void* data, size_t size)
{
fwrite(data, 1, size, static_cast<FILE*>(context));
}
void LogStreamSocketWrite(void* context, const void* data, size_t size)
{
// socket_t for windows and socklen_t for Apple
send(static_cast<socket_t>(reinterpret_cast<long>(context)), static_cast<const char*>(data), (int)size, 0);
}
} // namespace
namespace FLog
{
bool SetLogStreamFile(const char* path)
{
FILE* file;
if (fopen_s(&file, path, "wb") == 0)
{
printf("LogStream: Dumping to file %s\n", path);
Detail::SetBinaryLog(file, LogStreamFileWrite);
return true;
}
return false;
}
bool SetLogStreamNetworkClient(const char* address, unsigned int port)
{
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(1, 1), &wsaData);
#endif
socket_t sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
#if defined(__linux) || defined(__APPLE__)
return false;
#else
if (SOCKET_VALID(sock))
{
sockaddr_in sin = {};
sin.sin_family = AF_INET;
InetPton(AF_INET, address, &sin.sin_addr.s_addr);
sin.sin_port = htons(port);
printf("LogStream: Connecting to %s:%d\n", address, port);
if (connect(sock, reinterpret_cast<sockaddr*>(&sin), sizeof(sin)) >= 0)
{
printf("LogStream: Dumping to socket\n");
Detail::SetBinaryLog(reinterpret_cast<void*>(sock), LogStreamSocketWrite);
return true;
}
SOCKET_CLOSE(sock);
}
return false;
#endif
}
bool SetLogStreamNetworkServer(unsigned int port)
{
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(1, 1), &wsaData);
#endif
#ifndef __APPLE__
socket_t sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_VALID(sock))
{
sockaddr_in sin = {};
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
if (bind(sock, reinterpret_cast<sockaddr*>(&sin), sizeof(sin)) >= 0)
{
if (listen(sock, 1) >= 0)
{
printf("LogStream: Listening for incoming connection on port %d\n", port);
socket_t client = accept(sock, NULL, NULL);
if (SOCKET_VALID(client))
{
printf("LogStream: Dumping to socket\n");
Detail::SetBinaryLog(reinterpret_cast<void*>(client), LogStreamSocketWrite);
return true;
}
}
}
SOCKET_CLOSE(sock);
}
#endif
return false;
}
} // namespace FLog

View File

@@ -0,0 +1,12 @@
#pragma once
#include "FastLog.hpp"
namespace FLog
{
bool SetLogStreamFile(const char* path);
bool SetLogStreamNetworkClient(const char* address, unsigned int port);
bool SetLogStreamNetworkServer(unsigned int port);
} // namespace FLog

View File

@@ -0,0 +1,70 @@
#pragma once
namespace Aya
{
struct GlobalVectorItemBase
{
bool valid;
GlobalVectorItemBase()
: valid(false){};
};
// this class manages a sparse static array.
// allocate one of these "smartptr-like" classes to get the next available entry in the list.
// T should derive from GlobalVectorItemBase
template<class T>
class GlobalVectorItemPtr
{
T* p;
T* newp;
public:
GlobalVectorItemPtr(T* list, size_t count)
: p(0)
, newp(0)
{
for (size_t i = 0; i < count; ++i, ++list)
{
if (!list->valid)
{
p = list;
p->valid = true;
return;
}
}
// out of space. just allocate one.
newp = new T();
p = newp;
}
~GlobalVectorItemPtr()
{
if (newp)
{
delete newp;
newp = 0;
p = 0;
}
else
{
// free slot.
p->valid = false;
p = 0;
}
}
T* operator->()
{
return this->p;
}
T* get()
{
return this->p;
}
};
} // namespace Aya

View File

@@ -0,0 +1,65 @@
#include "boost/lexical_cast.hpp"
#include "geekinfo.hpp"
namespace Aya
{
unsigned char CPUCount(unsigned int* TotAvailLogical, unsigned int* TotAvailCore, unsigned int* PhysicalNum)
{
// this is using GeekInfo library. EL.
unsigned char StatusFlag(0);
std::string val = systemMetric(kSystemMetricCPULogicalCount);
try
{
*TotAvailLogical = boost::lexical_cast<unsigned int>(val.c_str());
}
catch (boost::bad_lexical_cast&)
{
StatusFlag = 1;
}
val = systemMetric(kSystemMetricCPUCoreCount);
try
{
*TotAvailCore = boost::lexical_cast<unsigned int>(val.c_str());
}
catch (boost::bad_lexical_cast&)
{
StatusFlag = 1;
}
val = systemMetric(kSystemMetricCPUPhysicalCount);
try
{
*PhysicalNum = boost::lexical_cast<unsigned int>(val.c_str());
}
catch (boost::bad_lexical_cast&)
{
StatusFlag = 1;
}
return StatusFlag; // 0 == success
}
} // namespace Aya
#if 0
#include <iostream>
// UNIT TEST (by eric l.)!
// to test this, use this line in the console (with gcc).
//
// g++ -IGeekInfo/src/geekinfo-2.1.4/include -I../boost_1_38_0/include HardwareInfo.cpp -o runHardareInfo -Wl,-syslibroot,/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 -arch i386 GeekInfo/src/geekinfo-2.1.4/build.x86_32/libgeekinfo.a -LGeekInfo/src/geekinfo-2.1.4/build.x86_32/lib -framework Carbon -framework IOKit -framework Cocoa -framework WebKit -framework AGL -framework OpenGL
//
//
int main() {
std::cout << systemMetricName(kSystemMetricCPU) << ": " << systemMetric(kSystemMetricCPU) << std::endl;
unsigned int l, c, p;
NewCPUCount( &l, &c, &p );
fprintf(stderr,"logical: %d Core: %d Physical: %d\n", l, c, p);
return 0;
}
#endif

View File

@@ -0,0 +1,6 @@
namespace Aya
{
unsigned char CPUCount(unsigned int* TotAvailLogical, unsigned int* TotAvailCore, unsigned int* PhysicalNum);
}

198
engine/core/src/ImGui.cpp Normal file
View File

@@ -0,0 +1,198 @@
#include "ImGui.hpp"
#include "DataModel/ContentProvider.hpp"
#define INPUT_CONFIG
struct InputState
{
int mouseX, mouseY, mouseWheel;
bool mouseButton[4];
};
InputState gImGuiInputState;
bool gImGuiContextCreated = false;
bool gImGuiFontReady = false;
#ifdef AYAIMGUI
namespace Aya
{
namespace ImGui
{
bool isInitialized()
{
return gImGuiContextCreated && gImGuiFontReady;
}
void onFrame()
{
if (!gImGuiContextCreated || !gImGuiFontReady)
return;
::ImGui::NewFrame();
}
void gpuInit()
{
::IMGUI_CHECKVERSION();
::ImGui::CreateContext();
::ImPlot::CreateContext();
// ImGui config
ImGuiIO& imio = ::ImGui::GetIO();
imio.DeltaTime = 1.0 / 60.0f;
imio.DisplaySize = ImVec2(800.0f, 600.0f); // will be updated each frame
// imio.Fonts->AddFontFromFileTTF(ContentProvider::getAssetFile("fonts/SourceSansPro-Regular.ttf").c_str(), 16.0f);
// imio.ConfigFlags |= INPUT_CONFIG;
// imio.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
// ImGui style
ImGuiStyle& style = ::ImGui::GetStyle();
ImVec4* colors = style.Colors;
#define COL(r, g, b) ::ImGui::ColorConvertU32ToFloat4(IM_COL32(r, g, b, 255))
#define COLA(r, g, b, a) ::ImGui::ColorConvertU32ToFloat4(IM_COL32(r, g, b, a))
// === Base ===
colors[ImGuiCol_WindowBg] = COL(247, 247, 247);
colors[ImGuiCol_ChildBg] = COL(255, 255, 255);
colors[ImGuiCol_PopupBg] = COL(255, 255, 255);
colors[ImGuiCol_Border] = COL(204, 204, 204);
colors[ImGuiCol_FrameBg] = COL(242, 242, 242);
colors[ImGuiCol_FrameBgHovered] = COL(230, 230, 255);
colors[ImGuiCol_FrameBgActive] = COL(217, 217, 255);
colors[ImGuiCol_Text] = COL(26, 26, 26);
colors[ImGuiCol_TextDisabled] = COL(102, 102, 102);
// === Title Bar ===
colors[ImGuiCol_TitleBg] = COL(133, 64, 255); // #8540FF
colors[ImGuiCol_TitleBgActive] = COL(133, 64, 255);
colors[ImGuiCol_TitleBgCollapsed] = COL(191, 166, 255);
// === Buttons & Headers ===
colors[ImGuiCol_Button] = COL(235, 235, 235);
colors[ImGuiCol_ButtonHovered] = COL(224, 224, 255);
colors[ImGuiCol_ButtonActive] = COL(209, 209, 255);
colors[ImGuiCol_Header] = COL(230, 217, 255);
colors[ImGuiCol_HeaderHovered] = COL(217, 204, 255);
colors[ImGuiCol_HeaderActive] = COL(191, 166, 255);
colors[ImGuiCol_TextSelectedBg] = COLA(133, 64, 255, 89);
// === Scrollbar ===
colors[ImGuiCol_ScrollbarBg] = COL(247, 247, 247);
colors[ImGuiCol_ScrollbarGrab] = COL(204, 204, 204);
colors[ImGuiCol_ScrollbarGrabHovered] = COL(179, 179, 230);
colors[ImGuiCol_ScrollbarGrabActive] = COL(153, 153, 217);
style.WindowRounding = 6.0f;
style.ChildRounding = 4.0f;
style.FrameRounding = 2.0f;
style.PopupRounding = 2.0f;
style.GrabRounding = 1.0f;
style.TabRounding = 2.0f;
#undef COL
#undef COLA
gImGuiContextCreated = true;
}
void gpuShutdown()
{
::ImPlot::DestroyContext();
::ImGui::DestroyContext();
gImGuiContextCreated = false;
gImGuiFontReady = false;
}
bool isCapturingMouseInput()
{
if (!gImGuiContextCreated)
return false;
return ::ImGui::GetIO().WantCaptureMouse;
}
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton)
{
if (!gImGuiContextCreated)
return false;
if (flags & Flag_MouseMove)
{
gImGuiInputState.mouseX = mouseX;
gImGuiInputState.mouseY = mouseY;
}
if (flags & Flag_MouseWheel)
gImGuiInputState.mouseWheel = mouseWheel;
if (flags & Flag_MouseDown)
gImGuiInputState.mouseButton[mouseButton] = true;
if (flags & Flag_MouseUp)
gImGuiInputState.mouseButton[mouseButton] = false;
ImGuiIO& imio = ::ImGui::GetIO();
imio.MousePos = ImVec2(std::max(gImGuiInputState.mouseX, 0), std::max(gImGuiInputState.mouseY, 0));
imio.MouseWheel = gImGuiInputState.mouseWheel;
imio.MouseDown[0] = gImGuiInputState.mouseButton[0];
imio.MouseDown[1] = gImGuiInputState.mouseButton[1];
imio.MouseDown[2] = gImGuiInputState.mouseButton[2];
return isCapturingMouseInput();
}
void getFont(unsigned char** pixels, int* w, int* h)
{
::ImGui::GetIO().Fonts->GetTexDataAsRGBA32(pixels, w, h);
// The font texture data has been produced; the renderer should upload it and then
// set the GPU texture id into ImGui (or we can treat this flag as "font ready").
gImGuiFontReady = true;
}
void render(Renderer* renderer, unsigned int width, unsigned int height)
{
// Need a valid context to call Render, and also ensure font/texture has been produced/uploaded.
if (!gImGuiContextCreated)
return;
if (!gImGuiFontReady)
return;
ImGuiIO& imio = ::ImGui::GetIO();
imio.DisplaySize = ImVec2((float)width, (float)height);
::ImGui::Render();
renderer->draw(::ImGui::GetDrawData());
gImGuiInputState.mouseWheel = 0;
}
} // namespace ImGui
} // namespace Aya
#else
namespace Aya
{
namespace ImGui
{
void onFrame() {}
void gpuInit() {}
void gpuShutdown() {}
bool isCapturingMouseInput()
{
return false;
}
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton)
{
return false;
}
void render(Renderer* renderer, unsigned int width, unsigned int height) {}
} // namespace ImGui
} // namespace Aya
#endif

40
engine/core/src/ImGui.hpp Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include <imgui.h>
#include <implot.h>
#define AYAIMGUI
namespace Aya
{
namespace ImGui
{
void onFrame();
enum Flags
{
Flag_MouseMove = 1 << 0,
Flag_MouseWheel = 1 << 1,
Flag_MouseDown = 1 << 2,
Flag_MouseUp = 1 << 3,
};
void gpuInit();
void gpuShutdown();
bool isCapturingMouseInput();
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton);
bool isInitialized();
struct Renderer
{
virtual ~Renderer() {}
virtual void draw(ImDrawData* data) = 0;
};
void getFont(unsigned char** pixels, int* w, int* h);
void render(Renderer* renderer, unsigned int width, unsigned int height);
} // namespace ImGui
} // namespace Aya

164
engine/core/src/Log.cpp Normal file
View File

@@ -0,0 +1,164 @@
#include "Log.hpp"
#include "AyaFormat.hpp"
#ifdef _WIN32
#include <Windows.h>
#define sprints sprintf_s
#define wcstombs wcstombs_s
#else
#endif
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"
#include "boost/date_time/gregorian/greg_year.hpp"
using namespace Aya;
#include "time.hpp"
ILogProvider* Log::provider = NULL;
Log::Severity Log::aggregateWorstSeverity = Log::Information;
void Log::timeStamp(std::ofstream& stream, bool includeDate)
{
/*
boost::posix_time::ptime stime(boost::posix_time::second_clock::local_time());
char s[256];
if (includeDate)
{
boost::gregorian::date date(stime.date());
snprintf(s, ARRAYSIZE(s), "%02u.%02u.%u ", date.day().as_number(), date.month().as_number(), (unsigned short)date.year());
stream << s;
}
boost::posix_time::time_duration dur(stime.time_of_day());
snprintf(s, ARRAYSIZE(s), "%02u:%02u:%02u.%03u (%03.07f)", (unsigned int)dur.hours(), (unsigned int)dur.minutes(), (unsigned int)dur.seconds(),
(unsigned short)dur.total_milliseconds(), Time::nowFastSec()); stream << s; stream.flush();
// d9mz - why is it still trying to do log shit after i disabled it?
*/
}
Log::Log(const char* logFile, const char* name)
: stream(logFile)
, logFile(logFile)
, worstSeverity(Information)
, name(name)
{
Log::timeStamp(stream, true);
stream << "Log \"" << name << "\"\n";
stream.flush();
}
Log::~Log(void)
{
Log::timeStamp(stream, true);
stream << "End Log\n";
}
void Log::setLogProvider(ILogProvider* provider)
{
Log::provider = provider;
}
void Log::writeEntry(Severity severity, const wchar_t* message)
{
// Convert to a char*
size_t origsize = wcslen(message) + 1;
const size_t newsize = origsize + 100;
size_t convertedChars = 0;
char* nstring = new char[newsize];
#ifdef _WIN32
wcstombs_s(&convertedChars, nstring, origsize, message, _TRUNCATE);
#else
convertedChars = wcstombs(nstring, message, origsize);
#endif
if (convertedChars >= origsize - 1)
nstring[origsize - 1] = '\0';
writeEntry(severity, nstring);
delete[] nstring;
}
void Log::writeEntry(Severity severity, const char* message)
{
/*
static const char* error = " Error: ";
static const char* warning = " Warning: ";
static const char* information = " ";
Log::timeStamp(stream, false);
switch (severity)
{
case Log::Error:
stream << error;
break;
case Log::Warning:
stream << warning;
break;
case Log::Information:
stream << information;
break;
}
stream << message;
stream << '\n';
stream.flush();
*/
}
std::string Log::formatMem(uint64_t bytes)
{
char* suffix[] = {"B", "KB", "MB", "GB", "TB"};
char length = sizeof(suffix) / sizeof(suffix[0]);
int i = 0;
double dblBytes = bytes;
if (bytes > 1024)
{
for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024)
dblBytes = bytes / 1024.0;
}
static char output[200];
sprintf(output, "%.02lf %s", dblBytes, suffix[i]);
return std::string(output);
}
std::string Log::formatTime(double time)
{
char buffer[64];
if (time == 0.0)
snprintf(buffer, ARRAYSIZE(buffer), "0s");
if (time < 0.0)
snprintf(buffer, ARRAYSIZE(buffer), "%.3gs", time);
else if (time >= 0.1)
snprintf(buffer, ARRAYSIZE(buffer), "%.3gs", time);
else
snprintf(buffer, ARRAYSIZE(buffer), "%.3gms", time * 1000.0);
return buffer;
}
void Log::timeStamp(bool includeDate)
{
Log::timeStamp(Log::currentStream(), includeDate);
}
LOGVARIABLE(Crash, 1)
LOGVARIABLE(HangDetection, 0)
LOGVARIABLE(ContentProviderCleanup, 0)
LOGVARIABLE(ISteppedLifetime, 0)
LOGVARIABLE(MutexLifetime, 0)
LOGVARIABLE(TaskScheduler, 0)
LOGVARIABLE(TaskSchedulerInit, 0)
LOGVARIABLE(TaskSchedulerRun, 0)
LOGVARIABLE(TaskSchedulerFindJob, 0)
LOGVARIABLE(TaskSchedulerSteps, 0)
LOGVARIABLE(Asserts, 0)
LOGVARIABLE(FWLifetime, 0)
LOGVARIABLE(FWUpdate, 0)
LOGVARIABLE(KernelStats, 0)
void initBaseLog() {}

78
engine/core/src/Log.hpp Normal file
View File

@@ -0,0 +1,78 @@
#ifndef _A1177B91C54B40259B36DD55E5DF7726
#define _A1177B91C54B40259B36DD55E5DF7726
#include <list>
#include <string>
#include "Debug.hpp"
#include <sstream>
#include <fstream>
namespace Aya
{
class Log;
// Returns a Log instance. Multithreaded apps should return a different
// instance for each thread, so that Scope objects don't interact with each other
class AyaInterface ILogProvider
{
public:
virtual Log* provideLog() = 0;
};
class Log
{
const std::string name;
public:
// return a string representing the amount of memory
static std::string formatMem(uint64_t bytes);
static std::string formatTime(double time);
enum Severity
{
Information = 0,
Warning = 1,
Error = 2
};
static Severity aggregateWorstSeverity; // The worst severity level reported by any Log
Severity worstSeverity; // The worst severity level reported by this Log
void writeEntry(Severity severity, const char* message);
void writeEntry(Severity severity, const wchar_t* message);
void timeStamp(bool includeDate);
static void setLogProvider(ILogProvider* provider);
Log(const char* logFile, const char* name);
virtual ~Log(void);
const std::string logFile;
static inline Log* current()
{
return provider ? provider->provideLog() : NULL;
}
static void timeStamp(std::ofstream& stream, bool includeDate);
private:
std::ofstream stream;
static ILogProvider* provider;
static inline std::ofstream& currentStream()
{
AYAASSERT(provider->provideLog() != NULL);
return provider->provideLog()->stream;
}
friend class Entry;
};
} // namespace Aya
#endif

View File

@@ -0,0 +1,158 @@
#include "Debug.hpp"
#include "MathUtil.hpp"
#include <float.h>
#include <cmath>
#ifdef AYA_OS_WINDOWS
#include <Windows.h>
#endif
namespace Aya
{
int GetMappedDOF(int dof)
{
if (dof <= 0)
{
return 0;
}
else if (dof <= 30)
{
return dof;
}
else if (dof < 40)
{
return 31;
}
else if (dof < 50)
{
return 32;
}
else if (dof < 60)
{
return 33;
}
else if (dof < 80)
{
return 34;
}
else if (dof < 100)
{
return 35;
}
else if (dof < 120)
{
return 36;
}
else if (dof == 120)
{
return 37;
}
return 38;
};
const double DOFs[][5] = {
{1, 6.314, 12.71, 63.66, 636.6},
{2, 2.92, 4.303, 9.925, 31.6},
{3, 2.353, 3.182, 5.841, 12.92},
{4, 2.132, 2.776, 4.604, 8.61},
{5, 2.015, 2.571, 4.032, 6.869},
{6, 1.943, 2.447, 3.707, 5.959},
{7, 1.895, 2.365, 3.499, 5.408},
{8, 1.86, 2.306, 3.355, 5.041},
{9, 1.833, 2.262, 3.25, 4.781},
{10, 1.812, 2.228, 3.169, 4.587},
{11, 1.796, 2.201, 3.106, 4.437},
{12, 1.782, 2.179, 3.055, 4.318},
{13, 1.771, 2.16, 3.012, 4.221},
{14, 1.761, 2.145, 2.977, 4.14},
{15, 1.753, 2.131, 2.947, 4.073},
{16, 1.746, 2.12, 2.921, 4.015},
{17, 1.74, 2.11, 2.898, 3.965},
{18, 1.734, 2.101, 2.878, 3.922},
{19, 1.729, 2.093, 2.861, 3.883},
{20, 1.725, 2.086, 2.845, 3.85},
{21, 1.721, 2.08, 2.831, 3.819},
{22, 1.717, 2.074, 2.819, 3.792},
{23, 1.714, 2.069, 2.807, 3.767},
{24, 1.711, 2.064, 2.797, 3.745},
{25, 1.708, 2.06, 2.787, 3.725},
{26, 1.706, 2.056, 2.779, 3.707},
{27, 1.703, 2.052, 2.771, 3.69},
{28, 1.701, 2.048, 2.763, 3.674},
{29, 1.699, 2.045, 2.756, 3.659},
{30, 1.697, 2.042, 2.75, 3.646},
{40, 1.684, 2.021, 2.704, 3.551},
{50, 1.676, 2.009, 2.678, 3.496},
{60, 1.671, 2, 2.66, 3.46},
{80, 1.664, 1.99, 2.639, 3.416},
{100, 1.66, 1.984, 2.626, 3.39},
{120, 1.658, 1.98, 2.617, 3.373},
{121, 1.645, 1.96, 2.576, 3.291},
};
inline double TCritical(unsigned dof, Confidence conf)
{
if (dof == 0)
return DBL_MAX;
int mappedDOF = GetMappedDOF(dof);
AYAASSERT(conf < 4);
AYAASSERT(mappedDOF < ARRAYSIZE(DOFs));
return DOFs[mappedDOF][conf + 1];
}
double IsValueOutlier(double value, unsigned int count, double average, double std, Confidence conf)
{
double tCritical = TCritical(count - 1, conf);
double tScore = fabs((value - average) / std);
return tScore > tCritical;
}
void GetConfidenceInterval(double average, double variance, Confidence conf, double* minV, double* maxV)
{
double std = sqrt(variance);
switch (conf)
{
case C90:
if (minV)
*minV = average - std * 1.644;
if (maxV)
*maxV = average + std * 1.644;
break;
case C95:
if (minV)
*minV = average - std * 2;
if (maxV)
*maxV = average + std * 2;
break;
case C99:
if (minV)
*minV = average - std * 3;
if (maxV)
*maxV = average + std * 3;
break;
case C99p9:
if (minV)
*minV = average - std * 4;
if (maxV)
*maxV = average + std * 4;
break;
default:
return;
}
}
} // namespace Aya

View File

@@ -0,0 +1,20 @@
#pragma once
namespace Aya
{
enum Confidence
{
C90,
C95,
C99,
C99p9,
ConfidenceMax
};
double IsValueOutlier(double value, unsigned int count, double average, double std, Confidence conf);
void GetConfidenceInterval(double average, double variance, Confidence conf, double* minV, double* maxV);
} // namespace Aya

111
engine/core/src/Memory.cpp Normal file
View File

@@ -0,0 +1,111 @@
// This prevent inclusion of winsock.h in Windows.h, which prevents windows redifinition errors
// Look at winsock2.h for details, winsock2.h is #included from boost.hpp & other places.
#ifdef _WIN32
#define _WINSOCKAPI_
#endif
#include "Memory.hpp"
bool Aya::roblox_allocator::crashOnAllocationFailure = true;
namespace Aya
{
std::vector<size_t*> poolAvailabilityList;
std::vector<releaseFunc> poolReleaseMemoryFuncList;
std::vector<size_t*> poolAllocationList;
#ifdef AYA_MEMORY_SCALABLE_MALLOC
char* roblox_allocator::malloc(const size_type size)
{
return reinterpret_cast<char*>(scalable_malloc(size > 0 ? size : 1));
}
void roblox_allocator::free(char* const block)
{
return scalable_free(block);
}
char* roblox_allocator::realloc(char* ptr, size_t nsize)
{
return reinterpret_cast<char*>(scalable_realloc(ptr, nsize));
}
#else
char* roblox_allocator::malloc(const size_type size)
{
char* result = (char*)std::malloc(size > 0 ? size : 1);
if (!result && size && crashOnAllocationFailure)
AYACRASH(); // We want a nice fat crash here so that the process quits and we can log it
return result;
}
void roblox_allocator::free(char* const block)
{
return std::free(block);
}
char* roblox_allocator::realloc(char* ptr, size_t nsize)
{
char* result = (char*)std::realloc(ptr, nsize);
if (!result && nsize && crashOnAllocationFailure)
AYACRASH(); // We want a nice fat crash here so that the process quits and we can log it
return result;
}
#endif
} // namespace Aya
// Globally override new/delete operators
#ifdef AYA_MEMORY_SCALABLE_MALLOC
void* operator new(size_t size)
{
// No use of std::new_handler because it cannot be done in portable
// and thread-safe way
//
// We throw std::bad_alloc() when scalable_malloc returns NULL
//(we return NULL if it is a no-throw implementation)
if (void* ptr = scalable_malloc(size > 0 ? size : 1))
return ptr;
if (Aya::roblox_allocator::crashOnAllocationFailure)
AYACRASH(); // We want a nice fat crash here so that the process quits and we can log it
throw std::bad_alloc();
}
void* operator new[](size_t size)
{
return operator new(size);
}
void* operator new(size_t size, const std::nothrow_t&) throw()
{
if (void* ptr = scalable_malloc(size > 0 ? size : 1))
return ptr;
return NULL;
}
void* operator new[](size_t size, const std::nothrow_t&) throw()
{
return operator new(size, std::nothrow);
}
void operator delete(void* ptr) throw()
{
if (ptr != 0)
scalable_free(ptr);
}
void operator delete[](void* ptr) throw()
{
operator delete(ptr);
}
void operator delete(void* ptr, const std::nothrow_t&) throw()
{
if (ptr != 0)
scalable_free(ptr);
}
void operator delete[](void* ptr, const std::nothrow_t&) throw()
{
operator delete(ptr, std::nothrow);
}
#endif

286
engine/core/src/Memory.hpp Normal file
View File

@@ -0,0 +1,286 @@
#ifndef _0632EE02291848e49902EAA033B8C2EA
#define _0632EE02291848e49902EAA033B8C2EA
#include "boost/pool/singleton_pool.hpp"
#include "boost/scoped_ptr.hpp"
#include <assert.h>
#include "Debug.hpp"
#include "atomic.hpp"
#include <vector>
// Note - Interlock Incs, Decs are turned off on count because of possible performance issues
// TODO: Benchmark AYA_ALLOCATOR_COUNTS
#ifdef _DEBUG
#define AYA_ALLOCATOR_COUNTS
#define AYA_POOL_ALLOCATION_STATS
#endif
// TODO: Benchmark:
#ifndef _DEBUG
// Note: Using this option makes it harder to find memory leaks
#define AYA_ALLOCATOR_SINGLETON_POOL
#endif
// TODO: Benchmark:
// #define AYA_MEMORY_SCALABLE_MALLOC
namespace Aya
{
#ifdef AYA_POOL_ALLOCATION_STATS
extern std::vector<size_t*> poolAllocationList;
#endif
typedef bool (*releaseFunc)();
extern std::vector<size_t*> poolAvailabilityList;
extern std::vector<releaseFunc> poolReleaseMemoryFuncList;
inline void addToPool(size_t* allocatedSize, size_t* availableSize, size_t size)
{
if (size > *availableSize)
{
#ifdef AYA_POOL_ALLOCATION_STATS
(*allocatedSize) += (size);
#endif
}
else
{
(*availableSize) -= (size);
}
}
inline void removeFromPool(size_t* availableSize, size_t size)
{
(*availableSize) += (size);
}
// You can use this allocator when using std or boost collections
class roblox_allocator
{
public:
static bool crashOnAllocationFailure; // TODO: Put this in more places, including std allocator overrides?
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static char* malloc(const size_type bytes);
static void free(char* const block);
static char* realloc(char* ptr, size_t nsize);
};
template<class T>
class Allocator
{
#ifdef AYA_ALLOCATOR_COUNTS
static Aya::atomic<int> count;
#endif
public:
static size_t allocatedSize;
static size_t availableSize;
static bool initialized;
Allocator()
{
if (!initialized)
{
#ifdef AYA_POOL_ALLOCATION_STATS
poolAllocationList.push_back(&allocatedSize);
#endif
poolAvailabilityList.push_back(&availableSize);
bool (*pReleaseMemory)() = releaseMemory;
poolReleaseMemoryFuncList.push_back(pReleaseMemory);
initialized = true;
}
}
#ifdef AYA_ALLOCATOR_SINGLETON_POOL
// TODO: Benchmark this allocator vs. other kinds
void* operator new(size_t nSize)
{
assert(nSize == sizeof(T));
void* result = boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::malloc();
if (!result)
{
if (roblox_allocator::crashOnAllocationFailure)
AYACRASH(); // We want a nice fat crash here so that the process quits and we can log it
throw std::bad_alloc();
}
#ifdef AYA_ALLOCATOR_COUNTS
count++;
#endif
addToPool(&allocatedSize, &availableSize, nSize);
return result;
}
void* operator new(size_t size, void* p)
{
addToPool(&allocatedSize, &availableSize, size);
return p;
}
void operator delete(void*, void*)
{
removeFromPool(&availableSize, sizeof(T));
}
static bool releaseMemory()
{
#ifdef AYA_POOL_ALLOCATION_STATS
allocatedSize -= availableSize;
#endif
availableSize = 0;
return boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::release_memory();
}
static bool purgeMemory()
{
// Be very careful when calling this as this is singleton pool purge
#ifdef AYA_POOL_ALLOCATION_STATS
allocatedSize = 0;
#endif
availableSize = 0;
return boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::purge_memory();
}
void operator delete(void* p)
{
boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::free(p);
#ifdef AYA_ALLOCATOR_COUNTS
count--;
#endif
removeFromPool(&availableSize, sizeof(T));
}
/////////////////////////////////////////////////////////////////////////////////////
#else
void* operator new(size_t nSize)
{
assert(nSize == sizeof(T));
void* result = (void*)roblox_allocator::malloc(nSize);
if (!result)
{
if (roblox_allocator::crashOnAllocationFailure)
AYACRASH(); // We want a nice fat crash here so that the process quits and we can log it
throw std::bad_alloc();
}
#ifdef AYA_ALLOCATOR_COUNTS
count++;
#endif
return result;
}
void operator delete(void* p)
{
roblox_allocator::free((char*)p);
#ifdef AYA_ALLOCATOR_COUNTS
count--;
#endif
}
void* operator new(size_t size, void* p)
{
return p;
}
void operator delete(void*, void*)
{
// placement delete, nothing to do
}
static bool releaseMemory()
{
// pool not used, nothing to do
return true;
}
static bool purgeMemory()
{
// pool not used, nothing to do
return true;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
#ifdef AYA_ALLOCATOR_COUNTS
static long getCount()
{
return count;
}
static long getHeapSize()
{
return sizeof(T) * count;
}
#endif
};
template<class T>
size_t Allocator<T>::allocatedSize = 0;
template<class T>
size_t Allocator<T>::availableSize = 0;
template<class T>
bool Allocator<T>::initialized = false;
#ifdef AYA_ALLOCATOR_COUNTS
template<class T>
Aya::atomic<int> Allocator<T>::count;
#endif
// This class is a wrapper for boost::pool<>. It allocates extra memory used by AutoPoolObject
// to store a pointer back to the pool.
class AutoMemPool
{
boost::scoped_ptr<boost::pool<>> pool;
public:
// A pool object that auto free itself from the pool it was allocated from
// MUST use this with AutoMemPool
class Object
{
public:
void* operator new(size_t size, AutoMemPool* pool)
{
AYAASSERT(((size_t)pool->getRequestedSize()) == size + sizeof(AutoMemPool*));
void* mem = pool->malloc();
*(AutoMemPool**)mem = &(*pool); // store the pool at start of memory block
return (char*)mem + sizeof(AutoMemPool*); // skip over the pool
}
void operator delete(void* p, AutoMemPool* pool)
{
pool->free(p);
}
void operator delete(void* p)
{
p = (char*)p - sizeof(AutoMemPool*);
AutoMemPool* pool = *(AutoMemPool**)p;
pool->free(p);
}
};
AutoMemPool(int requested_size)
{
// allocate extra bytes to store pointer to the pool
pool.reset(new boost::pool<>(requested_size + sizeof(this)));
}
inline void* malloc()
{
return pool->malloc();
}
inline void free(void* p)
{
AYAASSERT(pool->is_from(p));
pool->free(p);
}
inline int getRequestedSize()
{
return int(pool->get_requested_size());
}
};
} // namespace Aya
#endif

5
engine/core/src/Nil.hpp Normal file
View File

@@ -0,0 +1,5 @@
#if defined(__APPLE__)
#ifdef nil
#undef nil
#endif
#endif

View File

@@ -0,0 +1,198 @@
#include "ProcessPerfCounter.hpp"
#include "Debug.hpp"
#include "AyaDbgInfo.hpp"
#include <map>
#ifdef _WIN32
#include "PdhMsg.h"
PerfCounter::PerfCounter()
{
#ifdef _WIN32
PDH_STATUS pdhResult = PdhOpenQuery(NULL, 0, &hQuery);
#else
#warning "MACPORT - NEED TO HANDLE THIS CASE ON THE MAC"
#endif
}
CProcessPerfCounter::CProcessPerfCounter()
{
#ifdef _WIN32
init(::GetCurrentProcessId());
#else
#warning "MACPORT - NEED TO HANDLE THIS CASE ON THE MAC"
#endif
}
CProcessPerfCounter::CProcessPerfCounter(int pid)
{
init(pid);
}
void PerfCounter::CollectData()
{
#ifdef _WIN32
PDH_STATUS result = PdhCollectQueryData(hQuery);
#else
#warning "MACPORT - NEED TO HANDLE THIS CASE ON THE MAC"
#endif
}
void PerfCounter::GetData2(HCOUNTER counter, long& result)
{
#ifdef _WIN32
PDH_FMT_COUNTERVALUE stFormattedValue = {0};
PDH_STATUS pdhResult = PdhGetFormattedCounterValue(counter, PDH_FMT_LONG, NULL, &stFormattedValue);
result = stFormattedValue.longValue;
#else
#warning "MACPORT - NEED TO HANDLE THIS CASE ON THE MAC"
#endif
}
void PerfCounter::GetData2(HCOUNTER counter, double& result)
{
#ifdef _WIN32
PDH_FMT_COUNTERVALUE stFormattedValue = {0};
PDH_STATUS pdhResult = PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, NULL, &stFormattedValue);
result = stFormattedValue.doubleValue;
#else
#warning "MACPORT - NEED TO HANDLE THIS CASE ON THE MAC"
#endif
}
double CProcessPerfCounter::GetProcessCores()
{
double totalProcessorTime; // 0%-100% of all CPUs
GetData2(totalProcessorTimeCounter, totalProcessorTime);
double processorTime; // 0%-100% of all processes
GetData2(processorTimeCounter, processorTime);
return totalProcessorTime / 100.0 * processorTime / 100.0 * (double)numCores;
}
void CProcessPerfCounter::init(int pid)
{
::GetSystemInfo(&systemInfo);
numCores = systemInfo.dwNumberOfProcessors;
Aya::AyaDbgInfo::s_instance.NumCores = numCores;
totalProcessorTimeCounter = 0;
processorTimeCounter = 0;
privateBytesCounter = 0;
pageFaultsPerSecondCounter = 0;
pageFileBytesCounter = 0;
virtualBytesCounter = 0;
workingSetPrivateCounter = 0;
TCHAR* buffer = new TCHAR[100000];
TCHAR* instanceName;
{
DWORD length = 100000;
DWORD dummy = 0;
PDH_STATUS status = PdhEnumObjectItems(NULL, NULL, "Process", NULL, &dummy, buffer, &length, PERF_DETAIL_EXPERT, 0);
AYAASSERT(SUCCEEDED(status) || status == PDH_MORE_DATA);
std::map<std::string, int> instanceCount;
instanceName = buffer;
while (instanceName[0] != 0)
{
std::string name(instanceName);
// Insert the item if it doesn't exist:
instanceCount.insert(std::pair<std::string, int>(name, 0));
TCHAR szCounterPath[1024];
DWORD dwPathSize = 1024;
PDH_COUNTER_PATH_ELEMENTS pe = {0};
CQuery hQuery;
PDH_STATUS pdhResult = PdhOpenQuery(NULL, 0, &hQuery);
pe.szObjectName = "Process";
pe.szCounterName = "ID Process";
pe.szInstanceName = instanceName;
pe.dwInstanceIndex = instanceCount[name]++;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
CQuery counter;
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &counter);
pdhResult = PdhCollectQueryData(hQuery);
PDH_FMT_COUNTERVALUE stFormattedValue = {0};
pdhResult = PdhGetFormattedCounterValue(counter, PDH_FMT_LONG, NULL, &stFormattedValue);
if (stFormattedValue.longValue == pid)
break;
instanceName += strlen(instanceName) + 1;
}
}
TCHAR szCounterPath[1024];
DWORD dwPathSize = 1024;
PDH_COUNTER_PATH_ELEMENTS pe = {0};
pe.szObjectName = "Process";
pe.szCounterName = "% Processor Time";
pe.szInstanceName = instanceName;
PDH_STATUS pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &processorTimeCounter);
AYAASSERT(SUCCEEDED(pdhResult));
pe.szCounterName = "Elapsed Time";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &elapsedTimeCounter);
AYAASSERT(SUCCEEDED(pdhResult));
pe.szCounterName = "Private Bytes";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &privateBytesCounter);
AYAASSERT(SUCCEEDED(pdhResult));
pe.szCounterName = "Page Faults/sec";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &pageFaultsPerSecondCounter);
AYAASSERT(SUCCEEDED(pdhResult));
pe.szCounterName = "Page File Bytes";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &pageFileBytesCounter);
AYAASSERT(SUCCEEDED(pdhResult));
pe.szCounterName = "Virtual Bytes";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &virtualBytesCounter);
AYAASSERT(SUCCEEDED(pdhResult));
pe.szCounterName = "Working Set - Private";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &workingSetPrivateCounter);
if (pdhResult != 0)
{
// WinXP doesn't support "Working Set - Private"
pe.szCounterName = "Working Set";
dwPathSize = 1024;
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
// AYAASSERT(SUCCEEDED(pdhResult));
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &workingSetPrivateCounter);
// AYAASSERT(SUCCEEDED(pdhResult));
}
pdhResult = PdhAddCounter(hQuery, "\\Processor(_Total)\\% Processor Time", 0, &totalProcessorTimeCounter);
AYAASSERT(SUCCEEDED(pdhResult));
delete[] buffer;
}
#endif

View File

@@ -0,0 +1,121 @@
#pragma once
#include "boost.hpp"
#include "ScopedSingleton.hpp"
#if defined(_WIN32) && !defined(AYA_PLATFORM_DURANGO)
#include <pdh.h>
class CQuery
{
HQUERY handle;
public:
CQuery(HQUERY handle)
: handle(handle)
{
}
CQuery()
: handle(0)
{
}
HQUERY* operator&()
{
return &handle;
}
operator HQUERY() const
{
return handle;
}
~CQuery()
{
PdhCloseQuery(handle);
}
};
class PerfCounter
{
protected:
PerfCounter();
CQuery hQuery;
static void GetData2(HCOUNTER counter, long& result);
static void GetData2(HCOUNTER counter, double& result);
public:
void CollectData();
};
class CProcessPerfCounter
: public PerfCounter
, public Aya::ScopedSingleton<CProcessPerfCounter>
{
public:
CProcessPerfCounter();
CProcessPerfCounter(int pid);
// The number of cores used by the process
double GetProcessCores();
double GetElapsedTime()
{
double result;
PerfCounter::GetData2(elapsedTimeCounter, result);
return result;
}
long GetTotalProcessorTime()
{
long result;
PerfCounter::GetData2(totalProcessorTimeCounter, result);
return result;
}
long GetProcessorTime()
{
long result;
PerfCounter::GetData2(processorTimeCounter, result);
return result;
}
long GetPrivateBytes()
{
long result;
PerfCounter::GetData2(privateBytesCounter, result);
return result;
}
long GetPageFaultsPerSecond()
{
long result;
PerfCounter::GetData2(pageFaultsPerSecondCounter, result);
return result;
}
long GetPageFileBytes()
{
long result;
PerfCounter::GetData2(pageFileBytesCounter, result);
return result;
}
long GetVirtualBytes()
{
long result;
PerfCounter::GetData2(virtualBytesCounter, result);
return result;
}
long GetPrivateWorkingSetBytes()
{
long result;
PerfCounter::GetData2(workingSetPrivateCounter, result);
return result;
}
private:
unsigned int numCores;
SYSTEM_INFO systemInfo;
HCOUNTER elapsedTimeCounter;
HCOUNTER totalProcessorTimeCounter;
HCOUNTER processorTimeCounter;
HCOUNTER privateBytesCounter;
HCOUNTER pageFaultsPerSecondCounter;
HCOUNTER pageFileBytesCounter;
HCOUNTER virtualBytesCounter;
HCOUNTER workingSetPrivateCounter;
void init(int pid);
};
#endif

View File

@@ -0,0 +1,374 @@
#include "Profiler.hpp"
#include "Debug.hpp"
#if defined(AYAPROFILER) && !defined(__APPLE__)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244 4995 4996)
#endif
#define NOMINMAX
#ifdef __ANDROID__
#include <android/log.h>
#define MICROPROFILE_PRINTF(...) __android_log_print(ANDROID_LOG_DEBUG, "MicroProfile", __VA_ARGS__)
#endif
#ifdef _WIN32
static void MicroProfileDebugPrintf(const char* format, ...)
{
char message[256];
va_list args;
va_start(args, format);
vsprintf_s(message, format, args);
va_end(args);
}
#define MICROPROFILE_PRINTF(...) MicroProfileDebugPrintf(__VA_ARGS__)
#endif
#define MP_ASSERT(e) AYAASSERT(e)
#define MICROPROFILE_WEBSERVER 0
#ifdef AYA_PLATFORM_DURANGO
#define MICROPROFILE_WEBSERVER_PORT 4600
#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0
#define getenv(name) NULL
#endif
#if defined(__APPLE__) && !defined(AYA_PLATFORM_IOS)
#include <OpenGL/gl3.h>
#define MICROPROFILE_GPU_TIMERS_GL 0
#endif
#define MICROPROFILE_IMPL
#include "microprofile/microprofile.hpp"
#define MICROPROFILE_DRAWCURSOR 1
#include "microprofile/microprofileui.hpp"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <vector>
struct InputState
{
int mouseX, mouseY, mouseWheel;
bool mouseButton[4];
};
Aya::Profiler::Renderer* gProfileRenderer = 0;
InputState gProfilerInputState;
void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, uint32_t nNumCharacters)
{
gProfileRenderer->drawText(nX, nY, nColor, pText, nNumCharacters, MICROPROFILE_TEXT_WIDTH, MICROPROFILE_TEXT_HEIGHT);
}
void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType type)
{
if (type == MicroProfileBoxTypeBar)
{
uint32_t r = 0xff & (nColor >> 16);
uint32_t g = 0xff & (nColor >> 8);
uint32_t b = 0xff & nColor;
uint32_t nMax = MicroProfileMax(MicroProfileMax(MicroProfileMax(r, g), b), 30u);
uint32_t nMin = MicroProfileMin(MicroProfileMin(MicroProfileMin(r, g), b), 180u);
uint32_t r0 = 0xff & ((r + nMax) / 2);
uint32_t g0 = 0xff & ((g + nMax) / 2);
uint32_t b0 = 0xff & ((b + nMax) / 2);
uint32_t r1 = 0xff & ((r + nMin) / 2);
uint32_t g1 = 0xff & ((g + nMin) / 2);
uint32_t b1 = 0xff & ((b + nMin) / 2);
uint32_t nColor0 = (r0 << 16) | (g0 << 8) | (b0 << 0) | (0xff000000 & nColor);
uint32_t nColor1 = (r1 << 16) | (g1 << 8) | (b1 << 0) | (0xff000000 & nColor);
gProfileRenderer->drawBox(nX, nY, nX1, nY1, nColor0, nColor1);
}
else
{
gProfileRenderer->drawBox(nX, nY, nX1, nY1, nColor, nColor);
}
}
void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, uint32_t nColor)
{
gProfileRenderer->drawLine(nVertices, const_cast<const float*>(pVertices), nColor);
}
namespace Aya
{
namespace Profiler
{
MicroProfileTokenType getTokenType(const char* group)
{
return strcmp(group, "GPU") == 0 ? MicroProfileTokenTypeGpu : MicroProfileTokenTypeCpu;
}
Token getToken(const char* group, const char* name, int color)
{
return MicroProfileGetToken(group, name, color, getTokenType(group));
}
Token getLabelToken(const char* group)
{
return MicroProfileGetLabelToken(group, getTokenType(group));
}
Token getCounterToken(const char* name)
{
return MicroProfileGetCounterToken(name);
}
uint64_t enterRegion(Token token)
{
return MicroProfileEnter(token);
}
void addLabel(Token token, const char* name)
{
MicroProfileLabel(token, name);
}
void addLabelFormat(Token token, const char* name, ...)
{
va_list args;
va_start(args, name);
MicroProfileLabelFormatV(token, name, args);
va_end(args);
}
void leaveRegion(Token token, uint64_t enterTimestamp)
{
MicroProfileLeave(token, enterTimestamp);
}
void counterAdd(Token token, long long count)
{
MicroProfileCounterAdd(token, count);
}
void counterSet(Token token, long long count)
{
MicroProfileCounterSet(token, count);
}
void onThreadCreate(const char* name)
{
MicroProfileOnThreadCreate(name);
}
void onThreadExit()
{
MicroProfileOnThreadExit();
}
void onFrame()
{
if (g_MicroProfile.nWebServerDataSent)
{
// Set the defaults for web serving after the first connect
MicroProfileSetEnableAllGroups(true);
}
MicroProfileFlip();
}
void gpuInit(void* context)
{
MicroProfileGpuInit(context);
}
void gpuShutdown()
{
MicroProfileGpuShutdown();
}
bool isCapturingMouseInput()
{
return (MicroProfileIsDrawing() && !g_MicroProfile.nRunning);
}
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton)
{
if (flags & Flag_MouseMove)
{
gProfilerInputState.mouseX = mouseX;
gProfilerInputState.mouseY = mouseY;
}
if (flags & Flag_MouseWheel)
gProfilerInputState.mouseWheel = mouseWheel;
if (flags & Flag_MouseDown)
gProfilerInputState.mouseButton[mouseButton] = true;
if (flags & Flag_MouseUp)
gProfilerInputState.mouseButton[mouseButton] = false;
MicroProfileMousePosition(std::max(gProfilerInputState.mouseX, 0), std::max(gProfilerInputState.mouseY, 0), gProfilerInputState.mouseWheel);
MicroProfileMouseButton(gProfilerInputState.mouseButton[0], gProfilerInputState.mouseButton[1]);
return isCapturingMouseInput();
}
bool toggleVisible()
{
if (MicroProfileIsDrawing())
{
MicroProfileSetDisplayMode(MP_DRAW_OFF);
if (!g_MicroProfile.nRunning)
g_MicroProfile.nToggleRunning = 1;
}
else
{
MicroProfileSetDisplayMode(MP_DRAW_FRAME);
}
return true;
}
bool forceOn()
{
{
MicroProfileSetDisplayMode(MP_DRAW_FRAME);
}
return true;
}
bool forceOff()
{
{
MicroProfileSetDisplayMode(MP_DRAW_OFF);
if (!g_MicroProfile.nRunning)
g_MicroProfile.nToggleRunning = 1;
}
return false;
}
bool togglePause()
{
if (MicroProfileIsDrawing())
{
if (g_MicroProfile.nRunning && g_MicroProfile.nDisplay == MP_DRAW_FRAME)
MicroProfileSetDisplayMode(MP_DRAW_DETAILED);
else if (!g_MicroProfile.nRunning && g_MicroProfile.nDisplay == MP_DRAW_DETAILED)
MicroProfileSetDisplayMode(MP_DRAW_FRAME);
MicroProfileTogglePause();
return true;
}
return false;
}
bool isVisible()
{
return MicroProfileIsDrawing();
}
void render(Renderer* renderer, unsigned int width, unsigned int height)
{
static bool initialized = false;
if (!initialized)
{
MicroProfileInitUI();
g_MicroProfileUI.nOpacityBackground = 0x40 << 24;
}
gProfileRenderer = renderer;
MicroProfileDraw(width, height);
gProfileRenderer = 0;
if (!initialized)
{
// MicroProfileDraw loads the preset; after it has loaded it we can check if
// we need to set up the defaults to enable all groups
if (g_MicroProfile.nActiveGroupWanted == 0)
MicroProfileSetEnableAllGroups(true);
initialized = true;
}
gProfilerInputState.mouseWheel = 0;
}
} // namespace Profiler
} // namespace Aya
#else
namespace Aya
{
namespace Profiler
{
Token getToken(const char* group, const char* name, int color)
{
return 0;
}
uint64_t enterRegion(Token token)
{
return 0;
}
void addLabel(Token token, const char* name) {}
void leaveRegion(Token token, uint64_t enterTimestamp) {}
void onThreadCreate(const char* name) {}
void onThreadExit() {}
void onFrame() {}
void gpuInit(void* context) {}
void gpuShutdown() {}
bool isCapturingMouseInput()
{
return false;
}
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton)
{
return false;
}
bool toggleVisible()
{
return false;
}
bool togglePause()
{
return false;
}
bool isVisible()
{
return false;
}
void render(Renderer* renderer, unsigned int width, unsigned int height) {}
} // namespace Profiler
} // namespace Aya
#endif

View File

@@ -0,0 +1,115 @@
#pragma once
#include <stdint.h>
#include "AyaFormat.hpp" // for AYA_PRINTF_ATTR
#if defined(_WIN32) || defined(__linux__)
#define AYAPROFILER
#endif
namespace Aya
{
namespace Profiler
{
typedef uint64_t Token;
Token getToken(const char* group, const char* name, int color = -1);
Token getLabelToken(const char* group);
Token getCounterToken(const char* name);
uint64_t enterRegion(Token token);
void leaveRegion(Token token, uint64_t enterTimestamp);
void addLabel(Token token, const char* name);
AYA_PRINTF_ATTR(2, 3) void addLabelFormat(Token token, const char* name, ...);
void counterAdd(Token token, long long count);
void counterSet(Token token, long long count);
void onThreadCreate(const char* name);
void onThreadExit();
void onFrame();
enum Flags
{
Flag_MouseMove = 1 << 0,
Flag_MouseWheel = 1 << 1,
Flag_MouseDown = 1 << 2,
Flag_MouseUp = 1 << 3,
};
void gpuInit(void* context);
void gpuShutdown();
bool isCapturingMouseInput();
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton);
bool forceOn();
bool forceOff();
bool toggleVisible();
bool togglePause();
struct Renderer
{
virtual ~Renderer() {}
virtual void drawText(
int x, int y, unsigned int color, const char* text, unsigned int length, unsigned int textWidth, unsigned int textHeight) = 0;
virtual void drawBox(int x0, int y0, int x1, int y1, unsigned int color0, unsigned int color1) = 0;
virtual void drawLine(unsigned int vertexCount, const float* vertexData, unsigned int color) = 0;
};
bool isVisible();
void render(Renderer* renderer, unsigned int width, unsigned int height);
struct Scope
{
Token token;
uint64_t timestamp;
Scope(Token token)
: token(token)
{
timestamp = enterRegion(token);
}
~Scope()
{
leaveRegion(token, timestamp);
}
};
} // namespace Profiler
} // namespace Aya
#define AYAPROFILER_TOKEN_PASTE0(a, b) a##b
#define AYAPROFILER_TOKEN_PASTE(a, b) AYAPROFILER_TOKEN_PASTE0(a, b)
#ifdef AYAPROFILER
#define AYAPROFILER_SCOPE(group, name, ...) \
static ::Aya::Profiler::Token AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::Aya::Profiler::getToken(group "", name "", ##__VA_ARGS__); \
::Aya::Profiler::Scope AYAPROFILER_TOKEN_PASTE(profscope, __LINE__)(AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__))
#define AYAPROFILER_LABEL(group, label) \
static ::Aya::Profiler::Token AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::Aya::Profiler::getLabelToken(group ""); \
::Aya::Profiler::addLabel(AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__), label)
#define AYAPROFILER_LABELF(group, label, ...) \
static ::Aya::Profiler::Token AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::Aya::Profiler::getLabelToken(group ""); \
::Aya::Profiler::addLabelFormat(AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__), label, ##__VA_ARGS__)
#define AYAPROFILER_COUNTER_ADD(name, count) \
static ::Aya::Profiler::Token AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::Aya::Profiler::getCounterToken(name ""); \
::Aya::Profiler::counterAdd(AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__), static_cast<long long>(count))
#define AYAPROFILER_COUNTER_SUB(name, count) \
static ::Aya::Profiler::Token AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::Aya::Profiler::getCounterToken(name ""); \
::Aya::Profiler::counterAdd(AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__), -static_cast<long long>(count))
#define AYAPROFILER_COUNTER_SET(name, count) \
static ::Aya::Profiler::Token AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::Aya::Profiler::getCounterToken(name ""); \
::Aya::Profiler::counterSet(AYAPROFILER_TOKEN_PASTE(proftoken, __LINE__), count)
#else
#define AYAPROFILER_SCOPE(group, name, ...) (void)0
#define AYAPROFILER_LABEL(group, label) (void)0
#define AYAPROFILER_LABELF(group, label, ...) (void)sizeof(0, __VA_ARGS__)
#define AYAPROFILER_COUNTER_ADD(name, count) (void)0
#define AYAPROFILER_COUNTER_SUB(name, count) (void)0
#define AYAPROFILER_COUNTER_SET(name, count) (void)0
#endif

View File

@@ -0,0 +1,400 @@
/**
@file RegistryUtil.cpp
@created 2006-04-06
@edited 2006-04-24
Copyright 2000-2006, Morgan McGuire.
All rights reserved.
*/
// This prevent inclusion of winsock.h in Windows.h, which prevents windows redifinition errors
// Look at winsock2.h for details, winsock2.h is #included from boost.hpp & other places.
#ifdef _WIN32
#define _WINSOCKAPI_
#include "RegistryUtil.hpp"
#undef min
#undef max
#include "Debug.hpp"
// declare HKEY constants as needed for VC6
#if !defined(HKEY_PERFORMANCE_DATA)
#define HKEY_PERFORMANCE_DATA ((HKEY)((LONG)0x80000004))
#endif
#if !defined(HKEY_PERFORMANCE_TEXT)
#define HKEY_PERFORMANCE_TEXT ((HKEY)((LONG)0x80000050))
#endif
#if !defined(HKEY_PERFORMANCE_NLSTEXT)
#define HKEY_PERFORMANCE_NLSTEXT ((HKEY)((LONG)0x80000060))
#endif
namespace Aya
{
// static helpers
static HKEY getKeyFromString(const char* str, UINT32 length);
bool RegistryUtil::keyExists(const std::string& key)
{
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
if (result == ERROR_SUCCESS)
{
RegCloseKey(openKey);
return true;
}
else
{
return false;
}
}
bool RegistryUtil::read32bitNumber(const std::string& key, INT32& valueData)
{
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
size_t valuePos = key.rfind('\\');
if (valuePos != std::string::npos)
{
std::string subKey = key.substr(pos + 1, valuePos - pos - 1);
std::string value = key.substr(valuePos + 1, key.size() - valuePos);
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, subKey.c_str(), 0, KEY_READ, &openKey);
if (result == ERROR_SUCCESS)
{
UINT32 dataSize = sizeof(INT32);
result = RegQueryValueEx(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&valueData), reinterpret_cast<LPDWORD>(&dataSize));
AYAASSERT(result == ERROR_SUCCESS && "Could not read registry key value.");
RegCloseKey(openKey);
return (result == ERROR_SUCCESS);
}
}
return false;
}
bool RegistryUtil::readBinaryData(const std::string& key, BYTE* valueData, UINT32& dataSize)
{
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
size_t valuePos = key.rfind('\\');
if (valuePos != std::string::npos)
{
std::string subKey = key.substr(pos + 1, valuePos - pos - 1);
std::string value = key.substr(valuePos + 1, key.size() - valuePos);
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, subKey.c_str(), 0, KEY_READ, &openKey);
if (result == ERROR_SUCCESS)
{
if (valueData == NULL)
{
result = RegQueryValueEx(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
}
else
{
result = RegQueryValueEx(openKey, value.c_str(), NULL, NULL, valueData, reinterpret_cast<LPDWORD>(&dataSize));
}
AYAASSERT(result == ERROR_SUCCESS && "Could not read registry key value.");
RegCloseKey(openKey);
return (result == ERROR_SUCCESS);
}
}
return false;
}
bool RegistryUtil::readString(const std::string& key, std::string& valueData)
{
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
size_t valuePos = key.rfind('\\');
if (valuePos != std::string::npos)
{
std::string subKey = key.substr(pos + 1, valuePos - pos - 1);
std::string value = key.substr(valuePos + 1, key.size() - valuePos);
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, subKey.c_str(), 0, KEY_READ, &openKey);
if (result == ERROR_SUCCESS)
{
UINT32 dataSize = 0;
result = RegQueryValueEx(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
if (result == ERROR_SUCCESS)
{
char* tmpStr = new char[dataSize];
memset(tmpStr, 0, dataSize);
result = RegQueryValueEx(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(tmpStr), reinterpret_cast<LPDWORD>(&dataSize));
if (result == ERROR_SUCCESS)
{
valueData = tmpStr;
}
delete[] tmpStr;
}
// AYAASSERT(result == ERROR_SUCCESS && "Could not read registry key value.");
RegCloseKey(openKey);
return (result == ERROR_SUCCESS);
}
}
return false;
}
bool RegistryUtil::write32bitNumber(const std::string& key, INT32 valueData)
{
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
size_t valuePos = key.rfind('\\');
if (valuePos != std::string::npos)
{
std::string subKey = key.substr(pos + 1, valuePos - pos - 1);
std::string value = key.substr(valuePos + 1, key.size() - valuePos);
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, subKey.c_str(), 0, KEY_ALL_ACCESS, &openKey);
if (result == ERROR_SUCCESS)
{
result = RegSetValueEx(openKey, value.c_str(), NULL, REG_DWORD, reinterpret_cast<const BYTE*>(&valueData), sizeof(INT32));
AYAASSERT(result == ERROR_SUCCESS && "Could not write registry key value.");
RegCloseKey(openKey);
return (result == ERROR_SUCCESS);
}
}
return false;
}
bool RegistryUtil::writeBinaryData(const std::string& key, const BYTE* valueData, UINT32 dataSize)
{
AYAASSERT(valueData);
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
size_t valuePos = key.rfind('\\');
if (valuePos != std::string::npos)
{
std::string subKey = key.substr(pos + 1, valuePos - pos - 1);
std::string value = key.substr(valuePos + 1, key.size() - valuePos);
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, subKey.c_str(), 0, KEY_ALL_ACCESS, &openKey);
if (result == ERROR_SUCCESS)
{
if (valueData)
{
result = RegSetValueEx(openKey, value.c_str(), NULL, REG_BINARY, reinterpret_cast<const BYTE*>(valueData), dataSize);
}
AYAASSERT(result == ERROR_SUCCESS && "Could not write registry key value.");
RegCloseKey(openKey);
return (result == ERROR_SUCCESS);
}
}
return false;
}
bool RegistryUtil::writeString(const std::string& key, const std::string& valueData)
{
size_t pos = key.find('\\', 0);
if (pos == std::string::npos)
{
return false;
}
HKEY hkey = getKeyFromString(key.c_str(), pos);
if (hkey == NULL)
{
return false;
}
AYAASSERT(key.size() > (pos + 1));
size_t valuePos = key.rfind('\\');
if (valuePos != std::string::npos)
{
std::string subKey = key.substr(pos + 1, valuePos - pos - 1);
std::string value = key.substr(valuePos + 1, key.size() - valuePos);
HKEY openKey;
INT32 result = RegOpenKeyEx(hkey, subKey.c_str(), 0, KEY_ALL_ACCESS, &openKey);
if (result == ERROR_SUCCESS)
{
result = RegSetValueEx(openKey, value.c_str(), NULL, REG_SZ, reinterpret_cast<const BYTE*>(valueData.c_str()), (valueData.size() + 1));
AYAASSERT(result == ERROR_SUCCESS && "Could not write registry key value.");
RegCloseKey(openKey);
return (result == ERROR_SUCCESS);
}
}
return false;
}
// static helpers
static HKEY getKeyFromString(const char* str, UINT32 length)
{
AYAASSERT(str);
if (str)
{
if (strncmp(str, "HKEY_CLASSES_ROOT", length) == 0)
{
return HKEY_CLASSES_ROOT;
}
else if (strncmp(str, "HKEY_CURRENT_CONFIG", length) == 0)
{
return HKEY_CURRENT_CONFIG;
}
else if (strncmp(str, "HKEY_CURRENT_USER", length) == 0)
{
return HKEY_CURRENT_USER;
}
else if (strncmp(str, "HKEY_LOCAL_MACHINE", length) == 0)
{
return HKEY_LOCAL_MACHINE;
}
else if (strncmp(str, "HKEY_PERFORMANCE_DATA", length) == 0)
{
return HKEY_PERFORMANCE_DATA;
}
else if (strncmp(str, "HKEY_PERFORMANCE_NLSTEXT", length) == 0)
{
return HKEY_PERFORMANCE_NLSTEXT;
}
else if (strncmp(str, "HKEY_PERFORMANCE_TEXT", length) == 0)
{
return HKEY_PERFORMANCE_TEXT;
}
else if (strncmp(str, "HKEY_CLASSES_ROOT", length) == 0)
{
return HKEY_CLASSES_ROOT;
}
else
{
return NULL;
}
}
else
{
return NULL;
}
}
} // namespace Aya
#endif

View File

@@ -0,0 +1,71 @@
#pragma once
#if defined(_WIN32) && !defined(AYA_PLATFORM_DURANGO)
#include "pdh.h"
#include <string>
namespace Aya
{
/**
Provides generalized Windows registry querying.
All key names are one string in the format:
"[base key]\[sub-keys]\value"
[base key] can be any of the following:
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_PERFORMANCE_DATA
HKEY_PERFORMANCE_NLSTEXT
HKEY_PERFORMANCE_TEXT
HKEY_USERS
keyExists() should be used to validate a key before reading or writing
to ensure that a debug assert or false return is for a different error.
*/
class RegistryUtil
{
public:
/** returns true if the key exists */
static bool keyExists(const std::string& key);
/** returns false if the key could not be read for any reason. */
static bool read32bitNumber(const std::string& key, INT32& valueData);
/**
Reads an arbitrary amount of data from a binary registry key.
returns false if the key could not be read for any reason.
@param valueData pointer to the output buffer of sufficient size. Pass NULL as valueData in order to have available data size returned in
dataSize.
@param dataSize size of the output buffer. When NULL is passed for valueData, contains the size of available data on successful return.
*/
static bool readBinaryData(const std::string& key, BYTE* valueData, UINT32& dataSize);
/** returns false if the key could not be read for any reason. */
static bool readString(const std::string& key, std::string& valueData);
/** returns false if the key could not be written for any reason. */
static bool write32bitNumber(const std::string& key, INT32 valueData);
/**
Writes an arbitrary amount of data to a binary registry key.
returns false if the key could not be written for any reason.
@param valueData pointer to the input buffer
@param dataSize size of the input buffer that should be written
*/
static bool writeBinaryData(const std::string& key, const BYTE* valueData, UINT32 dataSize);
/** returns false if the key could not be written for any reason. */
static bool writeString(const std::string& key, const std::string& valueData);
};
} // namespace Aya
#endif // _WIN32

View File

@@ -0,0 +1,931 @@
#pragma once
#include "atomic.hpp"
#include "boost.hpp"
#include "time.hpp"
#include "Debug.hpp"
#include "MathUtil.hpp"
#include <boost/static_assert.hpp>
#include <boost/circular_buffer.hpp>
#include <boost/array.hpp>
#include <boost/shared_ptr.hpp>
#include <cmath>
namespace Aya
{
template<typename ValueType = double, typename AverageType = double>
class RunningAverage
{
boost::shared_ptr<boost::circular_buffer<ValueType>> buffer;
public:
RunningAverage(double lerp = 0.05, ValueType initialValue = 0, unsigned int bufferSize = 0)
: lerp(lerp)
, lastSampleValue(initialValue)
, averageValue(initialValue)
, averageVariance(0)
, firstTime(true)
{
if (bufferSize)
buffer.reset(new boost::circular_buffer<ValueType>(bufferSize));
}
void sample(ValueType value)
{
if (isFinite(value))
{
sampleValue(value);
sampleVariance(value);
if (buffer)
buffer->push_back(value);
}
}
// Get the current value
AverageType value() const
{
return averageValue;
}
AverageType variance() const
{
return averageVariance;
}
AverageType standard_deviation() const
{
// TODO: Cache this value?
return std::sqrt(averageVariance);
}
AverageType variance_to_mean_ratio() const
{
return averageVariance / (averageValue * averageValue);
}
AverageType coefficient_of_variation() const
{
return standard_deviation() / averageValue;
}
// Get the last sampled value
ValueType lastSample() const
{
return lastSampleValue;
}
template<class F>
void iter(F& f) const
{
if (buffer)
{
for (typename boost::circular_buffer<ValueType>::const_iterator it = buffer->begin(); it != buffer->end(); ++it)
{
f(*it);
}
}
}
void reset(ValueType resetValue = 0)
{
lastSampleValue = resetValue;
averageValue = resetValue;
averageVariance = 0;
firstTime = true;
if (buffer)
buffer->clear();
}
bool hasSampled()
{
return !firstTime;
}
const double lerp;
private:
ValueType lastSampleValue;
AverageType averageValue;
AverageType averageVariance;
bool firstTime;
inline void sampleValue(ValueType value)
{
averageValue = firstTime ? value : (1.0 - lerp) * averageValue + lerp * (AverageType)value;
lastSampleValue = value;
firstTime = false;
}
inline void sampleVariance(ValueType value)
{
const double diff = value - averageValue;
const double variance = diff * diff;
averageVariance = (1.0 - lerp) * averageVariance + lerp * variance;
}
};
template<typename ValueType = double, typename AverageType = double>
class WindowAverage
{
protected:
boost::circular_buffer<ValueType> buffer;
public:
struct Stats
{
Stats(size_t samples, const AverageType& average, const AverageType& variance)
: samples(samples)
, average(average)
, variance(variance) {};
size_t samples;
AverageType average;
AverageType variance;
};
WindowAverage(size_t maxSamples)
: buffer(maxSamples)
{
}
void setMaxSamples(size_t maxSamples)
{
buffer.set_capacity(maxSamples);
}
size_t getMaxSamples() const
{
return buffer.capacity();
}
void sample(ValueType value)
{
buffer.push_back(value);
}
// calls fonbeforedrop if an item is about to be dropped
template<class F>
void sample(ValueType value, F& fonbeforedrop)
{
if (buffer.full())
{
fonbeforedrop(buffer.front());
}
sample(value);
}
Stats getSanitizedStats(Confidence conf = C90) const
{
Stats regularStats = getStats();
if (regularStats.samples <= 1)
return regularStats;
Stats result(0, AverageType(), AverageType());
AverageType std = sqrt(regularStats.variance);
for (typename boost::circular_buffer<ValueType>::const_iterator it = buffer.begin(); it != buffer.end(); ++it)
{
double value = *it;
if (IsValueOutlier(value, regularStats.samples, regularStats.average, std, conf))
continue;
result.samples++;
result.average += value;
}
result.average /= result.samples;
for (typename boost::circular_buffer<ValueType>::const_iterator it = buffer.begin(); it != buffer.end(); ++it)
{
double value = *it;
if (IsValueOutlier(value, regularStats.samples, regularStats.average, std, conf))
continue;
AverageType diff = (result.average - value);
result.variance = result.variance + diff * diff;
}
result.variance /= (result.samples - 1);
return result;
}
Stats getStats(size_t samples = ~0) const // get n last frames.
{
samples = std::min(buffer.size(), samples);
Stats result(samples, AverageType(), AverageType());
typename boost::circular_buffer<ValueType>::const_reverse_iterator it;
size_t ii;
for (it = buffer.rbegin(), ii = 0; ii < samples; ++it, ++ii)
{
result.average = result.average + *it;
}
if (samples != 0)
{
result.average /= samples;
}
for (it = buffer.rbegin(), ii = 0; ii < samples; ++it, ++ii)
{
AverageType diff = (result.average - *it);
result.variance = result.variance + diff * diff;
}
if (samples > 1)
{
result.variance /= (samples - 1);
}
return result;
}
AverageType getLatest() const // get data from the last frame
{
return buffer.empty() ? 0 : *(buffer.rbegin());
}
template<class F>
void iter(F& f) const
{
for (typename boost::circular_buffer<ValueType>::const_iterator it = buffer.begin(); it != buffer.end(); ++it)
{
f(*it);
}
}
void clear()
{
buffer.clear();
}
size_t size() const
{
return buffer.size();
}
};
// A class that follows the pattern of RunningAverage, but keeps track of step frequency.
template<Time::SampleMethod sampleMethod = Time::Benchmark>
class RunningAverageTimeInterval
{
public:
RunningAverageTimeInterval(double lerp = 0.05)
: firstTime(true)
, average(lerp)
{
}
void sample()
{
if (firstTime)
{
timer.reset();
firstTime = false;
}
else
{
average.sample(timer.reset().seconds());
}
}
// Get the current value
Time::Interval value() const
{
Time::Interval timeSinceLastSample = timer.delta();
double v = average.value();
if (timeSinceLastSample.seconds() > 2.0 * v)
return timeSinceLastSample;
else
return Time::Interval(v);
}
double rate() const
{
double v = value().seconds();
return v > 0.0 ? 1.0 / v : 0.0;
}
double variance() const
{
return average.variance();
}
double standard_deviation() const
{
return average.standard_deviation();
}
double variance_to_mean_ratio() const
{
return average.variance_to_mean_ratio();
}
double coefficient_of_variation() const
{
return average.coefficient_of_variation();
}
double getLerp() const
{
return average.lerp;
}
Time::Interval lastSample() const
{
return Time::Interval(average.lastSample());
}
private:
Timer<sampleMethod> timer;
bool firstTime;
RunningAverage<> average;
};
struct FOnBeforeDrop
{
WindowAverage<>& average;
Time::Interval& currentWindow;
Time::Interval& maxWindow;
FOnBeforeDrop(WindowAverage<>& average, Time::Interval& currentWindow, Time::Interval& maxWindow)
: average(average)
, currentWindow(currentWindow)
, maxWindow(maxWindow) {};
void operator()(double sample)
{
if (currentWindow.seconds() < maxWindow.seconds())
{
// prevent dropping.
// grow window size.
average.setMaxSamples(average.getMaxSamples() * 2);
}
else
{
// allow drop. adjust total counter.
currentWindow -= Time::Interval(sample);
}
}
};
// A class that follows the pattern of RunningAverage, but keeps track of step frequency.
template<Time::SampleMethod sampleMethod = Time::Benchmark>
class WindowAverageTimeInterval
{
public:
WindowAverageTimeInterval(Time::Interval maxWindow)
: maxWindow(maxWindow)
, average(16)
{
}
void setMaxWindow(Time::Interval maxWindow)
{
this->maxWindow = maxWindow;
if (maxWindow.seconds() == 0.0)
{
// special case, release memory
average.setMaxSamples(16);
}
};
Time::Interval getMaxWindow() const
{
return maxWindow;
};
size_t getCapacity() const
{
return average.getMaxSamples();
};
void sample()
{
if (firstTime)
{
timer.reset();
firstTime = false;
}
else
{
double dt = timer.reset().seconds();
currentWindow += Time::Interval(dt);
// this functor will allow our ring buffer to grow geometrically
// as long as it doesn't contain maxWindow worth of interval measurments.
FOnBeforeDrop fonbeforedrop(average, currentWindow, maxWindow);
average.sample(dt, fonbeforedrop);
}
}
struct Stats
{
Stats(size_t samples, Time::Interval averagedt, Time::Interval variancedt, Time::Interval totalt, double samplespersecond)
: samples(samples)
, average(averagedt)
, variance(variancedt)
, sum(totalt)
, samplespersecond(samplespersecond) {};
size_t samples;
Time::Interval average;
Time::Interval variance;
Time::Interval sum;
double samplespersecond;
};
struct FSum
{
FSum()
: vsum(0.0) {};
double vsum;
void operator()(double v)
{
vsum += v;
};
};
Stats getStats(size_t samples = ~0) const
{
WindowAverage<>::Stats basicstats = average.getStats(samples);
FSum fsum;
average.iter(fsum);
return Stats(basicstats.samples, Time::Interval(basicstats.average), Time::Interval(basicstats.variance), Time::Interval(fsum.vsum),
samples / fsum.vsum);
}
template<class F>
void iter(F& f) const
{
average.iter(f);
}
void clear()
{
firstTime = true;
average.clear();
}
size_t size() const
{
return average.size();
}
private:
Time::Interval currentWindow;
Time::Interval maxWindow;
WindowAverage<> average;
Timer<sampleMethod> timer;
bool firstTime;
};
template<typename ValueType = int, Time::SampleMethod sampleMethod = Time::Benchmark>
class TotalCountTimeInterval
{
Timer<sampleMethod> timer;
double interval;
ValueType valueLastInterval;
ValueType valueCurrentInterval;
public:
TotalCountTimeInterval(double interval = 1.0f)
: interval(interval)
, valueCurrentInterval(0)
, valueLastInterval(0)
{
}
void increment(ValueType count = 1)
{
if (timer.delta().seconds() >= interval)
{
valueLastInterval = valueCurrentInterval;
valueCurrentInterval = 0;
timer.reset();
}
valueCurrentInterval += count;
}
void decrement(ValueType count = 1)
{
if (timer.delta().seconds() >= interval)
{
valueLastInterval = valueCurrentInterval;
valueCurrentInterval = 0;
timer.reset();
}
valueCurrentInterval -= count;
}
ValueType getCount() const
{
return (timer.delta().seconds() <= interval) ? valueLastInterval : 0;
}
};
class ThrottlingHelper
{
int* eventsPerMinute;
int* eventsPerObjectPerMinute;
int requestCounter;
int maxObjectCount;
Time lastTimestamp;
public:
ThrottlingHelper(int* eventsPerMinute, int* eventsPerObjectPerMinute = NULL)
: // Designed to pass FInt
eventsPerMinute(eventsPerMinute)
, eventsPerObjectPerMinute(eventsPerObjectPerMinute)
, requestCounter(0)
, maxObjectCount(0)
, lastTimestamp(Time::nowFast())
{
}
bool checkLimit(int objectCount = 0)
{
Time now = Time::nowFast();
if ((now - lastTimestamp).seconds() > 60)
{
requestCounter = 0;
lastTimestamp = now;
maxObjectCount = 0;
}
requestCounter++;
maxObjectCount = std::max(objectCount, maxObjectCount);
int totalCount = *eventsPerMinute;
if (eventsPerObjectPerMinute)
totalCount += maxObjectCount * (*eventsPerObjectPerMinute);
if (requestCounter > totalCount)
return false;
return true;
}
};
class BudgetedThrottlingHelper
{
float currentBudget;
public:
BudgetedThrottlingHelper()
: currentBudget(0.0)
{
}
void addBudget(float budget, float maxBudget)
{
currentBudget = std::min(currentBudget + budget, maxBudget);
}
bool checkAndReduceBudget()
{
if (currentBudget < 0)
return false;
currentBudget--;
return true;
}
float getBudget()
{
return currentBudget;
};
};
// A class that follows the pattern of RunningAverage, but keeps track of time spent in a cyclical task.
template<Time::SampleMethod sampleMethod = Time::Benchmark>
class RunningAverageDutyCycle
{
public:
RunningAverageDutyCycle(double lerp)
: time(lerp)
, interval(lerp)
{
}
RunningAverageDutyCycle(double lerp, int timeBufferSize)
: time(lerp, 0, timeBufferSize)
, interval(lerp)
{
}
void sample(Time::Interval elapsedTime)
{
interval.sample();
time.sample(elapsedTime.seconds());
}
Aya::Time startSample() const
{
return Time::now<sampleMethod>();
}
void stopSample(Aya::Time start)
{
sample(Time::now<sampleMethod>() - start);
}
double dutyCycle() const
{
double averageInterval = interval.value().seconds();
double averageTime = time.value();
return averageInterval != 0 ? averageTime / averageInterval : (averageTime > 0 ? 1 : 0);
}
double rate() const
{
return interval.rate();
}
const RunningAverageTimeInterval<sampleMethod>& stepInterval() const
{
return interval;
}
Time::Interval lastStepInterval()
{
return interval.lastSample();
}
const RunningAverage<double>& stepTime() const
{
return time;
}
double getIntervalLerp() const
{
return interval.getLerp();
}
private:
RunningAverage<double> time;
RunningAverageTimeInterval<sampleMethod> interval;
};
// A class that follows the pattern of WindowAverage, but keeps track of time spent in a cyclical task.
template<Time::SampleMethod sampleMethod = Time::Benchmark>
class WindowAverageDutyCycle
{
public:
WindowAverageDutyCycle(Time::Interval maxWindow)
: time(16)
, interval(maxWindow)
{
}
void setMaxWindow(Time::Interval maxWindow)
{
interval.setMaxWindow(maxWindow);
if (maxWindow.seconds() == 0.0)
{
// special case, release memory
time.setMaxSamples(16);
}
};
Time::Interval getMaxWindow() const
{
return interval.getMaxWindow();
};
void sample(Time::Interval elapsedTime)
{
interval.sample();
// make sure time's buffer size tracks interval's buffer size.
if (time.getMaxSamples() != interval.getCapacity())
{
time.setMaxSamples(interval.getCapacity());
}
time.sample(elapsedTime.seconds());
}
struct Stats
{
Stats(const typename WindowAverageTimeInterval<sampleMethod>::Stats& interval, const WindowAverage<double>::Stats& time, double dutyfraction)
: interval(interval)
, time(time)
, dutyfraction(dutyfraction) {};
typename WindowAverageTimeInterval<sampleMethod>::Stats interval;
WindowAverage<double>::Stats time;
double dutyfraction; // 1.0: duty time is 100% of interval time
};
Stats getStats(size_t samples = ~0) const
{
typename WindowAverage<>::Stats timestats = time.getStats(samples);
typename WindowAverageTimeInterval<sampleMethod>::Stats intervalstats = interval.getStats(samples);
double interval = intervalstats.average.seconds();
return Stats(intervalstats, timestats, interval ? timestats.average / interval : 0);
}
template<class F>
void iterTimes(F& f) const
{
time.iter(f);
}
template<class F>
void iterIntervals(F& f) const
{
interval.iter(f);
}
struct GTCounter
{
GTCounter(double gt)
: c(0)
, gtValue(gt) {};
size_t c;
double gtValue;
void operator()(double dt)
{
if (dt > gtValue)
c++;
}
};
size_t countTimesGreaterThan(Time::Interval dt) const
{
GTCounter count(dt.seconds());
iterTimes(count);
return count.c;
}
size_t countIntervalsGreaterThan(Time::Interval dt) const
{
GTCounter count(dt.seconds());
iterIntervals(count);
return count.c;
}
void clear()
{
time.clear();
interval.clear();
}
size_t timesamples() const
{
return time.size();
}
size_t intervalsamples() const
{
return interval.size();
}
private:
WindowAverage<double> time;
WindowAverageTimeInterval<sampleMethod> interval;
};
// A thread-safe, lock-free DutyCycle meter
template<int windowSeconds>
class ActivityMeter
{
static const int bucketCount = windowSeconds * 1024;
boost::array<char, bucketCount> buckets;
Aya::atomic<int> currentTime;
Aya::atomic<int> currentValue;
Aya::atomic<int> totalValue;
Aya::Time startTime;
Aya::Time lastSampleTime;
public:
ActivityMeter()
: currentTime(-1)
, currentValue(0)
, totalValue(0)
, startTime(Aya::Time::now<Time::Fast>())
{
for (size_t i = 0; i < buckets.size(); ++i)
buckets[i] = 0;
}
double averageValue()
{
updateBuckets();
return (double)totalValue / (double)bucketCount;
}
void increment()
{
updateBuckets();
++currentValue;
}
void decrement()
{
updateBuckets();
--currentValue;
}
void updateBuckets()
{
Aya::Time now = Time::now<Time::Fast>();
if (lastSampleTime == now)
return;
lastSampleTime = now;
unsigned long newTime = ((unsigned long)(bucketCount * (lastSampleTime - startTime).seconds()));
unsigned long oldTime = currentTime.swap(newTime);
if (oldTime < newTime)
{
long newValue = currentValue;
for (unsigned long i = oldTime + 1; i <= newTime; ++i)
{
int index = i % bucketCount;
int oldBucketValue = buckets[index];
for (int j = 0; j < oldBucketValue; ++j)
--totalValue;
buckets[index] = (char)newValue;
for (int j = 0; j < newValue; ++j)
++totalValue;
}
}
}
};
template<int windowSeconds>
class InvocationMeter
{
static const int bucketCount = windowSeconds * 1024;
boost::array<char, bucketCount> buckets;
Aya::atomic<int> currentTime;
Aya::atomic<int> totalValue;
Aya::Time startTime;
Aya::Time lastSampleTime;
public:
InvocationMeter()
: currentTime(-1)
, totalValue(0)
, startTime(Aya::Time::now<Time::Fast>())
{
for (size_t i = 0; i < buckets.size(); ++i)
buckets[i] = 0;
}
double getTotalValuePerSecond()
{
updateBuckets(false);
return totalValue / windowSeconds;
}
void increment()
{
updateBuckets(true);
}
void updateBuckets(bool increment)
{
Aya::Time now = Time::now<Time::Fast>();
if (lastSampleTime == now)
return;
lastSampleTime = now;
unsigned long newTime = ((unsigned long)(bucketCount * (lastSampleTime - startTime).seconds()));
unsigned long oldTime = currentTime.swap(newTime);
// AYAASSERT(oldTime <= newTime);
// if (oldTime != newTime)
if (oldTime < newTime) // changed per Erik 11/18/09
{
for (unsigned long i = oldTime + 1; i <= newTime; ++i)
{
int index = i % bucketCount;
int oldBucketValue = buckets[index];
for (int j = 0; j < oldBucketValue; ++j)
--totalValue;
buckets[index] = 0;
}
}
if (increment)
{
int newValue = 1;
int newIndex = newTime % bucketCount;
buckets[newIndex] = newValue;
for (int j = 0; j < newValue; ++j)
++totalValue;
}
}
};
} // namespace Aya

View File

@@ -0,0 +1,16 @@
#pragma once
namespace Aya
{
inline void safeToLower(std::string& s)
{
for (unsigned i = 0; i < s.size(); ++i)
{
if (isupper(s[i]))
{
s[i] = tolower(s[i]);
}
}
}
} // namespace Aya

View File

@@ -0,0 +1,53 @@
#pragma once
// #include "pdh.h"
#include "boost/weak_ptr.hpp"
#include "boost/shared_ptr.hpp"
#include "Debug.hpp"
#include "threadsafe.hpp"
namespace Aya
{
template<class T>
class ScopedSingleton
{
private:
static int& initCount()
{
static int initcount = 0;
return initcount;
}
protected:
// use this to validate usage pattern.
// some usage patterns will want to check that this doesn't
// increase more than 1.
static int getInitCount()
{
return initCount();
}
SAFE_STATIC(Aya::spin_mutex, sync)
SAFE_STATIC(boost::weak_ptr<T>, s_instance)
public:
static boost::shared_ptr<T> getInstance()
{
Aya::spin_mutex::scoped_lock lock(sync());
shared_ptr<T> result = s_instance().lock();
if (!result)
{
initCount()++;
result = shared_ptr<T>(new T());
s_instance() = result;
}
return result;
}
static boost::shared_ptr<T> getInstanceOptional()
{
return s_instance().lock();
}
};
} // namespace Aya

View File

@@ -0,0 +1,14 @@
#pragma once
namespace Aya
{
enum SelectState
{
SELECT_NORMAL,
SELECT_LIMIT,
SELECT_HOVER,
};
} // namespace Aya

View File

@@ -0,0 +1,112 @@
#include "SimpleJSON.hpp"
#include "rapidjson/document.h"
#include <sstream>
// used for the old parser
#ifndef AYA_BOOTSTRAPPER_MAC
#include "boost/algorithm/string.hpp"
#endif
void SimpleJSON::ReadFromStream(const char* stream)
{
rapidjson::Document root;
root.Parse<0>(stream);
if (root.GetParseError())
{
// RapidJSON actually crashes if you attempt to iterate over an invalid document.
size_t errOffset = root.GetErrorOffset();
std::string errMessage = "RapidJSON error " + root.GetParseError();
std::stringstream msg("SimpleJson, @");
msg << errOffset << ": " << errMessage;
_error = true;
_errorString = std::string(msg.str());
return;
}
for (rapidjson::Value::MemberIterator it = root.MemberBegin(); it != root.MemberEnd(); ++it)
{
std::string valueName(it->name.GetString());
std::stringstream valueData;
// (only IsString was originally supported by the original SimpleJSON)
if (it->value.IsString())
{
valueData << it->value.GetString();
}
else if (it->value.IsInt())
{
valueData << it->value.GetInt();
}
else if (it->value.IsBool())
{
valueData << (it->value.GetBool() ? "True" : "False");
}
else
{
// error
continue;
}
parser dataParser = _propValues[valueName];
if (dataParser != NULL)
{
dataParser(valueData.str().c_str());
}
else
{
DefaultHandler(valueName, valueData.str());
}
}
}
bool SimpleJSON::ParseBool(const char* value)
{
return (strcmp(value, "true") == 0 || strcmp(value, "True") == 0) ? true : false;
}
// Below is the old version that will be kept around for a while.
//
static const char* ValueEnclosure = "\"";
static const char LineBreak = 10;
static bool needTrimSymbol(char c)
{
return (c == ValueEnclosure[0]) || c == LineBreak || c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
#ifdef AYA_BOOTSTRAPPER_MAC
static void trim_str_left(std::string& s)
{
std::string::iterator iter = s.begin();
for (; iter != s.end(); ++iter)
{
if (!needTrimSymbol(*iter))
break;
}
s.erase(s.begin(), iter);
}
static void trim_str_right(std::string& s)
{
std::string::iterator iter = s.end();
for (; iter != s.begin();)
{
if (!needTrimSymbol(*(--iter)))
{
++iter;
break;
}
}
s.erase(iter, s.end());
}
static void trim_str(std::string& s)
{
try
{
trim_str_left(s);
trim_str_right(s);
}
catch (...)
{
}
}
#endif

View File

@@ -0,0 +1,108 @@
#pragma once
#include <string>
#include <map>
class SimpleJSON;
typedef void (*parser)(const char* stream);
#define START_DATA_MAP(className) \
static className* _thisPtr; \
void Init(); \
className() \
{ \
_thisPtr = this; \
Init(); \
} \
virtual ~className() {}
#define END_DATA_MAP()
#define DATA_MAP_IMPL_START(className) \
className* className::_thisPtr = NULL; \
void className::Init() \
{
#define DATA_MAP_IMPL_END() }
#define DECLARE_DATA_INT(name) \
private: \
int _prop##name; \
\
public: \
int GetValue##name() const \
{ \
return _prop##name; \
} \
static void ReadValue##name(const char* stream) \
{ \
_thisPtr->_prop##name = atoi(stream); \
}
#define DECLARE_DATA_BOOL(name) \
private: \
bool _prop##name; \
\
public: \
bool GetValue##name() const \
{ \
return _prop##name; \
} \
static void ReadValue##name(const char* stream) \
{ \
_thisPtr->_prop##name = ParseBool(stream); \
}
#define DECLARE_DATA_STRING(name) \
private: \
std::string _prop##name; \
\
public: \
const char* GetValue##name() const \
{ \
return _prop##name.c_str(); \
} \
static void ReadValue##name(const char* stream) \
{ \
_thisPtr->_prop##name = std::string(stream); \
}
#define IMPL_DATA(name, def) \
_prop##name = def; \
_propValues[std::string(#name)] = ReadValue##name;
class SimpleJSON
{
protected:
std::map<std::string, parser> _propValues;
bool _error;
std::string _errorString;
public:
SimpleJSON()
: _error(false){};
virtual ~SimpleJSON(){};
static bool ParseBool(const char* value);
void ReadFromStream(const char* stream);
bool GetError() const
{
return _error;
}
void ClearError()
{
_error = false;
}
std::string GetErrorString() const
{
return _errorString;
}
protected:
virtual bool DefaultHandler(const std::string& valueName, const std::string& valueData)
{
return false;
};
};

View File

@@ -0,0 +1,15 @@
#include "StreamHelpers.hpp"
namespace Aya
{
void readStreamIntoString(std::istream& stream, std::string& content)
{
size_t contentLength = 0;
stream.seekg(0, std::ios::end);
contentLength = static_cast<size_t>(stream.tellg());
stream.seekg(0, std::ios::beg);
content = std::string(contentLength, '\0');
stream.read(&content[0], contentLength);
}
} // namespace Aya

View File

@@ -0,0 +1,7 @@
#include <istream>
#include <string>
namespace Aya
{
void readStreamIntoString(std::istream& stream, std::string& content);
} // namespace Aya

View File

@@ -0,0 +1,44 @@
// d9mz - might be something wrong with my sdk, but this is needed to compile shadercompiler - it sucks
#ifndef PTRDIFF_MAX
#include "StringConv.hpp"
#else
#include "StringConv.hpp"
#endif
#if !defined(__linux) && !defined(__APPLE__)
#include <Windows.h>
#endif
namespace Aya
{
#if !defined(__linux) && !defined(__APPLE__)
// convert wstring to UTF-8 string
std::string utf8_encode(const std::wstring& path)
{
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &path[0], (int)path.size(), NULL, 0, NULL, NULL);
std::string tgt(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &path[0], (int)path.size(), &tgt[0], size_needed, NULL, NULL);
return tgt;
}
// convert UTF-8 string to wstring
std::wstring utf8_decode(const std::string& path)
{
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &path[0], (int)path.size(), NULL, 0);
std::wstring tgt(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, &path[0], (int)path.size(), &tgt[0], size_needed);
return tgt;
}
#else
std::string utf8_encode(const std::string& path)
{
return path;
}
std::string utf8_decode(const std::string& path)
{
return path;
}
#endif
} // namespace Aya

View File

@@ -0,0 +1,15 @@
#pragma once
#include <string>
namespace Aya
{
#if defined(_WIN32)
typedef std::wstring SysPathString;
#else // assume we're on Linux / Unix, which usually don't require conversions
typedef std::string SysPathString;
#endif
std::string utf8_encode(const SysPathString& path);
SysPathString utf8_decode(const std::string& path);
} // namespace Aya

View File

@@ -0,0 +1,191 @@
#include "SystemUtil.hpp"
#include "StreamHelpers.hpp"
#include "FastLog.hpp"
#include "AyaFormat.hpp"
#ifdef __ANDROID_API__
#include <android/api-level.h>
#endif
#include <fstream>
#include <string>
#include <filesystem>
#include <stdio.h>
#include <stdlib.h>
#include <glad/gl.h>
#ifdef _WIN32
#include <Windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <d3d9.h>
#include "ddraw.h"
#include <d3d11.h>
#include <dxgi1_4.h>
#else
#include <dirent.h>
#include <unistd.h>
#endif
using namespace Aya;
namespace Aya
{
namespace SystemUtil
{
std::string mOSVersion; // Set in JNIGLActivity.cpp
std::string mDeviceName;
std::string getCPUMake()
{
#ifdef __arm_
return "ARM";
#elif __aarch64__
return "AArch64";
#elif __i386__ || __amd64__
return "Intel";
#else
#error Unsupported platform.
#endif
}
uint64_t getCPUSpeed()
{
return 0;
}
uint64_t getCPULogicalCount()
{
return getCPUCoreCount();
}
uint64_t getCPUCoreCount()
{
#ifdef _WIN32
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#else
return sysconf(_SC_NPROCESSORS_CONF);
#endif
}
uint64_t getCPUPhysicalCount()
{
return getCPUCoreCount();
}
bool isCPU64Bit()
{
#ifdef __arm64__ || __amd64__ || _WIN64
return true;
#else
#ifdef _WIN32
BOOL result = false;
return IsWow64Process(GetCurrentProcess(), &result) && result;
#else
return false;
#endif
#endif
}
uint64_t getMBSysRAM()
{
return 16 * 1024;
}
uint64_t getMBSysAvailableRAM()
{
return getMBSysRAM();
}
uint64_t getVideoMemory()
{
GLint total_mem_kb = 0;
// return 8gb
return 8 * 1024 * 1024;
}
std::string osPlatform()
{
#ifdef __ANDROID_API__
return "Android";
#elif _WIN32
return "Win32";
#elif __APPLE__
return "Apple";
#else
return "Linux";
#endif
}
int osPlatformId()
{
#ifdef __ANDROID_API__
return __ANDROID_API__;
#elif _WIN32
return VER_PLATFORM_WIN32_NT;
#else
return 0x7999999999;
#endif
}
std::string osVer()
{
#ifdef _WIN32
OSVERSIONINFO osvi = {0};
osvi.dwOSVersionInfoSize = sizeof(osvi);
#pragma warning(disable : 4996 28159)
GetVersionEx(&osvi);
return Aya::format("%d.%d.%d.%d", osvi.dwOSVersionInfoSize, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
#else
return mOSVersion;
#endif
}
std::string deviceName()
{
#ifndef __ANDROID_API__
return "PC";
#else
return Aya::SystemUtil::mDeviceName;
#endif
}
std::string getGPUMake()
{
#ifdef __ANDROID_API__
return "Android";
#elif _WIN32
DISPLAY_DEVICE displayDevice;
ZeroMemory(&displayDevice, sizeof(displayDevice));
displayDevice.cb = sizeof(displayDevice);
EnumDisplayDevices(NULL, 0, &displayDevice, 0);
return std::string(displayDevice.DeviceString);
#else
return "Some Linux GPU";
#endif
}
std::string getMaxRes()
{
return "";
}
std::string getExecutablePath()
{
#ifdef _WIN32
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
return std::filesystem::path(buffer).parent_path().string();
#else
char buffer[PATH_MAX] = {0};
readlink("/proc/self/exe", buffer, sizeof(buffer));
return std::filesystem::path(buffer).parent_path();
#endif
}
} // namespace SystemUtil
} // namespace Aya

View File

@@ -0,0 +1,42 @@
#pragma once
#include <string>
#include <stdint.h>
#ifdef _WIN32
typedef unsigned __int64 uint64_t;
#endif
namespace Aya
{
namespace SystemUtil
{
/// CPU Related
std::string getCPUMake();
uint64_t getCPUSpeed();
uint64_t getCPULogicalCount();
uint64_t getCPUCoreCount();
uint64_t getCPUPhysicalCount();
bool isCPU64Bit();
/// Memory Related
uint64_t getMBSysRAM();
uint64_t getMBSysAvailableRAM();
uint64_t getVideoMemory();
/// OS Related
std::string osPlatform();
int osPlatformId();
std::string osVer();
std::string deviceName();
/// GPU Related
std::string getGPUMake();
// Display Resolution
std::string getMaxRes();
// Directory storing the current executing binary
std::string getExecutablePath();
} // namespace SystemUtil
} // namespace Aya

View File

@@ -0,0 +1,317 @@
#include "TaskScheduler.Job.hpp"
#include "Coordinator.hpp"
#include "Log.hpp"
#include "FastLog.hpp"
using namespace Aya;
LOGGROUP(TaskSchedulerSteps)
#if HANG_DETECTION
#include <../../App/include/Utility/standardout.h>
#define STEPTIME_SAMPLE_INTEVAL 60.0
double Aya::TaskScheduler::Job::stepTimeThreshold = 0.0;
#endif
TaskScheduler::Job::SleepAdjustMethod TaskScheduler::Job::sleepAdjustMethod(AverageInterval);
double Aya::TaskScheduler::Job::throttledSleepTime = 0.01;
double TaskScheduler::Job::averageDutyCycle() const
{
return dutyCycle.dutyCycle();
}
double TaskScheduler::Job::averageSleepRate() const
{
return sleepRate.value();
}
double TaskScheduler::Job::averageStepsPerSecond() const
{
return dutyCycle.stepInterval().rate();
}
double TaskScheduler::Job::averageStepTime() const
{
return dutyCycle.stepTime().value();
}
Time::Interval TaskScheduler::Job::getSleepingTime() const
{
if (state != Sleeping)
return Time::Interval::zero();
else
return Time::now<Time::Fast>() - timeofLastSleep;
}
double TaskScheduler::Job::averageError() const
{
return runningAverageError.value();
}
void TaskScheduler::Job::removeCoordinator(class boost::shared_ptr<class Aya::Tasks::Coordinator> coordinator)
{
coordinator->onRemoved(this);
{
Aya::mutex::scoped_lock lock(coordinatorMutex);
coordinators.erase(std::find(coordinators.begin(), coordinators.end(), coordinator));
}
}
void TaskScheduler::Job::addCoordinator(class boost::shared_ptr<class Aya::Tasks::Coordinator> coordinator)
{
{
Aya::mutex::scoped_lock lock(coordinatorMutex);
coordinators.push_back(coordinator);
}
coordinator->onAdded(this);
}
bool TaskScheduler::Job::isDisabled()
{
if (coordinators.size() == 0)
return false;
Aya::mutex::scoped_lock lock(coordinatorMutex);
// TODO: Write more efficiently without boost bind
return std::find_if(coordinators.begin(), coordinators.end(), boost::bind(&Tasks::Coordinator::isInhibited, _1, this)) != coordinators.end();
}
const double lerpJob = 0.05;
extern Aya::Time::Interval maxDutyCycleWindow;
TaskScheduler::Job::Job(const char* name, shared_ptr<TaskScheduler::Arbiter> arbiter, Time::Interval stepBudget)
: name(name)
, sharedArbiter(arbiter)
, weakArbiter(boost::weak_ptr<Arbiter>())
, baldArbiter(NULL)
, state(Unknown)
, sleepRate(lerpJob)
, dutyCycle(lerpJob, 8)
, runningAverageError(lerpJob)
, timespanOfLastStep(0.0)
, isRemoveRequested(false)
, dutyCycleWindow(maxDutyCycleWindow)
, stepBudget(stepBudget)
, overStepTimeThresholdCount(0)
, allotedConcurrency(-1)
, cyclicExecutive(false)
, cyclicPriority(CyclicExecutiveJobPriority_Default)
{
FASTLOG2(FLog::TaskSchedulerInit, "Job Created - this(%p) arbiter(%p)", this, arbiter.get());
FASTLOGS(FLog::TaskSchedulerInit, "JobName(%s)", name);
}
TaskScheduler::Job::~Job()
{
AYAASSERT(!TaskScheduler::SleepingHook::is_linked());
AYAASSERT(!TaskScheduler::WaitingHook::is_linked());
while (coordinators.size() > 0)
{
coordinators.back()->onRemoved(this);
coordinators.pop_back();
}
FASTLOG1(FLog::TaskSchedulerInit, "Job Destroyed - this(%p)", this);
FASTLOGS(FLog::TaskSchedulerInit, "JobName(%s)", name);
}
// Use this to generate the error function if you just want to try to track the desiredHz
TaskScheduler::Job::Error TaskScheduler::Job::computeStandardError(const Stats& stats, double desiredHz)
{
return stats.timespanSinceLastStep.seconds() * desiredHz;
}
// Same as computeStandardError but incorporates sleeping in Cyclic Executive.
TaskScheduler::Job::Error TaskScheduler::Job::computeStandardErrorCyclicExecutiveSleeping(const Stats& stats, double desiredHz)
{
TaskScheduler::Job::Error error = computeStandardError(stats, desiredHz);
if (Aya::TaskScheduler::singleton().isCyclicExecutive() && cyclicExecutive)
{
// Waking up at error >= 1.0 means that a task always runs late. In practice
// there is always scheduling jitter, however in the spirit of minimally
// intrusive changes allow the task to wake up just a tiny bit early.
if (error.error < 0.98)
{
error.error = 0.0; // Sleep until ready.
}
}
return error;
}
Time::Interval TaskScheduler::Job::computeStandardSleepTime(const Stats& stats, double desiredHz)
{
// bool schedulerIsCyclicExecutive = Aya::TaskScheduler::singleton().isCyclicExecutive();
// AYAASSERT( !schedulerIsCyclicExecutive || cyclicExecutive || desiredHz <= 5 );
shared_ptr<Arbiter> ar(getArbiter());
Time::Interval minTime = ar && ar->isThrottled() ? Time::Interval(throttledSleepTime) : Time::Interval::zero();
const double interval = 1.0 / desiredHz;
switch (sleepAdjustMethod)
{
case AverageInterval:
// If we're not being called as frequently as we'd like, then don't sleep
if (dutyCycle.stepInterval().value().seconds() > 1.05 * interval)
return minTime;
break;
case LastSample:
if (dutyCycle.lastStepInterval().seconds() > 1.05 * interval)
return minTime;
break;
default:
break;
}
Time::Interval result = Time::Interval(interval) - stats.timespanSinceLastStep;
if (result < minTime)
return minTime;
return result;
}
TaskScheduler::Job::Stats::Stats(Job& job, Time now)
{
timeNow = now;
timespanSinceLastStep = now - job.timeofLastStep;
timespanOfLastStep = job.timespanOfLastStep;
}
void TaskScheduler::Job::startWaiting()
{
state = Waiting;
}
void TaskScheduler::Job::startSleeping()
{
state = Sleeping;
timeofLastSleep = Time::now<Time::Fast>();
}
void TaskScheduler::Job::updateWakeTime()
{
Time now = Time::now<Time::Fast>();
Job::Stats stats(*this, now);
wakeTime = now + sleepTime(stats);
}
void TaskScheduler::Job::updateError(const Time& now)
{
Job::Stats stats(*this, now);
currentError = error(stats);
AYAASSERT(currentError.error < std::numeric_limits<double>::max());
if (currentError.error > 0)
runningAverageError.sample(currentError.error);
}
void TaskScheduler::Job::notifyCoordinatorsPreStep()
{
if (coordinators.size() > 0)
{
Aya::mutex::scoped_lock lock(coordinatorMutex);
std::for_each(coordinators.begin(), coordinators.end(), boost::bind(&Tasks::Coordinator::onPreStep, _1, this));
}
}
void TaskScheduler::Job::preStep()
{
state = Running;
FASTLOG4(FLog::TaskSchedulerRun, "JobStart. this: %p arbiter: %p time: %u error: %d", this, getArbiter().get(),
(unsigned)stepStartTime.timestampSeconds(), currentError.urgent ? -1 : (int)currentError.error);
FASTLOGS(FLog::TaskSchedulerRun, "JobStart %s", name.c_str());
shared_ptr<Arbiter> ar(getArbiter());
if (ar)
ar->preStep(this);
}
void TaskScheduler::Job::postStep(StepResult result)
{
state = Unknown;
timeofLastStep = stepStartTime;
const Time now = Time::now<Time::Fast>();
timespanOfLastStep = now - stepStartTime;
// TODO: extract stepTime from dutyCycle?
dutyCycle.sample(timespanOfLastStep);
if (!stepBudget.isZero() && timespanOfLastStep > stepBudget)
{
// We were over budget
}
#if HANG_DETECTION
if ((stepTimeThreshold > 0) && (timespanOfLastStep.seconds() > stepTimeThreshold))
{
overStepTimeThresholdCount++;
StandardOut::singleton()->printf(MESSAGE_WARNING, "TaskScheduler::Job: %s step time over threshold", name.c_str());
// send to log
if (Aya::Log::current() && ((Time::now<Time::Fast>() - stepTimeSampleTime).seconds() > STEPTIME_SAMPLE_INTEVAL))
{
std::string msg = "TaskScheduler::Job: " + name + " step time over threshold.";
Aya::Log::current()->writeEntry(Aya::Log::Error, msg.c_str());
stepTimeSampleTime = Time::now<Time::Fast>();
}
}
#endif
if (dutyCycleWindow.getMaxWindow().seconds() > 0)
{
dutyCycleWindow.sample(timespanOfLastStep);
}
FASTLOG3(FLog::TaskSchedulerRun, "JobStop. This: %p arbiter: %p time: %u", this, getArbiter().get(), (unsigned)now.timestampSeconds());
FASTLOGS(FLog::TaskSchedulerRun, "JobStop %s", name.c_str());
shared_ptr<Arbiter> ar(getArbiter());
if (ar)
ar->postStep(this);
}
void TaskScheduler::Job::notifyCoordinatorsPostStep()
{
if (coordinators.size() > 0)
{
Aya::mutex::scoped_lock lock(coordinatorMutex);
std::for_each(coordinators.begin(), coordinators.end(), boost::bind(&Tasks::Coordinator::onPostStep, _1, this));
}
}
void TaskScheduler::Job::updatePriority()
{
switch (TaskScheduler::priorityMethod)
{
case FIFO:
priority = 1.0;
break;
case LastError:
priority = currentError.error;
break;
case AccumulatedError:
double avg = averageError();
if (avg > 0)
priority = avg;
else
priority = currentError.error;
priority *= getPriorityFactor();
double st = std::max(0.01, averageDutyCycle());
priority /= st;
break;
}
}

View File

@@ -0,0 +1,256 @@
#pragma once
#include "Countable.hpp"
#include "TaskScheduler.hpp"
#include "boost/enable_shared_from_this.hpp"
#include "boost/weak_ptr.hpp"
#define HANG_DETECTION 0
namespace Aya
{
namespace Tasks
{
class Coordinator;
}
enum CyclicExecutiveJobPriority
{
CyclicExecutiveJobPriority_EarlyRendering,
CyclicExecutiveJobPriority_Network_ReceiveIncoming,
CyclicExecutiveJobPriority_Network_ProcessIncoming,
CyclicExecutiveJobPriority_Default,
CyclicExecutiveJobPriority_Physics,
CyclicExecutiveJobPriority_Heartbeat,
CyclicExecutiveJobPriority_Network_ProcessOutgoing,
CyclicExecutiveJobPriority_Render
};
class AyaBaseClass TaskScheduler::Job
: boost::noncopyable
, public boost::enable_shared_from_this<Job>
, Aya::Diagnostics::Countable<Job>
, public TaskScheduler::SleepingHook
, public TaskScheduler::WaitingHook
{
friend class TaskScheduler;
boost::weak_ptr<Thread> lastThreadUsed; // attempt to re-use a thread for thread affinity
Aya::mutex coordinatorMutex;
std::vector<boost::shared_ptr<Aya::Tasks::Coordinator>> coordinators;
boost::shared_ptr<TaskScheduler::Arbiter> const sharedArbiter;
boost::weak_ptr<TaskScheduler::Arbiter> const weakArbiter;
TaskScheduler::Arbiter* const baldArbiter;
// stepBudget of 0 means no budget
public:
const std::string name;
static double throttledSleepTime;
const Time::Interval stepBudget;
Time stepStartTime;
int allotedConcurrency;
bool cyclicExecutive;
CyclicExecutiveJobPriority cyclicPriority;
#if HANG_DETECTION
static double
stepTimeThreshold; // in seconds. A count is incremented when a job's stepTime is over this value. A value of 0 means disable counting.
Time stepTimeSampleTime;
#endif
struct Stats
{
Stats(Job& job, Time now);
Time timeNow;
Time::Interval timespanSinceLastStep;
Time::Interval timespanOfLastStep;
};
struct Error
{
double error;
bool urgent; // experimental feature to prevent UI deadlocks (Essentially bumps the Job up in the priority queue)
Error()
: error(0.0)
, urgent(false)
{
}
Error(double error)
: error(error)
, urgent(false)
{
}
bool isDefault()
{
return error == 0.0 && !urgent;
} // Generic jobs are flagged as urgent for Cyclic Executive.
};
void addCoordinator(shared_ptr<Tasks::Coordinator> coordinator);
void removeCoordinator(shared_ptr<Tasks::Coordinator> coordinator);
Time::Interval getSleepingTime() const; // returns > 0 if the job is asleep
double averageDutyCycle() const;
const RunningAverageDutyCycle<>& getStepStats() const
{
return dutyCycle;
}
double averageSleepRate() const;
double averageStepsPerSecond() const;
double averageStepTime() const;
double averageError() const;
bool isRunning() const
{
return state == Running;
}
bool isDisabled();
typedef enum
{
None,
LastSample,
AverageInterval
} SleepAdjustMethod;
static SleepAdjustMethod sleepAdjustMethod;
typedef enum
{
Unknown,
Sleeping,
Waiting,
Running
} State;
State getState() const
{
return state;
}
Time getWakeTime() const
{
return wakeTime;
}
Time::Interval getWake() const
{
return wakeTime - Time::now<Time::Fast>();
}
double getPriority() const
{
return priority;
}
std::string getDebugName() const
{
shared_ptr<Arbiter> ar(getArbiter());
if (ar)
return Aya::format("%s:%s", ar->arbiterName().c_str(), name.c_str());
else
return name;
}
static bool isLowerWakeTime(const TaskScheduler::Job& job1, const TaskScheduler::Job& job2)
{
return job1.wakeTime < job2.wakeTime;
}
WindowAverageDutyCycle<>& getDutyCycleWindow()
{
return dutyCycleWindow;
}
const WindowAverageDutyCycle<>& getDutyCycleWindow() const
{
return dutyCycleWindow;
}
protected:
Job(const char* name, shared_ptr<TaskScheduler::Arbiter> arbiter, Time::Interval stepBudget = Time::Interval(0));
virtual ~Job();
public:
inline const shared_ptr<TaskScheduler::Arbiter>& getArbiter() const
{
return sharedArbiter;
}
inline bool hasArbiter(TaskScheduler::Arbiter* test) const
{
return sharedArbiter.get() == test || sharedArbiter->getSyncronizationArbiter() == test;
};
inline static bool haveDifferentArbiters(const Job* job1, const Job* job2)
{
Arbiter* a1 = job1->sharedArbiter.get();
if (a1)
a1 = a1->getSyncronizationArbiter();
Arbiter* a2 = job2->sharedArbiter.get();
if (a2)
a2 = a2->getSyncronizationArbiter();
return a1 != a2;
}
//////////////////////////////////////////////////////////////////
// Abstract functions
private:
// sleepTime>0 means the job will sleep
virtual Time::Interval sleepTime(const Stats& stats) = 0;
// error==0 means the job won't be scheduled
virtual Error error(const Stats& stats) = 0;
// Used in Cyclic Executive to decide if we should re-run entire TaskScheduler loop
// This is used to let LegacyLocks clear in Studio when waiting for Render job.
virtual bool tryJobAgain()
{
return false;
};
// Used to determine which job gets priority. The priority is multiplied by this factor
virtual double getPriorityFactor() = 0;
// The Job is being asked to step.
virtual StepResult step(const Stats& stats) = 0;
virtual int getDesiredConcurrencyCount() const
{
// return >1 if the job intends to do parallel work (using multiple threads)
// During the step function, query the number of threads alloted by checking
// allotedConcurrency
return 1;
}
protected:
// Use this to generate the error function if you just want to try to track the desiredHz
Error computeStandardError(const Stats& stats, double desiredHz);
Error computeStandardErrorCyclicExecutiveSleeping(const Stats& stats, double desiredHz);
Time::Interval computeStandardSleepTime(const Stats& stats, double desiredHz);
private:
State state;
bool isRemoveRequested;
Time timeofLastStep;
Time timeofLastSleep;
Time::Interval timespanOfLastStep;
Error currentError;
double priority;
Time wakeTime;
int overStepTimeThresholdCount;
boost::shared_ptr<CEvent> joinEvent; // Used when joining to the event after it is removed
RunningAverageDutyCycle<> dutyCycle;
RunningAverage<double> sleepRate;
RunningAverage<double> runningAverageError;
WindowAverageDutyCycle<> dutyCycleWindow;
void updateError(const Time& time);
void notifyCoordinatorsPreStep();
void preStep();
void postStep(StepResult result);
void notifyCoordinatorsPostStep();
void updatePriority();
void updateWakeTime();
void startSleeping();
void startWaiting();
};
} // namespace Aya

View File

@@ -0,0 +1,404 @@
#include "TaskScheduler.hpp"
#include "boost.hpp"
#include "TaskScheduler.Job.hpp"
#include "CPUCount.hpp"
#include "boost/enable_shared_from_this.hpp"
#include "Log.hpp"
#include "Profiler.hpp"
using namespace Aya;
using boost::shared_ptr;
#include "CPUCount.hpp"
#define JOIN_TIMEOUT (1000 * 20) // 20 seconds
LOGGROUP(TaskSchedulerRun)
LOGGROUP(TaskSchedulerFindJob)
class TaskScheduler::Thread : public boost::enable_shared_from_this<Thread>
{
boost::scoped_ptr<boost::thread> thread;
volatile bool done;
TaskScheduler* const taskScheduler;
Thread(TaskScheduler* taskScheduler)
: done(false)
, enabled(true)
, taskScheduler(taskScheduler)
{
}
public:
shared_ptr<Job> job;
volatile bool enabled;
static shared_ptr<Thread> create(TaskScheduler* taskScheduler)
{
shared_ptr<Thread> thread(new Thread(taskScheduler));
static Aya::atomic<int> count;
std::string name = Aya::format("Roblox TaskScheduler Thread %d", static_cast<int>(++count));
// loop holds a shared_ptr to the thread, so it won't be collected before the loop exits :)
thread->thread.reset(new boost::thread(Aya::thread_wrapper(boost::bind(&Thread::loop, thread), name.c_str())));
return thread;
}
void end()
{
done = true;
}
void join()
{
end();
if (thread->get_id() != boost::this_thread::get_id())
thread->timed_join(boost::posix_time::milliseconds(JOIN_TIMEOUT));
}
void printJobInfo()
{
if (Aya::Log::current() && job)
{
Time now = Time::now<Time::Fast>();
std::stringstream ss;
if (job->isRunning())
ss << "TaskScheduler::Job: " << job->name.c_str() << ", state: " << (int)job->getState()
<< ", seconds spend in job: " << (now - job->stepStartTime).seconds();
else
ss << "TaskScheduler::Job: " << job->name.c_str() << ", state: " << (int)job->getState();
Aya::Log::current()->writeEntry(Aya::Log::Information, ss.str().c_str());
}
}
~Thread()
{
join();
}
void loop();
void releaseJob();
TaskScheduler::StepResult runJob();
};
void TaskScheduler::printJobs()
{
std::for_each(threads.begin(), threads.end(), boost::bind(&Thread::printJobInfo, _1));
}
void TaskScheduler::endAllThreads()
{
std::for_each(threads.begin(), threads.end(), boost::bind(&Thread::join, _1));
}
void TaskScheduler::setThreadCount(ThreadPoolConfig threadConfig)
{
static const unsigned int kDefaultCoreCount = 1;
int realCoreCount = RbxTotalUsableCoreCount(kDefaultCoreCount);
int requestedCount;
Aya::mutex::scoped_lock lock(mutex);
if (threadConfig == Auto)
{
// automatic: 1 thread per core
requestedCount = realCoreCount;
}
else if (threadConfig >= PerCore1)
{
requestedCount = realCoreCount * (threadConfig - PerCore1 + 1);
}
else
{
requestedCount = (int)threadConfig;
}
AYAASSERT(requestedCount > 0);
desiredThreadCount = (size_t)requestedCount;
while (threads.size() < (size_t)requestedCount)
threads.push_back(Thread::create(this));
}
TaskScheduler::StepResult TaskScheduler::Thread::runJob()
{
job->stepStartTime = Time::now<Time::Fast>();
TaskScheduler::Job::Stats stats(*job, job->stepStartTime);
job->preStep();
TaskScheduler::StepResult result;
++taskScheduler->runningJobCount;
AYAASSERT(currentJob.get() == NULL);
currentJob.reset(job.get());
// No need for exception handling. If an exception is thrown here
// then we should abort the application.
taskScheduler->taskCount++;
result = job->step(stats);
AYAASSERT(currentJob.get() == job.get());
currentJob.reset(NULL);
--taskScheduler->runningJobCount;
job->postStep(result);
job->lastThreadUsed = shared_from_this();
return result;
}
bool TaskScheduler::conflictsWithScheduledJob(Aya::TaskScheduler::Job* job) const
{
if (threads.size() == 1)
return false;
const shared_ptr<Arbiter>& arbiter = job->getArbiter();
if (!arbiter)
return false;
for (Threads::const_iterator iter = threads.begin(); iter != threads.end(); ++iter)
{
Aya::TaskScheduler::Job* other = (*iter)->job.get();
if (other)
{
if (Job::haveDifferentArbiters(job, other))
continue; // different Arbiter domains can run concurrently
if (arbiter->areExclusive(job, other))
return true;
}
}
return false;
}
void TaskScheduler::Thread::releaseJob()
{
job->state = Job::Unknown;
if (job->isRemoveRequested)
{
if (job->joinEvent)
job->joinEvent->Set();
if (taskScheduler->cyclicExecutiveEnabled && job->cyclicExecutive)
{
taskScheduler->releaseCyclicExecutive(job.get());
}
}
else
{
taskScheduler->scheduleJob(*job);
}
job.reset();
}
bool TaskScheduler::shouldDropThread() const
{
AYAASSERT(desiredThreadCount <= threads.size());
return desiredThreadCount < threads.size();
}
void TaskScheduler::dropThread(Thread* thread)
{
thread->end();
for (Threads::iterator iter = threads.begin(); iter != threads.end(); ++iter)
{
if (iter->get() == thread)
{
threads.erase(iter);
break;
}
}
}
void TaskScheduler::disableThreads(int count, Threads& threads)
{
for (Threads::const_iterator iter = this->threads.begin(); count > 0 && iter != this->threads.end(); ++iter)
{
const shared_ptr<Thread>& t = *iter;
if (t->job)
continue;
if (!t->enabled)
continue;
t->enabled = false;
threads.push_back(t);
count--;
}
}
void TaskScheduler::enableThreads(Threads& threads)
{
for (Threads::const_iterator iter = threads.begin(); iter != threads.end(); ++iter)
{
AYAASSERT(!(*iter)->enabled);
(*iter)->enabled = true;
}
threads.clear();
}
void TaskScheduler::Thread::loop()
{
Profiler::onThreadCreate(format("TS %p", this).c_str());
taskScheduler->incrementThreadCount();
TaskScheduler::Threads participatingThreads; // threads that are turned off during a parallel run
shared_ptr<Thread> self(shared_from_this());
while (!done)
{
{
Aya::mutex::scoped_lock lock(taskScheduler->mutex);
FASTLOG1(FLog::TaskSchedulerFindJob, "Took mutex %p in thread TaskScheduler::Thread::loop", &(taskScheduler->mutex));
const Aya::Time start(taskScheduler->schedulerDutyCycle.startSample());
if (job)
{
taskScheduler->enableThreads(participatingThreads);
job->allotedConcurrency = -1;
job->notifyCoordinatorsPostStep();
releaseJob();
}
if (taskScheduler->shouldDropThread())
taskScheduler->dropThread(this);
else
{
if (enabled && !done)
{
job = taskScheduler->findJobToRun(self);
if (job)
{
AYAASSERT(!job->isDisabled());
// This must be synchronized with findJobToRun
// because Coordinators expect atomicity with
// isInhibited
job->notifyCoordinatorsPreStep();
taskScheduler->disableThreads(job->getDesiredConcurrencyCount() - 1, participatingThreads);
job->allotedConcurrency = int(participatingThreads.size()) + 1;
}
}
}
taskScheduler->schedulerDutyCycle.stopSample(start);
FASTLOG1(FLog::TaskSchedulerFindJob, "Releasing mutex %p in TaskScheduler::Thread::loop", &(taskScheduler->mutex));
if (done)
break;
}
if (job)
{
if (runJob() == TaskScheduler::Done)
job->isRemoveRequested = true;
}
else
{
Time::Interval sleepTime = taskScheduler->getShortestSleepTime();
if (sleepTime.seconds() > 0)
{
// The most efficient thing is to sleep for a super-short period of time.
// This is more efficient than waiting on a mutex, and the timespan is
// short enough to make the system responsive.
#ifdef _WIN32
::Sleep(1);
#else
::usleep(1000);
#endif
}
}
}
if (job)
{
Aya::mutex::scoped_lock lock(taskScheduler->mutex);
taskScheduler->enableThreads(participatingThreads);
job->allotedConcurrency = -1;
job->notifyCoordinatorsPostStep();
releaseJob();
}
taskScheduler->decrementThreadCount();
Profiler::onThreadExit();
}
void TaskScheduler::getJobsInfo(std::vector<shared_ptr<const Job>>& result)
{
Aya::mutex::scoped_lock lock(mutex);
for (AllJobs::iterator iter = allJobs.begin(); iter != allJobs.end(); ++iter)
result.push_back(*iter);
}
static void setJobExtendedStatsWindow(shared_ptr<TaskScheduler::Job> job, double seconds)
{
job->getDutyCycleWindow().setMaxWindow(Time::Interval(seconds));
if (seconds == 0.0)
{
job->getDutyCycleWindow().clear();
}
}
Aya::Time::Interval maxDutyCycleWindow(0.0);
void TaskScheduler::setJobsExtendedStatsWindow(double seconds)
{
maxDutyCycleWindow = Time::Interval(seconds);
std::vector<boost::shared_ptr<TaskScheduler::Job>> jobs;
{
Aya::mutex::scoped_lock lock(mutex);
for (AllJobs::iterator iter = allJobs.begin(); iter != allJobs.end(); ++iter)
jobs.push_back(*iter);
}
std::for_each(jobs.begin(), jobs.end(), boost::bind(setJobExtendedStatsWindow, _1, seconds));
}
void TaskScheduler::cancelCyclicExecutive()
{
// It turns out that determining this is a server may happen late enough that a lock is needed.
Aya::mutex::scoped_lock lock(mutex);
cyclicExecutiveEnabled = false;
for (CyclicExecutiveJobs::iterator i = cyclicExecutiveJobs.begin(); i != cyclicExecutiveJobs.end(); ++i)
{
Job* j = i->job.get();
j->cyclicExecutive = false;
if (i->isRunning == false)
{
scheduleJob(*j);
}
}
cyclicExecutiveJobs.clear();
AYAASSERT(cyclicExecutiveJobs.empty());
}
void TaskScheduler::releaseCyclicExecutive(TaskScheduler::Job* job)
{
AYAASSERT(std::find(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), *job) != cyclicExecutiveJobs.end());
cyclicExecutiveJobs.erase(std::find(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), *job));
}
void TaskScheduler::getJobsByName(const std::string& name, std::vector<boost::shared_ptr<const Job>>& result)
{
Aya::mutex::scoped_lock lock(mutex);
for (AllJobs::iterator iter = allJobs.begin(); iter != allJobs.end(); ++iter)
{
if ((*iter)->name == name)
{
result.push_back(*iter);
}
}
}

View File

@@ -0,0 +1,875 @@
// TaskScheduler.cpp : Defines the entry point for the console application.
//
#include "TaskScheduler.hpp"
#include "TaskScheduler.Job.hpp"
#include "Debug.hpp"
#include "FastLog.hpp"
#include "boost.hpp"
#include "boost/scoped_ptr.hpp"
#include "ProcessPerfCounter.hpp"
#include "Profiler.hpp"
#include "DataModel/GameBasicSettings.hpp"
using namespace Aya;
using boost::shared_ptr;
// #define TASKSCHEDULAR_PROFILING
LOGVARIABLE(TaskSchedulerTiming, 0)
FASTFLAGVARIABLE(TaskSchedulerCyclicExecutive, true)
FASTFLAGVARIABLE(DebugTaskSchedulerProfiling, false)
DYNAMIC_FASTINTVARIABLE(TaskSchedularBatchErrorCalcFPS, 300)
DYNAMIC_FASTFLAGVARIABLE(CyclicExecutiveForServerTweaks, false)
Aya::TaskScheduler::PriorityMethod TaskScheduler::priorityMethod = AccumulatedError;
// Aya::TaskScheduler::PriorityMethod TaskScheduler::priorityMethod = FIFO;
#ifdef AYA_TEST_BUILD
int TaskScheduler::findJobFPS = 100;
bool TaskScheduler::updateJobPriorityOnWake = false;
#endif
double TaskScheduler::getSchedulerDutyCyclePerThread() const
{
return threads.size() ? schedulerDutyCycle.dutyCycle() / threads.size() : 0;
}
Aya::thread_specific_reference<TaskScheduler::Job> TaskScheduler::currentJob;
struct PrintTaskSchedulerItem
{
int count;
double averageError;
double averagePriority;
double dutyCycle;
double averageStepsPerSecond;
double coefficient_of_variation;
double averageStepTime;
PrintTaskSchedulerItem()
: count(0)
, averageError(0)
, averagePriority(0)
, dutyCycle(0)
, averageStepsPerSecond(0)
, coefficient_of_variation(0)
, averageStepTime(0)
{
}
};
static std::string computeKey(const Aya::TaskScheduler::Job* job)
{
std::string key = job->name;
size_t index = key.find(':');
if (index == std::string::npos)
return key;
index = key.find_last_of(' ');
key = job->name.substr(0, index);
return key;
}
void PrintArbiters(std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>>& jobs)
{
std::set<shared_ptr<Aya::TaskScheduler::Arbiter>> arbiters;
for (std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>>::iterator iter = jobs.begin(); iter != jobs.end(); ++iter)
{
if ((*iter)->getArbiter())
arbiters.insert((*iter)->getArbiter());
}
double total = 0;
for (std::set<shared_ptr<Aya::TaskScheduler::Arbiter>>::iterator iter = arbiters.begin(); iter != arbiters.end(); ++iter)
{
double activity = (*iter)->getAverageActivity();
printf("Arbiter %s\t%.1f%%\t%s\n", (*iter)->arbiterName().c_str(), 100.0 * activity, (*iter)->isThrottled() ? "throttled" : "");
total += (*iter)->getAverageActivity();
}
printf("Total activity\t%.1f%%\n", 100.0 * total);
}
void PrintTasks(std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>>& jobs)
{
printf("%25.25s\tSleep\tPrior\t%%\tSteps\tCV\tStep\n", "Arbiter:TaskName");
for (std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>>::iterator iter = jobs.begin(); iter != jobs.end(); ++iter)
{
if ((*iter)->getSleepingTime() > Time::Interval(2))
printf("%25.25s\tAsleep for %.1fs\n", (*iter)->getDebugName().c_str(), (*iter)->getSleepingTime().seconds());
else
printf("%25.25s\t%d%%\t%.1g\t%.1f%%\t%.1f/s\t%.1f%%\t%.3fs\n", (*iter)->getDebugName().c_str(), int(100.0 * (*iter)->averageSleepRate()),
(*iter)->getPriority(), 100.0 * (*iter)->averageDutyCycle(), (*iter)->averageStepsPerSecond(),
100.0 * (*iter)->getStepStats().stepInterval().coefficient_of_variation(), (*iter)->averageStepTime());
}
}
void PrintAggregatedTasks(std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>>& jobs)
{
std::map<std::string, PrintTaskSchedulerItem> items;
for (std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>>::iterator iter = jobs.begin(); iter != jobs.end(); ++iter)
{
std::string key = computeKey(iter->get());
if ((*iter)->getSleepingTime() < Time::Interval(5))
{
PrintTaskSchedulerItem& item(items[key]);
item.count++;
item.averageError += (*iter)->averageError();
item.averagePriority += (*iter)->getPriority();
item.dutyCycle += (*iter)->averageDutyCycle();
item.averageStepsPerSecond += (*iter)->averageStepsPerSecond();
item.coefficient_of_variation += (*iter)->getStepStats().stepInterval().coefficient_of_variation();
item.averageStepTime += (*iter)->averageStepTime();
}
}
printf("%15.15s\tCount\tError\tPrior\t%%\tSteps\tCV\tStep\n", "Task");
for (std::map<std::string, PrintTaskSchedulerItem>::iterator iter = items.begin(); iter != items.end(); ++iter)
{
PrintTaskSchedulerItem& item(iter->second);
printf("%15.15s\t%d\t%.2f\t%.1f\t%.1f%%\t%.1f/s\t%.1f%%\t%.3fs\n", iter->first.c_str(), item.count, item.averageError / (double)item.count,
item.averagePriority / (double)item.count, 100.0 * item.dutyCycle, item.averageStepsPerSecond / (double)item.count,
100.0 * item.coefficient_of_variation / (double)item.count, item.averageStepTime / (double)item.count);
}
}
void TaskScheduler::printDiagnostics(bool aggregateJobs)
{
std::vector<boost::shared_ptr<const Aya::TaskScheduler::Job>> jobs;
getJobsInfo(jobs);
if (aggregateJobs)
PrintAggregatedTasks(jobs);
else
PrintTasks(jobs);
PrintArbiters(jobs);
printf("sleep %.1f, wait %.1f, run %.2f, affinity %.2f, scheduling %.1f/s (%.2g%%)\n", numSleepingJobs(), numWaitingJobs(), numRunningJobs(),
threadAffinity(), schedulerRate(), getSchedulerDutyCyclePerThread() * 100);
printf("\n");
}
Aya::ExclusiveArbiter Aya::ExclusiveArbiter::singleton;
bool Aya::ExclusiveArbiter::areExclusive(TaskScheduler::Job* task1, TaskScheduler::Job* task2)
{
AYAASSERT(task1->hasArbiter(this));
AYAASSERT(task2->hasArbiter(this));
return true;
}
static TaskScheduler* sing;
void TaskScheduler::static_init()
{
static TaskScheduler s;
s.setThreadCount(TaskScheduler::Auto); // Use auto by default
sing = &s;
}
TaskScheduler& TaskScheduler::singleton()
{
static boost::once_flag flag = BOOST_ONCE_INIT;
boost::call_once(static_init, flag);
return *sing;
}
const static double lerpTaskScheduler = 0.05;
TaskScheduler::TaskScheduler()
: sampleRunningJobCountEvent(true)
, threadCount(0)
, threadAffinityPreference(1.5) // TODO: Come up with a good number here
, runningJobCount(0)
, waitingJobCount(lerpTaskScheduler)
, sleepingJobCount(lerpTaskScheduler)
, averageRunningJobCount(lerpTaskScheduler)
, schedulerDutyCycle(lerpTaskScheduler)
, averageThreadAffinity(lerpTaskScheduler, 1)
, nextWakeTime(Time::max())
, lastSortTime(Time())
, desiredThreadCount(0)
, cyclicExecutiveLoopId(0)
, DataModel30fpsThrottle(true)
, cyclicExecutiveWaitForNextFrame(false)
, nonCyclicJobsToDo(0)
, lastCyclcTimestamp(Time::now<Time::Fast>())
{
runningJobCounterThread.reset(
new boost::thread(Aya::thread_wrapper(boost::bind(&TaskScheduler::sampleRunningJobCount, this), "Roblox sampleRunningJobCount")));
// Publish the fast flag out to the schedulers API so that it can be overridden by specific clients.
// E.g. this is not yet something to be done on the server.
cyclicExecutiveEnabled = FFlag::TaskSchedulerCyclicExecutive;
}
TaskScheduler::~TaskScheduler()
{
FASTLOG(FLog::TaskSchedulerInit, "Destroying TaskScheduler");
sampleRunningJobCountEvent.Set();
runningJobCounterThread->join();
endAllThreads();
}
void TaskScheduler::remove(boost::shared_ptr<TaskScheduler::Job> task, bool joinTask, boost::function<void()> callbackPing)
{
if (!task)
return;
// You can't join to yourself, but logically
// there should be no concurrency risk to not join
joinTask &= task.get() != currentJob.get();
shared_ptr<CEvent> joinEvent(joinTask ? new CEvent(true) : NULL);
remove(task, joinEvent);
if (joinTask)
{
if (callbackPing)
{
for (int i = 0; i < 10 * 60 * 5; ++i)
{
callbackPing();
if (joinEvent->Wait(100))
return;
}
#ifdef _DEBUG
AYAASSERT(false); // Why did it take so long to join?
#endif
AYACRASH(); // We want to learn about this. Blocking a long time means a thread in the pool is locked up!
}
else
{
if (!joinEvent->Wait(1000 * 60 * 5))
{
#ifdef _DEBUG
AYAASSERT(false); // Why did it take so long to join?
#endif
AYACRASH(); // We want to learn about this. Blocking a long time means a thread in the pool is locked up!
}
}
}
}
void TaskScheduler::reschedule(boost::shared_ptr<Job> job)
{
{
Aya::mutex::scoped_lock lock(mutex);
if (job->SleepingHook::is_linked())
{
AYAASSERT(!cyclicExecutiveEnabled || job->cyclicExecutive == false);
sleepingJobs.erase(sleepingJobs.iterator_to(*job));
scheduleJob(*job);
}
else if (cyclicExecutiveEnabled && job->cyclicExecutive)
{
CyclicExecutiveJobs::iterator i = std::find(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), job);
AYAASSERT(i != cyclicExecutiveJobs.end());
if (i != cyclicExecutiveJobs.end())
{
// Reschedule immediately without waiting for other jobs. Prevents theoretical deadlocks.
i->cyclicExecutiveExecuted = false;
}
}
}
}
bool TaskScheduler::jobCompare(const CyclicExecutiveJob& jobA, const CyclicExecutiveJob& jobB)
{
return (jobA.job->cyclicPriority < jobB.job->cyclicPriority);
}
void TaskScheduler::add(boost::shared_ptr<Job> job)
{
Aya::mutex::scoped_lock lock(mutex);
AYAASSERT(allJobs.find(job) == allJobs.end());
allJobs.insert(job);
if (cyclicExecutiveEnabled)
{
if (job->cyclicExecutive)
{
AYAASSERT(std::find(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), job) == cyclicExecutiveJobs.end());
cyclicExecutiveJobs.push_back(job);
std::sort(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), jobCompare);
}
else
{
scheduleJob(*job);
}
}
else
{
job->cyclicExecutive = false;
scheduleJob(*job);
}
}
template<class List, class Item, class IsLess>
void insert_from_back(List& list, Item& f, IsLess isLess)
{
for (typename List::reverse_iterator iter = list.rbegin(); iter != list.rend(); ++iter)
if (!isLess(f, *iter))
{
list.insert(iter.base(), f);
return;
}
list.push_front(f);
}
void TaskScheduler::scheduleJob(Job& job)
{
if (cyclicExecutiveEnabled && job.cyclicExecutive)
{
CyclicExecutiveJobs::iterator i = std::find(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), job);
AYAASSERT(i != cyclicExecutiveJobs.end());
if (i != cyclicExecutiveJobs.end())
{
i->isRunning = false;
}
return;
}
job.updateWakeTime();
const Time now = Time::now<Time::Fast>();
if (job.wakeTime <= now)
{
#ifdef AYA_TEST_BUILD
errorCalculationPerSec.sample();
#endif
job.updateError(now);
if (job.currentError.error > 0)
{
job.sleepRate.sample(0);
job.startWaiting();
#ifdef AYA_TEST_BUILD
if (updateJobPriorityOnWake)
#endif
{
job.updatePriority();
}
enqueueWaitingJob(job);
return;
}
}
job.sleepRate.sample(1);
job.startSleeping();
insert_from_back(sleepingJobs, job, Job::isLowerWakeTime);
}
void TaskScheduler::remove(const boost::shared_ptr<TaskScheduler::Job>& job, boost::shared_ptr<CEvent> joinEvent)
{
Aya::mutex::scoped_lock lock(mutex);
AYAASSERT(allJobs.find(job) != allJobs.end());
FASTLOG1(FLog::TaskSchedulerRun, "Removing job %p from allJobs (::remove)", job.get());
allJobs.erase(job);
if (job->SleepingHook::is_linked())
{
AYAASSERT(!cyclicExecutiveEnabled || job->cyclicExecutive == false);
FASTLOG1(FLog::TaskSchedulerRun, "Removing job %p from sleepingJobs (::remove)", job.get());
sleepingJobs.erase(sleepingJobs.iterator_to(*job));
if (joinEvent)
joinEvent->Set();
}
else if (job->WaitingHook::is_linked())
{
AYAASSERT(!cyclicExecutiveEnabled || job->cyclicExecutive == false);
FASTLOG1(FLog::TaskSchedulerRun, "Removing job %p from waitingJobs (::remove)", job.get());
waitingJobs.erase(waitingJobs.iterator_to(*job));
if (joinEvent)
joinEvent->Set();
}
else
{
if (cyclicExecutiveEnabled && job->cyclicExecutive)
{
CyclicExecutiveJobs::iterator i = std::find(cyclicExecutiveJobs.begin(), cyclicExecutiveJobs.end(), job);
AYAASSERT(i != cyclicExecutiveJobs.end());
if (i != cyclicExecutiveJobs.end())
{
if (!i->isRunning)
{
cyclicExecutiveJobs.erase(i);
if (joinEvent)
joinEvent->Set();
return;
}
}
}
if (joinEvent)
{
AYAASSERT(!job->joinEvent); // Did somebody else already try to remove this task????
// TODO: Support multiple joins by using a collection?
// We must wait for the task to be completed before signaling the joinEvent
job->joinEvent = joinEvent;
}
// We can't remove it yet. Wait for it to finish stepping
job->isRemoveRequested = true;
}
}
void TaskScheduler::incrementThreadCount()
{
++threadCount;
}
void TaskScheduler::decrementThreadCount()
{
--threadCount;
}
bool TaskScheduler::areExclusive(TaskScheduler::Job* task1, TaskScheduler::Job* task2, const shared_ptr<Arbiter>& arbiterHint)
{
if (Job::haveDifferentArbiters(task1, task2))
return false; // different Arbiter domains can run concurrently
if (!arbiterHint)
return false; // No Arbiter means the task can run concurrently
return arbiterHint->areExclusive(task1, task2);
}
void TaskScheduler::sampleRunningJobCount()
{
while (!sampleRunningJobCountEvent.Wait(71))
{
waitingJobCount.sample(int(waitingJobs.size() + cyclicExecutiveJobs.size()));
sleepingJobCount.sample(int(sleepingJobs.size()));
averageRunningJobCount.sample(int(runningJobCount));
}
}
void TaskScheduler::enqueueWaitingJob(Job& job)
{
FASTLOG1(FLog::TaskSchedulerFindJob, "Adding job %p to waitingJobs (::enqueueWaitingJob)", &job);
AYAASSERT(!cyclicExecutiveEnabled || job.cyclicExecutive == false);
waitingJobs.push_back(job);
}
Time::Interval TaskScheduler::getShortestSleepTime() const
{
return nextWakeTime - Time::now<Time::Fast>();
}
void TaskScheduler::wakeSleepingJobs()
{
Time now = Time::now<Time::Fast>();
SleepingJobs::iterator iter = sleepingJobs.begin();
while (iter != sleepingJobs.end())
{
Job& job(*iter);
AYAASSERT(!cyclicExecutiveEnabled || job.cyclicExecutive == false);
if (job.wakeTime > now)
{
nextWakeTime = job.wakeTime;
return;
}
else
{
iter = sleepingJobs.erase(iter);
#ifdef AYA_TEST_BUILD
errorCalculationPerSec.sample();
#endif
job.updateError(now);
#ifdef AYA_TEST_BUILD
if (updateJobPriorityOnWake)
#endif
{
job.updatePriority();
}
enqueueWaitingJob(job);
}
}
nextWakeTime = Time::max();
}
void TaskScheduler::checkStillWaitingNextFrame(Time now)
{
float fpsCap = 60.0f;
switch (Aya::GameBasicSettings::singleton().getMaxFramerate())
{
case Aya::GameBasicSettings::MaxFramerate::FPS_30:
fpsCap = 30.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_60:
fpsCap = 60.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_75:
fpsCap = 75.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_120:
fpsCap = 120.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_144:
fpsCap = 144.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_200:
fpsCap = 200.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_240:
fpsCap = 240.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_360:
fpsCap = 360.0f;
break;
case Aya::GameBasicSettings::MaxFramerate::FPS_UNCAPPED:
fpsCap = 9999.0f;
break;
}
float minTimespan = 1.0 / fpsCap;
if ((now - lastCyclcTimestamp).seconds() < minTimespan)
{
// Do nothing
}
else
{
lastCyclcTimestamp = now;
cyclicExecutiveWaitForNextFrame = false;
}
}
boost::shared_ptr<TaskScheduler::Job> TaskScheduler::findJobToRun(shared_ptr<Thread> requestingThread)
{
#ifdef TASKSCHEDULAR_PROFILING
int theCount = 0;
Aya::Timer<Aya::Time::Precise> timer;
#endif
Time now = Time::now<Time::Fast>();
if (cyclicExecutiveEnabled)
{
// Once all "cyclic jobs" have been done, this code falls through to the low priority queue one time.
bool hasRemaingJobs = false;
if ((nonCyclicJobsToDo == 0) && cyclicExecutiveWaitForNextFrame && DataModel30fpsThrottle)
{
checkStillWaitingNextFrame(now);
}
if (!cyclicExecutiveWaitForNextFrame || !DataModel30fpsThrottle)
{
for (CyclicExecutiveJobs::iterator iter = cyclicExecutiveJobs.begin(); iter != cyclicExecutiveJobs.end(); ++iter)
{
Job& job(*iter->job);
if (iter->cyclicExecutiveExecuted)
{
continue;
}
if (job.isDisabled())
continue;
hasRemaingJobs = true;
if (iter->isRunning)
{
continue;
}
if (conflictsWithScheduledJob(&job))
continue;
if (job.tryJobAgain())
{
break;
}
iter->cyclicExecutiveExecuted = true;
job.updateError(now);
if (job.currentError.isDefault())
{
// Various jobs, including all the rendering tasks, signal they do not want
// to run by setting their error to 0.
continue;
}
iter->isRunning = true;
shared_ptr<Job> result = job.shared_from_this();
FASTLOG2(FLog::TaskSchedulerFindJob, "CyclicExecutive, job: %p, arbiter %p", result.get(), result->getArbiter().get());
return result;
}
if (!hasRemaingJobs)
{
for (CyclicExecutiveJobs::iterator iter = cyclicExecutiveJobs.begin(); iter != cyclicExecutiveJobs.end(); ++iter)
{
iter->cyclicExecutiveExecuted = false;
}
cyclicExecutiveWaitForNextFrame = true;
nonCyclicJobsToDo = numNonCyclicJobsWithWork();
FASTLOG1F(FLog::TaskSchedulerTiming, "Finished all jobs in Cycle in: %4.7f",
(float)(Time::now<Time::Fast>() - lastCyclcTimestamp).seconds());
}
}
if (nonCyclicJobsToDo > 0)
{
// Run nonCyclicExecutive jobs that have been in the Queue before moving on to
// the next CyclicExecutive frame
shared_ptr<Job> result = findJobToRunNonCyclicJobs(requestingThread, now);
if (result)
{
// At end of Cyclic RenderJob will have DataModel Lock
// We must make sure that we only decrement jobs to do when
// we do them, because we are almost guaranteed that result
// will be null the first few checks.
nonCyclicJobsToDo--;
}
else if (numNonCyclicJobsWithWork() == 0)
{
// In case something modifies the jobs after end of cycle
// we want to be able to verify that job's error didn't
// return to 0
nonCyclicJobsToDo = 0;
}
return result;
}
else
{
// If tryJobAgain breaks out of the loop, we want the opportunity to do
// work on Non-Cyclic jobs while we wait for job to be ready.
shared_ptr<Job> result = findJobToRunNonCyclicJobs(requestingThread, now);
return result;
}
}
else // !cyclicExecutiveEnabled
{
// nextScheduledJob is an optimization. Under heavy load, we cut the time for the scheduler
// in half because it buffers a job from the last go-around.
// TODO: Buffer n jobs instead?
// TODO: Buffer one job per thread to help with thread affinity?
if (nextScheduledJob)
{
shared_ptr<Job> result(nextScheduledJob);
nextScheduledJob.reset();
if (result->WaitingHook::is_linked())
if (!result->isDisabled())
if (!conflictsWithScheduledJob(result.get()))
{
FASTLOG1(FLog::TaskSchedulerFindJob, "Removing nextScheduledJob %p from waitingJobs (::findJobToRun)", result.get());
waitingJobs.erase(waitingJobs.iterator_to(*result));
averageThreadAffinity.sample(result->lastThreadUsed.lock() == requestingThread);
return result;
}
}
return findJobToRunNonCyclicJobs(requestingThread, now);
}
}
int TaskScheduler::numNonCyclicJobsWithWork()
{
int jobCount = 0;
for (WaitingJobs::iterator iter = waitingJobs.begin(); iter != waitingJobs.end(); ++iter)
{
Job& job(*iter);
if (job.currentError.error > 0 && !job.isDisabled())
{
// We only add jobs that have work to do!
jobCount++;
}
}
return jobCount;
}
shared_ptr<TaskScheduler::Job> TaskScheduler::findJobToRunNonCyclicJobs(boost::shared_ptr<Thread> requestingThread, Aya::Time now)
{
shared_ptr<TaskScheduler::Job> result;
wakeSleepingJobs();
WaitingJobs::iterator bestJob = waitingJobs.end();
// Note: These definitions don't strictly need to be initialized, but it eliminates pesky compiler warnings
bool bestHasAffinity = false;
bool bestIsThrottled = false;
bool shouldCalcError;
Time::Interval timeSinceLastSorting = now - lastSortTime;
int fps = DFInt::TaskSchedularBatchErrorCalcFPS;
if (fps < 10)
{
fps = 10;
}
double errorCalcInterval = 1.f / (double)fps;
#ifdef AYA_TEST_BUILD
errorCalcInterval = 1.f / findJobFPS;
#endif
shouldCalcError = (timeSinceLastSorting.seconds() > errorCalcInterval);
if (shouldCalcError)
{
#ifdef AYA_TEST_BUILD
sortFrequency.sample();
#endif
lastSortTime = now;
}
FASTLOG1(FLog::TaskSchedulerFindJob, "Starting to iterate through waitingJobs. Size = %d", waitingJobs.size());
for (WaitingJobs::iterator iter = waitingJobs.begin(); iter != waitingJobs.end(); ++iter)
{
#ifdef TASKSCHEDULAR_PROFILING
theCount++;
#endif
Job& job(*iter);
if (job.isDisabled())
continue;
if (priorityMethod != FIFO)
{
if (shouldCalcError)
{
#ifdef AYA_TEST_BUILD
errorCalculationPerSec.sample();
#endif
job.updateError(now);
}
if (job.currentError.error <= 0)
{
continue;
}
}
if (conflictsWithScheduledJob(&job))
{
continue;
}
if (shouldCalcError)
{
job.updatePriority();
}
const bool hasAffinity = job.lastThreadUsed.lock() == requestingThread;
bool match = false;
shared_ptr<Arbiter> const arbiter(job.getArbiter());
if (bestJob == waitingJobs.end())
{
match = true;
}
else if (job.currentError.urgent != bestJob->currentError.urgent)
{
if (job.currentError.urgent)
match = true;
}
else if (bestIsThrottled && (!arbiter || !arbiter->isThrottled()))
{
match = true;
}
else if (hasAffinity == bestHasAffinity)
{
if (job.priority > bestJob->priority)
match = true;
}
else if (hasAffinity)
{
if (job.priority * threadAffinityPreference > bestJob->priority)
match = true;
}
else
{
if (job.priority > bestJob->priority * threadAffinityPreference)
match = true;
}
if (match)
{
if (!cyclicExecutiveEnabled)
{
// Cache off old best job
if (bestJob != waitingJobs.end())
{
if (!areExclusive(&job, &*bestJob, arbiter))
{
nextScheduledJob = bestJob->shared_from_this();
}
else if (nextScheduledJob && areExclusive(&job, &*nextScheduledJob, arbiter))
{
nextScheduledJob.reset();
}
}
}
bestJob = iter;
bestHasAffinity = hasAffinity;
if (priorityMethod == FIFO)
break;
bestIsThrottled = arbiter && arbiter->isThrottled();
}
}
FASTLOG(FLog::TaskSchedulerFindJob, "Finished iterating through waitingJobs");
if (bestJob != waitingJobs.end())
{
result = bestJob->shared_from_this();
AYAASSERT(bestJob->WaitingHook::is_linked());
waitingJobs.erase(waitingJobs.iterator_to(*bestJob));
averageThreadAffinity.sample(bestHasAffinity);
FASTLOG3(FLog::TaskSchedulerFindJob, "RunJob, job: %p, arbiter %p error: %u", result.get(), result->getArbiter().get(),
(unsigned)result->currentError.error);
}
#ifdef TASKSCHEDULAR_PROFILING
if (FFlag::DebugTaskSchedulerProfiling)
{
static Aya::Timer<Aya::Time::Fast> totalTimer;
static int totalCount = 0;
static double taskSchedulerTime = 0.f;
static int lastTotalCount = 0;
double timeElapsed = timer.delta().seconds();
taskSchedulerTime += timeElapsed;
totalCount += theCount;
if (totalCount - lastTotalCount >= 1000000)
{
lastTotalCount = totalCount;
double totalTimeElapsed = totalTimer.delta().seconds();
printf("Jobs: %d, time used: %f, total time: %f, job/s: %d, percent: %.2f%%\n", totalCount, taskSchedulerTime, totalTimeElapsed,
(int)((double)totalCount / totalTimeElapsed), taskSchedulerTime / totalTimeElapsed * 100.f);
}
}
#endif
return result;
}
Aya::atomic<int> Aya::SimpleThrottlingArbiter::arbiterCount;
bool Aya::SimpleThrottlingArbiter::isThrottlingEnabled = false;

View File

@@ -0,0 +1,358 @@
#pragma once
#include <set>
#include <vector>
#include "RunningAverage.hpp"
#include "Declarations.hpp"
#include "Debug.hpp"
#include "threadsafe.hpp"
#include "boost.hpp"
#include "CEvent.hpp"
#include "atomic.hpp"
#include "boost/thread.hpp"
#include "boost/function.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/scoped_ptr.hpp"
#include "boost/noncopyable.hpp"
#include "boost/intrusive/list.hpp"
#ifdef _WIN32
#undef min
#undef max
#endif
LOGGROUP(TaskSchedulerInit)
LOGGROUP(TaskSchedulerRun)
LOGGROUP(TaskSchedulerFindJob)
namespace Aya
{
/// A singleton object responsible for scheduling the execution TaskScheduler.Jobs.
class TaskScheduler
{
struct SleepingTag;
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<SleepingTag>> SleepingHook;
struct WaitingTag;
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<WaitingTag>> WaitingHook;
public:
class Thread;
typedef std::vector<boost::shared_ptr<Thread>> Threads;
class Job;
bool DataModel30fpsThrottle;
Time lastCyclcTimestamp;
bool cyclicExecutiveWaitForNextFrame;
int nonCyclicJobsToDo;
int cyclicExecutiveLoopId;
class AyaBaseClass Arbiter
{
protected:
ActivityMeter<2> activityMeter;
public:
virtual std::string arbiterName() = 0;
virtual bool areExclusive(Job* job1, Job* job2) = 0;
virtual bool isThrottled() = 0;
virtual void preStep(TaskScheduler::Job* job)
{
activityMeter.increment();
}
virtual void postStep(TaskScheduler::Job* job)
{
activityMeter.decrement();
}
double getAverageActivity()
{
return activityMeter.averageValue();
}
virtual Arbiter* getSyncronizationArbiter()
{
return this;
};
virtual int getNumPlayers() const
{
return 1;
}
};
typedef enum
{
Done, // The job will be removed from the TaskScheduler
Stepped, // Another step will be scheduled
} StepResult;
static TaskScheduler& singleton();
typedef enum
{
LastError,
AccumulatedError,
FIFO
} PriorityMethod;
static PriorityMethod priorityMethod;
#ifdef AYA_TEST_BUILD
static int findJobFPS;
static bool updateJobPriorityOnWake;
#endif
double threadAffinityPreference;
typedef enum
{
PerCore4 = 104,
PerCore3 = 103,
PerCore2 = 102,
PerCore1 = 101,
Auto = 0,
Threads1 = 1,
Threads2 = 2,
Threads3 = 3,
Threads4 = 4,
Threads8 = 8,
Threads16 = 16
} ThreadPoolConfig;
bool shouldDropThread() const;
void dropThread(Thread* thread);
size_t getThreadCount()
{
return threadCount;
}
void setThreadCount(ThreadPoolConfig threadConfig);
void disableThreads(int count, Threads& threads);
void enableThreads(Threads& threads);
void add(boost::shared_ptr<TaskScheduler::Job> job);
void reschedule(boost::shared_ptr<TaskScheduler::Job> job);
void remove(boost::shared_ptr<TaskScheduler::Job> job)
{
remove(job, false, NULL);
}
// This version of remove might lead to deadlocks in some cases, so try not to use it
void removeBlocking(boost::shared_ptr<TaskScheduler::Job> job)
{
remove(job, true, NULL);
}
// This version of remove calls back several times a second while waiting.
// You might use this to process events in order to avoid deadlocks.
void removeBlocking(boost::shared_ptr<TaskScheduler::Job> job, boost::function<void()> callbackPing)
{
remove(job, true, callbackPing);
}
void getJobsInfo(std::vector<boost::shared_ptr<const Job>>& result);
void getJobsByName(const std::string& name, std::vector<boost::shared_ptr<const Job>>& result);
// Performance counters
double numSleepingJobs() const
{
return sleepingJobCount.value();
}
double numWaitingJobs() const
{
return waitingJobCount.value();
}
double numRunningJobs() const
{
return averageRunningJobCount.value();
}
double threadAffinity() const
{
return averageThreadAffinity.value();
}
size_t threadPoolSize() const
{
return threads.size();
}
double schedulerRate() const
{
return schedulerDutyCycle.rate();
}
double getSchedulerDutyCyclePerThread() const;
double getErrorCalculationRate() const
{
return errorCalculationPerSec.rate();
}
double getSortFrequency() const
{
return sortFrequency.rate();
}
Aya::atomic<int> taskCount;
void printDiagnostics(bool aggregateJobs);
void printJobs();
void setJobsExtendedStatsWindow(double seconds); // set seconds to 0.0 to disable.
void cancelCyclicExecutive();
bool isCyclicExecutive()
{
return cyclicExecutiveEnabled;
}
void releaseCyclicExecutive(TaskScheduler::Job* job);
private:
// ** Here for thread saftey. **
struct CyclicExecutiveJob
{
boost::shared_ptr<TaskScheduler::Job> job;
bool cyclicExecutiveExecuted;
bool isRunning;
CyclicExecutiveJob(const boost::shared_ptr<TaskScheduler::Job>& j)
{
job = j;
cyclicExecutiveExecuted = false;
isRunning = false;
}
// Allows for using std::find.
bool operator==(const boost::shared_ptr<TaskScheduler::Job>& j)
{
return job == j;
}
bool operator==(const TaskScheduler::Job& j)
{
return job.get() == &j;
}
};
TaskScheduler();
~TaskScheduler();
void endAllThreads();
void sampleRunningJobCount();
static bool jobCompare(const CyclicExecutiveJob& jobA, const CyclicExecutiveJob& jobB);
void remove(boost::shared_ptr<TaskScheduler::Job> job, bool joinJob, boost::function<void()> callbackPing);
void remove(const boost::shared_ptr<TaskScheduler::Job>& job, boost::shared_ptr<CEvent> joinEvent);
void scheduleJob(Job& job);
static bool areExclusive(Job* job1, Job* job2, const shared_ptr<Arbiter>& arbiterHint);
bool conflictsWithScheduledJob(Job* item) const;
void incrementThreadCount();
void decrementThreadCount();
Aya::mutex mutex;
typedef std::set<shared_ptr<Job>> AllJobs;
AllJobs allJobs;
typedef boost::intrusive::list<Job, boost::intrusive::base_hook<SleepingHook>> SleepingJobs;
SleepingJobs sleepingJobs;
bool cyclicExecutiveEnabled;
typedef std::vector<CyclicExecutiveJob> CyclicExecutiveJobs;
CyclicExecutiveJobs cyclicExecutiveJobs;
typedef boost::intrusive::list<Job, boost::intrusive::base_hook<WaitingHook>> WaitingJobs;
WaitingJobs waitingJobs;
shared_ptr<Job> nextScheduledJob;
void wakeSleepingJobs();
void enqueueWaitingJob(Job& job);
Time::Interval getShortestSleepTime() const;
boost::shared_ptr<Job> findJobToRun(boost::shared_ptr<Thread> requestingThread);
boost::shared_ptr<Job> findJobToRunNonCyclicJobs(boost::shared_ptr<Thread> requestingThread, Aya::Time now);
int numNonCyclicJobsWithWork();
void checkStillWaitingNextFrame(Time now);
RunningAverage<int> sleepingJobCount;
RunningAverage<int> waitingJobCount;
RunningAverage<int> averageRunningJobCount;
RunningAverageDutyCycle<Time::Precise> schedulerDutyCycle; // time spent scheduling jobs
RunningAverage<double> averageThreadAffinity;
RunningAverageTimeInterval<> errorCalculationPerSec;
RunningAverageTimeInterval<> sortFrequency;
Aya::atomic<int> runningJobCount;
Time nextWakeTime;
Time lastSortTime;
Threads threads;
size_t desiredThreadCount;
CEvent sampleRunningJobCountEvent;
boost::scoped_ptr<boost::thread> runningJobCounterThread;
Aya::atomic<int> threadCount;
static Aya::thread_specific_reference<TaskScheduler::Job> currentJob;
static void static_init();
};
// A simple arbiter that prevents all members of it to execute concurrently
class ExclusiveArbiter
: public TaskScheduler::Arbiter
, boost::noncopyable
{
public:
virtual bool areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2);
virtual std::string arbiterName()
{
return "ExclusiveArbiter";
}
virtual bool isThrottled()
{
return false;
}
static ExclusiveArbiter singleton;
};
class SimpleThrottlingArbiter : public TaskScheduler::Arbiter
{
mutable bool throttled;
Aya::atomic<int> updatingThrottle;
static Aya::atomic<int> arbiterCount;
public:
static bool isThrottlingEnabled;
SimpleThrottlingArbiter()
: throttled(false)
, updatingThrottle(0)
{
++arbiterCount;
}
~SimpleThrottlingArbiter()
{
--arbiterCount;
}
virtual bool isThrottled()
{
if (!isThrottlingEnabled)
return false;
long count = arbiterCount;
if (count <= 1)
return false;
if (updatingThrottle.swap(1) == 0)
{
double cutoff = ((double)Aya::TaskScheduler::singleton().getThreadCount()) / (double)count;
// hysteresis
if (throttled)
{
throttled = getAverageActivity() >= cutoff;
}
else
{
throttled = getAverageActivity() >= 1.1 * cutoff;
}
--updatingThrottle;
}
return throttled;
}
};
} // namespace Aya

View File

@@ -0,0 +1,65 @@
#pragma once
#include "intrusive_ptr_target.hpp"
#include <boost/function.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
namespace Aya
{
// Returns a function that executes threadfunc with a given name
boost::function0<void> thread_wrapper(const boost::function0<void>& threadfunc, const char* name);
void set_thread_name(const char* name);
// Returns the name of a thread if it was created with one of the above functions
const char* get_thread_name();
// The worker thread runs a process in a low-priority thread
// The function you provide is called:
// 1) Once after worker_thread is constructed
// 2) Immediately after the function returns work_result::more
// 3) After wake() is called
//
// The thread ends execution after worker_thread is deleted, but it doesn't
// interrupt processing of work_function
//
// The client is responsible for ensuring that any data used by the work_function
// is still valid. Note that the work_function might continue to execute for a
// period of time after the owning worker_thread is destroyed.
//
class worker_thread : public boost::noncopyable
{
struct data
{
boost::mutex sync;
boost::condition_variable_any wakeCondition; // TODO: condition_variable?
bool endRequest;
data()
: endRequest(false)
{
}
};
boost::shared_ptr<data> _data;
boost::thread t;
public:
enum work_result
{
done,
more
};
worker_thread(const boost::function0<work_result>& work_function, const char* name);
~worker_thread();
void wake(); // causes the work_function to be called (if the thread had been sleeping)
void join(); // asks the thread to stop and then joins it
private:
static void threadProc(boost::shared_ptr<data> data, const boost::function0<work_result>& work_function);
};
} // namespace Aya

View File

@@ -0,0 +1,79 @@
#include "boost.hpp"
#include "threadsafe.hpp"
#include "Debug.hpp"
#include "atomic.hpp"
Aya::concurrency_catcher::scoped_lock::scoped_lock(concurrency_catcher& m)
: m(m)
{
if (m.value.swap(concurrency_catcher::locked) != concurrency_catcher::unlocked)
{
AYACRASH();
}
}
Aya::concurrency_catcher::scoped_lock::~scoped_lock()
{
m.value.swap(concurrency_catcher::unlocked);
}
const unsigned long Aya::reentrant_concurrency_catcher::noThreadId = 4493024;
Aya::reentrant_concurrency_catcher::scoped_lock::scoped_lock(Aya::reentrant_concurrency_catcher& m)
: m(m)
{
const long threadId(GetCurrentThreadId());
isChild = threadId == m.threadId;
if (isChild)
return; // Ah ha! I'm a recursive lock request. Just go ahead :)
if (m.value.swap(reentrant_concurrency_catcher::locked) != reentrant_concurrency_catcher::unlocked)
{
AYACRASH();
}
// We own the lock, so assign the current thread ID
AYAASSERT(m.threadId == reentrant_concurrency_catcher::noThreadId);
m.threadId = threadId;
}
Aya::reentrant_concurrency_catcher::scoped_lock::~scoped_lock()
{
if (!isChild)
{
m.threadId = reentrant_concurrency_catcher::noThreadId;
m.value.swap(reentrant_concurrency_catcher::unlocked);
}
}
Aya::readwrite_concurrency_catcher::scoped_write_request::scoped_write_request(readwrite_concurrency_catcher& m)
: m(m)
{
if (m.write_requested.swap(readwrite_concurrency_catcher::locked) != readwrite_concurrency_catcher::unlocked)
AYAASSERT(false); // should be a AYACRASH();
if (m.read_requested > 0)
AYAASSERT(false); // should be a AYACRASH();
}
Aya::readwrite_concurrency_catcher::scoped_write_request::~scoped_write_request()
{
if (m.write_requested.swap(readwrite_concurrency_catcher::unlocked) != readwrite_concurrency_catcher::locked)
AYAASSERT(false); // should be a AYACRASH();
}
// Place this code around tasks that write to a DataModel
Aya::readwrite_concurrency_catcher::scoped_read_request::scoped_read_request(readwrite_concurrency_catcher& m)
: m(m)
{
if (m.write_requested != readwrite_concurrency_catcher::unlocked)
AYAASSERT(false); // should be a AYACRASH();
++m.read_requested;
}
Aya::readwrite_concurrency_catcher::scoped_read_request::~scoped_read_request()
{
if (--m.read_requested < 0)
AYAASSERT(false); // should be a AYACRASH();
}

View File

@@ -0,0 +1,11 @@
#pragma once
// This file is used only to build a very special in-house version of Roblox Studio needed to develop Xbox-specific CoreScripts.
// Set to 1 to enable the special build. DO NOT SUBMIT TO CI OR TRUNK WITH THIS SET TO ON.
#define ENABLE_XBOX_STUDIO_BUILD 0
#ifndef _MSC_VER // visual studio only
#undef ENABLE_XBOX_STUDIO_BUILD
#define ENABLE_XBOX_STUDIO_BUILD 0
#endif

127
engine/core/src/atomic.hpp Normal file
View File

@@ -0,0 +1,127 @@
#pragma once
#if defined(AYA_PLATFORM_IOS) || defined(__APPLE__) || __ANDROID__ || __linux || __APPLE__
namespace Aya
{
template<typename T>
class atomic
{
T v;
public:
atomic(T value = 0)
: v(value)
{
}
T operator=(T v)
{
this->v = v;
return v;
}
operator T() const
{
return v;
}
T compare_and_swap(T value, T comparand)
{
return __sync_val_compare_and_swap(&v, comparand, value);
}
T operator++()
{
return __sync_add_and_fetch(&v, 1);
}
T operator--()
{
return __sync_sub_and_fetch(&v, 1);
}
T operator++(int)
{
return __sync_fetch_and_add(&v, 1);
}
T operator--(int)
{
return __sync_fetch_and_sub(&v, 1);
}
T swap(T value)
{
return __sync_lock_test_and_set(&v, value);
}
};
} // namespace Aya
#elif defined(_WIN32) // Windows
#include "Debug.hpp"
#include "boost/detail/interlocked.hpp"
#include "boost/static_assert.hpp"
#include <cstdint>
namespace Aya
{
template<typename T>
class atomic
{
BOOST_STATIC_ASSERT(sizeof(T) == sizeof(long));
long v;
public:
atomic(T value = 0)
: v(value)
{
// http://msdn.microsoft.com/en-us/library/ms683614.aspx: The variable pointed to must be aligned on a 32-bit boundary
AYAASSERT((((uintptr_t)(&(this->v))) & (sizeof(T) - 1)) == 0);
}
T operator=(T v)
{
this->v = v;
return v;
}
operator T() const
{
return v;
}
T compare_and_swap(T value, T comparand)
{
return BOOST_INTERLOCKED_COMPARE_EXCHANGE(&v, value, comparand);
}
T operator++()
{
return BOOST_INTERLOCKED_INCREMENT(&v);
}
T operator--()
{
return BOOST_INTERLOCKED_DECREMENT(&v);
}
T operator++(int)
{
return BOOST_INTERLOCKED_INCREMENT(&v) - 1;
}
T operator--(int)
{
return BOOST_INTERLOCKED_DECREMENT(&v) + 1;
}
T swap(T value)
{
return BOOST_INTERLOCKED_EXCHANGE(&v, value);
}
};
} // namespace Aya
#else // you are using the wrong atomic
#error "not supported"
#endif

171
engine/core/src/boost.cpp Normal file
View File

@@ -0,0 +1,171 @@
#include "boost.hpp"
#include "Thread.hpp"
#include "AyaPlatform.hpp"
#include <boost/type_traits.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#ifndef _WIN32
#include <pthread.h>
#else
#include <Windows.h>
#endif
namespace Aya
{
bool isFinite(double value)
{
return boost::math::isfinite(value);
}
bool isFinite(int value)
{
return boost::math::isfinite(value);
}
bool nameThreads = true;
namespace boost_detail
{
boost::once_flag once_init_foo = BOOST_ONCE_INIT;
static boost::thread_specific_ptr<std::string>* foo;
void init_foo(void)
{
static boost::thread_specific_ptr<std::string> value;
foo = &value;
}
static boost::thread_specific_ptr<std::string>& threadName()
{
boost::call_once(init_foo, once_init_foo);
return *foo;
}
} // namespace boost_detail
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable : 6312)
#pragma warning(disable : 6322)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;
void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName)
{
if (nameThreads)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = szThreadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
// Comment this out to allow AQ time to work per Erik - 5/22/07
// Search on AQTime, Profiling, VTune, Performance, threadName - to find this
// TODO: Put an early check in DllMain to set nameThreads off if you pass in some cmd-line argument
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
}
__except (EXCEPTION_CONTINUE_EXECUTION)
{
}
}
}
#pragma warning(pop)
#endif
void set_thread_name(const char* name)
{
Aya::boost_detail::threadName().reset(new std::string(name));
#ifdef _WIN32
Aya::SetThreadName(GetCurrentThreadId(), name);
#else
#ifdef __linux__
if (nameThreads)
::pthread_setname_np(pthread_self(), name);
#endif
#endif
}
const char* get_thread_name()
{
static const char* none = "unnamed thread"; // an alphanumeric default string
std::string* result = Aya::boost_detail::threadName().get();
return result ? result->c_str() : none;
}
static void thread_function(const boost::function0<void>& threadfunc, std::string name)
{
set_thread_name(name.c_str());
threadfunc();
}
boost::function0<void> thread_wrapper(const boost::function0<void>& threadfunc, const char* name)
{
// We wrap "name" with a std::string to ensure the buffer is not lost
return boost::bind(&thread_function, threadfunc, std::string(name));
}
worker_thread::worker_thread(const boost::function0<work_result>& work_function, const char* name)
: _data(new data())
// Start the thread
, t(thread_wrapper(boost::bind(&threadProc, _data, work_function), name))
{
}
worker_thread::~worker_thread()
{
boost::mutex::scoped_lock lock(_data->sync);
_data->endRequest = true;
_data->wakeCondition.notify_all();
// _data will get collected when threadProc exits
}
void worker_thread::join()
{
{
boost::mutex::scoped_lock lock(_data->sync);
_data->endRequest = true;
_data->wakeCondition.notify_all();
}
t.join();
}
void worker_thread::wake()
{
boost::mutex::scoped_lock lock(_data->sync);
_data->wakeCondition.notify_all();
}
void worker_thread::threadProc(boost::shared_ptr<data> data, const boost::function0<work_result>& work_function)
{
while (true)
{
// Do all open work unless an end is requested
do
{
if (data->endRequest)
return;
} while (work_function() == more);
// Sleep until a request for more work comes through
{
boost::mutex::scoped_lock lock(data->sync);
data->wakeCondition.wait(lock);
}
}
// "data" will get collected when worker_thread is destroyed
}
} // namespace Aya

220
engine/core/src/boost.hpp Normal file
View File

@@ -0,0 +1,220 @@
#pragma once
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <algorithm> // defines std::min and std::max before Windows.h takes over
#include <boost/config/user.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/weak_ptr.hpp>
// for placement_any
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_reference.hpp>
#include <boost/throw_exception.hpp>
#include <boost/static_assert.hpp>
#include <boost/noncopyable.hpp>
using boost::scoped_ptr;
using boost::shared_ptr;
using boost::weak_ptr;
#ifdef _WIN32
// #include <Windows.h>
#else
#include "AyaFormat.hpp"
#include <pthread.h>
// This is a hack. Truncates a pointer.
#if defined(__linux)
#define GetCurrentThreadId() (static_cast<unsigned long>(pthread_self()))
#else
#define GetCurrentThreadId() (static_cast<unsigned>(reinterpret_cast<long>(pthread_self())))
#endif
#define SwitchToThread() \
{ \
sched_yield(); \
}
// We may decide to use the following instead on Mac, but we would prefer the above.
// #define SwitchToThread() {struct timespec req = {0, 1}; nanosleep(&req, NULL);}
#endif
namespace Aya
{
// TODO: Does boost have a nicer way of doing this?
template<class T>
void del_fun(T* t)
{
delete t;
}
bool isFinite(double value);
bool isFinite(int value);
} // namespace Aya
namespace Aya
{
namespace implementation
{
class type_holder : boost::noncopyable
{
public: // operations
void (*destruct)(char* dest);
void (*construct)(const char* src, char* dest);
};
template<typename ValueType>
class typed_holder : public type_holder
{
typed_holder()
{
construct = &construct_func;
destruct = &destruct_func;
}
public:
static const typed_holder* singleton()
{
static typed_holder<ValueType> s;
return &s;
}
static void construct_func(const char* src, char* dest)
{
const ValueType* value = reinterpret_cast<const ValueType*>(src);
ValueType* v = reinterpret_cast<ValueType*>(dest);
new (v) ValueType(*value);
}
static void destruct_func(char* dest)
{
ValueType* value = reinterpret_cast<ValueType*>(dest);
value->~ValueType();
}
};
} // namespace implementation
// placement_any is a reworking of boost::any that embeds the value inside of
// itself, rather than in the heap. This eliminates new/delete operations. However,
// you must know in advance how big the values are able to be. Also, placement_any
// allocates enough memory for the largest possible object, even when it is void.
// SizeType must be a class that is as large as the largest sized object that
// will be placed inside placement_any
template<typename SizeType>
class placement_any
{
public: // structors
placement_any()
: holder(0)
{
}
placement_any(const placement_any& other)
: holder(0)
{
if (other.holder)
(*other.holder->construct)(other.data, data);
holder = other.holder; // construct didn't throw, so we can assign the holder now
}
template<typename ValueType>
explicit placement_any(const ValueType& value)
: holder(0)
{
// If this fails, then make ValueType the new SizeType!
BOOST_STATIC_ASSERT((sizeof(ValueType) <= sizeof(SizeType)));
ValueType* v = reinterpret_cast<ValueType*>(data);
new (v) ValueType(value);
holder = implementation::typed_holder<ValueType>::singleton(); // construct didn't throw, so we can assign it now
}
~placement_any()
{
if (holder)
(*holder->destruct)(data);
}
public: // modifiers
placement_any& swap(placement_any& rhs)
{
placement_any temp(*this);
*this = rhs;
rhs = temp;
return *this;
}
template<typename ValueType>
placement_any& operator=(const ValueType& rhs)
{
const implementation::typed_holder<ValueType>* s = implementation::typed_holder<ValueType>::singleton();
if (holder == s)
{
// Optimization. Is this worth it?
ValueType* dest = reinterpret_cast<ValueType*>(data);
*dest = rhs;
}
else
{
if (holder)
{
(*holder->destruct)(data);
holder = 0;
}
// If this fails, then make ValueType the new SizeType!
BOOST_STATIC_ASSERT((sizeof(ValueType) <= sizeof(SizeType)));
ValueType* v = reinterpret_cast<ValueType*>(data);
new (v) ValueType(rhs);
holder = s;
}
return *this;
}
placement_any& operator=(const placement_any& rhs)
{
if (&rhs == this)
return *this;
if (holder)
{
(*holder->destruct)(data);
holder = 0;
}
if (rhs.holder)
{
(*rhs.holder->construct)(rhs.data, data);
holder = rhs.holder;
}
return *this;
}
public: // queries
bool empty() const
{
return holder == 0;
}
const char* getData() const
{
return holder ? data : NULL;
}
char* getData()
{
return holder ? data : NULL;
}
private: // representation
const implementation::type_holder* holder;
char data[sizeof(SizeType)];
};
} // namespace Aya

View File

@@ -0,0 +1,237 @@
#include "boost/type_traits/function_traits.hpp"
namespace Aya
{
// icallable and callable are a lightweight wrapper similar to boost::function,
// but with more efficient storage. Because the instances are special-cased for
// the functor involved you can't treat callable generically - it has to be
// used in the context of template code that knows what to do with it.
// See Aya::signals for an implementation that uses this in lieu of boost::function
template<int arity, typename Signature>
class icallable;
template<typename Signature>
class icallable<0, Signature>
{
public:
virtual void call() = 0;
};
template<typename Signature>
class icallable<1, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1) = 0;
};
template<typename Signature>
class icallable<2, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2) = 0;
};
template<typename Signature>
class icallable<3, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3) = 0;
};
template<typename Signature>
class icallable<4, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4) = 0;
};
template<typename Signature>
class icallable<5, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5) = 0;
};
template<typename Signature>
class icallable<6, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6) = 0;
};
template<typename Signature>
class icallable<7, Signature>
{
public:
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6,
typename boost::function_traits<Signature>::arg7_type arg7) = 0;
};
template<class Base, class Delegate, int arity, typename Signature>
class callable;
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 0, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& d, Arg1 arg1)
: Base(arg1)
, deleg(d)
{
}
virtual void call()
{
deleg();
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 1, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1)
{
deleg(arg1);
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 2, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2)
{
deleg(arg1, arg2);
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 3, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3)
{
deleg(arg1, arg2, arg3);
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 4, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4)
{
deleg(arg1, arg2, arg3, arg4);
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 5, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5)
{
deleg(arg1, arg2, arg3, arg4, arg5);
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 6, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6)
{
deleg(arg1, arg2, arg3, arg4, arg5, arg6);
}
};
template<class Base, class Delegate, typename Signature>
class callable<Base, Delegate, 7, Signature> : public Base
{
Delegate deleg;
public:
template<typename Arg1>
callable(const Delegate& deleg, Arg1 arg1)
: Base(arg1)
, deleg(deleg)
{
}
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6,
typename boost::function_traits<Signature>::arg7_type arg7)
{
deleg(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
};
} // namespace Aya

View File

@@ -0,0 +1,286 @@
#ifdef WIN32
#include <Objbase.h>
#include <Windows.h>
#endif
#include "format_string.hpp"
#ifdef __linux
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <stdexcept>
#endif
#if defined(__APPLE__) || defined(__ANDROID__)
inline int _vscprintf(const char* format, va_list argptr)
{
return vsnprintf(NULL, 0, format, argptr);
}
static const size_t _TRUNCATE = 0;
inline int vsnprintf_s(char* buffer, size_t sizeOfBuffer, size_t count, const char* format, va_list argptr)
{
return vsnprintf(buffer, sizeOfBuffer, format, argptr);
}
#endif
std::string convert_w2s(const std::wstring& str)
{
#ifdef _WIN32
std::string result;
LPSTR szText = new CHAR[str.size() + 1];
WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, szText, (int)str.size() + 1, 0, 0);
result = szText;
delete[] szText;
return result;
#else
std::string result;
std::copy(str.begin(), str.end(), std::back_inserter(result));
return result;
#endif
}
std::wstring convert_s2w(const std::string& str)
{
#ifdef _WIN32
std::wstring result;
LPWSTR szTextW = new WCHAR[str.size() + 1];
MultiByteToWideChar(0, 0, str.c_str(), -1, szTextW, (int)str.size() + 1);
result = szTextW;
delete[] szTextW;
return result;
#else
std::wstring result;
std::copy(str.begin(), str.end(), std::back_inserter(result));
return result;
#endif
}
#ifndef __linux
std::string vformat(const char* fmt, va_list argPtr)
{
// We draw the line at a 1MB string.
const int maxSize = 1000000;
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
char stackBuffer[bufSize];
int actualSize = _vscprintf(fmt, argPtr) + 1;
if (actualSize > bufSize)
{
// Now use the heap.
char* heapBuffer = NULL;
if (actualSize < maxSize)
{
heapBuffer = (char*)malloc(maxSize + 1);
if (!heapBuffer)
throw std::bad_alloc();
vsnprintf_s(heapBuffer, maxSize, _TRUNCATE, fmt, argPtr);
heapBuffer[maxSize] = '\0';
}
else
{
heapBuffer = (char*)malloc(actualSize);
vsnprintf_s(heapBuffer, actualSize - 1, _TRUNCATE, fmt, argPtr);
heapBuffer[actualSize - 1] = '\0';
}
std::string formattedString(heapBuffer);
free(heapBuffer);
return formattedString;
}
else
{
vsnprintf_s(stackBuffer, bufSize - 1, _TRUNCATE, fmt, argPtr);
stackBuffer[bufSize - 1] = '\0';
return std::string(stackBuffer);
}
}
#else
std::string vformat(const char* fmt, va_list argPtr)
{
const int maxSize = 1000000;
const int bufSize = 161;
char stackBuffer[bufSize];
char* heapBuffer = nullptr;
std::string formattedString;
// Using va_copy to safely use the argPtr again after vsnprintf for determining the buffer size
va_list argPtrCopy;
va_copy(argPtrCopy, argPtr);
int requiredSize = vsnprintf(nullptr, 0, fmt, argPtrCopy) + 1; // +1 for the null-terminator
va_end(argPtrCopy);
if (requiredSize > bufSize)
{
int bufferSize = requiredSize < maxSize ? maxSize : requiredSize;
heapBuffer = static_cast<char*>(malloc(bufferSize));
if (!heapBuffer)
throw std::bad_alloc();
vsnprintf(heapBuffer, bufferSize, fmt, argPtr);
formattedString.assign(heapBuffer);
free(heapBuffer);
}
else
{
vsnprintf(stackBuffer, bufSize, fmt, argPtr);
formattedString.assign(stackBuffer);
}
return formattedString;
}
#endif
#ifndef __linux
std::string format_string(const char* fmt, ...)
{
va_list argList;
va_start(argList, fmt);
std::string result = vformat(fmt, argList);
va_end(argList);
return result;
}
#else
std::string format_string(const char* fmt, ...)
{
va_list argList;
va_start(argList, fmt);
std::string result = vformat(fmt, argList);
va_end(argList);
return result;
}
#endif
#ifdef WIN32
std::wstring vformat(const wchar_t* fmt, va_list argPtr)
{
// We draw the line at a 1MB string.
const int maxSize = 1000000;
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
wchar_t stackBuffer[bufSize];
int actualSize = _vscwprintf(fmt, argPtr) + 1;
if (actualSize > bufSize)
{
// Now use the heap.
wchar_t* heapBuffer = NULL;
if (actualSize < maxSize)
{
heapBuffer = (wchar_t*)malloc((maxSize + 1) * sizeof(wchar_t));
if (!heapBuffer)
throw std::bad_alloc();
_vsnwprintf_s(heapBuffer, maxSize, _TRUNCATE, fmt, argPtr);
heapBuffer[maxSize] = '\0';
}
else
{
heapBuffer = (wchar_t*)malloc(actualSize * sizeof(wchar_t));
_vsnwprintf_s(heapBuffer, actualSize - 1, _TRUNCATE, fmt, argPtr);
heapBuffer[actualSize - 1] = '\0';
}
std::wstring formattedString(heapBuffer);
free(heapBuffer);
return formattedString;
}
else
{
_vsnwprintf_s(stackBuffer, bufSize - 1, _TRUNCATE, fmt, argPtr);
stackBuffer[bufSize - 1] = '\0';
return std::wstring(stackBuffer);
}
}
std::wstring format_string(const wchar_t* fmt, ...)
{
va_list argList;
va_start(argList, fmt);
std::wstring result = vformat(fmt, argList);
va_end(argList);
return result;
}
#endif
std::vector<std::string> splitOn(const std::string& str, const char& delimeter, const bool trimEmpty)
{
std::vector<std::string> tokens;
std::size_t begin = 0;
std::size_t end = str.find(delimeter);
if (end != std::string::npos)
{
while (end != std::string::npos)
{
std::string tmp = str.substr(begin, end - begin);
if (!tmp.empty() || !trimEmpty)
tokens.push_back(tmp);
begin = end + 1;
end = str.find(delimeter, begin);
if (end == std::string::npos)
{
tmp = str.substr(begin, str.length() - begin);
if (!tmp.empty() || !trimEmpty)
tokens.push_back(tmp);
}
}
}
else if (!str.empty() || !trimEmpty)
tokens.push_back(str);
return tokens;
}
std::vector<std::wstring> splitOn(const std::wstring& str, const wchar_t& delimeter, const bool trimEmpty)
{
std::vector<std::wstring> tokens;
std::size_t begin = 0;
std::size_t end = str.find(delimeter);
if (end != std::wstring::npos)
{
while (end != std::wstring::npos)
{
std::wstring tmp = str.substr(begin, end - begin);
if (!tmp.empty() || !trimEmpty)
tokens.push_back(tmp);
begin = end + 1;
end = str.find(delimeter, begin);
if (end == std::wstring::npos)
{
tmp = str.substr(begin, str.length() - begin);
if (!tmp.empty() || !trimEmpty)
tokens.push_back(tmp);
}
}
}
else if (!str.empty() || !trimEmpty)
tokens.push_back(str);
return tokens;
}

View File

@@ -0,0 +1,139 @@
#pragma once
#include <string>
#include <fstream>
#include <vector>
#ifdef _WIN32
#include <Windows.h>
#include <locale>
#include <codecvt>
#include <combaseapi.h>
#else
#include <cstdarg>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
std::string vformat(const char* fmt, va_list argPtr);
std::wstring vformat(const wchar_t* fmt, va_list argPtr);
std::string format_string(const char* fmt, ...);
std::wstring format_string(const wchar_t* fmt, ...);
std::string convert_w2s(const std::wstring& str);
std::wstring convert_s2w(const std::string& str);
std::vector<std::string> splitOn(const std::string& str, const char& delimeter, const bool trimEmpty = true);
std::vector<std::wstring> splitOn(const std::wstring& str, const wchar_t& delimeter, const bool trimEmpty = true);
#ifdef UNICODE
#define CVTW2S(value) convert_w2s(value)
#else
#define CVTW2S(value) (value)
#endif
#ifdef UNICODE
#define CVTS2W(value) convert_s2w(value)
#else
#define CVTS2W(value) (value)
#endif
#ifdef UNICODE
#define MAKE_STRING(value) std::wstring(L##value)
#else
#define MAKE_STRING(value) std::string(value)
#endif
template<class CHARTYPE>
class simple_logger
{
typedef std::basic_string<CHARTYPE, std::char_traits<CHARTYPE>, std::allocator<CHARTYPE>> STRING;
private:
void write_logentry_raw(const char* entry)
{
#ifdef _WIN32
SYSTEMTIME sysTime = {};
GetSystemTime(&sysTime);
char timeBuf[128];
sprintf_s(timeBuf, 128, "%02d-%02d-%d %02d:%02d:%02d.%d ", sysTime.wMonth, sysTime.wDay, sysTime.wYear, sysTime.wHour, sysTime.wMinute,
sysTime.wSecond, sysTime.wMilliseconds);
log << timeBuf << entry << "\n";
log.flush();
#endif
}
STRING logFileName;
std::ofstream log;
public:
simple_logger()
: logFileName(get_temp_filename(MAKE_STRING("log").c_str()))
, log(logFileName.c_str())
{
}
simple_logger(const CHARTYPE* fileName)
: logFileName(fileName)
, log(logFileName.c_str())
{
}
static STRING get_tmp_path()
{
#ifdef _WIN32
CHARTYPE szPath[_MAX_DIR];
if (!GetTempPathA(_MAX_DIR, szPath))
{
throw std::runtime_error("GetTempPath in simple_logger::getTempFileName failed");
}
return STRING(szPath);
#else
return STRING(MAKE_STRING("/tmp/"));
#endif
}
static STRING get_temp_filename(const CHARTYPE* ext)
{
#ifdef _WIN32
GUID g;
::CoCreateGuid(&g);
char szFilePath[MAX_PATH];
sprintf_s(szFilePath, MAX_PATH, "%SRBX-%08X.%S", get_tmp_path().c_str(), g.Data1, ext);
return STRING(CVTS2W(szFilePath));
#else
// Unix does not have GUIDs like Windows, so use a unique filename creation mechanism
std::string tmpFileName = "/tmp/logXXXXXX"; // Template for mkstemp
int tmpFd = mkstemp(&tmpFileName[0]); // Creates a unique temporary file name and opens it
if (tmpFd < 0)
{
throw std::runtime_error("Unable to create temp file in simple_logger::get_temp_filename");
}
close(tmpFd); // Close the file descriptor, we just want the name
// Append the extension
tmpFileName += ".";
tmpFileName += ext;
return STRING(tmpFileName.begin(), tmpFileName.end()); // Convert to the desired character type
#endif
}
void write_logentry(const char* format, ...)
{
va_list argList;
va_start(argList, format); // Now va_start should be recognized
std::string result = vformat(format, argList);
write_logentry_raw(result.c_str());
va_end(argList); // And va_end should be recognized as well
}
STRING& log_filename()
{
return logFileName;
}
};

658
engine/core/src/ifaddrs.cpp Normal file
View File

@@ -0,0 +1,658 @@
/*
Copyright (c) 2013, Kenneth MacKay
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ifaddrs.hpp"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
typedef struct NetlinkList
{
struct NetlinkList *m_next;
struct nlmsghdr *m_data;
unsigned int m_size;
} NetlinkList;
static int netlink_socket(void)
{
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(l_socket < 0)
{
return -1;
}
struct sockaddr_nl l_addr;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
{
close(l_socket);
return -1;
}
return l_socket;
}
static int netlink_send(int p_socket, int p_request)
{
char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
memset(l_buffer, 0, sizeof(l_buffer));
struct nlmsghdr *l_hdr = (struct nlmsghdr *)l_buffer;
struct rtgenmsg *l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
l_hdr->nlmsg_type = p_request;
l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
l_hdr->nlmsg_pid = 0;
l_hdr->nlmsg_seq = p_socket;
l_msg->rtgen_family = AF_UNSPEC;
struct sockaddr_nl l_addr;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
}
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
{
struct msghdr l_msg;
struct iovec l_iov = { p_buffer, p_len };
struct sockaddr_nl l_addr;
int l_result;
for(;;)
{
l_msg.msg_name = (void *)&l_addr;
l_msg.msg_namelen = sizeof(l_addr);
l_msg.msg_iov = &l_iov;
l_msg.msg_iovlen = 1;
l_msg.msg_control = NULL;
l_msg.msg_controllen = 0;
l_msg.msg_flags = 0;
int l_result = recvmsg(p_socket, &l_msg, 0);
if(l_result < 0)
{
if(errno == EINTR)
{
continue;
}
return -2;
}
if(l_msg.msg_flags & MSG_TRUNC)
{ // buffer was too small
return -1;
}
return l_result;
}
}
static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
{
size_t l_size = 4096;
void *l_buffer = NULL;
for(;;)
{
free(l_buffer);
l_buffer = malloc(l_size);
if (l_buffer == NULL)
{
return NULL;
}
int l_read = netlink_recv(p_socket, l_buffer, l_size);
*p_size = l_read;
if(l_read == -2)
{
free(l_buffer);
return NULL;
}
if(l_read >= 0)
{
pid_t l_pid = getpid();
struct nlmsghdr *l_hdr;
for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
*p_done = 1;
break;
}
if(l_hdr->nlmsg_type == NLMSG_ERROR)
{
free(l_buffer);
return NULL;
}
}
return l_buffer;
}
l_size *= 2;
}
}
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
{
NetlinkList *l_item = malloc(sizeof(NetlinkList));
if (l_item == NULL)
{
return NULL;
}
l_item->m_next = NULL;
l_item->m_data = p_data;
l_item->m_size = p_size;
return l_item;
}
static void freeResultList(NetlinkList *p_list)
{
NetlinkList *l_cur;
while(p_list)
{
l_cur = p_list;
p_list = p_list->m_next;
free(l_cur->m_data);
free(l_cur);
}
}
static NetlinkList *getResultList(int p_socket, int p_request)
{
if(netlink_send(p_socket, p_request) < 0)
{
return NULL;
}
NetlinkList *l_list = NULL;
NetlinkList *l_end = NULL;
int l_size;
int l_done = 0;
while(!l_done)
{
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
if(!l_hdr)
{ // error
freeResultList(l_list);
return NULL;
}
NetlinkList *l_item = newListItem(l_hdr, l_size);
if (!l_item)
{
freeResultList(l_list);
return NULL;
}
if(!l_list)
{
l_list = l_item;
}
else
{
l_end->m_next = l_item;
}
l_end = l_item;
}
return l_list;
}
static size_t maxSize(size_t a, size_t b)
{
return (a > b ? a : b);
}
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
{
switch(p_family)
{
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
case AF_PACKET:
return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
default:
return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
}
}
static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
{
switch(p_family)
{
case AF_INET:
memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
break;
case AF_INET6:
memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
break;
case AF_PACKET:
memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
break;
default:
memcpy(p_dest->sa_data, p_data, p_size);
break;
}
p_dest->sa_family = p_family;
}
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
{
if(!*p_resultList)
{
*p_resultList = p_entry;
}
else
{
struct ifaddrs *l_cur = *p_resultList;
while(l_cur->ifa_next)
{
l_cur = l_cur->ifa_next;
}
l_cur->ifa_next = p_entry;
}
}
static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
{
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
size_t l_dataSize = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
struct rtattr *l_rta;
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
break;
case IFLA_IFNAME:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
case IFLA_STATS:
l_dataSize += NLMSG_ALIGN(l_rtaSize);
break;
default:
break;
}
}
struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = "";
char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_name = l_index + sizeof(int);
char *l_addr = l_name + l_nameSize;
char *l_data = l_addr + l_addrSize;
// save the interface index so we can look it up when handling the addresses.
memcpy(l_index, &l_info->ifi_index, sizeof(int));
l_entry->ifa_flags = l_info->ifi_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
{
size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
if(l_rta->rta_type == IFLA_ADDRESS)
{
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFLA_IFNAME:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
case IFLA_STATS:
memcpy(l_data, l_rtaData, l_rtaDataSize);
l_entry->ifa_data = l_data;
break;
default:
break;
}
}
addToEnd(p_resultList, l_entry);
return 0;
}
static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
{
int l_num = 0;
struct ifaddrs *l_cur = *p_links;
while(l_cur && l_num < p_numLinks)
{
char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
int l_index;
memcpy(&l_index, l_indexPtr, sizeof(int));
if(l_index == p_index)
{
return l_cur;
}
l_cur = l_cur->ifa_next;
++l_num;
}
return NULL;
}
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
{
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
int l_addedNetmask = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
struct rtattr *l_rta;
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
if(l_info->ifa_family == AF_PACKET)
{
continue;
}
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_LOCAL:
if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
{ // make room for netmask
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
l_addedNetmask = 1;
}
case IFA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
break;
case IFA_LABEL:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
default:
break;
}
}
struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_addr = l_name + l_nameSize;
l_entry->ifa_flags = l_info->ifa_flags;
if(l_interface)
{
l_entry->ifa_flags |= l_interface->ifa_flags;
}
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_BROADCAST:
case IFA_LOCAL:
{
size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
if(l_info->ifa_family == AF_INET6)
{
if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
{
((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
}
}
if(l_rta->rta_type == IFA_ADDRESS)
{ // apparently in a point-to-point network IFA_ADDRESS contains the dest address and IFA_LOCAL contains the local address
if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
}
else if(l_rta->rta_type == IFA_LOCAL)
{
if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = l_entry->ifa_addr;
}
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFA_LABEL:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
default:
break;
}
}
if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
{
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
char l_mask[16] = {0};
unsigned i;
for(i=0; i<(l_prefix/8); ++i)
{
l_mask[i] = 0xff;
}
if(l_prefix % 8)
{
l_mask[i] = 0xff << (8 - (l_prefix % 8));
}
makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
l_entry->ifa_netmask = (struct sockaddr *)l_addr;
}
addToEnd(p_resultList, l_entry);
return 0;
}
static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList)
{
int l_numLinks = 0;
pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWLINK)
{
if(interpretLink(l_hdr, p_resultList) == -1)
{
return -1;
}
++l_numLinks;
}
}
}
return l_numLinks;
}
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks)
{
pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWADDR)
{
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
{
return -1;
}
}
}
}
return 0;
}
int getifaddrs(struct ifaddrs **ifap)
{
if(!ifap)
{
return -1;
}
*ifap = NULL;
int l_socket = netlink_socket();
if(l_socket < 0)
{
return -1;
}
NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK);
if(!l_linkResults)
{
close(l_socket);
return -1;
}
NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR);
if(!l_addrResults)
{
close(l_socket);
freeResultList(l_linkResults);
return -1;
}
int l_result = 0;
int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
{
l_result = -1;
}
freeResultList(l_linkResults);
freeResultList(l_addrResults);
close(l_socket);
return l_result;
}
void freeifaddrs(struct ifaddrs *ifa)
{
struct ifaddrs *l_cur;
while(ifa)
{
l_cur = ifa;
ifa = ifa->ifa_next;
free(l_cur);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 1995, 1999
* Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
*/
#ifndef _IFADDRS_H_
#define _IFADDRS_H_
struct ifaddrs
{
struct ifaddrs* ifa_next;
char* ifa_name;
unsigned int ifa_flags;
struct sockaddr* ifa_addr;
struct sockaddr* ifa_netmask;
struct sockaddr* ifa_dstaddr;
void* ifa_data;
};
/*
* This may have been defined in <net/if.h>. Note that if <net/if.h> is
* to be included it must be included before this header file.
*/
#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
#endif
#include <sys/cdefs.h>
__BEGIN_DECLS
extern int getifaddrs(struct ifaddrs** ifap);
extern void freeifaddrs(struct ifaddrs* ifa);
__END_DECLS
#endif

View File

@@ -0,0 +1,271 @@
#pragma once
#include "Debug.hpp"
#include "atomic.hpp"
#include "Declarations.hpp"
#include "boost/cast.hpp"
#ifdef _WIN32
#undef min
#undef max
#endif
/// Forward Declarations
namespace Aya
{
/*
quick_intrusive_ptr_target<> is a mix-in that implements all functions required
to use boost::intrusive_ptr.
maxRefs should be used if Count is a byte or short and there is the risk of an
overflow. Some algorithms do not have this risk. To avoid writing less performant
code, maxRefs is not strictly enforced. In a multithreaded environment you should
pick a "reasonable" value that is less than std::numeric_limits<Count>::max. For
example, if Count=byte, then maxRefs could be 240
*/
template<class T, typename Count, Count maxRefs>
class quick_intrusive_ptr_target;
/*
intrusive_ptr_target<> is a mix-in that implements all functions required
to use boost::intrusive_ptr and Aya::intrusive_weak_ptr. If you don't need
weak reference support, then use quick_intrusive_ptr_target
TODO: Allow custom allocators
*/
template<class T, typename Count, Count maxStrong, Count maxWeak>
class intrusive_ptr_target;
} // namespace Aya
/// Template Specialization for boost intrusive ptrs
namespace boost
{
/// Template Specialization for boost intrusive ptrs for quick_intrusive_ptr_target
template<class T, typename Count, Count maxRefs>
void intrusive_ptr_add_ref(const Aya::quick_intrusive_ptr_target<T, Count, maxRefs>* p);
template<class T, typename Count, Count maxRefs>
void intrusive_ptr_release(const Aya::quick_intrusive_ptr_target<T, Count, maxRefs>* p);
/// Template Specialization for boost intrusive ptrs for intrusive_ptr_target
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_add_ref(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_release(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
template<class T, typename Count, Count maxStrong, Count maxWeak>
bool intrusive_ptr_expired(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
template<class T, typename Count, Count maxStrong, Count maxWeak>
bool intrusive_ptr_try_lock(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_add_weak_ref(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_weak_release(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
} // namespace boost
namespace Aya
{
class too_many_refs : public std::exception
{
public:
virtual const char* what() const throw()
{
return "too many refs";
}
};
#pragma pack(push)
#pragma pack(8) // Packing is useful if Count is short or byte
template<class T, typename Count = int, Count maxRefs = 0>
class AyaBaseClass quick_intrusive_ptr_target
{
private:
Aya::atomic<Count> refs;
public:
inline quick_intrusive_ptr_target()
{
refs = 0;
}
friend void boost::intrusive_ptr_add_ref<>(const quick_intrusive_ptr_target<T, Count, maxRefs>* p);
friend void boost::intrusive_ptr_release<>(const quick_intrusive_ptr_target<T, Count, maxRefs>* p);
};
#pragma pack(pop)
template<class T, typename Count = int, Count maxStrong = 0, Count maxWeak = maxStrong>
class AyaBaseClass intrusive_ptr_target
{
private:
// The "counts" struct is placed in memory at the head of the object
#pragma pack(push)
#pragma pack(8) // Packing is useful if Count is short or byte
struct counts
{
Aya::atomic<Count> strong; // #shared
Aya::atomic<Count> weak; // #weak + (#shared != 0)
counts()
{
strong = 0;
weak = 1;
}
};
#pragma pack(pop)
static inline counts* fetch(const T* t)
{
return reinterpret_cast<counts*>((char*)t - sizeof(counts));
}
public:
void* operator new(std::size_t t)
{
void* c = ::malloc(sizeof(counts) + t);
// placement new the counts:
new (c) counts();
return (char*)c + sizeof(counts);
}
void operator delete(void* p)
{
counts* c = fetch(reinterpret_cast<T*>(p));
// operator delete should only be called if this object
// never got touched by the intrusive_ptr functions
AYAASSERT(c->strong == 0);
AYAASSERT(c->weak == 1);
::free(c);
}
friend void boost::intrusive_ptr_add_ref<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
friend void boost::intrusive_ptr_release<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
friend void boost::intrusive_ptr_add_weak_ref<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
friend bool boost::intrusive_ptr_expired<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
friend bool boost::intrusive_ptr_try_lock<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
friend void boost::intrusive_ptr_weak_release<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
};
} // namespace Aya
namespace boost
{
/// Template specialization quick_intrusive_ptr_target
template<class T, typename Count, Count maxRefs>
void intrusive_ptr_add_ref(const Aya::quick_intrusive_ptr_target<T, Count, maxRefs>* p)
{
if (maxRefs > 0 && p->refs >= maxRefs)
throw Aya::too_many_refs();
const_cast<Aya::quick_intrusive_ptr_target<T, Count, maxRefs>*>(p)->refs++;
}
template<class T, typename Count, Count maxRefs>
void intrusive_ptr_release(const Aya::quick_intrusive_ptr_target<T, Count, maxRefs>* p)
{
AYAASSERT(p->refs > 0);
if (--(const_cast<Aya::quick_intrusive_ptr_target<T, Count, maxRefs>*>(p)->refs) == 0)
delete static_cast<const T*>(p);
}
/// Template specialization intrusive_ptr_target
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_add_ref(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
{
typename Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c =
Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
if (maxStrong > 0)
{
if (++(c->strong) > maxStrong)
throw Aya::too_many_refs();
}
else
{
c->strong++;
AYAASSERT(c->strong < std::numeric_limits<Count>::max() - 10);
}
}
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_release(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
{
const T* t = static_cast<const T*>(p);
typedef typename Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts Counts;
Counts* c = Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(t);
if (--(c->strong) == 0)
{
// placement delete the object, but not the counts
t->~T();
if (--(c->weak) == 0)
{
// placement delete the counts and reclaim composite object memory
c->Counts::~counts();
::free((void*)c);
}
}
}
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_add_weak_ref(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
{
typename Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c =
Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
AYAASSERT(c->strong > 0);
if (maxWeak > 0)
{
if (++(c->weak) > maxWeak + 1) // weak already has a ref because of the strong refs
throw Aya::too_many_refs();
}
else
{
++(c->weak);
AYAASSERT(c->weak < std::numeric_limits<Count>::max() - 10);
}
}
template<class T, typename Count, Count maxStrong, Count maxWeak>
bool intrusive_ptr_expired(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
{
typename Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c =
Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
return c->strong == 0;
}
template<class T, typename Count, Count maxStrong, Count maxWeak>
bool intrusive_ptr_try_lock(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
{
typename Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c =
Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
while (true)
{
Count tmp = c->strong;
if (tmp == 0)
return false;
if (c->strong.compare_and_swap(tmp + 1, tmp) == tmp)
return true;
}
}
template<class T, typename Count, Count maxStrong, Count maxWeak>
void intrusive_ptr_weak_release(const Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
{
const T* t = static_cast<const T*>(p);
typedef typename Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts Counts;
Counts* c = Aya::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(t);
if (--(c->weak) == 0)
{
AYAASSERT(c->strong == 0);
// placement delete the counts and reclaim composite object memory
c->Counts::~counts();
::free((void*)c);
}
}
} // namespace boost

View File

@@ -0,0 +1,268 @@
#pragma once
#include "Debug.hpp"
#include "boost/noncopyable.hpp"
namespace Aya
{
namespace Intrusive
{
// A very efficient, constant time, unordered set
// Features:
// Constant-time insert
// Constant-time remove
// Constant-time membership test
// items can remove themselves
// Items in the set auto-remove themselves upon destruction
// no memory is allocated for operations
// everything is nothrow
//
// TODO: Adding an item to a set silently removes it for membership in another set. Is this desirable? Should it be a runtime error?
// TODO: Implement ConstIterator
template<class Item, class Tag = Item>
class Set : boost::noncopyable
{
private:
class NextRef
{
friend class Set;
protected:
Item* next;
inline NextRef() throw()
: next(0)
{
}
inline NextRef(const NextRef& other) throw()
: next(0)
{
// Copies of objects aren't automatically added to containers
}
inline NextRef& operator=(const NextRef& other) throw()
{
// This object retains its membership to its container
}
};
public:
class Hook : public NextRef
{
friend class Set;
Set* _container;
NextRef* prev;
public:
inline Hook() throw()
: _container(0)
, prev(0)
{
}
inline Hook(const Hook& other) throw()
: _container(0)
, prev(0)
{
// Copies of objects aren't automatically added to containers
}
inline Hook& operator=(const Hook& other) throw()
{
// This object retains its membership to its container
}
inline ~Hook() throw()
{
remove();
}
inline void remove() throw()
{
if (is_linked())
{
AYAASSERT(prev != 0 || Set::NextRef::next != 0);
if (prev)
prev->Set::NextRef::next = NextRef::next;
if (NextRef::next)
NextRef::next->Set::Hook::prev = prev;
_container->count--;
NextRef::next = 0;
prev = 0;
_container = 0;
}
}
inline bool is_linked() const throw()
{
AYAASSERT((_container != 0) == ((Set::NextRef::next != 0) || (prev != 0)));
return _container != 0;
}
inline Set* container() throw()
{
return _container;
}
};
class Iterator
{
friend class Set;
Item* item;
Iterator(Item* item) throw()
: item(item)
{
AYAASSERT(!item || item->Set::Hook::is_linked());
}
public:
inline Iterator() throw()
: item(0)
{
}
inline bool operator==(const Iterator& other) const throw()
{
return item == other.item;
}
inline bool operator!=(const Iterator& other) const throw()
{
return item != other.item;
}
inline Item* operator->() throw()
{
AYAASSERT(item);
AYAASSERT(!item || item->Set::Hook::is_linked());
return item;
}
inline Item& operator*() throw()
{
AYAASSERT(item);
AYAASSERT(!item || item->Set::Hook::is_linked());
return *item;
}
inline Iterator& operator++() throw()
{
AYAASSERT(item);
item = item->Set::Hook::next;
AYAASSERT(!item || item->Set::Hook::is_linked());
return *this;
}
inline bool empty() const throw()
{
return item == 0;
}
// for std iterators:
typedef std::forward_iterator_tag iterator_category;
typedef Item& value_type;
typedef void difference_type;
typedef /*typename*/ Item* pointer;
typedef /*typename*/ Item& reference;
};
inline Set() throw()
: count(0)
{
}
inline ~Set() throw()
{
for (Iterator iter = begin(); !iter.empty(); iter = erase(iter))
;
}
inline size_t size() const throw()
{
return count;
}
inline bool empty() const throw()
{
return count == 0;
}
Iterator erase(Iterator iter) throw()
{
Item& item(*iter);
++iter;
remove_element(item);
return iter;
}
bool remove_element(Item& item) throw()
{
if (item.Set::Hook::_container == this)
{
item.Set::Hook::remove();
return true;
}
else
return false;
}
void insert(Item& item) throw()
{
if (item.Set::Hook::_container == this)
return;
// Items can be in only one list at a time
item.Set::Hook::remove();
AYAASSERT(!item.Set::Hook::next);
AYAASSERT(!item.Set::Hook::prev);
Item* head = head_ref.next;
if (head)
{
AYAASSERT(head->Set::Hook::is_linked());
AYAASSERT(head->Set::Hook::container() == this);
item.Set::NextRef::next = head;
head->Set::Hook::prev = &item;
}
head_ref.next = &item;
item.Set::Hook::prev = &head_ref;
item.Set::Hook::_container = this;
AYAASSERT(item.Set::Hook::next || item.Set::Hook::prev);
count++;
}
inline Iterator begin() throw()
{
return Iterator(head_ref.next);
}
inline Iterator end() throw()
{
return Iterator();
}
// For the std iterator pattern:
typedef Iterator iterator;
// For the boost::intrusive pattern:
inline void push_front(Item& item) throw()
{
insert(item);
}
// For the boost::intrusive pattern:
inline Iterator iterator_to(Item& item) throw()
{
return item.Set::Hook::_container == this ? Iterator(&item) : Iterator();
}
private:
size_t count;
NextRef head_ref;
};
} // namespace Intrusive
} // namespace Aya

View File

@@ -0,0 +1,138 @@
#pragma once
#include "boost/intrusive_ptr.hpp"
namespace Aya
{
/*
An extension to boost::intrusive_ptr.
To use this class, the target class needs to implement
some functions in addition to those required by boost::instrusive_ptr:
bool intrusive_ptr_expired(const T* p);
bool intrusive_ptr_try_lock(const T* p);
void intrusive_ptr_add_weak_ref(const T* p);
void intrusive_ptr_weak_release(const T* p);
Note: Aya::intrusive_ptr_target is a nice wrapper class that implements
all required functions
*/
template<class T>
class intrusive_weak_ptr
{
T* p_;
public:
inline intrusive_weak_ptr()
: p_(0)
{
}
inline intrusive_weak_ptr(T* p)
: p_(p)
{
if (p_ != 0)
boost::intrusive_ptr_add_weak_ref(p_);
}
template<class U>
inline intrusive_weak_ptr(const intrusive_weak_ptr<U>& rhs)
: p_(0)
{
if (!rhs.expired())
{
p_ = rhs.raw();
boost::intrusive_ptr_add_weak_ref(p_);
}
}
inline intrusive_weak_ptr(const intrusive_weak_ptr& rhs)
: p_(0)
{
if (!rhs.expired())
{
p_ = rhs.raw();
boost::intrusive_ptr_add_weak_ref(p_);
}
}
template<class U>
inline intrusive_weak_ptr(const boost::intrusive_ptr<U>& rhs)
: p_(rhs.get())
{
if (p_ != 0)
boost::intrusive_ptr_add_weak_ref(p_);
}
template<class U>
inline intrusive_weak_ptr& operator=(T* p)
{
reset(p);
return *this;
}
template<class U>
inline intrusive_weak_ptr& operator=(const boost::intrusive_ptr<U>& rhs)
{
reset(rhs.get());
return *this;
}
inline intrusive_weak_ptr& operator=(const Aya::intrusive_weak_ptr<T>& rhs)
{
reset(rhs.raw());
return *this;
}
template<class U>
inline intrusive_weak_ptr& operator=(const Aya::intrusive_weak_ptr<U>& rhs)
{
reset(rhs.raw());
return *this;
}
inline ~intrusive_weak_ptr()
{
if (p_ != 0)
boost::intrusive_ptr_weak_release(p_);
}
inline void reset()
{
if (p_ != 0)
{
boost::intrusive_ptr_weak_release(p_);
p_ = 0;
}
}
inline void reset(T* p)
{
if (p_ != 0)
boost::intrusive_ptr_weak_release(p_);
p_ = p;
if (p_ != 0)
boost::intrusive_ptr_add_weak_ref(p_);
}
inline boost::intrusive_ptr<T> lock() const
{
if (p_ && boost::intrusive_ptr_try_lock(p_))
return boost::intrusive_ptr<T>(p_, false);
else
return boost::intrusive_ptr<T>();
}
inline bool expired() const
{
return (!p_ || boost::intrusive_ptr_expired(p_));
}
// TODO: Can we hide this?
inline T* raw() const
{
return p_;
}
};
} // namespace Aya

View File

@@ -0,0 +1,175 @@
#pragma once
#include <boost/config.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/type_traits/type_with_alignment.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <cstddef>
#include <new>
// This is a copy of make_shared from boost 1.42.1
// When we upgrade we can use boost directly
// NOTE: 1.38.1 has an undocumented make_shared.hpp, but I don't know if it is
// safe or not to use.
namespace Aya
{
namespace detail
{
template<std::size_t N, std::size_t A>
struct sp_aligned_storage
{
union type
{
char data_[N];
typename boost::type_with_alignment<A>::type align_;
};
};
template<class T>
class sp_ms_deleter
{
private:
typedef typename sp_aligned_storage<sizeof(T), ::boost::alignment_of<T>::value>::type storage_type;
bool initialized_;
storage_type storage_;
private:
void destroy()
{
if (initialized_)
{
reinterpret_cast<T*>(storage_.data_)->~T();
initialized_ = false;
}
}
public:
sp_ms_deleter()
: initialized_(false)
{
}
// optimization: do not copy storage_
sp_ms_deleter(sp_ms_deleter const&)
: initialized_(false)
{
}
~sp_ms_deleter()
{
destroy();
}
void operator()(T*)
{
destroy();
}
void* address()
{
return storage_.data_;
}
void set_initialized()
{
initialized_ = true;
}
};
#if defined(BOOST_HAS_RVALUE_REFS)
template<class T>
T&& sp_forward(T& t)
{
return static_cast<T&&>(t);
}
#endif
} // namespace detail
// TODO: This implementation may not support shared_from_this properly. Upgrade to new boost, which implements this for us
template<class T>
boost::shared_ptr<T> make_shared()
{
boost::shared_ptr<T> pt(static_cast<T*>(0), Aya::detail::sp_ms_deleter<T>());
Aya::detail::sp_ms_deleter<T>* pd = boost::get_deleter<Aya::detail::sp_ms_deleter<T>>(pt);
void* pv = pd->address();
::new (pv) T();
pd->set_initialized();
T* pt2 = static_cast<T*>(pv);
return boost::shared_ptr<T>(pt, pt2);
}
template<class T>
boost::shared_ptr<T> make_shared(std::allocator<T> a)
{
boost::shared_ptr<T> pt(static_cast<T*>(0), Aya::detail::sp_ms_deleter<T>(), a);
Aya::detail::sp_ms_deleter<T>* pd = boost::get_deleter<Aya::detail::sp_ms_deleter<T>>(pt);
void* pv = pd->address();
::new (pv) T();
pd->set_initialized();
T* pt2 = static_cast<T*>(pv);
return boost::shared_ptr<T>(pt, pt2);
}
template<class T, class A1>
boost::shared_ptr<T> make_shared(A1 const& a1)
{
boost::shared_ptr<T> pt(static_cast<T*>(0), Aya::detail::sp_ms_deleter<T>());
Aya::detail::sp_ms_deleter<T>* pd = boost::get_deleter<Aya::detail::sp_ms_deleter<T>>(pt);
void* pv = pd->address();
::new (pv) T(a1);
pd->set_initialized();
T* pt2 = static_cast<T*>(pv);
return boost::shared_ptr<T>(pt, pt2);
}
template<class T, class A1, class A2>
boost::shared_ptr<T> make_shared(A1 const& a1, A2 const& a2)
{
boost::shared_ptr<T> pt(static_cast<T*>(0), Aya::detail::sp_ms_deleter<T>());
Aya::detail::sp_ms_deleter<T>* pd = boost::get_deleter<Aya::detail::sp_ms_deleter<T>>(pt);
void* pv = pd->address();
::new (pv) T(a1, a2);
pd->set_initialized();
T* pt2 = static_cast<T*>(pv);
return boost::shared_ptr<T>(pt, pt2);
}
template<class T, class A1, class A2, class A3>
boost::shared_ptr<T> make_shared(A1 const& a1, A2 const& a2, A3 const& a3)
{
boost::shared_ptr<T> pt(static_cast<T*>(0), Aya::detail::sp_ms_deleter<T>());
Aya::detail::sp_ms_deleter<T>* pd = boost::get_deleter<Aya::detail::sp_ms_deleter<T>>(pt);
void* pv = pd->address();
::new (pv) T(a1, a2, a3);
pd->set_initialized();
T* pt2 = static_cast<T*>(pv);
return boost::shared_ptr<T>(pt, pt2);
}
} // namespace Aya

View File

@@ -0,0 +1,6 @@
# microprofile [![Build Status](https://travis-ci.org/zeux/microprofile.svg?branch=master)](https://travis-ci.org/zeux/microprofile)
This is a fork of [microprofile by Jonas Meyer](https://bitbucket.org/jonasmeyer/microprofile).
microprofile is an embeddable CPU/GPU profiler with an in-app and HTML visualizers.
![Screenshot](https://pbs.twimg.com/media/BnvzublCEAA0Mqf.png:large)

View File

@@ -0,0 +1,10 @@
#!/bin/sh
MPPIPE=/tmp/.microprofilecspipe
while true;
do
echo "DTrace run; output file: $MPPIPE"
mkfifo $MPPIPE
dtrace -q -n fbt::thread_dispatch:return'{printf("MPTD %x %x %x\n", cpu, tid, machtimestamp)}' -o $MPPIPE
rm $MPPIPE
read -p "DTrace run finished, press Enter to restart..."
done

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,556 @@
// This is free and unencumbered software released into the public domain.
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// For more information, please refer to <http://unlicense.org/>
#ifndef MICROPROFILE_ENABLED
#error "microprofile.h must be included before including microprofiledraw.h"
#endif
#ifndef MICROPROFILEDRAW_ENABLED
#define MICROPROFILEDRAW_ENABLED MICROPROFILE_ENABLED
#endif
#ifndef MICROPROFILEDRAW_API
#define MICROPROFILEDRAW_API
#endif
#if MICROPROFILEDRAW_ENABLED
MICROPROFILEDRAW_API void MicroProfileDrawInitGL();
MICROPROFILEDRAW_API void MicroProfileRender(uint32_t nWidth, uint32_t nHeight, float fScale);
MICROPROFILEDRAW_API void MicroProfileBeginDraw(uint32_t nWidth, uint32_t nHeight, float* pfProjection);
MICROPROFILEDRAW_API void MicroProfileBeginDraw(uint32_t nWidth, uint32_t nHeight, float fScale);
MICROPROFILEDRAW_API void MicroProfileEndDraw();
#ifdef MICROPROFILEDRAW_IMPL
struct MicroProfileDrawVertex
{
float nX;
float nY;
uint32_t nColor;
float fU;
float fV;
};
struct MicroProfileDrawCommand
{
uint32_t nCommand;
uint32_t nNumVertices;
};
struct MicroProfileDrawContext
{
enum
{
MAX_COMMANDS = 32,
MAX_VERTICES = 16384,
};
bool bInitialized;
GLuint nVAO;
GLuint nVertexBuffer;
GLuint nProgram;
GLuint nTexture;
int nAttributePosition;
int nAttributeColor;
int nAttributeTexture;
int nUniformProjectionMatrix;
uint32_t nVertexPos;
uint32_t nCommandPos;
MicroProfileDrawVertex nVertices[MAX_VERTICES];
MicroProfileDrawCommand nCommands[MAX_COMMANDS];
};
MicroProfileDrawContext g_MicroProfileDraw;
#define Q0(d, member, v) d[0].member = v
#define Q1(d, member, v) \
d[1].member = v; \
d[3].member = v
#define Q2(d, member, v) d[4].member = v
#define Q3(d, member, v) \
d[2].member = v; \
d[5].member = v
#define FONT_TEX_X 1024
#define FONT_TEX_Y 9
#define FONT_SIZE (FONT_TEX_X * FONT_TEX_Y * 4)
namespace
{
extern const uint8_t g_MicroProfileFont[];
extern const uint16_t g_MicroProfileFontDescription[];
extern const char g_MicroProfileVertexShader_110[];
extern const char g_MicroProfileFragmentShader_110[];
extern const char g_MicroProfileVertexShader_150[];
extern const char g_MicroProfileFragmentShader_150[];
} // namespace
bool MicroProfileCompileShader(GLuint* pnHandle, int nType, const char* pShader)
{
*pnHandle = glCreateShader(nType);
glShaderSource(*pnHandle, 1, &pShader, 0);
glCompileShader(*pnHandle);
GLint compiled = 0;
glGetShaderiv(*pnHandle, GL_COMPILE_STATUS, &compiled);
if (!compiled)
{
char temp[4096];
glGetShaderInfoLog(*pnHandle, 4096, NULL, temp);
printf("SHADER FAILED TO COMPILE:\n%s\n", temp);
return false;
}
return true;
}
bool MicroProfileLinkProgram(GLuint* pnHandle, GLuint nVertexShader, GLuint nFragmentShader)
{
*pnHandle = glCreateProgram();
glAttachShader(*pnHandle, nVertexShader);
glAttachShader(*pnHandle, nFragmentShader);
glLinkProgram(*pnHandle);
GLint linked = 0;
glGetProgramiv(*pnHandle, GL_LINK_STATUS, &linked);
if (!linked)
{
char temp[4096];
glGetProgramInfoLog(*pnHandle, 4096, NULL, temp);
printf("PROGRAM FAILED TO LINK:\n%s\n", temp);
return false;
}
return true;
}
void MicroProfileDrawInitGL()
{
MicroProfileDrawContext& S = g_MicroProfileDraw;
MP_ASSERT(!S.bInitialized);
const GLubyte* pGLVersion = glGetString(GL_VERSION);
const GLubyte* pGLSLVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);
int nGLVersion = (pGLVersion[0] - '0') * 10 + (pGLVersion[2] - '0');
int nGLSLVersion = (pGLSLVersion[0] - '0') * 100 + (pGLSLVersion[2] - '0') * 10 + (pGLSLVersion[3] - '0');
glGenBuffers(1, &S.nVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, S.nVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(S.nVertices), 0, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
if (nGLVersion >= 3)
glGenVertexArrays(1, &S.nVAO);
else
S.nVAO = 0;
GLuint nVertexShader;
if (!MicroProfileCompileShader(
&nVertexShader, GL_VERTEX_SHADER, nGLSLVersion >= 150 ? g_MicroProfileVertexShader_150 : g_MicroProfileVertexShader_110))
return;
GLuint nFragmentShader;
if (!MicroProfileCompileShader(
&nFragmentShader, GL_FRAGMENT_SHADER, nGLSLVersion >= 150 ? g_MicroProfileFragmentShader_150 : g_MicroProfileFragmentShader_110))
return;
if (!MicroProfileLinkProgram(&S.nProgram, nVertexShader, nFragmentShader))
return;
S.nAttributePosition = glGetAttribLocation(S.nProgram, "VertexIn");
S.nAttributeColor = glGetAttribLocation(S.nProgram, "ColorIn");
S.nAttributeTexture = glGetAttribLocation(S.nProgram, "TCIn");
S.nUniformProjectionMatrix = glGetUniformLocation(S.nProgram, "ProjectionMatrix");
glUseProgram(S.nProgram);
glUniform1i(glGetUniformLocation(S.nProgram, "Texture"), 0);
glUniform1f(glGetUniformLocation(S.nProgram, "RcpFontHeight"), 1.f / FONT_TEX_Y);
glUseProgram(0);
uint32_t* pUnpacked = (uint32_t*)alloca(FONT_SIZE);
int idx = 0;
int end = FONT_TEX_X * FONT_TEX_Y / 8;
for (int i = 0; i < end; i++)
{
unsigned char pValue = g_MicroProfileFont[i];
for (int j = 0; j < 8; ++j)
{
pUnpacked[idx++] = pValue & 0x80 ? (uint32_t)-1 : 0;
pValue <<= 1;
}
}
pUnpacked[idx - 1] = 0xffffffff;
uint32_t* p4 = &pUnpacked[0];
glGenTextures(1, &S.nTexture);
glBindTexture(GL_TEXTURE_2D, S.nTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FONT_TEX_X, FONT_TEX_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, &p4[0]);
glBindTexture(GL_TEXTURE_2D, 0);
S.bInitialized = true;
}
void MicroProfileBeginDraw(uint32_t nWidth, uint32_t nHeight, float* pfProjection)
{
MicroProfileDrawContext& S = g_MicroProfileDraw;
if (!S.bInitialized)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
if (S.nVAO)
glBindVertexArray(S.nVAO);
glUseProgram(S.nProgram);
glUniformMatrix4fv(S.nUniformProjectionMatrix, 1, 0, pfProjection);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, S.nTexture);
glBindBuffer(GL_ARRAY_BUFFER, S.nVertexBuffer);
int nStride = sizeof(MicroProfileDrawVertex);
glVertexAttribPointer(S.nAttributePosition, 2, GL_FLOAT, 0, nStride, (void*)(offsetof(MicroProfileDrawVertex, nX)));
glVertexAttribPointer(S.nAttributeColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, nStride, (void*)(offsetof(MicroProfileDrawVertex, nColor)));
glVertexAttribPointer(S.nAttributeTexture, 2, GL_FLOAT, 0, nStride, (void*)(offsetof(MicroProfileDrawVertex, fU)));
glEnableVertexAttribArray(S.nAttributePosition);
glEnableVertexAttribArray(S.nAttributeColor);
glEnableVertexAttribArray(S.nAttributeTexture);
S.nVertexPos = 0;
S.nCommandPos = 0;
}
void MicroProfileBeginDraw(uint32_t nWidth, uint32_t nHeight, float fScale)
{
MicroProfileDrawContext& S = g_MicroProfileDraw;
if (!S.bInitialized)
return;
float left = 0.f;
float right = nWidth / fScale;
float bottom = nHeight / fScale;
float top = 0.f;
float near_plane = -1.f;
float far_plane = 1.f;
float projection[16] = {};
projection[0] = 2.0f / (right - left);
projection[5] = 2.0f / (top - bottom);
projection[10] = -2.0f / (far_plane - near_plane);
projection[12] = -(right + left) / (right - left);
projection[13] = -(top + bottom) / (top - bottom);
projection[14] = -(far_plane + near_plane) / (far_plane - near_plane);
projection[15] = 1.f;
MicroProfileBeginDraw(nWidth, nHeight, projection);
}
void MicroProfileFlush()
{
MicroProfileDrawContext& S = g_MicroProfileDraw;
if (S.nVertexPos == 0)
return;
MICROPROFILE_SCOPEI("MicroProfile", "Flush", 0xffff3456);
glBufferSubData(GL_ARRAY_BUFFER, 0, S.nVertexPos * sizeof(MicroProfileDrawVertex), S.nVertices);
int nOffset = 0;
for (int i = 0; i < int(S.nCommandPos); ++i)
{
int nCount = S.nCommands[i].nNumVertices;
glDrawArrays(S.nCommands[i].nCommand, nOffset, nCount);
nOffset += nCount;
}
S.nVertexPos = 0;
S.nCommandPos = 0;
}
MicroProfileDrawVertex* MicroProfilePushVertices(uint32_t nCommand, int nCount)
{
MP_ASSERT(nCount <= MicroProfileDrawContext::MAX_VERTICES);
MicroProfileDrawContext& S = g_MicroProfileDraw;
if (S.nVertexPos + nCount > MicroProfileDrawContext::MAX_VERTICES)
MicroProfileFlush();
if (S.nCommandPos && S.nCommands[S.nCommandPos - 1].nCommand == nCommand)
{
S.nCommands[S.nCommandPos - 1].nNumVertices += nCount;
}
else
{
if (S.nCommandPos == MicroProfileDrawContext::MAX_COMMANDS)
MicroProfileFlush();
S.nCommands[S.nCommandPos].nCommand = nCommand;
S.nCommands[S.nCommandPos].nNumVertices = nCount;
S.nCommandPos++;
}
uint32_t nOut = S.nVertexPos;
S.nVertexPos += nCount;
return &S.nVertices[nOut];
}
void MicroProfileEndDraw()
{
MicroProfileDrawContext& S = g_MicroProfileDraw;
if (!S.bInitialized)
return;
MicroProfileFlush();
glDisableVertexAttribArray(S.nAttributePosition);
glDisableVertexAttribArray(S.nAttributeColor);
glDisableVertexAttribArray(S.nAttributeTexture);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
if (S.nVAO)
glBindVertexArray(0);
glDisable(GL_BLEND);
}
void MicroProfileRender(uint32_t nWidth, uint32_t nHeight, float fScale)
{
MicroProfileBeginDraw(nWidth, nHeight, fScale);
MicroProfileDraw(nWidth, nHeight);
MicroProfileEndDraw();
}
void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, uint32_t nLen)
{
MICROPROFILE_SCOPEI("MicroProfile", "TextDraw", 0xff88ee);
const float fOffsetU = 5.f / 1024.f;
MP_ASSERT(nLen <= strlen(pText));
float fX = (float)nX;
float fY = (float)nY;
float fY2 = fY + (MICROPROFILE_TEXT_HEIGHT + 1);
MicroProfileDrawVertex* pVertex = MicroProfilePushVertices(GL_TRIANGLES, 6 * nLen);
if (!pVertex)
return;
const char* pStr = pText;
nColor = 0xff000000 | ((nColor & 0xff) << 16) | (nColor & 0xff00) | ((nColor >> 16) & 0xff);
for (uint32_t j = 0; j < nLen; ++j)
{
int16_t nOffset = g_MicroProfileFontDescription[uint8_t(*pStr++)];
float fOffset = nOffset / 1024.f;
Q0(pVertex, nX, fX);
Q0(pVertex, nY, fY);
Q0(pVertex, nColor, nColor);
Q0(pVertex, fU, fOffset);
Q0(pVertex, fV, 0.f);
Q1(pVertex, nX, fX + MICROPROFILE_TEXT_WIDTH);
Q1(pVertex, nY, fY);
Q1(pVertex, nColor, nColor);
Q1(pVertex, fU, fOffset + fOffsetU);
Q1(pVertex, fV, 0.f);
Q2(pVertex, nX, fX + MICROPROFILE_TEXT_WIDTH);
Q2(pVertex, nY, fY2);
Q2(pVertex, nColor, nColor);
Q2(pVertex, fU, fOffset + fOffsetU);
Q2(pVertex, fV, 1.f);
Q3(pVertex, nX, fX);
Q3(pVertex, nY, fY2);
Q3(pVertex, nColor, nColor);
Q3(pVertex, fU, fOffset);
Q3(pVertex, fV, 1.f);
fX += MICROPROFILE_TEXT_WIDTH + 1;
pVertex += 6;
}
}
void MicroProfileDrawBox(int nX0, int nY0, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType Type)
{
MicroProfileDrawVertex* pVertex = MicroProfilePushVertices(GL_TRIANGLES, 6);
if (!pVertex)
return;
if (Type == MicroProfileBoxTypeFlat)
{
nColor = ((nColor & 0xff) << 16) | ((nColor >> 16) & 0xff) | (0xff00ff00 & nColor);
Q0(pVertex, nX, (float)nX0);
Q0(pVertex, nY, (float)nY0);
Q0(pVertex, nColor, nColor);
Q0(pVertex, fU, 2.f);
Q0(pVertex, fV, 2.f);
Q1(pVertex, nX, (float)nX1);
Q1(pVertex, nY, (float)nY0);
Q1(pVertex, nColor, nColor);
Q1(pVertex, fU, 2.f);
Q1(pVertex, fV, 2.f);
Q2(pVertex, nX, (float)nX1);
Q2(pVertex, nY, (float)nY1);
Q2(pVertex, nColor, nColor);
Q2(pVertex, fU, 2.f);
Q2(pVertex, fV, 2.f);
Q3(pVertex, nX, (float)nX0);
Q3(pVertex, nY, (float)nY1);
Q3(pVertex, nColor, nColor);
Q3(pVertex, fU, 2.f);
Q3(pVertex, fV, 2.f);
}
else
{
uint32_t r = 0xff & (nColor >> 16);
uint32_t g = 0xff & (nColor >> 8);
uint32_t b = 0xff & nColor;
uint32_t nMax = MicroProfileMax(MicroProfileMax(MicroProfileMax(r, g), b), 30u);
uint32_t nMin = MicroProfileMin(MicroProfileMin(MicroProfileMin(r, g), b), 180u);
uint32_t r0 = 0xff & ((r + nMax) / 2);
uint32_t g0 = 0xff & ((g + nMax) / 2);
uint32_t b0 = 0xff & ((b + nMax) / 2);
uint32_t r1 = 0xff & ((r + nMin) / 2);
uint32_t g1 = 0xff & ((g + nMin) / 2);
uint32_t b1 = 0xff & ((b + nMin) / 2);
uint32_t nColor0 = (r0 << 0) | (g0 << 8) | (b0 << 16) | (0xff000000 & nColor);
uint32_t nColor1 = (r1 << 0) | (g1 << 8) | (b1 << 16) | (0xff000000 & nColor);
Q0(pVertex, nX, (float)nX0);
Q0(pVertex, nY, (float)nY0);
Q0(pVertex, nColor, nColor0);
Q0(pVertex, fU, 2.f);
Q0(pVertex, fV, 2.f);
Q1(pVertex, nX, (float)nX1);
Q1(pVertex, nY, (float)nY0);
Q1(pVertex, nColor, nColor0);
Q1(pVertex, fU, 3.f);
Q1(pVertex, fV, 2.f);
Q2(pVertex, nX, (float)nX1);
Q2(pVertex, nY, (float)nY1);
Q2(pVertex, nColor, nColor1);
Q2(pVertex, fU, 3.f);
Q2(pVertex, fV, 3.f);
Q3(pVertex, nX, (float)nX0);
Q3(pVertex, nY, (float)nY1);
Q3(pVertex, nColor, nColor1);
Q3(pVertex, fU, 2.f);
Q3(pVertex, fV, 3.f);
}
}
void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, uint32_t nColor)
{
if (!nVertices)
return;
MicroProfileDrawVertex* pVertex = MicroProfilePushVertices(GL_LINES, 2 * (nVertices - 1));
nColor = 0xff000000 | ((nColor & 0xff) << 16) | (nColor & 0xff00ff00) | ((nColor >> 16) & 0xff);
for (uint32_t i = 0; i < nVertices - 1; ++i)
{
pVertex[0].nX = pVertices[i * 2];
pVertex[0].nY = pVertices[i * 2 + 1];
pVertex[0].nColor = nColor;
pVertex[0].fU = 2.f;
pVertex[0].fV = 2.f;
pVertex[1].nX = pVertices[(i + 1) * 2];
pVertex[1].nY = pVertices[(i + 1) * 2 + 1];
pVertex[1].nColor = nColor;
pVertex[1].fU = 2.f;
pVertex[1].fV = 2.f;
pVertex += 2;
}
}
namespace
{
const char g_MicroProfileVertexShader_110[] = "#version 110\n\
uniform mat4 ProjectionMatrix; \
attribute vec3 VertexIn; attribute vec4 ColorIn; attribute vec2 TCIn; \
varying vec2 TC; varying vec4 Color; \
void main() { Color = ColorIn; TC = TCIn; gl_Position = ProjectionMatrix * vec4(VertexIn, 1.0); }";
const char g_MicroProfileVertexShader_150[] = "#version 150\n\
uniform mat4 ProjectionMatrix; \
in vec3 VertexIn; in vec4 ColorIn; in vec2 TCIn; \
out vec2 TC; out vec4 Color; \
void main() { Color = ColorIn; TC = TCIn; gl_Position = ProjectionMatrix * vec4(VertexIn, 1.0); }";
const char g_MicroProfileFragmentShader_110[] = "#version 110\n\
uniform sampler2D Texture; uniform float RcpFontHeight; \
varying vec2 TC; varying vec4 Color; \
void main() { \
vec4 c0 = texture2D(Texture, TC.xy); \
vec4 c1 = texture2D(Texture, TC.xy + vec2(0.0, RcpFontHeight)); \
gl_FragColor = c0.w < 0.5 ? vec4(0, 0, 0, c1.w) : c0 * Color; \
} \
";
const char g_MicroProfileFragmentShader_150[] = "#version 150\n\
uniform sampler2D Texture; uniform float RcpFontHeight; \
in vec2 TC; in vec4 Color; \
out vec4 result; \
void main() { \
vec4 c0 = texture(Texture, TC.xy); \
vec4 c1 = texture(Texture, TC.xy + vec2(0.0, RcpFontHeight)); \
result = c0.w < 0.5 ? vec4(0, 0, 0, c1.w) : c0 * Color; \
} \
";
#include "microprofilefont.hpp"
} // namespace
#endif
#endif

File diff suppressed because it is too large Load Diff

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,90 @@
#pragma once
#include "boost/pool/object_pool.hpp"
namespace Aya
{
// T must have a non-throwing destructor
template<typename T, typename UserAllocator>
class object_pool : public boost::object_pool<T, UserAllocator>
{
protected:
struct CallDestructor
{
void operator()(T* item)
{
item->~T();
}
} callDestructor;
public:
#ifndef _WIN32
// gcc can't access typdef from base class...
typedef typename boost::pool<UserAllocator>::size_type size_type;
#endif
// This constructor parameter is an extension!
explicit object_pool<T, UserAllocator>(const size_type next_size = 32)
: boost::object_pool<T, UserAllocator>(next_size)
{
}
template<class F>
void for_each(F& f)
{
// handle trivial case
if (!this->list.valid())
return;
boost::details::PODptr<size_type> iter = this->list;
boost::details::PODptr<size_type> next = iter;
// Start 'freed_iter' at beginning of free list
void* freed_iter = this->first;
const size_type partition_size = this->alloc_size();
do
{
// increment next
next = next.next();
// delete all contained objects that aren't freed
// Iterate 'i' through all chunks in the memory block
for (char* i = iter.begin(); i != iter.end(); i += partition_size)
{
// If this chunk is free
if (i == freed_iter)
{
// Increment freed_iter to point to next in free list
freed_iter = boost::simple_segregated_storage<size_type>::nextof(freed_iter);
// Continue searching chunks in the memory block
continue;
}
// This chunk is not free (allocated), so call f
f(static_cast<T*>(static_cast<void*>(i)));
// and continue searching chunks in the memory block
}
// increment iter
iter = next;
} while (iter.valid());
}
// go through all objects, calling desctructors.
// then use store's purge operation (which doesn't call destructors)
void clear()
{
for_each(callDestructor);
boost::pool<UserAllocator>::purge_memory();
}
};
} // namespace Aya

View File

@@ -0,0 +1,48 @@
#include "signal.hpp"
LOGVARIABLE(ScopedConnection, 0);
struct Init
{
static void initStaticData() {}
Init()
{
static boost::once_flag flag = BOOST_ONCE_INIT;
boost::call_once(&initStaticData, flag);
}
};
static Init init;
using namespace Aya;
using namespace signals;
boost::function<void(std::exception&)> Aya::signals::slot_exception_handler(0);
void connection::disconnect() const
{
boost::intrusive_ptr<islot> s(weak_slot.lock());
if (s)
s->disconnect();
}
bool connection::connected() const
{
boost::intrusive_ptr<islot> s(weak_slot.lock());
return s && s->connected();
}
bool connection::operator==(const connection& other) const
{
return weak_slot.lock() == other.weak_slot.lock();
}
bool connection::operator!=(const connection& other) const
{
return weak_slot.lock() != other.weak_slot.lock();
}
connection& connection::operator=(const connection& con)
{
weak_slot = con.weak_slot;
return *this;
}

774
engine/core/src/signal.hpp Normal file
View File

@@ -0,0 +1,774 @@
#pragma once
#include "boost.hpp"
#include "intrusive_ptr_target.hpp"
#include "Memory.hpp"
#include "intrusive_weak_ptr.hpp"
#include "boost/any.hpp"
#include "boost/type_traits.hpp"
#include "threadsafe.hpp"
#include "Debug.hpp"
#include <limits>
#include "callable.hpp"
// aya - instead of intrusive_ptr, use shared_ptr on unix
using boost::shared_ptr;
using boost::weak_ptr;
LOGGROUP(ScopedConnection);
#ifdef _DEBUG
#define AYA_SIGNALS_DEBUGGING
#endif
#ifdef AYA_SIGNALS_DEBUGGING
#define AYA_SIGNALS_ASSERT AYA_CRASH_ASSERT
#pragma optimize("", off)
#else
#define AYA_SIGNALS_ASSERT AYAASSERT
#endif
namespace Aya
{
// The classes in this namespace mimic a small fraction of the features contained
// in boost signals
namespace signals
{
/*
This signal class is similar to boost::signal, but with a few
important differences. First, it doesn't implement nearly as
much functionality as boost's. It merely implements those
portions that Roblox uses.
The big advantage to this implementation is its (limited) thread
safety. Any thread is allowed to connect new slots to this
signal and also disconnect them at any time. The firing of a signal
is not thread safe - only one thread is allowed to fire at a time.
*/
// Set this to whatever you want to handle exceptions thrown by a slot
extern boost::function<void(std::exception&)> slot_exception_handler;
class connection
{
public:
class islot
: boost::noncopyable
#if 0
// No need to test maxStrong, since all strong references are internal
// However, weak references are external via connection object. It is
// expected that the total weak references to a slot are much less than 64000
// NOTE: unsigned short is slightly slower than int on Win32. However, it
// saves 4 bytes.
, public Aya::intrusive_ptr_target<islot, unsigned short, 0, 0>
#else
, public Aya::intrusive_ptr_target<islot>
#endif
{
protected:
islot() {}
public:
virtual ~islot() {}
virtual void disconnect() = 0;
virtual bool connected() const = 0;
};
inline connection(islot* slot)
: weak_slot(slot)
{
}
inline connection(const connection& con)
: weak_slot(con.weak_slot)
{
}
inline connection() {}
connection& operator=(const connection& con);
void disconnect() const;
bool connected() const;
bool operator==(const connection& other) const;
bool operator!=(const connection& other) const;
void flogPrint()
{
boost::intrusive_ptr<islot> s(weak_slot.lock());
FASTLOG2(FLog::Always, "Connection %p, slot %p", this, s.get());
}
private:
// to make connections copyable, the data for a connection are shared
Aya::intrusive_weak_ptr<islot> weak_slot; // must be weak to avoid memory leaks
};
class scoped_connection : boost::noncopyable
{
// Has-a instead of Is-a. We do this because demoting scoped_connection reference
// to a connection will alter the meaning of the = operator, leading to strange
// bugs.
connection con;
public:
inline scoped_connection() {}
inline scoped_connection(const connection& con)
: con(con)
{
}
inline scoped_connection& operator=(const connection& con)
{
if (this->con != con)
{
disconnect();
this->con = con;
}
return *this;
}
inline ~scoped_connection()
{
disconnect();
}
// Accessor to underlying connection, if you really want it
inline connection& get()
{
return con;
}
// implementation of connection contract
inline void disconnect() const
{
con.disconnect();
}
inline bool connected() const
{
return con.connected();
}
inline bool operator==(const connection& other) const
{
return con == other;
}
inline bool operator!=(const connection& other) const
{
return con != other;
}
};
class scoped_connection_logged : boost::noncopyable
{
// Has-a instead of Is-a. We do this because demoting scoped_connection reference
// to a connection will alter the meaning of the = operator, leading to strange
// bugs.
connection con;
bool logged;
public:
inline scoped_connection_logged()
: logged(false)
{
}
inline scoped_connection_logged(bool logged)
: logged(logged)
{
}
// Helper for using FastLog groups as trigger
inline scoped_connection_logged(FLog::Channel channelId)
: logged(channelId != 0)
{
}
inline scoped_connection_logged(const connection& con)
: con(con)
{
}
inline scoped_connection_logged& operator=(const connection& con)
{
if (this->con != con)
{
disconnect();
this->con = con;
if (logged)
{
FASTLOG2(FLog::Always, "Scoped connection %p assign: %p", this, &con);
}
}
return *this;
}
inline ~scoped_connection_logged()
{
if (logged)
FASTLOG1(FLog::Always, "Scoped connection %p destructor", this);
disconnect();
}
// Accessor to underlying connection, if you really want it
inline connection& get()
{
return con;
}
inline void setLogged(bool logged)
{
this->logged = logged;
}
// implementation of connection contract
inline void disconnect() const
{
if (logged)
FASTLOG2(FLog::Always, "Scoped connection %p disconnect, previously connected: %u", this, con.connected());
con.disconnect();
}
inline bool connected() const
{
return con.connected();
}
inline bool operator==(const connection& other) const
{
return con == other;
}
inline bool operator!=(const connection& other) const
{
return con != other;
}
};
template<typename Signature>
class signal : boost::noncopyable
{
protected:
friend class slot;
class slot
: public connection::islot
, public icallable<boost::function_traits<Signature>::arity, Signature>
{
public:
boost::intrusive_ptr<slot> next;
signal* sig;
inline slot(signal* sig)
: sig(sig)
{
}
virtual bool connected() const
{
return sig != NULL;
}
public:
SAFE_HEAP_STATIC(boost::mutex, mutex);
virtual void disconnect()
{
if (!sig)
return;
boost::mutex::scoped_lock lock(mutex());
if (sig)
{
signal* s = sig;
sig = NULL;
s->remove(this);
}
}
};
template<class Delegate>
class callable_slot : public callable<slot, Delegate, boost::function_traits<Signature>::arity, Signature>
{
public:
inline callable_slot(const Delegate& deleg, signal* sig)
: callable<slot, Delegate, boost::function_traits<Signature>::arity, Signature>(deleg, sig)
{
}
};
private:
// The slots are stored in a linked list, with "head" as a dummy slot used to anchor the list.
boost::intrusive_ptr<slot> head;
// TODO: Avoid contention by using one mutex per signal? Or an array of signals?
// TODO: Is boost::mutex the best choice? Does it start up with a spin?
// However, at least we have a separate mutex for each signature
// SAFE_HEAP_STATIC is used instead of SAFE_STATIC to work around global variables using signals (like GameSettings)
// If you use SAFE_STATIC, mutex can be destroyed before other global variables using signals,
// so signal destructor will fail on mutex access
SAFE_HEAP_STATIC(boost::mutex, mutex)
void remove(slot* item)
{
// Invariant: the value of item->next does not change
AYAASSERT(!boost::intrusive_ptr_expired(item));
if (item == head)
head = item->next;
else
{
// Find "prev". This is O(n)
slot* prev = head.get();
// TODO: Can we just assert that prev!=NULL?
while (prev && prev->next != item)
prev = prev->next.get();
// In theory prev should never be NULL, because for it to be NULL
// the slot would be destroyed, in which case remove() can't be
// called. Let's play it safe and null-check anyway.
AYA_SIGNALS_ASSERT(!prev || prev->next.get() == item);
if (prev)
prev->next = item->next;
}
AYAASSERT(!boost::intrusive_ptr_expired(item));
// item is now deletable
}
void insert(slot* item)
{
AYA_SIGNALS_ASSERT(item);
boost::mutex::scoped_lock lock(mutex());
if (!head)
{
head = item;
}
else
{
item->next = head;
head = item;
}
}
public:
inline signal()
{
mutex();
}
inline ~signal()
{
disconnectAll();
}
void disconnectAll()
{
while (head)
{
boost::intrusive_ptr<slot> node;
{
boost::mutex::scoped_lock lock(mutex());
// See DE131 for a justification of this "chunk" code
const int chunkSize = 10;
int count = chunkSize;
for (node = head; node; node = node->next)
{
node->sig = NULL;
if (count-- == 0)
{
// After 10 iterations we need to break out and collect
// the slots. Otherwise we risk a stack crash
break;
}
}
}
// the next line will cause nodes to be destroyed.
// Notice that we want them to be destroyed
// outside of the mutex lock because
// destruction could have side-effects.
head = node;
}
}
inline bool empty() const
{
return !head;
}
template<class Delegate>
connection connect(const Delegate& function)
{
slot* item = new callable_slot<Delegate>(function, this);
insert(item);
return connection(item);
}
// For debugging:
static size_t sizeof_slot()
{
return sizeof(slot);
}
void flogPrint()
{
printf("Signal - %p\n", this);
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
while (this->next(item))
printf("Signal slot = %p\n", item.get());
// FASTLOG1(FLog::Always, "Signal slot = %p", item.get());
}
protected:
void on_error(std::exception& e)
{
if (slot_exception_handler)
slot_exception_handler(e);
}
bool next(boost::intrusive_ptr<slot>& item)
{
if (!item)
{
// Start iterating; this is safe to read from
// If another thread is in the process of prepending, we can get old head or new head
// If we do get the new head it should already have the new next so this is race-free
item = this->head;
}
else
{
// Advance the iterator; we keep item alive so next is safe to read from
// If another thread is in the process of removing the 'item->next' connection we may see
// the next pointer either pointing to the element that's being removed or to the next one
// Since replacing next is atomic and we can't observe any other values than these two this is
// also race-free.
item = item->next;
}
if (!item)
{
// Done iterating
return false;
}
else
{
// Iteration succeeded
return true;
}
}
};
template<int arity, typename Signature>
class signal_with_args;
template<typename Signature>
class signal_with_args<0, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call();
}
public:
void operator()()
{
if (this->empty())
return;
typedef typename Aya::signals::signal<Signature>::slot slot;
boost::intrusive_ptr<slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get());
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<1, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<2, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
typename boost::function_traits<Signature>::arg2_type arg2)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1, arg2);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1, arg2);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<3, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1, arg2, arg3);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1, arg2, arg3);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<4, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
typename boost::function_traits<Signature>::arg4_type arg4)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1, arg2, arg3, arg4);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1, arg2, arg3, arg4);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<5, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1, arg2, arg3, arg4, arg5);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<6, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5,
typename boost::function_traits<Signature>::arg6_type arg6)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1, arg2, arg3, arg4, arg5, arg6);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5, arg6);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
template<typename Signature>
class signal_with_args<7, Signature> : public signal<Signature>
{
static inline void fireItem(typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1,
typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3,
typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5,
typename boost::function_traits<Signature>::arg6_type arg6, typename boost::function_traits<Signature>::arg7_type arg7)
{
if (item->sig) // Make sure this guy hasn't been disconnected
item->call(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
public:
void operator()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2,
typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4,
typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6,
typename boost::function_traits<Signature>::arg7_type arg7)
{
if (this->empty())
return;
boost::intrusive_ptr<typename Aya::signals::signal<Signature>::slot> item;
begin:
try
{
while (this->next(item))
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
catch (Aya::base_exception& e)
{
Aya::signals::signal<Signature>::on_error(e);
// Note: We put this handler on the outside of the for loop
// as an optimization. This is why we have a goto statement.
goto begin;
}
}
};
} // namespace signals
template<typename Signature>
class signal : public signals::signal_with_args<boost::function_traits<Signature>::arity, Signature>
{
};
// Note that remote signal is *not* virtualized against signal. This only works because the Event class is templatized, and not using polymorphism.
// If Event becomes polymorphics, THIS CODE WILL FAIL
template<typename Signature>
class remote_signal : public signal<Signature>
{
private:
typedef signal<Signature> Super;
public:
signal<void()> connectionSignal;
remote_signal() {}
template<typename F>
signals::connection connect(const F& function)
{
connectionSignal();
return Super::connect(function);
}
};
} // namespace Aya
#ifdef AYA_SIGNALS_DEBUGGING
#pragma optimize("", on)
#endif

View File

@@ -0,0 +1,542 @@
#pragma once
// Include this file only if you need the SIMD functionality (which is a large include).
// If you only need the SIMD types (v4f, v4u, v4i), include simd_types.h.
#include "simd/simd_types.hpp"
#include "Debug.hpp"
#include "boost/static_assert.hpp"
namespace Aya
{
namespace simd
{
//
// Vector Loading
//
// Load 4 elements from memory into a vector
// The address must be aligned to 16 byte boundary
// r = {s[0],s[1],s[2],s[3]}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> load(const ScalarType* s);
// Load 3 elements from memory into a vector
// The address doesn't need to be aligned
// The 4th element doesn't need to be readable.
// Only implemented for floats
// r = {s[0],s[1],s[2],undefined}
AYA_SIMD_INLINE v4f load3(const float* s);
// Load 4 elements from memory into a vector
// The address must be aligned to 4 byte boundary only
// r = {s[0],s[1],s[2],s[3]}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> loadUnaligned(const ScalarType* s);
// Load a single element from memory and duplicate it into all components
// r = {s[0],s[0],s[0],s[0]}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> loadSplat(const ScalarType* s);
// Load a single element from memory and place it in the first component
// r = {s[0],*,*,*}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> loadSingle(const ScalarType* s);
//
// Storing
//
// Store the 4 components of the vector into memory
// The destination address must be 16 bytes aligned
// dst[i] = r[i] for i = 0,1,2,3
template<class ScalarType>
AYA_SIMD_INLINE void store(ScalarType* dst, const v4<ScalarType>& v);
// Store the 4 components of the vector into memory
// The destination address must be 4 bytes aligned only
// dst[i] = r[i] for i = 0,1,2,3
template<class ScalarType>
AYA_SIMD_INLINE void storeUnaligned(ScalarType* dst, const v4<ScalarType>& v);
// Store the single x components of the vector into memory
// The destination address must be 4 bytes aligned only
// *dst = r[0]
template<class ScalarType>
AYA_SIMD_INLINE void storeSingle(ScalarType* dst, const v4<ScalarType>& v);
//
// Vector Forming
//
// Form a vector of floating point 0s
// r = {0.0f,0.0f,0.0f,0.0f}
AYA_SIMD_INLINE v4f zerof();
// Duplicate the scalar into all 4 components
// r = {s,s,s,s}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> splat(ScalarType s);
// Form a vector out of 4 scalars
// r = {x,y,z,w}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> form(ScalarType x, ScalarType y, ScalarType z, ScalarType w);
// Form a vector out of 3 scalars
// r = {x,y,z,undetermined}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> form(ScalarType x, ScalarType y, ScalarType z);
// Form a vector out of 2 scalars
// r = {x,y,undetermined,undetermined}
template<class ScalarType>
AYA_SIMD_INLINE v4<ScalarType> form(ScalarType x, ScalarType y);
//
// Insert / extract
//
// Extract the i'th component of the vector
// This a very slow operation
// r = v[i]
template<class VectorType>
AYA_SIMD_INLINE typename VectorType::elem_t extractSlow(const VectorType& v, uint32_t i);
//
// Casts: cast one type to the other preserving the bit representation
//
// Cast from float to unsigned
// r = 'reinterpret_cast< v4u >'( v )
AYA_SIMD_INLINE v4u reinterpretAsUInt(v4fArg v);
// Cast from float to int
// r = 'reinterpret_cast< v4u >'( v )
AYA_SIMD_INLINE v4i reinterpretAsInt(v4fArg v);
// Cast from unsigned to float
// r = 'reinterpret_cast< v4f >'( v )
AYA_SIMD_INLINE v4f reinterpretAsFloat(v4uArg v);
// Cast from int to float
// r = 'reinterpret_cast< v4f >'( v )
AYA_SIMD_INLINE v4f reinterpretAsFloat(v4iArg v);
// Cast from unsigned to int
// r = 'reinterpret_cast< v4i >'( v )
AYA_SIMD_INLINE v4i reinterpretAsInt(v4uArg v);
// Cast from int to unsigned
// r = 'reinterpret_cast< v4u >'( v )
AYA_SIMD_INLINE v4u reinterpretAsUInt(v4iArg v);
//
// Conversions
//
// Convert float to int rounding to the nearest
// r[i] = int( roundToNearest( v[i] ) )
AYA_SIMD_INLINE v4i convertFloat2IntNearest(v4fArg v);
// Convert float to int truncating towards 0
// r[i] = int( truncate( v[i] ) )
AYA_SIMD_INLINE v4i convertFloat2IntTruncate(v4fArg v);
// Convert int to float
// r[i] = float( v[i] )
AYA_SIMD_INLINE v4f convertIntToFloat(v4iArg v);
//
// Selects
//
// Returns a select mask:
// r[0] = ( a == 1 ) ? 0xffffffff : 0
// r[1] = ( b == 1 ) ? 0xffffffff : 0
// r[2] = ( c == 1 ) ? 0xffffffff : 0
// r[3] = ( d == 1 ) ? 0xffffffff : 0
template<uint32_t a, uint32_t b, uint32_t c, uint32_t d>
AYA_SIMD_INLINE v4u selectMask();
// Select elements between a and b
// r[i] = ( mask[i] == 0 ) ? a[i] : ( mask[i] == 0xffffffff ) ? b[i] : undefined
template<class VectorType>
AYA_SIMD_INLINE VectorType select(const VectorType& a, const VectorType& b, const v4u& mask);
// r[0] = ( a == 1 ) ? u[0] : v[0]
// r[1] = ( b == 1 ) ? u[1] : v[1]
// r[2] = ( c == 1 ) ? u[2] : v[2]
// r[3] = ( d == 1 ) ? u[3] : v[3]
template<uint32_t a, uint32_t b, uint32_t c, uint32_t d, class VectorType>
AYA_SIMD_INLINE VectorType select(const VectorType& u, const VectorType& v);
// Replace an element with an element of the second vector
// r = a
// r[index] = b[index]
template<uint32_t index, class VectorType>
AYA_SIMD_INLINE VectorType replace(const VectorType& a, const VectorType& b);
//
// Permutes
//
// r = { a[0], b[0], a[1], b[1] }
template<class VectorType>
AYA_SIMD_INLINE VectorType zipLow(const VectorType& u, const VectorType& v);
// r = { a[2], b[2], a[3], b[3] }
template<class VectorType>
AYA_SIMD_INLINE VectorType zipHigh(const VectorType& u, const VectorType& v);
// r0 = { a[0], b[0], a[1], b[1] }
// r1 = { a[2], b[2], a[3], b[3] }
template<class VectorType>
AYA_SIMD_INLINE void zip(VectorType& r0, VectorType& r1, const VectorType& u, const VectorType& v);
// r = { v[2], v[3], u[2], u[3] }
template<class VectorType>
AYA_SIMD_INLINE VectorType moveHighLow(const VectorType& u, const VectorType& v);
// r = { u[0], u[1], v[0], v[1] }
template<class VectorType>
AYA_SIMD_INLINE VectorType moveLowHigh(const VectorType& u, const VectorType& v);
// Shuffle two vectors
// r = { u[a], u[b], v[c], v[d] }
template<uint32_t a, uint32_t b, uint32_t c, uint32_t d, class VectorType>
AYA_SIMD_INLINE VectorType shuffle(const VectorType& u, const VectorType& v);
// Duplicate single component of a vector into all components of the result
// r = {v[i],v[i],v[i],v[i]}
template<unsigned i, class VectorType>
AYA_SIMD_INLINE VectorType splat(const VectorType& v);
// Permute the components of the vector with a compile time permutation
// r = {v[a],v[b],v[c],v[d]}
template<uint32_t a, uint32_t b, uint32_t c, uint32_t d, class VectorType>
AYA_SIMD_INLINE VectorType permute(const VectorType& v);
// Rotate the vector left by /offset/ elements
// r = {v[(offset%4)], v[((offset+1)%4)], v[((offset+2)%4)], v[((offset+3)%4)]}
template<uint32_t offset, class VectorType>
AYA_SIMD_INLINE VectorType rotateLeft(VectorType& v);
//
// Compares
//
// Return a mask resulting from the compare
// r[i] = a[i] > b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compareGreater(v4fArg a, v4fArg b);
// r[i] = a[i] > b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator>(v4fArg a, v4fArg b);
// r[i] = a[i] >= b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compareGreaterEqual(v4fArg a, v4fArg b);
// r[i] = a[i] >= b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator>=(v4fArg a, v4fArg b);
// r[i] = a[i] < b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compareLess(v4fArg a, v4fArg b);
// r[i] = a[i] < b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator<(v4fArg a, v4fArg b);
// r[i] = a[i] <= b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compareLessEqual(v4fArg a, v4fArg b);
// r[i] = a[i] <= b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator<=(v4fArg a, v4fArg b);
// r[i] = a[i] == b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compare(v4fArg a, v4fArg b);
// r[i] = a[i] == b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator==(v4fArg a, v4fArg b);
// r[i] = a[i] != b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator!=(v4fArg a, v4fArg b);
// r[i] = a[i] == b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compare(v4iArg a, v4iArg b);
// r[i] = a[i] == b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator==(v4iArg a, v4iArg b);
// r[i] = a[i] != b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator!=(v4iArg a, v4iArg b);
// r[i] = a[i] == b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u compare(v4uArg a, v4uArg b);
// r[i] = a[i] == b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator==(v4uArg a, v4uArg b);
// r[i] = a[i] != b[i] ? 0xffffffff : 0
AYA_SIMD_INLINE v4u operator!=(v4uArg a, v4uArg b);
//
// Set element to 0
//
// r = v; r[a] = 0;
template<uint32_t a>
AYA_SIMD_INLINE v4f replaceWithZero(v4fArg v);
// r = v; r[a] = 0;
template<uint32_t a>
AYA_SIMD_INLINE v4i replaceWithZero(v4iArg v);
// r = v; r[a] = 0;
template<uint32_t a>
AYA_SIMD_INLINE v4u replaceWithZero(v4uArg v);
//
// Float Arithmetics
//
// r[i] = a[i] + b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f operator+(v4fArg a, v4fArg b);
// a[i] = a[i] + b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f& operator+=(v4f& a, v4fArg b);
// r[i] = a[i] - b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f operator-(v4fArg a, v4fArg b);
// a[i] = a[i] - b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f& operator-=(v4f& a, v4fArg b);
// r[i] = a[i] * b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f operator*(v4fArg a, v4fArg b);
// a[i] = a[i] * b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f& operator*=(v4f& a, v4fArg b);
// r[i] = a[i] + b[i] * c[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f mulAdd(v4fArg a, v4fArg b, v4fArg c);
// r[i] = -a[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f operator-(v4fArg a);
// r[i] = a[i] / b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f operator/(v4fArg a, v4fArg b);
// a[i] = a[i] / b[i], i = 0,1,2,3
AYA_SIMD_INLINE v4f& operator/=(v4f& a, v4fArg b);
// r[i] = fabsf( a[i] ), i = 0,1,2,3
AYA_SIMD_INLINE v4f abs(v4fArg a);
// r[i] = max(a[i], b[i])
AYA_SIMD_INLINE v4f max(v4fArg a, v4fArg b);
// r[i] = min(a[i], b[i])
AYA_SIMD_INLINE v4f min(v4fArg a, v4fArg b);
//
// Estimates
//
// Estimate of the inverse
// About 12 bits of precision
AYA_SIMD_INLINE v4f inverseEstimate0(v4fArg a);
// This is like the normal version except for small inputs ( abs(a) < smallestInvertible() ) and large inputs
// ( abs(a) > largestInvertible() ) are not handled and will return an undefined number
AYA_SIMD_INLINE v4f inverseEstimate0Fast(v4fArg a);
// More precise version of the inverse estimate
AYA_SIMD_INLINE v4f inverseEstimate1(v4fArg a);
// This is like the normal version except for small inputs ( abs(a) < smallestInvertible() ) and large inputs
// ( abs(a) > largestInvertible() ) are not handled and will return an undefined number
AYA_SIMD_INLINE v4f inverseEstimate1Fast(v4fArg a);
// Returns the smallest floating point number r such that inverseEstimate1(r) < inf
// Before inverting, you can create a select mask: sel = r < smallestInvertible()
AYA_SIMD_INLINE v4f smallestInvertible();
// Returns the largest floating point number r such that inverseEstimate1(r) > 0.0f
// For inputs that are larger than this, the inverse will return 0.0f
AYA_SIMD_INLINE v4f largestInvertible();
// Return an estimate of the inverse square root.
// About 12 bits of precision
AYA_SIMD_INLINE v4f inverseSqrtEstimate0(v4fArg a);
// This is like the normal version except for small inputs ( abs(a) < smallestSqrtInvertible() ) and large inputs
// ( abs(a) > largestSqrtInvertible() ) are not handled and will return an undefined number
AYA_SIMD_INLINE v4f inverseSqrtEstimate0Fast(v4fArg a);
// Return an estimate of the inverse square root.
// More than 12 bits of precision
AYA_SIMD_INLINE v4f inverseSqrtEstimate1(v4fArg a);
// This is like the normal version except for small inputs ( abs(a) < smallestSqrtInvertible() ) and large inputs
// ( abs(a) > largestSqrtInvertible() ) are not handled and will return an undefined number
AYA_SIMD_INLINE v4f inverseSqrtEstimate1Fast(v4fArg a);
AYA_SIMD_INLINE v4f smallestSqrtInvertible();
AYA_SIMD_INLINE v4f largestSqrtInvertible();
//
// Inter-element arithmetic ops
//
// r[i] = a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3], i = 0,1,2,3
AYA_SIMD_INLINE v4f dotProduct(v4fArg a, v4fArg b);
// r[i] = a[0]*b[0] + a[1]*b[1] + a[2]*b[2], i = 0,1,2,3
AYA_SIMD_INLINE v4f dotProduct3(v4fArg a, v4fArg b);
// r[i] = a[0] + a[1] + a[2] + a[3], i = 0,1,2,3
AYA_SIMD_INLINE v4f sumAcross(v4fArg a);
// r[0] = a[0] + a[1]
// r[1] = b[0] + b[1]
// r[2] = undefined
// r[3] = undefined
AYA_SIMD_INLINE v4f sumAcross2(v4fArg a, v4fArg b);
// r[0] = a[0] + a[1] + a[2]
// r[1] = b[0] + b[1] + b[2]
// r[2] = undefined
// r[3] = undefined
AYA_SIMD_INLINE v4f sumAcross3(v4fArg a, v4fArg b);
// r[0] = a[0] + a[1] + a[2] + a[3]
// r[1] = b[0] + b[1] + b[2] + b[3]
// r[2] = undefined
// r[3] = undefined
AYA_SIMD_INLINE v4f sumAcross4(v4fArg a, v4fArg b);
// r[0] = a[0] + a[1]
// r[1] = b[0] + b[1]
// r[2] = c[0] + c[1]
// r[3] = undefined
AYA_SIMD_INLINE v4f sumAcross2(v4fArg a, v4fArg b, v4fArg c);
// r[0] = a[0] + a[1] + a[2]
// r[1] = b[0] + b[1] + b[2]
// r[3] = c[0] + c[1] + c[2]
// r[3] = undefined
AYA_SIMD_INLINE v4f sumAcross3(v4fArg a, v4fArg b, v4fArg c);
// r[0] = a[0] + a[1] + a[2] + a[3]
// r[1] = b[0] + b[1] + b[2] + b[3]
// r[3] = c[0] + c[1] + c[2] + c[3]
// r[3] = undefined
AYA_SIMD_INLINE v4f sumAcross4(v4fArg a, v4fArg b, v4fArg c);
// r[0] = a[0] + a[1]
// r[1] = b[0] + b[1]
// r[3] = c[0] + c[1]
// r[3] = d[0] + d[1]
AYA_SIMD_INLINE v4f sumAcross2(v4fArg a, v4fArg b, v4fArg c, v4fArg d);
// r[0] = a[0] + a[1] + a[2]
// r[1] = b[0] + b[1] + b[2]
// r[3] = c[0] + c[1] + c[2]
// r[3] = d[0] + d[1] + d[2]
AYA_SIMD_INLINE v4f sumAcross3(v4fArg a, v4fArg b, v4fArg c, v4fArg d);
// r[0] = a[0] + a[1] + a[2] + a[3]
// r[1] = b[0] + b[1] + b[2] + b[3]
// r[3] = c[0] + c[1] + c[2] + c[3]
// r[3] = d[0] + d[1] + d[2] + d[3]
AYA_SIMD_INLINE v4f sumAcross4(v4fArg a, v4fArg b, v4fArg c, v4fArg d);
//
// Packing / unpacking
//
// Pack 4 vector-3's into 3 vector-4's
// p[0] = {a[0],a[1],a[2],b[0]}
// p[1] = {b[1],b[2],c[0],c[1]}
// p[2] = {c[2],d[0],d[1],d[2]}
template<class T>
AYA_SIMD_INLINE void pack3(typename T::pod_t* __restrict p, const T& a, const T& b, const T& c, const T& d);
// Pack 3 vector-4's into 4 vector-3's
// a = {p[0][0],p[0][1],p[0][2],*}
// b = {p[0][3],p[1][0],p[1][1],*}
// c = {p[1][2],p[1][3],p[2][0],*}
// d = {p[2][1],p[2][2],p[2][3],*}
template<class T>
AYA_SIMD_INLINE void unpack3(T& a, T& b, T& c, T& d, const typename T::pod_t* p);
//
// Matrix permutations
//
// Gather the X components of a,b,c,d:
// r = {a[0],b[0],c[0],d[0]}
template<class T>
AYA_SIMD_INLINE T gatherX(const T& a, const T& b, const T& c, const T& d);
// Gather the X components of a,b,c:
// r = {a[0],b[0],c[0],undetermined}
template<class T>
AYA_SIMD_INLINE T gatherX(const T& a, const T& b, const T& c);
// Gather the X components of a,b:
// r = {a[0],b[0],undetermined,undetermined}
template<class T>
AYA_SIMD_INLINE T gatherX(const T& a, const T& b);
// Transpose a 4x4 matrix
// a = {x[0],y[0],z[0],w[0]}
// b = {x[1],y[1],z[1],w[1]}
// c = {x[2],y[2],z[2],w[2]}
// d = {x[3],y[3],z[3],w[3]}
template<class T>
AYA_SIMD_INLINE void transpose(T& a, T& b, T& c, T& d, const T& x, const T& y, const T& z, const T& w);
// Transpose a 3x4 matrix
// a = {x[0],y[0],z[0],undetermined}
// b = {x[1],y[1],z[1],undetermined}
// c = {x[2],y[2],z[2],undetermined}
// d = {x[3],y[3],z[3],undetermined}
template<class T>
AYA_SIMD_INLINE void transpose3x4(T& a, T& b, T& c, T& d, const T& x, const T& y, const T& z);
// Transpose a 4x3 matrix
// x = {a[0],b[0],c[0],d[0]}
// y = {a[1],b[1],c[1],d[1]}
// z = {a[2],b[2],c[2],d[2]}
template<class T>
AYA_SIMD_INLINE void transpose4x3(T& x, T& y, T& z, const T& a, const T& b, const T& c, const T& d);
// Transpose a 2x4 matrix
// a = {x[0],y[0],undetermined,undetermined}
// b = {x[1],y[1],undetermined,undetermined}
// c = {x[2],y[2],undetermined,undetermined}
// d = {x[3],y[3],undetermined,undetermined}
template<class T>
AYA_SIMD_INLINE void transpose2x4(T& a, T& b, T& c, T& d, const T& x, const T& y);
// Transpose a 2x4 matrix
// x = {a[0],b[0],c[0],d[0]}
// y = {a[1],b[1],c[1],d[1]}
template<class T>
AYA_SIMD_INLINE void transpose4x2(T& x, T& y, const T& a, const T& b, const T& c, const T& d);
} // namespace simd
} // namespace Aya
#if defined(AYA_SIMD_USE_SSE)
#include "simd/simd_sse.inl"
#elif defined(AYA_SIMD_USE_NEON)
#include "simd/simd_neon.inl"
#endif
#include "simd/simd_common.inl"

View File

@@ -0,0 +1,154 @@
#pragma once
// SIMD common platform independent implementation
#include "simd/simd.hpp"
namespace Aya
{
namespace simd
{
namespace details
{
AYA_SIMD_INLINE v4f inverseEstimate0Precision()
{
return splat(3e-04f);
}
AYA_SIMD_INLINE v4f inverseEstimate1Precision()
{
return splat(2e-07f);
}
AYA_SIMD_INLINE v4f inverseSqrtEstimate0Precision()
{
return splat(3.3e-05f);
}
AYA_SIMD_INLINE v4f inverseSqrtEstimate1Precision()
{
return splat(3e-07f);
}
} // namespace details
AYA_SIMD_INLINE v4f sumAcross2(v4fArg a, v4fArg b, v4fArg c)
{
return sumAcross2(a, b, c, c);
}
AYA_SIMD_INLINE v4f sumAcross3(v4fArg a, v4fArg b, v4fArg c)
{
return sumAcross3(a, b, c, c);
}
AYA_SIMD_INLINE v4f sumAcross4(v4fArg a, v4fArg b, v4fArg c)
{
return sumAcross4(a, b, c, c);
}
AYA_SIMD_INLINE v4f sumAcross2(v4fArg a, v4fArg b, v4fArg c, v4fArg d)
{
v4f a0c0a1c1 = zipLow(a, c);
v4f b0d0b1d1 = zipLow(b, d);
v4f a0b0c0d0, a1b1c1d1;
zip(a0b0c0d0, a1b1c1d1, a0c0a1c1, b0d0b1d1);
v4f sum = a0b0c0d0 + a1b1c1d1;
return sum;
}
AYA_SIMD_INLINE v4f sumAcross3(v4fArg a, v4fArg b, v4fArg c, v4fArg d)
{
v4f a0c0a1c1, a2c2xxxx;
zip(a0c0a1c1, a2c2xxxx, a, c);
v4f b0d0b1d1, b2d2xxxx;
zip(b0d0b1d1, b2d2xxxx, b, d);
v4f a0b0c0d0, a1b1c1d1;
zip(a0b0c0d0, a1b1c1d1, a0c0a1c1, b0d0b1d1);
v4f sum = a0b0c0d0 + a1b1c1d1;
v4f a2b2c2d2 = zipLow(a2c2xxxx, b2d2xxxx);
sum = sum + a2b2c2d2;
return sum;
}
AYA_SIMD_INLINE v4f sumAcross4(v4fArg a, v4fArg b, v4fArg c, v4fArg d)
{
v4f a0c0a1c1, a2c2a3c3;
zip(a0c0a1c1, a2c2a3c3, a, c);
v4f b0d0b1d1, b2d2b3d3;
zip(b0d0b1d1, b2d2b3d3, b, d);
v4f a0b0c0d0, a1b1c1d1;
zip(a0b0c0d0, a1b1c1d1, a0c0a1c1, b0d0b1d1);
v4f sum = a0b0c0d0 + a1b1c1d1;
v4f a2b2c2d2, a3b3c3d3;
zip(a2b2c2d2, a3b3c3d3, a2c2a3c3, b2d2b3d3);
sum = sum + (a2b2c2d2 + a3b3c3d3);
return sum;
}
template<class T>
AYA_SIMD_INLINE void transpose(T& a, T& b, T& c, T& d, const T& x, const T& y, const T& z, const T& w)
{
T x0z0x1z1, x2z2x3z3;
zip(x0z0x1z1, x2z2x3z3, x, z);
T y0w0y1w1, y2w2y3w3;
zip(y0w0y1w1, y2w2y3w3, y, w);
zip(a, b, x0z0x1z1, y0w0y1w1);
zip(c, d, x2z2x3z3, y2w2y3w3);
}
template<class T>
AYA_SIMD_INLINE void transpose4x3(T& x, T& y, T& z, const T& a, const T& b, const T& c, const T& d)
{
T dummy;
transpose(x, y, z, dummy, a, b, c, d);
}
template<class T>
AYA_SIMD_INLINE void transpose3x4(T& a, T& b, T& c, T& d, const T& x, const T& y, const T& z)
{
transpose(a, b, c, d, x, y, z, z);
}
template<class T>
AYA_SIMD_INLINE void transpose4x2(T& x, T& y, const T& a, const T& b, const T& c, const T& d)
{
T t0 = zipLow(a, c);
T t1 = zipLow(b, d);
zip(x, y, t0, t1);
}
template<class T>
AYA_SIMD_INLINE void transpose2x4(T& a, T& b, T& c, T& d, const T& x, const T& y)
{
zip(a, c, x, y);
b = moveHighLow(a, a);
d = moveHighLow(c, c);
}
template<class T>
AYA_SIMD_INLINE T gatherX(const T& a, const T& b, const T& c, const T& d)
{
T t0 = zipLow(a, c);
T t1 = zipLow(b, d);
return zipLow(t0, t1);
}
template<class T>
AYA_SIMD_INLINE T gatherX(const T& a, const T& b, const T& c)
{
T t0 = zipLow(a, c);
return zipLow(t0, b);
}
template<class T>
AYA_SIMD_INLINE T gatherX(const T& a, const T& b)
{
return zipLow(a, b);
}
} // namespace simd
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
#pragma once
#include <boost/cstdint.hpp>
#include <boost/static_assert.hpp>
#include <stdint.h>
#if defined(__i386__) || defined(_M_IX86) && !defined(_M_ARM64) && !defined(__aarch64__) // __i386__ on clang, _M_IX86 on MVC
#define AYA_SIMD_X86
#endif
#if defined(__x86_64__) || defined(_M_X64) && !defined(_M_ARM64) && !defined(__aarch64__) // __x86_64__ on clang, _M_X64 on MVC
#define AYA_SIMD_X64
#endif
#if defined(__ARM_NEON) || defined(__arm__) || defined(_M_ARM) || \
defined(_M_ARM64) // __ARM_NEON on apple clang for iOS, __arm__ on GCC, _M_ARM in MVC for Windows Phone
#define AYA_SIMD_ARM
#endif
#if defined(AYA_SIMD_X86) || defined(AYA_SIMD_X64) && !defined(_M_ARM64) && !defined(__aarch64__)
#define AYA_SIMD_USE_SSE
#elif defined(AYA_SIMD_ARM) || defined(_M_ARM64)
#define AYA_SIMD_USE_NEON
#endif
#ifdef AYA_SIMD_USE_SSE
#include <xmmintrin.h>
#include <emmintrin.h>
#include <mmintrin.h>
#endif
#ifdef AYA_SIMD_USE_NEON
#include <arm_neon.h>
#endif
#define AYA_SIMD_ALIGN_ASSERT(p, a) AYAASSERT_VERY_FAST(((uint64_t)(p) & (a - 1)) == 0)
#ifdef _MSC_VER
#define AYA_SIMD_INLINE __forceinline
#elif defined(__clang__) || defined(__GNUC__)
#define AYA_SIMD_INLINE inline __attribute__((always_inline))
#else
#define AYA_SIMD_INLINE inline
#endif
namespace Aya
{
namespace simd
{
namespace details
{
#if defined(AYA_SIMD_USE_SSE)
typedef __m128 vec4f_t;
typedef __m128i vec4i_t;
typedef __m128i vec4u_t;
#elif defined(AYA_SIMD_USE_NEON)
typedef float32x4_t vec4f_t;
typedef int32x4_t vec4i_t;
typedef uint32x4_t vec4u_t;
#endif
template<class ScalarType>
struct VectorTypeSelect;
template<>
struct VectorTypeSelect<float>
{
typedef vec4f_t type;
};
template<>
struct VectorTypeSelect<int32_t>
{
typedef vec4i_t type;
};
template<>
struct VectorTypeSelect<uint32_t>
{
typedef vec4u_t type;
};
} // namespace details
} // namespace simd
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
#pragma once
// Include this file if you only need the SIMD types but not the functionality (which could be a large include)
#include <boost/cstdint.hpp>
#include <boost/static_assert.hpp>
#include <stdint.h>
#include "simd/simd_platform.hpp"
namespace Aya
{
namespace simd
{
template<class ElemType>
class v4
{
public:
typedef typename details::VectorTypeSelect<ElemType>::type fun_t;
typedef ElemType elem_t;
typedef v4<elem_t> v4_t;
typedef const v4_t& arg_t;
typedef fun_t pod_t;
AYA_SIMD_INLINE v4() {}
AYA_SIMD_INLINE v4(const v4& u);
AYA_SIMD_INLINE v4(const pod_t& u);
AYA_SIMD_INLINE void operator=(const v4& u);
AYA_SIMD_INLINE void operator=(const pod_t& u);
AYA_SIMD_INLINE operator pod_t() const;
fun_t v;
};
// The SIMD types
typedef v4<float> v4f;
typedef v4<int32_t> v4i;
typedef v4<uint32_t> v4u;
// The SIMD pod types
typedef v4f::pod_t v4f_pod;
typedef v4i::pod_t v4i_pod;
typedef v4u::pod_t v4u_pod;
// Shortcuts for const refs
typedef const v4f& v4fArg;
typedef const v4i& v4iArg;
typedef const v4u& v4uArg;
} // namespace simd
} // namespace Aya
#include "simd/simd_types.inl"

View File

@@ -0,0 +1,45 @@
#pragma once
// Do not include this file directly
#include "simd/simd_types.hpp"
namespace Aya
{
namespace simd
{
template<class ElemType>
AYA_SIMD_INLINE v4<ElemType>::v4(const v4& u)
: v(u.v)
{
}
template<class ElemType>
AYA_SIMD_INLINE v4<ElemType>::v4(const pod_t& u)
: v(u)
{
}
template<class ElemType>
AYA_SIMD_INLINE void v4<ElemType>::operator=(const v4& u)
{
v = u.v;
}
template<class ElemType>
AYA_SIMD_INLINE void v4<ElemType>::operator=(const pod_t& u)
{
v = u;
}
template<class ElemType>
AYA_SIMD_INLINE v4<ElemType>::operator pod_t() const
{
return v;
}
} // namespace simd
} // namespace Aya

View File

@@ -0,0 +1,61 @@
#pragma once
#include "Debug.hpp"
#include <string>
namespace Aya
{
class StringReadBuffer
{
std::string::const_iterator cur;
const std::string& buffer;
public:
StringReadBuffer(const std::string& str)
: buffer(str)
{
cur = buffer.begin();
}
StringReadBuffer& operator>>(unsigned char& value)
{
AYAASSERT(cur != buffer.end());
if (cur == buffer.end())
{
throw Aya::runtime_error("Reading past end of string");
}
else
{
value = *cur++;
}
return *this;
}
bool eof()
{
return cur == buffer.end();
}
};
class StringWriteBuffer
{
std::string buffer;
public:
StringWriteBuffer()
: buffer(std::string())
{
}
StringWriteBuffer& operator<<(unsigned char value)
{
buffer.push_back(value);
return *this;
}
const std::string& str()
{
return buffer;
}
};
} // namespace Aya

View File

@@ -0,0 +1,572 @@
#pragma once
#include <queue>
#include "boost.hpp"
#include "Thread.hpp"
#include "time.hpp"
#include "atomic.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/noncopyable.hpp"
#include "AyaFormat.hpp"
#include "FastLog.hpp"
#include "AyaPlatform.hpp"
#include "boost/thread/mutex.hpp"
using boost::shared_ptr;
#ifdef _WIN32
#include <Windows.h>
#endif
LOGGROUP(MutexLifetime);
namespace Aya
{
// A lightweight mutex that uses CRITICAL_SECTION under Windows.
// This mutex is non-recursive.
class mutex
{
#ifdef _WIN32
CRITICAL_SECTION cs;
public:
mutex()
{
::InitializeCriticalSection(&cs);
FASTLOG1(FLog::MutexLifetime, "Aya::mutext init m = 0x%x", this);
}
~mutex()
{
::DeleteCriticalSection(&cs);
FASTLOG1(FLog::MutexLifetime, "Aya::mutext destroy m = 0x%x", this);
}
class scoped_lock : boost::noncopyable
{
public:
scoped_lock(mutex& m)
: m(m)
{
::EnterCriticalSection(&m.cs);
}
~scoped_lock()
{
::LeaveCriticalSection(&m.cs);
}
private:
mutex& m;
};
#else
pthread_mutex_t sl;
public:
mutex()
{
if (pthread_mutex_init(&sl, NULL) != 0)
throw std::runtime_error("failed in mutex to initialize pthread_mutex_init.");
}
~mutex()
{
if (pthread_mutex_destroy(&sl) != 0)
{
// printf("Error at pthread_spin_destroy()");
}
}
class scoped_lock : boost::noncopyable
{
public:
scoped_lock(mutex& m0)
: m(m0)
{
int rc = pthread_mutex_lock(&m.sl);
if (rc != 0)
{
// fprintf(stderr,"Test FAILED: child failed to get spin lock,error code:%d\n" , rc);
}
//::EnterCriticalSection(&m.sl);
}
~scoped_lock()
{
if (pthread_mutex_unlock(&m.sl) != 0)
{
// fprintf(stderr,"child: Error at pthread_spin_unlock()\n");
}
//::LeaveCriticalSection(&m.sl);
}
private:
mutex& m;
};
#endif
};
// calls AYACRASH() on contention.
class concurrency_catcher : boost::noncopyable
{
Aya::atomic<int> value;
static const long unlocked = 0;
static const long locked = 1;
public:
concurrency_catcher()
: value(unlocked)
{
}
class scoped_lock : boost::noncopyable
{
public:
scoped_lock(concurrency_catcher& m);
~scoped_lock();
private:
concurrency_catcher& m;
};
};
// calls AYACRASH() on contention.
struct reentrant_concurrency_catcher : boost::noncopyable
{
Aya::atomic<int> value;
volatile unsigned long threadId;
static const long unlocked = 0;
static const long locked = 1;
static const unsigned long noThreadId;
public:
reentrant_concurrency_catcher()
: value(unlocked)
, threadId(noThreadId)
{
}
class scoped_lock : boost::noncopyable
{
public:
scoped_lock(reentrant_concurrency_catcher& m);
~scoped_lock();
private:
bool isChild;
reentrant_concurrency_catcher& m;
};
};
class readwrite_concurrency_catcher : boost::noncopyable
{
friend class scoped_write_request;
friend class scoped_read_request;
Aya::atomic<int> write_requested;
Aya::atomic<int> read_requested;
static const long unlocked = 0;
static const long locked = 1;
public:
readwrite_concurrency_catcher()
: write_requested(unlocked)
, read_requested(0) {};
class scoped_write_request
{
readwrite_concurrency_catcher& m;
public:
// Place this code around tasks that write to a DataModel
scoped_write_request(readwrite_concurrency_catcher& mt);
~scoped_write_request();
};
class scoped_read_request
{
readwrite_concurrency_catcher& m;
public:
// Place this code around tasks that write to a DataModel
scoped_read_request(readwrite_concurrency_catcher& m);
~scoped_read_request();
};
};
} // namespace Aya
namespace Aya
{
class spin_mutex
{
Aya::atomic<int> sl;
public:
spin_mutex()
{
// init
}
~spin_mutex()
{
// destroy
}
bool try_lock()
{
return sl.compare_and_swap(1, 0) == 0;
}
void lock()
{
while (sl.compare_and_swap(1, 0) != 0)
{
}
}
void unlock()
{
sl.compare_and_swap(0, 1);
}
class scoped_lock : boost::noncopyable
{
public:
scoped_lock(spin_mutex& m0)
: m(m0)
{
for (;;)
{
if (m.try_lock())
break;
}
}
~scoped_lock()
{
m.unlock();
}
private:
spin_mutex& m;
};
};
// Use this queue when you want fast performance, low
// resource usage, and the queue is not very busy.
// For very busy queues, use tbb::concurrent_queue (correction, we dont have tbb anymore).
template<typename T>
class safe_queue : boost::noncopyable
{
protected:
std::queue<T> queue;
// TODO: spin_mutex is possibly a bad choice for expensive T types
typedef spin_mutex mutex;
mutex m;
public:
void clear()
{
mutex::scoped_lock lock(m);
while (!queue.empty())
queue.pop();
}
void push(const T& value)
{
mutex::scoped_lock lock(m);
queue.push(value);
}
bool pop_if_present(T& value)
{
mutex::scoped_lock lock(m);
if (!queue.empty())
{
value = queue.front();
queue.pop();
return true;
}
else
return false;
}
bool pop_if_present()
{
mutex::scoped_lock lock(m);
if (!queue.empty())
{
queue.pop();
return true;
}
else
return false;
}
// WARNING: Peeking has side effects, if T has copy constructors and destructors
bool peek_if_present(T& value)
{
mutex::scoped_lock lock(m);
if (!queue.empty())
{
value = queue.front();
return true;
}
else
return false;
}
// Lock and spin-free calls:
inline size_t size() const
{
return queue.size();
}
inline bool empty() const
{
return queue.empty();
}
};
namespace implementation
{
template<typename T>
struct timestamped_safe_queue_item
{
T value;
Aya::Time timestamp;
timestamped_safe_queue_item() {}
timestamped_safe_queue_item(const T& value)
: timestamp(Aya::Time::now<Aya::Time::Fast>())
, value(value)
{
}
};
} // namespace implementation
template<typename T>
class timestamped_safe_queue : protected safe_queue<implementation::timestamped_safe_queue_item<T>>
{
typedef safe_queue<implementation::timestamped_safe_queue_item<T>> Super;
double headTimestamp;
#if defined(__GNUC__) || defined(__clang__)
// GCC/Clang won't inherit the mutex type defined in Super. Therefore we redeclare it here. Yuck!
typedef spin_mutex mutex;
#endif
public:
void clear()
{
headTimestamp = 0.f;
Super::clear();
}
void push(const T& value)
{
implementation::timestamped_safe_queue_item<T> item(value);
Super::push(item);
headTimestamp = item.timestamp.timestampSeconds();
}
bool pop_if_present(T& value)
{
mutex::scoped_lock lock(this->m);
if (!this->queue.empty())
{
value = this->queue.front().value;
this->queue.pop();
if (!this->queue.empty())
{
headTimestamp = this->queue.front().timestamp.timestampSeconds();
}
else
{
headTimestamp = 0.f;
}
return true;
}
else
return false;
}
// pops the head item if it has been waiting at least waitTime
bool pop_if_waited(Aya::Time::Interval waitTime, T& value)
{
mutex::scoped_lock lock(this->m);
if (this->queue.empty())
return false;
if (Aya::Time::now<Aya::Time::Fast>() < this->queue.front().timestamp + waitTime)
return false;
value = this->queue.front().value;
this->queue.pop();
if (!this->queue.empty())
{
headTimestamp = this->queue.front().timestamp.timestampSeconds();
}
else
{
headTimestamp = 0.f;
}
return true;
}
// Returns the time that the head item has been waiting or zero.
double head_waittime_sec(const Aya::Time& timeNow) const
{
if (headTimestamp > 0.f)
{
return timeNow.timestampSeconds() - headTimestamp;
}
else
return 0.f;
}
inline size_t size() const
{
return this->queue.size();
}
inline bool empty() const
{
return this->queue.empty();
}
};
template<typename T>
class safe_heap : boost::noncopyable
{
std::vector<T> vector;
// TODO: spin_mutex is possibly a bad choice for expensive T types
typedef spin_mutex mutex;
mutex m;
public:
void clear()
{
mutex::scoped_lock lock(m);
vector.clear();
}
void push_heap(const T& value)
{
mutex::scoped_lock lock(m);
vector.push_back(value);
std::push_heap(vector.begin(), vector.end());
}
bool pop_heap_if_present(T& value)
{
mutex::scoped_lock lock(m);
if (!vector.empty())
{
std::pop_heap(vector.begin(), vector.end());
value = vector.back();
vector.pop_back();
return true;
}
else
return false;
}
bool pop_heap_if_present()
{
mutex::scoped_lock lock(m);
if (!vector.empty())
{
std::pop_heap(vector.begin(), vector.end());
vector.pop_back();
return true;
}
else
return false;
}
// Lock and spin-free calls:
inline size_t size() const
{
return vector.size();
}
inline bool empty() const
{
return vector.empty();
}
};
#define SAFE_STATIC(TYPE, NAME) \
static TYPE* safe_static_do_get_##NAME() \
{ \
static TYPE value; \
return &value; \
} \
static void safe_static_init_##NAME() \
{ \
safe_static_do_get_##NAME(); \
} \
static TYPE& NAME() \
{ \
static boost::once_flag once_init_##NAME = BOOST_ONCE_INIT; \
boost::call_once(safe_static_init_##NAME, once_init_##NAME); \
return *safe_static_do_get_##NAME(); \
}
#define SAFE_HEAP_STATIC(TYPE, NAME) \
static TYPE* safe_static_do_get_##NAME() \
{ \
static TYPE* value = new TYPE; \
return value; \
} \
static void safe_static_init_##NAME() \
{ \
safe_static_do_get_##NAME(); \
} \
static TYPE& NAME() \
{ \
static boost::once_flag once_init_##NAME = BOOST_ONCE_INIT; \
boost::call_once(safe_static_init_##NAME, once_init_##NAME); \
return *safe_static_do_get_##NAME(); \
}
// A wrapper around thread_specific_ptr that lets you
// have a thread-specific reference to an object
template<typename T>
class thread_specific_reference
{
typedef T* TPTR;
boost::thread_specific_ptr<TPTR> ptr;
public:
T* get()
{
TPTR* p = ptr.get();
if (p)
return *p;
else
return 0;
}
void reset(T* value)
{
TPTR* p = new TPTR(value);
ptr.reset(p);
}
};
// A wrapper around thread_specific_ptr that lets you
// have a thread-specific shared_ptr to an object
template<typename T>
class thread_specific_shared_ptr : boost::noncopyable
{
typedef shared_ptr<T> TPTR;
boost::thread_specific_ptr<TPTR> ptr;
public:
operator shared_ptr<T>() const
{
TPTR* p = ptr.get();
if (p)
return *p;
else
return shared_ptr<T>();
}
void reset(shared_ptr<T> value)
{
TPTR* p = new TPTR(value);
ptr.reset(p);
}
};
} // namespace Aya

171
engine/core/src/time.cpp Normal file
View File

@@ -0,0 +1,171 @@
// This prevent inclusion of winsock.h in Windows.h, which prevents windows redifinition errors
// Look at winsock2.h for details, winsock2.h is #included from boost.hpp & other places.
#ifdef _WIN32
#define _WINSOCKAPI_
#endif
#if defined(__linux) || defined(__APPLE__)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#endif
#include "time.hpp"
#include "Debug.hpp"
#include "atomic.hpp"
#include <stdexcept>
#include "FastLog.hpp"
#include <algorithm>
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#ifdef __ANDROID__
#include <unistd.h>
#endif
#if defined(_WIN32) && !defined(AYA_PLATFORM_DURANGO)
#include <windows.h>
#include <timeapi.h>
#endif
FASTINTVARIABLE(SpeedTestPeriodMillis, 1000)
FASTINTVARIABLE(MaxSpeedDeltaMillis, 300)
FASTINTVARIABLE(SpeedCountCap, 5)
namespace Aya
{
long long Time::getTickCount()
{
#if defined(_WIN32)
LARGE_INTEGER ticks;
int rval = QueryPerformanceCounter(&ticks);
return ticks.QuadPart;
#elif defined(__APPLE__)
uint64_t ticks = mach_absolute_time();
return ticks;
#elif defined(__ANDROID__) || defined(__linux)
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec * 1e9 + now.tv_nsec;
#endif
}
#if defined(_WIN32)
static double getPerformanceFrequency()
{
static double frequency = 0.0;
if (frequency == 0.0)
{
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
frequency = static_cast<double>(freq.QuadPart);
}
return frequency;
}
#endif
long long Time::getStart()
{
// not worried about potential multi-threaded double-init.
// assumptions: underlying type is long long.
static const long long start = getTickCount();
return start;
}
Time::SampleMethod Time::preciseOverride = Time::Precise;
template<>
Time Time::now<Time::Precise>()
{
Time result;
#if defined(_WIN32)
result.sec = static_cast<double>(getTickCount() - getStart()) / getPerformanceFrequency();
#else
result.sec = (getTickCount() - getStart()) * 1e-9;
#endif
return result;
}
template<>
Time Time::now<Time::Multimedia>()
{
#if defined(_WIN32) && !defined(AYA_PLATFORM_DURANGO)
return Time(timeGetTime() / 1000.0);
#else
// TODO: Is this fast enough on Mac?
return now<Time::Precise>();
#endif
}
template<>
Time Time::now<Time::Fast>()
{
return now<Time::Precise>();
}
Time Time::nowFast()
{
return now<Time::Fast>();
}
double Time::nowFastSec()
{
return Time::nowFast().timestampSeconds();
}
template<>
Time Time::now<Time::Benchmark>()
{
if (preciseOverride <= Benchmark)
return now<Precise>();
else
return now<Fast>();
}
Time Time::now(SampleMethod sampleMethod)
{
switch (sampleMethod)
{
default:
case Fast:
return now<Fast>();
case Precise:
return now<Precise>();
case Benchmark:
return now<Benchmark>();
case Multimedia:
return now<Multimedia>();
}
}
Time::Interval operator-(const Time& t1, const Time& t0)
{
const double seconds = t1.sec - t0.sec;
return Time::Interval(seconds);
}
void Time::Interval::sleep()
{
#ifdef _WIN32
// Translate to milliseconds
Sleep((int)(sec * 1e3));
#else
// Translate to microseconds
usleep((int)(sec * 1e6));
#endif
}
} // namespace Aya

Some files were not shown because too many files have changed in this diff Show More