mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-22 12:55:34 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
417
Engine/source/platform/async/asyncBufferedStream.h
Normal file
417
Engine/source/platform/async/asyncBufferedStream.h
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCBUFFEREDSTREAM_H_
|
||||
#define _ASYNCBUFFEREDSTREAM_H_
|
||||
|
||||
#ifndef _TSTREAM_H_
|
||||
#include "core/stream/tStream.h"
|
||||
#endif
|
||||
#ifndef _THREADPOOL_H_
|
||||
#include "platform/threads/threadPool.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEDEQUE_H_
|
||||
#include "platform/threads/threadSafeDeque.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Disable nonsense warning about unreferenced
|
||||
// local function on VC.
|
||||
#ifdef TORQUE_COMPILER_VISUALC
|
||||
#pragma warning( disable: 4505 )
|
||||
#endif
|
||||
|
||||
|
||||
template< typename T, class Stream >
|
||||
class AsyncBufferedReadItem;
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// AsyncBufferedInputStream.
|
||||
//=============================================================================
|
||||
|
||||
|
||||
///
|
||||
template< typename T, class Stream = IInputStream< T >* >
|
||||
class AsyncBufferedInputStream : public IInputStreamFilter< T, Stream >,
|
||||
public ThreadSafeRefCount< AsyncBufferedInputStream< T, Stream > >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef IInputStreamFilter< T, Stream > Parent;
|
||||
|
||||
/// Type of elements read, buffered, and returned by this stream.
|
||||
typedef typename Parent::ElementType ElementType;
|
||||
|
||||
/// Type of the source stream being read by this stream.
|
||||
typedef typename Parent::SourceStreamType SourceStreamType;
|
||||
|
||||
/// Type of elements being read from the source stream.
|
||||
///
|
||||
/// @note This does not need to correspond to the type of elements buffered
|
||||
/// in this stream.
|
||||
typedef typename Parent::SourceElementType SourceElementType;
|
||||
|
||||
enum
|
||||
{
|
||||
/// The number of elements to buffer in advance by default.
|
||||
DEFAULT_STREAM_LOOKAHEAD = 3
|
||||
};
|
||||
|
||||
friend class AsyncBufferedReadItem< T, Stream >; // _onArrival
|
||||
|
||||
protected:
|
||||
|
||||
/// Stream elements are kept on deques that can be concurrently
|
||||
/// accessed by multiple threads.
|
||||
typedef ThreadSafeDeque< ElementType > ElementList;
|
||||
|
||||
/// If true, the stream will restart over from the beginning once
|
||||
/// it has been read in entirety.
|
||||
bool mIsLooping;
|
||||
|
||||
/// If true, no further requests should be issued on this stream.
|
||||
/// @note This in itself doesn't say anything about pending requests.
|
||||
bool mIsStopped;
|
||||
|
||||
/// Number of source elements remaining in the source stream.
|
||||
U32 mNumRemainingSourceElements;
|
||||
|
||||
/// Number of elements currently on buffer list.
|
||||
U32 mNumBufferedElements;
|
||||
|
||||
/// Maximum number of elements allowed on buffer list.
|
||||
U32 mMaxBufferedElements;
|
||||
|
||||
/// List of buffered elements.
|
||||
ElementList mBufferedElements;
|
||||
|
||||
/// The thread pool to which read items are queued.
|
||||
ThreadPool* mThreadPool;
|
||||
|
||||
/// The thread context used for prioritizing read items in the pool.
|
||||
ThreadContext* mThreadContext;
|
||||
|
||||
/// Request the next element from the underlying stream.
|
||||
virtual void _requestNext() = 0;
|
||||
|
||||
/// Called when an element read has been completed on the underlying stream.
|
||||
virtual void _onArrival( const ElementType& element );
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new buffered stream reading from "source".
|
||||
///
|
||||
/// @param stream The source stream from which to read the actual data elements.
|
||||
/// @param numSourceElementsToRead Total number of elements to read from "stream".
|
||||
/// @param numReadAhead Number of packets to read and buffer in advance.
|
||||
/// @param isLooping If true, the packet stream will loop infinitely over the source stream.
|
||||
/// @param pool The ThreadPool to use for asynchronous packet reads.
|
||||
/// @param context The ThreadContext to place asynchronous packet reads in.
|
||||
AsyncBufferedInputStream( const Stream& stream,
|
||||
U32 numSourceElementsToRead = 0,
|
||||
U32 numReadAhead = DEFAULT_STREAM_LOOKAHEAD,
|
||||
bool isLooping = false,
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL(),
|
||||
ThreadContext* context = ThreadContext::ROOT_CONTEXT() );
|
||||
|
||||
virtual ~AsyncBufferedInputStream();
|
||||
|
||||
/// @return true if the stream is looping infinitely.
|
||||
bool isLooping() const { return mIsLooping; }
|
||||
|
||||
/// @return the number of elements that will be read and buffered in advance.
|
||||
U32 getReadAhead() const { return mMaxBufferedElements; }
|
||||
|
||||
/// Initiate the request chain of the element stream.
|
||||
void start() { _requestNext(); }
|
||||
|
||||
/// Call for the request chain of the element stream to stop at the next
|
||||
/// synchronization point.
|
||||
void stop() { mIsStopped = true; }
|
||||
|
||||
// IInputStream.
|
||||
virtual U32 read( ElementType* buffer, U32 num );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
AsyncBufferedInputStream< T, Stream >::AsyncBufferedInputStream
|
||||
( const Stream& stream,
|
||||
U32 numSourceElementsToRead,
|
||||
U32 numReadAhead,
|
||||
bool isLooping,
|
||||
ThreadPool* threadPool,
|
||||
ThreadContext* threadContext )
|
||||
: Parent( stream ),
|
||||
mIsStopped( false ),
|
||||
mIsLooping( isLooping ),
|
||||
mNumRemainingSourceElements( numSourceElementsToRead ),
|
||||
mNumBufferedElements( 0 ),
|
||||
mMaxBufferedElements( numReadAhead ),
|
||||
mThreadPool( threadPool ),
|
||||
mThreadContext( threadContext )
|
||||
{
|
||||
if( mIsLooping )
|
||||
{
|
||||
// Stream is looping so we don't count down source elements.
|
||||
|
||||
mNumRemainingSourceElements = 0;
|
||||
}
|
||||
else if( !mNumRemainingSourceElements )
|
||||
{
|
||||
// If not given number of elements to read, see if the source
|
||||
// stream is sizeable. If so, take its size as the number of elements.
|
||||
|
||||
if( dynamic_cast< ISizeable<>* >( &Deref( stream ) ) )
|
||||
mNumRemainingSourceElements = ( ( ISizeable<>* ) &Deref( stream ) )->getSize();
|
||||
else
|
||||
{
|
||||
// Can't tell how many source elements there are.
|
||||
|
||||
mNumRemainingSourceElements = U32_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
AsyncBufferedInputStream< T, Stream >::~AsyncBufferedInputStream()
|
||||
{
|
||||
ElementType element;
|
||||
while( mBufferedElements.tryPopFront( element ) )
|
||||
destructSingle( element );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
void AsyncBufferedInputStream< T, Stream >::_onArrival( const ElementType& element )
|
||||
{
|
||||
mBufferedElements.pushBack( element );
|
||||
|
||||
// Adjust buffer count.
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
S32 numBuffered = mNumBufferedElements;
|
||||
if( dCompareAndSwap( mNumBufferedElements, numBuffered, numBuffered + 1 ) )
|
||||
{
|
||||
// If we haven't run against the lookahead limit and haven't reached
|
||||
// the end of the stream, immediately trigger a new request.
|
||||
|
||||
if( !mIsStopped && ( numBuffered + 1 ) < mMaxBufferedElements )
|
||||
_requestNext();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
U32 AsyncBufferedInputStream< T, Stream >::read( ElementType* buffer, U32 num )
|
||||
{
|
||||
if( !num )
|
||||
return 0;
|
||||
|
||||
U32 numRead = 0;
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
{
|
||||
// Try to pop a element off the buffered element list.
|
||||
|
||||
ElementType element;
|
||||
if( mBufferedElements.tryPopFront( element ) )
|
||||
{
|
||||
buffer[ i ] = element;
|
||||
numRead ++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the request chain going again, if it has stopped.
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
U32 numBuffered = mNumBufferedElements;
|
||||
U32 newNumBuffered = numBuffered - numRead;
|
||||
|
||||
if( dCompareAndSwap( mNumBufferedElements, numBuffered, newNumBuffered ) )
|
||||
{
|
||||
if( numBuffered == mMaxBufferedElements )
|
||||
_requestNext();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numRead;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// AsyncSingleBufferedInputStream.
|
||||
//=============================================================================
|
||||
|
||||
|
||||
/// Asynchronous work item for reading an element from the source stream.
|
||||
template< typename T, typename Stream = IInputStream< T >* >
|
||||
class AsyncBufferedReadItem : public ThreadWorkItem
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ThreadWorkItem Parent;
|
||||
typedef ThreadSafeRef< AsyncBufferedInputStream< T, Stream > > AsyncStreamRef;
|
||||
|
||||
protected:
|
||||
|
||||
/// The issueing async state.
|
||||
AsyncStreamRef mAsyncStream;
|
||||
|
||||
///
|
||||
Stream mSourceStream;
|
||||
|
||||
/// The element read from the stream.
|
||||
T mElement;
|
||||
|
||||
// WorkItem
|
||||
virtual void execute()
|
||||
{
|
||||
if( Deref( mSourceStream ).read( &mElement, 1 ) )
|
||||
{
|
||||
// Buffer the element.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
mAsyncStream->_onArrival( mElement );
|
||||
}
|
||||
}
|
||||
virtual void onCancelled()
|
||||
{
|
||||
Parent::onCancelled();
|
||||
destructSingle( mElement );
|
||||
mAsyncStream = NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
AsyncBufferedReadItem(
|
||||
const AsyncStreamRef& asyncStream,
|
||||
ThreadPool::Context* context = NULL
|
||||
)
|
||||
: Parent( context ),
|
||||
mAsyncStream( asyncStream ),
|
||||
mSourceStream( asyncStream->getSourceStream() )
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// A stream filter that performs background read-aheads on its source stream
|
||||
/// and buffers the results.
|
||||
///
|
||||
/// As each element is read in an independent threaded operation, reading an
|
||||
/// element should invole a certain amount of work for using this class to
|
||||
/// make sense.
|
||||
///
|
||||
/// @note For looping streams, the stream must implement the IResettable interface.
|
||||
///
|
||||
template< typename T, typename Stream = IInputStream< T >*, class ReadItem = AsyncBufferedReadItem< T, Stream > >
|
||||
class AsyncSingleBufferedInputStream : public AsyncBufferedInputStream< T, Stream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncBufferedInputStream< T, Stream > Parent;
|
||||
typedef typename Parent::ElementType ElementType;
|
||||
typedef typename Parent::SourceElementType SourceElementType;
|
||||
typedef typename Parent::SourceStreamType SourceStreamType;
|
||||
|
||||
protected:
|
||||
|
||||
// AsyncBufferedInputStream.
|
||||
virtual void _requestNext();
|
||||
|
||||
/// Create a new work item that reads the next element.
|
||||
virtual void _newReadItem( ThreadSafeRef< ThreadWorkItem >& outRef )
|
||||
{
|
||||
outRef = new ReadItem( this, this->mThreadContext );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new buffered stream reading from "source".
|
||||
///
|
||||
/// @param stream The source stream from which to read the actual data elements.
|
||||
/// @param numSourceElementsToRead Total number of elements to read from "stream".
|
||||
/// @param numReadAhead Number of packets to read and buffer in advance.
|
||||
/// @param isLooping If true, the packet stream will loop infinitely over the source stream.
|
||||
/// @param pool The ThreadPool to use for asynchronous packet reads.
|
||||
/// @param context The ThreadContext to place asynchronous packet reads in.
|
||||
AsyncSingleBufferedInputStream( const Stream& stream,
|
||||
U32 numSourceElementsToRead = 0,
|
||||
U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD,
|
||||
bool isLooping = false,
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL(),
|
||||
ThreadContext* context = ThreadContext::ROOT_CONTEXT() )
|
||||
: Parent( stream,
|
||||
numSourceElementsToRead,
|
||||
numReadAhead,
|
||||
isLooping,
|
||||
pool,
|
||||
context ) {}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream, class ReadItem >
|
||||
void AsyncSingleBufferedInputStream< T, Stream, ReadItem >::_requestNext()
|
||||
{
|
||||
Stream& stream = this->getSourceStream();
|
||||
bool isEOS = !this->mNumRemainingSourceElements;
|
||||
if( isEOS && this->mIsLooping )
|
||||
{
|
||||
SourceStreamType* s = &Deref( stream );
|
||||
dynamic_cast< IResettable* >( s )->reset();
|
||||
isEOS = false;
|
||||
}
|
||||
else if( isEOS )
|
||||
return;
|
||||
|
||||
//TODO: could scale priority depending on feed status
|
||||
|
||||
// Queue a stream packet work item.
|
||||
|
||||
if( !this->mIsLooping && this->mNumRemainingSourceElements != U32_MAX )
|
||||
-- this->mNumRemainingSourceElements;
|
||||
|
||||
ThreadSafeRef< ThreadWorkItem > workItem;
|
||||
_newReadItem( workItem );
|
||||
this->mThreadPool->queueWorkItem( workItem );
|
||||
}
|
||||
|
||||
#endif // !_ASYNCBUFFEREDSTREAM_H_
|
||||
314
Engine/source/platform/async/asyncPacketQueue.h
Normal file
314
Engine/source/platform/async/asyncPacketQueue.h
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCPACKETQUEUE_H_
|
||||
#define _ASYNCPACKETQUEUE_H_
|
||||
|
||||
#ifndef _TFIXEDSIZEQUEUE_H_
|
||||
#include "core/util/tFixedSizeDeque.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TSTREAM_H_
|
||||
#include "core/stream/tStream.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TYPETRAITS_H_
|
||||
#include "platform/typetraits.h"
|
||||
#endif
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
/// @file
|
||||
/// Time-based packet streaming.
|
||||
///
|
||||
/// The classes contained in this file can be used for any kind
|
||||
/// of continuous playback that depends on discrete samplings of
|
||||
/// a source stream (i.e. any kind of digital media streaming).
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async packet queue.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Time-based packet stream queue.
|
||||
///
|
||||
/// AsyncPacketQueue writes data packets to a consumer stream in sync to
|
||||
/// a tick time source. Outdated packets may optionally be dropped automatically
|
||||
/// by the queue. A fixed maximum number of packets can reside in the queue
|
||||
/// concurrently at any one time.
|
||||
///
|
||||
/// Be aware that using single item queues for synchronizing to a timer
|
||||
/// will usually result in bad timing behavior when packet uploading takes
|
||||
/// any non-trivial amount of time.
|
||||
///
|
||||
/// @note While the queue associates a variable tick count with each
|
||||
/// individual packet, the queue fill status is measured in number of
|
||||
/// packets rather than in total tick time.
|
||||
///
|
||||
/// @param Packet Value type of packets passed through this queue.
|
||||
/// @param TimeSource Value type for time tick source to which the queue
|
||||
/// is synchronized.
|
||||
/// @param Consumer Value type of stream to which the packets are written.
|
||||
///
|
||||
template< typename Packet, typename TimeSource = IPositionable< U32 >*, typename Consumer = IOutputStream< Packet >*, typename Tick = U32 >
|
||||
class AsyncPacketQueue
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
|
||||
/// The type of data packets being streamed through this queue.
|
||||
typedef typename TypeTraits< Packet >::BaseType PacketType;
|
||||
|
||||
/// The type of consumer that receives the packets from this queue.
|
||||
typedef typename TypeTraits< Consumer >::BaseType ConsumerType;
|
||||
|
||||
///
|
||||
typedef typename TypeTraits< TimeSource >::BaseType TimeSourceType;
|
||||
|
||||
/// Type for counting ticks.
|
||||
typedef Tick TickType;
|
||||
|
||||
protected:
|
||||
|
||||
/// Information about the time slice covered by an
|
||||
/// individual packet currently on the queue.
|
||||
struct QueuedPacket
|
||||
{
|
||||
/// First tick contained in this packet.
|
||||
TickType mStartTick;
|
||||
|
||||
/// First tick *not* contained in this packet anymore.
|
||||
TickType mEndTick;
|
||||
|
||||
QueuedPacket( TickType start, TickType end )
|
||||
: mStartTick( start ), mEndTick( end ) {}
|
||||
|
||||
/// Return the total number of ticks in this packet.
|
||||
TickType getNumTicks() const
|
||||
{
|
||||
return ( mEndTick - mStartTick );
|
||||
}
|
||||
};
|
||||
|
||||
typedef FixedSizeDeque< QueuedPacket > PacketQueue;
|
||||
|
||||
/// If true, packets that have missed their proper queuing timeframe
|
||||
/// will be dropped. If false, they will be queued nonetheless.
|
||||
bool mDropPackets;
|
||||
|
||||
/// Total number of ticks spanned by the total queue playback time.
|
||||
/// If this is zero, the total queue time is considered to be infinite.
|
||||
TickType mTotalTicks;
|
||||
|
||||
/// Total number of ticks submitted to the queue so far.
|
||||
TickType mTotalQueuedTicks;
|
||||
|
||||
/// Queue that holds records for each packet currently in the queue. New packets
|
||||
/// are added to back.
|
||||
PacketQueue mPacketQueue;
|
||||
|
||||
/// The time source to which we are sync'ing.
|
||||
TimeSource mTimeSource;
|
||||
|
||||
/// The output stream that this queue feeds into.
|
||||
Consumer mConsumer;
|
||||
|
||||
/// Total number of packets queued so far.
|
||||
U32 mTotalQueuedPackets;
|
||||
|
||||
public:
|
||||
|
||||
/// Construct an AsyncPacketQueue of the given length.
|
||||
///
|
||||
/// @param maxQueuedPackets The length of the queue in packets. Only a maximum of
|
||||
/// 'maxQueuedPackets' packets can be concurrently in the queue at any one time.
|
||||
/// @param timeSource The tick time source to which the queue synchronizes.
|
||||
/// @param consumer The output stream that receives the packets in sync to timeSource.
|
||||
/// @param totalTicks The total number of ticks that will be played back through the
|
||||
/// queue; if 0, the length is considered indefinite.
|
||||
/// @param dropPackets Whether the queue should drop outdated packets; if dropped, a
|
||||
/// packet will not reach the consumer.
|
||||
AsyncPacketQueue( U32 maxQueuedPackets,
|
||||
TimeSource timeSource,
|
||||
Consumer consumer,
|
||||
TickType totalTicks = 0,
|
||||
bool dropPackets = false )
|
||||
: mTotalTicks( totalTicks ),
|
||||
mTotalQueuedTicks( 0 ),
|
||||
mPacketQueue( maxQueuedPackets ),
|
||||
mTimeSource( timeSource ),
|
||||
mConsumer( consumer ),
|
||||
mDropPackets( dropPackets )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
mTotalQueuedPackets = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Return true if there are currently
|
||||
bool isEmpty() const { return mPacketQueue.isEmpty(); }
|
||||
|
||||
/// Return true if all packets have been streamed.
|
||||
bool isAtEnd() const;
|
||||
|
||||
/// Return true if the queue needs one or more new packets to be submitted.
|
||||
bool needPacket();
|
||||
|
||||
/// Submit a data packet to the queue.
|
||||
///
|
||||
/// @param packet The data packet.
|
||||
/// @param packetTicks The duration of the packet in ticks.
|
||||
/// @param isLast If true, the packet is the last one in the stream.
|
||||
/// @param packetPos The absolute position of the packet in the stream; if this is not supplied
|
||||
/// the packet is assumed to immediately follow the preceding packet.
|
||||
///
|
||||
/// @return true if the packet has been queued or false if it has been dropped.
|
||||
bool submitPacket( Packet packet,
|
||||
TickType packetTicks,
|
||||
bool isLast = false,
|
||||
TickType packetPos = TypeTraits< TickType >::MAX );
|
||||
|
||||
/// Return the current playback position according to the time source.
|
||||
TickType getCurrentTick() const { return Deref( mTimeSource ).getPosition(); }
|
||||
|
||||
/// Return the total number of ticks that have been queued so far.
|
||||
TickType getTotalQueuedTicks() const { return mTotalQueuedTicks; }
|
||||
|
||||
/// Return the total number of packets that have been queued so far.
|
||||
U32 getTotalQueuedPackets() const { return mTotalQueuedPackets; }
|
||||
};
|
||||
|
||||
template< typename Packet, typename TimeSource, typename Consumer, typename Tick >
|
||||
inline bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::isAtEnd() const
|
||||
{
|
||||
// Never at end if infinite.
|
||||
|
||||
if( !mTotalTicks )
|
||||
return false;
|
||||
|
||||
// Otherwise, we're at end if we're past the total tick count.
|
||||
|
||||
return ( getCurrentTick() >= mTotalTicks
|
||||
&& ( mDropPackets || mTotalQueuedTicks >= mTotalTicks ) );
|
||||
}
|
||||
|
||||
template< typename Packet, typename TimeSource, typename Consumer, typename Tick >
|
||||
bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::needPacket()
|
||||
{
|
||||
// Never need more packets once we have reached the
|
||||
// end.
|
||||
|
||||
if( isAtEnd() )
|
||||
return false;
|
||||
|
||||
// Always needs packets while the queue is not
|
||||
// filled up completely.
|
||||
|
||||
if( mPacketQueue.capacity() != 0 )
|
||||
return true;
|
||||
|
||||
// Unqueue packets that have expired their playtime.
|
||||
|
||||
TickType currentTick = getCurrentTick();
|
||||
while( mPacketQueue.size() && currentTick >= mPacketQueue.front().mEndTick )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketQueue] expired packet #%i: %i-%i (tick: %i; queue: %i)",
|
||||
mTotalQueuedPackets - mPacketQueue.size(),
|
||||
U32( mPacketQueue.front().mStartTick ),
|
||||
U32( mPacketQueue.front().mEndTick ),
|
||||
U32( currentTick ),
|
||||
mPacketQueue.size() );
|
||||
#endif
|
||||
|
||||
mPacketQueue.popFront();
|
||||
}
|
||||
|
||||
// Need more packets if the queue isn't full anymore.
|
||||
|
||||
return ( mPacketQueue.capacity() != 0 );
|
||||
}
|
||||
|
||||
template< typename Packet, typename TimeSource, typename Consumer, typename Tick >
|
||||
bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::submitPacket( Packet packet, TickType packetTicks, bool isLast, TickType packetPos )
|
||||
{
|
||||
AssertFatal( mPacketQueue.capacity() != 0,
|
||||
"AsyncPacketQueue::submitPacket() - Queue is full!" );
|
||||
|
||||
TickType packetStartPos;
|
||||
TickType packetEndPos;
|
||||
|
||||
if( packetPos != TypeTraits< TickType >::MAX )
|
||||
{
|
||||
packetStartPos = packetPos;
|
||||
packetEndPos = packetPos + packetTicks;
|
||||
}
|
||||
else
|
||||
{
|
||||
packetStartPos = mTotalQueuedTicks;
|
||||
packetEndPos = mTotalQueuedTicks + packetTicks;
|
||||
}
|
||||
|
||||
// Check whether the packet is outdated, if enabled.
|
||||
|
||||
bool dropPacket = false;
|
||||
if( mDropPackets )
|
||||
{
|
||||
TickType currentTick = getCurrentTick();
|
||||
if( currentTick >= packetEndPos )
|
||||
dropPacket = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketQueue] new packet #%i: %i-%i (ticks: %i, current: %i, queue: %i)%s",
|
||||
mTotalQueuedPackets,
|
||||
U32( mTotalQueuedTicks ),
|
||||
U32( packetEndPos ),
|
||||
U32( packetTicks ),
|
||||
U32( getCurrentTick() ),
|
||||
mPacketQueue.size(),
|
||||
dropPacket ? " !! DROPPED !!" : "" );
|
||||
#endif
|
||||
|
||||
// Queue the packet.
|
||||
|
||||
if( !dropPacket )
|
||||
{
|
||||
mPacketQueue.pushBack( QueuedPacket( packetStartPos, packetEndPos ) );
|
||||
Deref( mConsumer ).write( &packet, 1 );
|
||||
}
|
||||
|
||||
mTotalQueuedTicks = packetEndPos;
|
||||
if( isLast && !mTotalTicks )
|
||||
mTotalTicks = mTotalQueuedTicks;
|
||||
|
||||
mTotalQueuedPackets ++;
|
||||
|
||||
return !dropPacket;
|
||||
}
|
||||
|
||||
#undef DEBUG_SPEW
|
||||
#endif // _ASYNCPACKETQUEUE_H_
|
||||
327
Engine/source/platform/async/asyncPacketStream.h
Normal file
327
Engine/source/platform/async/asyncPacketStream.h
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCPACKETSTREAM_H_
|
||||
#define _ASYNCPACKETSTREAM_H_
|
||||
|
||||
#ifndef _ASYNCBUFFEREDSTREAM_H_
|
||||
#include "platform/async/asyncBufferedStream.h"
|
||||
#endif
|
||||
#ifndef _RAWDATA_H_
|
||||
#include "core/util/rawData.h"
|
||||
#endif
|
||||
#ifndef _THREADPOOLASYNCIO_H_
|
||||
#include "platform/threads/threadPoolAsyncIO.h"
|
||||
#endif
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
/// @file
|
||||
/// Input stream filter definitions for turning linear streams into
|
||||
/// streams that yield data in discrete packets using background
|
||||
/// reads.
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async stream packets.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Stream packet read by an asynchronous packet stream.
|
||||
template< typename T >
|
||||
class AsyncPacket : public RawDataT< T >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef RawDataT< T > Parent;
|
||||
|
||||
AsyncPacket()
|
||||
: mIndex( 0 ), mIsLast( false ), mSizeActual( 0 ) {}
|
||||
AsyncPacket( T* data, U32 size, bool ownMemory = false )
|
||||
: Parent( data, size, ownMemory ),
|
||||
mIndex( 0 ), mIsLast( false ), mSizeActual( 0 ) {}
|
||||
|
||||
/// Running number in stream.
|
||||
U32 mIndex;
|
||||
|
||||
/// Number of items that have actually been read into the packet.
|
||||
/// This may be less than "size" for end-of-stream packets in non-looping
|
||||
/// streams.
|
||||
///
|
||||
/// @note Extraneous space at the end of the packet will be cleared using
|
||||
/// constructArray() calls.
|
||||
U32 mSizeActual;
|
||||
|
||||
/// If true this is the last packet in the stream.
|
||||
bool mIsLast;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async packet streams.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// A packet stream turns a continuous stream of elements into a
|
||||
/// stream of discrete packets of elements.
|
||||
///
|
||||
/// All packets are of the exact same size even if, for end-of-stream
|
||||
/// packets, they actually contain less data than their actual size.
|
||||
/// Extraneous space is cleared.
|
||||
///
|
||||
/// @note For looping streams, the stream must implement the
|
||||
/// IResettable interface.
|
||||
template< typename Stream, class Packet = AsyncPacket< typename TypeTraits< Stream >::BaseType::ElementType > >
|
||||
class AsyncPacketBufferedInputStream : public AsyncBufferedInputStream< Packet*, Stream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncBufferedInputStream< Packet*, Stream > Parent;
|
||||
typedef Packet PacketType;
|
||||
typedef typename TypeTraits< Stream >::BaseType StreamType;
|
||||
|
||||
protected:
|
||||
|
||||
class PacketReadItem;
|
||||
friend class PacketReadItem; // _onArrival
|
||||
|
||||
/// Asynchronous work item for reading a packet from the source stream.
|
||||
class PacketReadItem : public AsyncReadItem< typename Parent::SourceElementType, StreamType >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncReadItem< typename AsyncPacketBufferedInputStream< Stream, Packet >::SourceElementType, StreamType > Parent;
|
||||
|
||||
PacketReadItem( const ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > >& asyncStream,
|
||||
PacketType* packet,
|
||||
U32 numElements,
|
||||
ThreadPool::Context* context = NULL )
|
||||
: Parent( asyncStream->getSourceStream(), numElements, 0, *packet, false, 0, context ),
|
||||
mAsyncStream( asyncStream ),
|
||||
mPacket( packet ) {}
|
||||
|
||||
protected:
|
||||
|
||||
typedef ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > > AsyncPacketStreamPtr;
|
||||
|
||||
/// The issueing async state.
|
||||
AsyncPacketStreamPtr mAsyncStream;
|
||||
|
||||
/// The packet that receives the data.
|
||||
PacketType* mPacket;
|
||||
|
||||
// WorkItem
|
||||
virtual void execute()
|
||||
{
|
||||
Parent::execute();
|
||||
mPacket->mSizeActual += this->mNumElementsRead;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] read %i elements into packet #%i with size %i",
|
||||
this->mNumElementsRead, mPacket->mIndex, mPacket->size );
|
||||
#endif
|
||||
|
||||
// Handle extraneous space at end of packet.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
U32 numExtraElements = mPacket->size - this->mNumElementsRead;
|
||||
if( numExtraElements )
|
||||
{
|
||||
if( mAsyncStream->mIsLooping
|
||||
&& dynamic_cast< IResettable* >( &Deref( this->getStream() ) ) )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] resetting stream and reading %i more elements", numExtraElements );
|
||||
#endif
|
||||
|
||||
// Wrap around and start re-reading from beginning of stream.
|
||||
|
||||
dynamic_cast< IResettable* >( &Deref( this->getStream() ) )->reset();
|
||||
|
||||
this->mOffsetInBuffer += this->mNumElementsRead;
|
||||
this->mOffsetInStream = 0;
|
||||
this->mNumElements = numExtraElements;
|
||||
|
||||
this->_prep();
|
||||
Parent::execute();
|
||||
|
||||
mPacket->mSizeActual += this->mNumElementsRead;
|
||||
}
|
||||
else
|
||||
constructArray( &mPacket->data[ this->mNumElementsRead ], numExtraElements );
|
||||
}
|
||||
|
||||
// Buffer the packet.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
mAsyncStream->_onArrival( mPacket );
|
||||
}
|
||||
virtual void onCancelled()
|
||||
{
|
||||
Parent::onCancelled();
|
||||
destructSingle< PacketType* >( mPacket );
|
||||
mAsyncStream = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< PacketReadItem > PacketReadItemRef;
|
||||
|
||||
/// Number of elements to read per packet.
|
||||
U32 mPacketSize;
|
||||
|
||||
/// Running number of next stream packet.
|
||||
U32 mNextPacketIndex;
|
||||
|
||||
/// Total number of elements in the source stream.
|
||||
U32 mNumTotalSourceElements;
|
||||
|
||||
/// Create a new stream packet of the given size.
|
||||
virtual PacketType* _newPacket( U32 packetSize ) { return constructSingle< PacketType* >( packetSize ); }
|
||||
|
||||
/// Request the next packet from the underlying stream.
|
||||
virtual void _requestNext();
|
||||
|
||||
/// Create a new work item that reads "numElements" into "packet".
|
||||
virtual void _newReadItem( PacketReadItemRef& outRef,
|
||||
PacketType* packet,
|
||||
U32 numElements )
|
||||
{
|
||||
outRef = new PacketReadItem( this, packet, numElements, this->mThreadContext );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new packet stream reading from "stream".
|
||||
///
|
||||
/// @note If looping is used and "stream" is not read from the beginning, "stream" should
|
||||
/// implement IPositionable<U32> or ISizeable<U32> so the async stream can tell how many elements
|
||||
/// there actually are in the stream after resetting.
|
||||
///
|
||||
/// @param stream The source stream from which to read the actual data elements.
|
||||
/// @param packetSize Size of stream packets returned by the stream in number of elements.
|
||||
/// @param numSourceElementsToRead Number of elements to read from "stream".
|
||||
/// @param numReadAhead Number of packets to read and buffer in advance.
|
||||
/// @param isLooping If true, the packet stream will loop infinitely over the source stream.
|
||||
/// @param pool The ThreadPool to use for asynchronous packet reads.
|
||||
/// @param context The ThreadContext to place asynchronous packet reads in.
|
||||
AsyncPacketBufferedInputStream( const Stream& stream,
|
||||
U32 packetSize,
|
||||
U32 numSourceElementsToRead = 0,
|
||||
U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD,
|
||||
bool isLooping = false,
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL(),
|
||||
ThreadContext* context = ThreadContext::ROOT_CONTEXT() );
|
||||
|
||||
/// @return the size of stream packets returned by this stream in number of elements.
|
||||
U32 getPacketSize() const { return mPacketSize; }
|
||||
};
|
||||
|
||||
template< typename Stream, class Packet >
|
||||
AsyncPacketBufferedInputStream< Stream, Packet >::AsyncPacketBufferedInputStream
|
||||
( const Stream& stream,
|
||||
U32 packetSize,
|
||||
U32 numSourceElementsToRead,
|
||||
U32 numReadAhead,
|
||||
bool isLooping,
|
||||
ThreadPool* threadPool,
|
||||
ThreadContext* threadContext )
|
||||
: Parent( stream, numSourceElementsToRead, numReadAhead, isLooping, threadPool, threadContext ),
|
||||
mPacketSize( packetSize ),
|
||||
mNumTotalSourceElements( numSourceElementsToRead ),
|
||||
mNextPacketIndex( 0 )
|
||||
{
|
||||
AssertFatal( mPacketSize > 0,
|
||||
"AsyncPacketStream::AsyncPacketStream() - packet size cannot be zero" );
|
||||
|
||||
// Determine total number of elements in stream, if possible.
|
||||
|
||||
IPositionable< U32 >* positionable = dynamic_cast< IPositionable< U32 >* >( &Deref( stream ) );
|
||||
if( positionable )
|
||||
mNumTotalSourceElements += positionable->getPosition();
|
||||
else
|
||||
{
|
||||
ISizeable< U32 >* sizeable = dynamic_cast< ISizeable< U32 >* >( &Deref( stream ) );
|
||||
if( sizeable )
|
||||
mNumTotalSourceElements = sizeable->getSize();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] %i remaining, %i total (%i packets)",
|
||||
this->mNumRemainingSourceElements, mNumTotalSourceElements,
|
||||
( this->mNumRemainingSourceElements / mPacketSize ) + ( this->mNumRemainingSourceElements % mPacketSize ? 1 : 0 ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
template< typename Stream, class Packet >
|
||||
void AsyncPacketBufferedInputStream< Stream, Packet >::_requestNext()
|
||||
{
|
||||
Stream& stream = this->getSourceStream();
|
||||
bool isEOS = !this->mNumRemainingSourceElements;
|
||||
if( isEOS && this->mIsLooping )
|
||||
{
|
||||
StreamType* s = &Deref( stream );
|
||||
IResettable* resettable = dynamic_cast< IResettable* >( s );
|
||||
if( resettable )
|
||||
{
|
||||
resettable->reset();
|
||||
isEOS = false;
|
||||
this->mNumRemainingSourceElements = mNumTotalSourceElements;
|
||||
}
|
||||
}
|
||||
else if( isEOS )
|
||||
return;
|
||||
|
||||
//TODO: scale priority depending on feed status
|
||||
|
||||
// Allocate a packet.
|
||||
|
||||
U32 numElements = mPacketSize;
|
||||
PacketType* packet = _newPacket( numElements );
|
||||
packet->mIndex = mNextPacketIndex;
|
||||
mNextPacketIndex ++;
|
||||
|
||||
// Queue a stream packet work item.
|
||||
|
||||
if( numElements >= this->mNumRemainingSourceElements )
|
||||
{
|
||||
if( !this->mIsLooping )
|
||||
{
|
||||
this->mNumRemainingSourceElements = 0;
|
||||
packet->mIsLast = true;
|
||||
}
|
||||
else
|
||||
this->mNumRemainingSourceElements = ( this->mNumTotalSourceElements - numElements + this->mNumRemainingSourceElements );
|
||||
}
|
||||
else
|
||||
this->mNumRemainingSourceElements -= numElements;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] packet %i, %i remaining, %i total",
|
||||
packet->mIndex, this->mNumRemainingSourceElements, mNumTotalSourceElements );
|
||||
#endif
|
||||
|
||||
ThreadSafeRef< PacketReadItem > workItem;
|
||||
_newReadItem( workItem, packet, numElements );
|
||||
this->mThreadPool->queueWorkItem( workItem );
|
||||
}
|
||||
|
||||
#undef DEBUG_SPEW
|
||||
#endif // !_ASYNCPACKETSTREAM_H_
|
||||
83
Engine/source/platform/async/asyncUpdate.cpp
Normal file
83
Engine/source/platform/async/asyncUpdate.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/async/asyncUpdate.h"
|
||||
#include "core/stream/tStream.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AsyncUpdateList implementation.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AsyncUpdateList::process( S32 timeOut )
|
||||
{
|
||||
U32 endTime = 0;
|
||||
if( timeOut != -1 )
|
||||
endTime = Platform::getRealMilliseconds() + timeOut;
|
||||
|
||||
// Flush the process list.
|
||||
|
||||
IPolled* ptr;
|
||||
IPolled* firstProcessedPtr = 0;
|
||||
|
||||
while( mUpdateList.tryPopFront( ptr ) )
|
||||
{
|
||||
if( ptr == firstProcessedPtr )
|
||||
{
|
||||
// We've wrapped around. Stop.
|
||||
|
||||
mUpdateList.pushFront( ptr );
|
||||
break;
|
||||
}
|
||||
|
||||
if( ptr->update() )
|
||||
{
|
||||
mUpdateList.pushBack( ptr );
|
||||
|
||||
if( !firstProcessedPtr )
|
||||
firstProcessedPtr = ptr;
|
||||
}
|
||||
|
||||
// Stop if we have exceeded our processing time budget.
|
||||
|
||||
if( timeOut != -1
|
||||
&& Platform::getRealMilliseconds() >= endTime )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// AsyncUpdateThread implementation.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void AsyncUpdateThread::run( void* )
|
||||
{
|
||||
_setName( getName() );
|
||||
|
||||
while( !checkForStop() )
|
||||
{
|
||||
_waitForEventAndReset();
|
||||
|
||||
if( !checkForStop() )
|
||||
mUpdateList->process();
|
||||
}
|
||||
}
|
||||
162
Engine/source/platform/async/asyncUpdate.h
Normal file
162
Engine/source/platform/async/asyncUpdate.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCUPDATE_H_
|
||||
#define _ASYNCUPDATE_H_
|
||||
|
||||
#ifndef _PLATFORM_THREADS_THREAD_H_
|
||||
# include "platform/threads/thread.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
# include "platform/threads/threadSafeRefCount.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEDEQUE_H_
|
||||
# include "platform/threads/threadSafeDeque.h"
|
||||
#endif
|
||||
|
||||
|
||||
class IPolled;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async update list.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// This structure keeps track of the objects that need
|
||||
/// updating.
|
||||
class AsyncUpdateList : public ThreadSafeRefCount< AsyncUpdateList >
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef ThreadSafeDeque< IPolled* > UpdateList;
|
||||
|
||||
/// List of structures currently in the update loop.
|
||||
UpdateList mUpdateList;
|
||||
|
||||
public:
|
||||
|
||||
virtual ~AsyncUpdateList() {}
|
||||
|
||||
/// Update the structures currently on the processing list.
|
||||
///
|
||||
/// @param timeOut Soft limit in milliseconds on the time
|
||||
/// spent on flushing the list. Default of -1 means no
|
||||
/// limit and function will only return, if update list
|
||||
/// has been fully flushed.
|
||||
virtual void process( S32 timeOut = -1 );
|
||||
|
||||
/// Add the structure to the update list. It will stay
|
||||
/// on this list, until its update() method returns false.
|
||||
///
|
||||
/// @note This can be called on different threads.
|
||||
virtual void add( IPolled* ptr )
|
||||
{
|
||||
mUpdateList.pushBack( ptr );
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async update thread.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Abstract baseclass for async update threads.
|
||||
class AsyncUpdateThread : public Thread, public ThreadSafeRefCount< AsyncUpdateThread >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Thread Parent;
|
||||
|
||||
protected:
|
||||
|
||||
/// Name of this thread.
|
||||
String mName;
|
||||
|
||||
/// Platform-dependent event data.
|
||||
void* mUpdateEvent;
|
||||
|
||||
/// The update list processed on this thread.
|
||||
ThreadSafeRef< AsyncUpdateList > mUpdateList;
|
||||
|
||||
/// Wait for an update event being triggered and
|
||||
/// immediately reset the event.
|
||||
///
|
||||
/// @note Note that this must be an atomic operation to avoid
|
||||
/// a race condition. Immediately resetting the event shields
|
||||
/// us from event releases happening during us updating getting
|
||||
/// ignored.
|
||||
virtual void _waitForEventAndReset();
|
||||
|
||||
public:
|
||||
|
||||
/// Create the update thread.
|
||||
/// The thread won't immediately start (we have virtual functions
|
||||
/// so construction needs to finish first) and will not auto-delete
|
||||
/// itself.
|
||||
AsyncUpdateThread( String name, AsyncUpdateList* updateList );
|
||||
|
||||
virtual ~AsyncUpdateThread();
|
||||
|
||||
virtual void run( void* );
|
||||
|
||||
/// Trigger the update event to notify the thread about
|
||||
/// pending updates.
|
||||
virtual void triggerUpdate();
|
||||
|
||||
///
|
||||
const String& getName() const { return mName; }
|
||||
|
||||
///
|
||||
void* getUpdateEvent() const { return mUpdateEvent; }
|
||||
};
|
||||
|
||||
/// Extension to update thread that also does automatic
|
||||
/// periodic updates.
|
||||
class AsyncPeriodicUpdateThread : public AsyncUpdateThread
|
||||
{
|
||||
typedef AsyncUpdateThread Parent;
|
||||
|
||||
protected:
|
||||
|
||||
/// Platform-dependent timer event.
|
||||
void* mUpdateTimer;
|
||||
|
||||
/// Time between periodic updates in milliseconds.
|
||||
U32 mIntervalMS;
|
||||
|
||||
virtual void _waitForEventAndReset();
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
/// Default interval between periodic updates in milliseconds.
|
||||
DEFAULT_UPDATE_INTERVAL = 4000
|
||||
};
|
||||
|
||||
///
|
||||
AsyncPeriodicUpdateThread( String name,
|
||||
AsyncUpdateList* updateList,
|
||||
U32 intervalMS = DEFAULT_UPDATE_INTERVAL );
|
||||
|
||||
virtual ~AsyncPeriodicUpdateThread();
|
||||
};
|
||||
|
||||
#endif // _TORQUE_CORE_ASYNC_ASYNCUPDATE_H_
|
||||
392
Engine/source/platform/event.h
Normal file
392
Engine/source/platform/event.h
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @file
|
||||
/// Library-wide input events
|
||||
///
|
||||
/// All external events are converted into system events, which are defined
|
||||
/// in this file.
|
||||
|
||||
///
|
||||
#ifndef _EVENT_H_
|
||||
#define _EVENT_H_
|
||||
|
||||
#include "platform/types.h"
|
||||
#include "core/util/journal/journaledSignal.h"
|
||||
|
||||
/// @defgroup input_constants Input system constants
|
||||
/// @{
|
||||
|
||||
/// Input event constants:
|
||||
enum InputObjectInstances
|
||||
{
|
||||
KEY_NULL = 0x000, ///< Invalid KeyCode
|
||||
KEY_BACKSPACE = 0x001,
|
||||
KEY_TAB = 0x002,
|
||||
KEY_RETURN = 0x003,
|
||||
KEY_CONTROL = 0x004,
|
||||
KEY_ALT = 0x005,
|
||||
KEY_SHIFT = 0x006,
|
||||
KEY_PAUSE = 0x007,
|
||||
KEY_CAPSLOCK = 0x008,
|
||||
KEY_ESCAPE = 0x009,
|
||||
KEY_SPACE = 0x00a,
|
||||
KEY_PAGE_DOWN = 0x00b,
|
||||
KEY_PAGE_UP = 0x00c,
|
||||
KEY_END = 0x00d,
|
||||
KEY_HOME = 0x00e,
|
||||
KEY_LEFT = 0x00f,
|
||||
KEY_UP = 0x010,
|
||||
KEY_RIGHT = 0x011,
|
||||
KEY_DOWN = 0x012,
|
||||
KEY_PRINT = 0x013,
|
||||
KEY_INSERT = 0x014,
|
||||
KEY_DELETE = 0x015,
|
||||
KEY_HELP = 0x016,
|
||||
|
||||
KEY_0 = 0x017,
|
||||
KEY_1 = 0x018,
|
||||
KEY_2 = 0x019,
|
||||
KEY_3 = 0x01a,
|
||||
KEY_4 = 0x01b,
|
||||
KEY_5 = 0x01c,
|
||||
KEY_6 = 0x01d,
|
||||
KEY_7 = 0x01e,
|
||||
KEY_8 = 0x01f,
|
||||
KEY_9 = 0x020,
|
||||
|
||||
KEY_A = 0x021,
|
||||
KEY_B = 0x022,
|
||||
KEY_C = 0x023,
|
||||
KEY_D = 0x024,
|
||||
KEY_E = 0x025,
|
||||
KEY_F = 0x026,
|
||||
KEY_G = 0x027,
|
||||
KEY_H = 0x028,
|
||||
KEY_I = 0x029,
|
||||
KEY_J = 0x02a,
|
||||
KEY_K = 0x02b,
|
||||
KEY_L = 0x02c,
|
||||
KEY_M = 0x02d,
|
||||
KEY_N = 0x02e,
|
||||
KEY_O = 0x02f,
|
||||
KEY_P = 0x030,
|
||||
KEY_Q = 0x031,
|
||||
KEY_R = 0x032,
|
||||
KEY_S = 0x033,
|
||||
KEY_T = 0x034,
|
||||
KEY_U = 0x035,
|
||||
KEY_V = 0x036,
|
||||
KEY_W = 0x037,
|
||||
KEY_X = 0x038,
|
||||
KEY_Y = 0x039,
|
||||
KEY_Z = 0x03a,
|
||||
|
||||
KEY_TILDE = 0x03b,
|
||||
KEY_MINUS = 0x03c,
|
||||
KEY_EQUALS = 0x03d,
|
||||
KEY_LBRACKET = 0x03e,
|
||||
KEY_RBRACKET = 0x03f,
|
||||
KEY_BACKSLASH = 0x040,
|
||||
KEY_SEMICOLON = 0x041,
|
||||
KEY_APOSTROPHE = 0x042,
|
||||
KEY_COMMA = 0x043,
|
||||
KEY_PERIOD = 0x044,
|
||||
KEY_SLASH = 0x045,
|
||||
KEY_NUMPAD0 = 0x046,
|
||||
KEY_NUMPAD1 = 0x047,
|
||||
KEY_NUMPAD2 = 0x048,
|
||||
KEY_NUMPAD3 = 0x049,
|
||||
KEY_NUMPAD4 = 0x04a,
|
||||
KEY_NUMPAD5 = 0x04b,
|
||||
KEY_NUMPAD6 = 0x04c,
|
||||
KEY_NUMPAD7 = 0x04d,
|
||||
KEY_NUMPAD8 = 0x04e,
|
||||
KEY_NUMPAD9 = 0x04f,
|
||||
KEY_MULTIPLY = 0x050,
|
||||
KEY_ADD = 0x051,
|
||||
KEY_SEPARATOR = 0x052,
|
||||
KEY_SUBTRACT = 0x053,
|
||||
KEY_DECIMAL = 0x054,
|
||||
KEY_DIVIDE = 0x055,
|
||||
KEY_NUMPADENTER = 0x056,
|
||||
|
||||
KEY_F1 = 0x057,
|
||||
KEY_F2 = 0x058,
|
||||
KEY_F3 = 0x059,
|
||||
KEY_F4 = 0x05a,
|
||||
KEY_F5 = 0x05b,
|
||||
KEY_F6 = 0x05c,
|
||||
KEY_F7 = 0x05d,
|
||||
KEY_F8 = 0x05e,
|
||||
KEY_F9 = 0x05f,
|
||||
KEY_F10 = 0x060,
|
||||
KEY_F11 = 0x061,
|
||||
KEY_F12 = 0x062,
|
||||
KEY_F13 = 0x063,
|
||||
KEY_F14 = 0x064,
|
||||
KEY_F15 = 0x065,
|
||||
KEY_F16 = 0x066,
|
||||
KEY_F17 = 0x067,
|
||||
KEY_F18 = 0x068,
|
||||
KEY_F19 = 0x069,
|
||||
KEY_F20 = 0x06a,
|
||||
KEY_F21 = 0x06b,
|
||||
KEY_F22 = 0x06c,
|
||||
KEY_F23 = 0x06d,
|
||||
KEY_F24 = 0x06e,
|
||||
|
||||
KEY_NUMLOCK = 0x06f,
|
||||
KEY_SCROLLLOCK = 0x070,
|
||||
KEY_LCONTROL = 0x071,
|
||||
KEY_RCONTROL = 0x072,
|
||||
KEY_LALT = 0x073,
|
||||
KEY_RALT = 0x074,
|
||||
KEY_LSHIFT = 0x075,
|
||||
KEY_RSHIFT = 0x076,
|
||||
KEY_WIN_LWINDOW = 0x077,
|
||||
KEY_WIN_RWINDOW = 0x078,
|
||||
KEY_WIN_APPS = 0x079,
|
||||
KEY_OEM_102 = 0x080,
|
||||
|
||||
KEY_MAC_OPT = 0x090,
|
||||
KEY_MAC_LOPT = 0x091,
|
||||
KEY_MAC_ROPT = 0x092,
|
||||
|
||||
KEY_BUTTON0 = 0x0100,
|
||||
KEY_BUTTON1 = 0x0101,
|
||||
KEY_BUTTON2 = 0x0102,
|
||||
KEY_BUTTON3 = 0x0103,
|
||||
KEY_BUTTON4 = 0x0104,
|
||||
KEY_BUTTON5 = 0x0105,
|
||||
KEY_BUTTON6 = 0x0106,
|
||||
KEY_BUTTON7 = 0x0107,
|
||||
KEY_BUTTON8 = 0x0108,
|
||||
KEY_BUTTON9 = 0x0109,
|
||||
KEY_BUTTON10 = 0x010A,
|
||||
KEY_BUTTON11 = 0x010B,
|
||||
KEY_BUTTON12 = 0x010C,
|
||||
KEY_BUTTON13 = 0x010D,
|
||||
KEY_BUTTON14 = 0x010E,
|
||||
KEY_BUTTON15 = 0x010F,
|
||||
KEY_BUTTON16 = 0x0110,
|
||||
KEY_BUTTON17 = 0x0111,
|
||||
KEY_BUTTON18 = 0x0112,
|
||||
KEY_BUTTON19 = 0x0113,
|
||||
KEY_BUTTON20 = 0x0114,
|
||||
KEY_BUTTON21 = 0x0115,
|
||||
KEY_BUTTON22 = 0x0116,
|
||||
KEY_BUTTON23 = 0x0117,
|
||||
KEY_BUTTON24 = 0x0118,
|
||||
KEY_BUTTON25 = 0x0119,
|
||||
KEY_BUTTON26 = 0x011A,
|
||||
KEY_BUTTON27 = 0x011B,
|
||||
KEY_BUTTON28 = 0x011C,
|
||||
KEY_BUTTON29 = 0x011D,
|
||||
KEY_BUTTON30 = 0x011E,
|
||||
KEY_BUTTON31 = 0x011F,
|
||||
KEY_ANYKEY = 0xfffe,
|
||||
|
||||
/// Joystick event codes.
|
||||
SI_XPOV = 0x204,
|
||||
SI_YPOV = 0x205,
|
||||
SI_UPOV = 0x206,
|
||||
SI_DPOV = 0x207,
|
||||
SI_LPOV = 0x208,
|
||||
SI_RPOV = 0x209,
|
||||
SI_XAXIS = 0x20B,
|
||||
SI_YAXIS = 0x20C,
|
||||
SI_ZAXIS = 0x20D,
|
||||
SI_RXAXIS = 0x20E,
|
||||
SI_RYAXIS = 0x20F,
|
||||
SI_RZAXIS = 0x210,
|
||||
SI_SLIDER = 0x211,
|
||||
SI_XPOV2 = 0x212,
|
||||
SI_YPOV2 = 0x213,
|
||||
SI_UPOV2 = 0x214,
|
||||
SI_DPOV2 = 0x215,
|
||||
SI_LPOV2 = 0x216,
|
||||
SI_RPOV2 = 0x217,
|
||||
|
||||
XI_CONNECT = 0x300,
|
||||
XI_THUMBLX = 0x301,
|
||||
XI_THUMBLY = 0x302,
|
||||
XI_THUMBRX = 0x303,
|
||||
XI_THUMBRY = 0x304,
|
||||
XI_LEFT_TRIGGER = 0x305,
|
||||
XI_RIGHT_TRIGGER = 0x306,
|
||||
|
||||
/*XI_DPAD_UP = 0x307,
|
||||
XI_DPAD_DOWN = 0x308,
|
||||
XI_DPAD_LEFT = 0x309,
|
||||
XI_DPAD_RIGHT = 0x310,*/
|
||||
|
||||
XI_START = 0x311,
|
||||
XI_BACK = 0x312,
|
||||
XI_LEFT_THUMB = 0x313,
|
||||
XI_RIGHT_THUMB = 0x314,
|
||||
XI_LEFT_SHOULDER = 0x315,
|
||||
XI_RIGHT_SHOULDER = 0x316,
|
||||
|
||||
XI_A = 0x317,
|
||||
XI_B = 0x318,
|
||||
XI_X = 0x319,
|
||||
XI_Y = 0x320,
|
||||
};
|
||||
|
||||
/// Input device types
|
||||
enum InputDeviceTypes
|
||||
{
|
||||
UnknownDeviceType,
|
||||
MouseDeviceType,
|
||||
KeyboardDeviceType,
|
||||
JoystickDeviceType,
|
||||
GamepadDeviceType,
|
||||
XInputDeviceType,
|
||||
|
||||
NUM_INPUT_DEVICE_TYPES
|
||||
};
|
||||
|
||||
/// Device Event Action Types
|
||||
enum InputActionType
|
||||
{
|
||||
/// Button was depressed.
|
||||
SI_MAKE = 0x01,
|
||||
|
||||
/// Button was released.
|
||||
SI_BREAK = 0x02,
|
||||
|
||||
/// An axis moved.
|
||||
SI_MOVE = 0x03,
|
||||
|
||||
/// A key repeat occurred. Happens in between a SI_MAKE and SI_BREAK.
|
||||
SI_REPEAT = 0x04,
|
||||
};
|
||||
|
||||
///Device Event Types
|
||||
enum InputEventType
|
||||
{
|
||||
SI_UNKNOWN = 0x01,
|
||||
SI_BUTTON = 0x02,
|
||||
SI_POV = 0x03,
|
||||
SI_AXIS = 0x04,
|
||||
SI_KEY = 0x0A,
|
||||
};
|
||||
|
||||
/// Wildcard match used by the input system.
|
||||
#define SI_ANY 0xff
|
||||
|
||||
// Modifier Keys
|
||||
enum InputModifiers
|
||||
{
|
||||
/// shift and ctrl are the same between platforms.
|
||||
SI_LSHIFT = BIT(0),
|
||||
SI_RSHIFT = BIT(1),
|
||||
SI_SHIFT = (SI_LSHIFT|SI_RSHIFT),
|
||||
SI_LCTRL = BIT(2),
|
||||
SI_RCTRL = BIT(3),
|
||||
SI_CTRL = (SI_LCTRL|SI_RCTRL),
|
||||
|
||||
/// win altkey, mapped to mac cmdkey.
|
||||
SI_LALT = BIT(4),
|
||||
SI_RALT = BIT(5),
|
||||
SI_ALT = (SI_LALT|SI_RALT),
|
||||
|
||||
/// mac optionkey
|
||||
SI_MAC_LOPT = BIT(6),
|
||||
SI_MAC_ROPT = BIT(7),
|
||||
SI_MAC_OPT = (SI_MAC_LOPT|SI_MAC_ROPT),
|
||||
|
||||
/// modifier keys used for common operations
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
SI_COPYPASTE = SI_ALT,
|
||||
SI_MULTISELECT = SI_ALT,
|
||||
SI_RANGESELECT = SI_SHIFT,
|
||||
SI_PRIMARY_ALT = SI_MAC_OPT, ///< Primary key used for toggling into alternates of commands.
|
||||
SI_PRIMARY_CTRL = SI_ALT, ///< Primary key used for triggering commands.
|
||||
#else
|
||||
SI_COPYPASTE = SI_CTRL,
|
||||
SI_MULTISELECT = SI_CTRL,
|
||||
SI_RANGESELECT = SI_SHIFT,
|
||||
SI_PRIMARY_ALT = SI_ALT,
|
||||
SI_PRIMARY_CTRL = SI_CTRL,
|
||||
#endif
|
||||
/// modfier key used in conjunction w/ arrow keys to move cursor to next word
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
SI_WORDJUMP = SI_MAC_OPT,
|
||||
#else
|
||||
SI_WORDJUMP = SI_CTRL,
|
||||
#endif
|
||||
/// modifier key used in conjunction w/ arrow keys to move cursor to beginning / end of line
|
||||
SI_LINEJUMP = SI_ALT,
|
||||
|
||||
/// modifier key used in conjunction w/ home & end to jump to the top or bottom of a document
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
SI_DOCJUMP = SI_ANY,
|
||||
#else
|
||||
SI_DOCJUMP = SI_CTRL,
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// Generic input event.
|
||||
struct InputEventInfo
|
||||
{
|
||||
InputEventInfo()
|
||||
{
|
||||
deviceInst = 0;
|
||||
fValue = 0.f;
|
||||
deviceType = (InputDeviceTypes)0;
|
||||
objType = (InputEventType)0;
|
||||
ascii = 0;
|
||||
objInst = (InputObjectInstances)0;
|
||||
action = (InputActionType)0;
|
||||
modifier = (InputModifiers)0;
|
||||
}
|
||||
|
||||
/// Device instance: joystick0, joystick1, etc
|
||||
U32 deviceInst;
|
||||
|
||||
/// Value ranges from -1.0 to 1.0
|
||||
F32 fValue;
|
||||
|
||||
/// What was the action? (MAKE/BREAK/MOVE)
|
||||
InputActionType action;
|
||||
InputDeviceTypes deviceType;
|
||||
InputEventType objType;
|
||||
InputObjectInstances objInst;
|
||||
|
||||
/// ASCII character code if this is a keyboard event.
|
||||
U16 ascii;
|
||||
|
||||
/// Modifiers to action: SI_LSHIFT, SI_LCTRL, etc.
|
||||
InputModifiers modifier;
|
||||
|
||||
inline void postToSignal(InputEvent &ie)
|
||||
{
|
||||
ie.trigger(deviceInst, fValue, deviceType, objType, ascii, objInst, action, modifier);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
127
Engine/source/platform/menus/menuBar.cpp
Normal file
127
Engine/source/platform/menus/menuBar.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/menus/menuBar.h"
|
||||
#include "platform/menus/popupMenu.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor/Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
MenuBar::MenuBar()
|
||||
{
|
||||
createPlatformPopupMenuData();
|
||||
|
||||
mCanvas = NULL;
|
||||
}
|
||||
|
||||
MenuBar::~MenuBar()
|
||||
{
|
||||
removeFromCanvas();
|
||||
|
||||
deletePlatformPopupMenuData();
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(MenuBar);
|
||||
|
||||
ConsoleDocClass( MenuBar,
|
||||
"@brief Used for rendering platform menu bars\n\n"
|
||||
"Internal use only\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MenuBar::addObject(SimObject *obj)
|
||||
{
|
||||
Parent::addObject(obj);
|
||||
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
|
||||
}
|
||||
|
||||
void MenuBar::removeObject(SimObject *obj)
|
||||
{
|
||||
Parent::removeObject(obj);
|
||||
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
|
||||
}
|
||||
|
||||
void MenuBar::insertObject(SimObject *obj, S32 pos)
|
||||
{
|
||||
Parent::addObject(obj);
|
||||
|
||||
if(pos >= size())
|
||||
pos = size() - 1;
|
||||
|
||||
if(pos < size())
|
||||
{
|
||||
if(pos < 0) pos = 0;
|
||||
Parent::reOrder(obj, at(pos));
|
||||
}
|
||||
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
|
||||
}
|
||||
|
||||
void MenuBar::pushObject(SimObject *obj)
|
||||
{
|
||||
Parent::pushObject(obj);
|
||||
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
|
||||
}
|
||||
|
||||
void MenuBar::popObject()
|
||||
{
|
||||
Parent::popObject();
|
||||
updateMenuBar();
|
||||
}
|
||||
|
||||
bool MenuBar::reOrder(SimObject *obj, SimObject *target /*= 0*/)
|
||||
{
|
||||
bool ret = Parent::reOrder(obj, target);
|
||||
if(ret)
|
||||
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(MenuBar, attachToCanvas, void, 4, 4, "(GuiCanvas, pos)")
|
||||
{
|
||||
object->attachToCanvas(dynamic_cast<GuiCanvas*>(Sim::findObject(argv[2])), dAtoi(argv[3]));
|
||||
}
|
||||
|
||||
ConsoleMethod(MenuBar, removeFromCanvas, void, 2, 2, "()")
|
||||
{
|
||||
object->removeFromCanvas();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(MenuBar, insert, void, 4, 4,"(object, pos) insert object at position")
|
||||
{
|
||||
SimObject* pObject = Sim::findObject(argv[2]);
|
||||
|
||||
if(pObject)
|
||||
object->insertObject(pObject, dAtoi(argv[3]));
|
||||
}
|
||||
71
Engine/source/platform/menus/menuBar.h
Normal file
71
Engine/source/platform/menus/menuBar.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/simBase.h"
|
||||
|
||||
#ifndef _MENUBAR_H_
|
||||
#define _MENUBAR_H_
|
||||
|
||||
// Forward Refs
|
||||
class PlatformMenuBarData;
|
||||
class PopupMenu;
|
||||
class GuiCanvas;
|
||||
|
||||
class MenuBar : public SimSet
|
||||
{
|
||||
typedef SimSet Parent;
|
||||
|
||||
protected:
|
||||
PlatformMenuBarData *mData;
|
||||
GuiCanvas *mCanvas;
|
||||
|
||||
/// Update the native menu bar to ensure consistency with the set
|
||||
void updateMenuBar(PopupMenu *menu = NULL);
|
||||
|
||||
void createPlatformPopupMenuData();
|
||||
void deletePlatformPopupMenuData();
|
||||
|
||||
public:
|
||||
MenuBar();
|
||||
virtual ~MenuBar();
|
||||
DECLARE_CONOBJECT(MenuBar);
|
||||
|
||||
/// Attach this menu bar to the native menu bar
|
||||
void attachToCanvas(GuiCanvas *owner, S32 pos);
|
||||
/// Remove this menu bar from the native menu bar
|
||||
void removeFromCanvas();
|
||||
|
||||
/// Returns true if this menu is attached to the menu bar
|
||||
bool isAttachedToCanvas() { return mCanvas != NULL; }
|
||||
|
||||
virtual void insertObject(SimObject *obj, S32 pos);
|
||||
|
||||
// Overridden SimSet methods to ensure menu bar consistency when attached
|
||||
virtual void addObject(SimObject *obj);
|
||||
virtual void removeObject(SimObject *obj);
|
||||
virtual void pushObject(SimObject *obj);
|
||||
virtual void popObject();
|
||||
|
||||
virtual bool reOrder(SimObject *obj, SimObject *target = 0);
|
||||
};
|
||||
|
||||
#endif // _MENUBAR_H_
|
||||
270
Engine/source/platform/menus/popupMenu.cpp
Normal file
270
Engine/source/platform/menus/popupMenu.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/menus/popupMenu.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
static U32 sMaxPopupGUID = 0;
|
||||
PopupMenuEvent PopupMenu::smPopupMenuEvent;
|
||||
bool PopupMenu::smSelectionEventHandled = false;
|
||||
|
||||
/// Event class used to remove popup menus from the event notification in a safe way
|
||||
class PopUpNotifyRemoveEvent : public SimEvent
|
||||
{
|
||||
public:
|
||||
void process(SimObject *object)
|
||||
{
|
||||
PopupMenu::smPopupMenuEvent.remove((PopupMenu *)object, &PopupMenu::handleSelectEvent);
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor/Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
PopupMenu::PopupMenu() : mCanvas(NULL)
|
||||
{
|
||||
createPlatformPopupMenuData();
|
||||
|
||||
mSubmenus = new SimSet;
|
||||
mSubmenus->registerObject();
|
||||
|
||||
mBarTitle = StringTable->insert("");
|
||||
mIsPopup = false;
|
||||
|
||||
mPopupGUID = sMaxPopupGUID++;
|
||||
}
|
||||
|
||||
PopupMenu::~PopupMenu()
|
||||
{
|
||||
// This searches the menu bar so is safe to call for menus
|
||||
// that aren't on it, since nothing will happen.
|
||||
removeFromMenuBar();
|
||||
|
||||
SimSet::iterator i;
|
||||
while((i = mSubmenus->begin()) != mSubmenus->end())
|
||||
{
|
||||
(*i)->deleteObject();
|
||||
}
|
||||
|
||||
mSubmenus->deleteObject();
|
||||
deletePlatformPopupMenuData();
|
||||
|
||||
PopupMenu::smPopupMenuEvent.remove(this, &PopupMenu::handleSelectEvent);
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(PopupMenu);
|
||||
|
||||
ConsoleDocClass( PopupMenu,
|
||||
"@brief PopupMenu represents a system menu.\n\n"
|
||||
"You can add menu items to the menu, but there is no torque object associated "
|
||||
"with these menu items, they exist only in a platform specific manner.\n\n"
|
||||
"@note Internal use only\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void PopupMenu::initPersistFields()
|
||||
{
|
||||
addField("isPopup", TypeBool, Offset(mIsPopup, PopupMenu), "true if this is a pop-up/context menu. defaults to false.");
|
||||
addField("barTitle", TypeCaseString, Offset(mBarTitle, PopupMenu), "the title of this menu when attached to a menu bar");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool PopupMenu::onAdd()
|
||||
{
|
||||
if(! Parent::onAdd())
|
||||
return false;
|
||||
|
||||
createPlatformMenu();
|
||||
|
||||
Con::executef(this, "onAdd");
|
||||
return true;
|
||||
}
|
||||
|
||||
void PopupMenu::onRemove()
|
||||
{
|
||||
Con::executef(this, "onRemove");
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void PopupMenu::onMenuSelect()
|
||||
{
|
||||
Con::executef(this, "onMenuSelect");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void PopupMenu::handleSelectEvent(U32 popID, U32 command)
|
||||
{
|
||||
if (popID == mPopupGUID && canHandleID(command))
|
||||
if (handleSelect(command))
|
||||
smSelectionEventHandled = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void PopupMenu::onAttachToMenuBar(GuiCanvas *canvas, S32 pos, const char *title)
|
||||
{
|
||||
mCanvas = canvas;
|
||||
|
||||
// Attached menus must be notified of menu events
|
||||
smPopupMenuEvent.notify(this, &PopupMenu::handleSelectEvent);
|
||||
|
||||
// Pass on to sub menus
|
||||
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
|
||||
{
|
||||
PopupMenu *mnu = dynamic_cast<PopupMenu *>(*i);
|
||||
if(mnu == NULL)
|
||||
continue;
|
||||
|
||||
mnu->onAttachToMenuBar(canvas, pos, title);
|
||||
}
|
||||
|
||||
// Call script
|
||||
if(isProperlyAdded())
|
||||
Con::executef(this, "onAttachToMenuBar", Con::getIntArg(canvas ? canvas->getId() : 0), Con::getIntArg(pos), title);
|
||||
}
|
||||
|
||||
void PopupMenu::onRemoveFromMenuBar(GuiCanvas *canvas)
|
||||
{
|
||||
mCanvas = NULL;
|
||||
|
||||
// We are no longer interested in select events, remove ourselves from the notification list in a safe way
|
||||
Sim::postCurrentEvent(this, new PopUpNotifyRemoveEvent());
|
||||
|
||||
// Pass on to sub menus
|
||||
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
|
||||
{
|
||||
PopupMenu *mnu = dynamic_cast<PopupMenu *>(*i);
|
||||
if(mnu == NULL)
|
||||
continue;
|
||||
|
||||
mnu->onRemoveFromMenuBar(canvas);
|
||||
}
|
||||
|
||||
// Call script
|
||||
if(isProperlyAdded())
|
||||
Con::executef(this, "onRemoveFromMenuBar", Con::getIntArg(canvas ? canvas->getId() : 0));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool PopupMenu::onMessageReceived(StringTableEntry queue, const char* event, const char* data)
|
||||
{
|
||||
return Con::executef(this, "onMessageReceived", queue, event, data);
|
||||
}
|
||||
|
||||
|
||||
bool PopupMenu::onMessageObjectReceived(StringTableEntry queue, Message *msg )
|
||||
{
|
||||
return Con::executef(this, "onMessageReceived", queue, Con::getIntArg(msg->getId()));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(PopupMenu, insertItem, S32, 3, 5, "(pos[, title][, accelerator])")
|
||||
{
|
||||
return object->insertItem(dAtoi(argv[2]), argc < 4 ? NULL : argv[3], argc < 5 ? "" : argv[4]);
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, removeItem, void, 3, 3, "(pos)")
|
||||
{
|
||||
object->removeItem(dAtoi(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, insertSubMenu, S32, 5, 5, "(pos, title, subMenu)")
|
||||
{
|
||||
PopupMenu *mnu = dynamic_cast<PopupMenu *>(Sim::findObject(argv[4]));
|
||||
if(mnu == NULL)
|
||||
{
|
||||
Con::errorf("PopupMenu::insertSubMenu - Invalid PopupMenu object specified for submenu");
|
||||
return -1;
|
||||
}
|
||||
return object->insertSubMenu(dAtoi(argv[2]), argv[3], mnu);
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, setItem, bool, 4, 5, "(pos, title[, accelerator])")
|
||||
{
|
||||
return object->setItem(dAtoi(argv[2]), argv[3], argc < 5 ? "" : argv[4]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(PopupMenu, enableItem, void, 4, 4, "(pos, enabled)")
|
||||
{
|
||||
object->enableItem(dAtoi(argv[2]), dAtob(argv[3]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, checkItem, void, 4, 4, "(pos, checked)")
|
||||
{
|
||||
object->checkItem(dAtoi(argv[2]), dAtob(argv[3]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, checkRadioItem, void, 5, 5, "(firstPos, lastPos, checkPos)")
|
||||
{
|
||||
object->checkRadioItem(dAtoi(argv[2]), dAtoi(argv[3]), dAtoi(argv[4]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, isItemChecked, bool, 3, 3, "(pos)")
|
||||
{
|
||||
return object->isItemChecked(dAtoi(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, getItemCount, S32, 2, 2, "()")
|
||||
{
|
||||
return object->getItemCount();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(PopupMenu, attachToMenuBar, void, 5, 5, "(GuiCanvas, pos, title)")
|
||||
{
|
||||
object->attachToMenuBar(dynamic_cast<GuiCanvas*>(Sim::findObject(argv[2])),dAtoi(argv[3]), argv[4]);
|
||||
}
|
||||
|
||||
ConsoleMethod(PopupMenu, removeFromMenuBar, void, 2, 2, "()")
|
||||
{
|
||||
object->removeFromMenuBar();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(PopupMenu, showPopup, void, 3, 5, "(Canvas,[x, y])")
|
||||
{
|
||||
GuiCanvas *pCanvas = dynamic_cast<GuiCanvas*>(Sim::findObject(argv[2]));
|
||||
S32 x = argc >= 4 ? dAtoi(argv[3]) : -1;
|
||||
S32 y = argc >= 5 ? dAtoi(argv[4]) : -1;
|
||||
object->showPopup(pCanvas, x, y);
|
||||
}
|
||||
189
Engine/source/platform/menus/popupMenu.h
Normal file
189
Engine/source/platform/menus/popupMenu.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/simBase.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "util/messaging/dispatcher.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
|
||||
#ifndef _POPUPMENU_H_
|
||||
#define _POPUPMENU_H_
|
||||
|
||||
// Forward ref used by the platform code
|
||||
struct PlatformPopupMenuData;
|
||||
class MenuBar;
|
||||
|
||||
// PopupMenu represents a menu.
|
||||
// You can add menu items to the menu, but there is no torque object associated
|
||||
// with these menu items, they exist only in a platform specific manner.
|
||||
class PopupMenu : public SimObject, public virtual Dispatcher::IMessageListener
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
friend class MenuBar;
|
||||
|
||||
private:
|
||||
/// Used by MenuBar to attach the menu to the menu bar. Do not use anywhere else.
|
||||
void attachToMenuBar(GuiCanvas *owner, S32 pos);
|
||||
|
||||
protected:
|
||||
PlatformPopupMenuData *mData;
|
||||
|
||||
SimSet *mSubmenus;
|
||||
SimObjectPtr<GuiCanvas> mCanvas;
|
||||
|
||||
StringTableEntry mBarTitle;
|
||||
|
||||
U32 mPopupGUID;
|
||||
|
||||
bool mIsPopup;
|
||||
|
||||
public:
|
||||
PopupMenu();
|
||||
virtual ~PopupMenu();
|
||||
void createPlatformPopupMenuData();
|
||||
void deletePlatformPopupMenuData();
|
||||
|
||||
DECLARE_CONOBJECT(PopupMenu);
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
static PopupMenuEvent smPopupMenuEvent;
|
||||
static bool smSelectionEventHandled; /// Set to true if any menu or submenu handles a selection event
|
||||
|
||||
/// Creates the platform specific menu object, a peer to this object.
|
||||
/// The platform menu *must* exist before calling any method that manipulates
|
||||
/// menu items or displays the menu.
|
||||
/// implementd on a per-platform basis.
|
||||
void createPlatformMenu();
|
||||
|
||||
void setBarTitle(const char * val) { mBarTitle = StringTable->insert(val, true); }
|
||||
StringTableEntry getBarTitle() const { return mBarTitle; }
|
||||
|
||||
/// pass NULL for @p title to insert a separator
|
||||
/// returns the menu item's ID, or -1 on failure.
|
||||
/// implementd on a per-platform basis.
|
||||
/// TODO: factor out common code
|
||||
S32 insertItem(S32 pos, const char *title, const char* accelerator);
|
||||
|
||||
/// Sets the name title and accelerator for
|
||||
/// an existing item.
|
||||
bool setItem(S32 pos, const char *title, const char* accelerator);
|
||||
|
||||
/// pass NULL for @p title to insert a separator
|
||||
/// returns the menu item's ID, or -1 on failure.
|
||||
/// adds the submenu to the mSubmenus vector.
|
||||
/// implemented on a per-platform basis.
|
||||
/// TODO: factor out common code
|
||||
S32 insertSubMenu(S32 pos, const char *title, PopupMenu *submenu);
|
||||
|
||||
/// remove the menu item at @p itemPos
|
||||
/// if the item has a submenu, it is removed from the mSubmenus list.
|
||||
/// implemented on a per-platform basis.
|
||||
/// TODO: factor out common code
|
||||
void removeItem(S32 itemPos);
|
||||
|
||||
/// implemented on a per-platform basis.
|
||||
void enableItem(S32 pos, bool enable);
|
||||
/// implemented on a per-platform basis.
|
||||
void checkItem(S32 pos, bool checked);
|
||||
|
||||
/// All items at positions firstPos through lastPos are unchecked, and the
|
||||
/// item at checkPos is checked.
|
||||
/// implemented on a per-platform basis.
|
||||
void checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos);
|
||||
bool isItemChecked(S32 pos);
|
||||
|
||||
/// Returns the number of items in the menu.
|
||||
U32 getItemCount();
|
||||
|
||||
/// Returns the popup GUID
|
||||
U32 getPopupGUID() { return mPopupGUID; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// New code should not use these methods directly, use the menu bar instead.
|
||||
//
|
||||
// They remain for compatibility with old code and will be changing/going away
|
||||
// once the existing code is moved over to the menu bar.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Places this menu in the menu bar of the application's main window.
|
||||
/// @param owner The GuiCanvas that owns the PlatformWindow that this call is associated with
|
||||
/// @param pos The relative position at which to place the menu.
|
||||
/// @param title The name of the menu
|
||||
void attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title);
|
||||
|
||||
/// Removes this menu from the menu bar.
|
||||
void removeFromMenuBar();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Called when the menu has been attached to the menu bar
|
||||
void onAttachToMenuBar(GuiCanvas *canvas, S32 pos, const char *title);
|
||||
|
||||
/// Called when the menu has been removed from the menu bar
|
||||
void onRemoveFromMenuBar(GuiCanvas *canvas);
|
||||
|
||||
/// Returns the position index of this menu on the bar.
|
||||
S32 getPosOnMenuBar();
|
||||
|
||||
/// Returns true if this menu is attached to the menu bar
|
||||
bool isAttachedToMenuBar() { return mCanvas != NULL; }
|
||||
|
||||
/// Displays this menu as a popup menu and blocks until the user has selected
|
||||
/// an item.
|
||||
/// @param canvas the owner to show this popup associated with
|
||||
/// @param x window local x coordinate at which to display the popup menu
|
||||
/// @param y window local y coordinate at which to display the popup menu
|
||||
/// implemented on a per-platform basis.
|
||||
void showPopup(GuiCanvas *owner, S32 x = -1, S32 y = -1);
|
||||
|
||||
/// Returns true iff this menu contains an item that matches @p iD.
|
||||
/// implemented on a per-platform basis.
|
||||
/// TODO: factor out common code
|
||||
bool canHandleID(U32 iD);
|
||||
|
||||
/// A menu item in this menu has been selected by id.
|
||||
/// Submenus are given a chance to respond to the command first.
|
||||
/// If no submenu can handle the command id, this menu handles it.
|
||||
/// The script callback this::onSelectItem( position, text) is called.
|
||||
/// If @p text is null, then the text arg passed to script is the text of
|
||||
/// the selected menu item.
|
||||
/// implemented on a per-platform basis.
|
||||
/// TODO: factor out common code
|
||||
bool handleSelect(U32 command, const char *text = NULL);
|
||||
|
||||
void onMenuSelect();
|
||||
|
||||
/// Helper function to allow menu selections from signal events.
|
||||
/// Wraps canHandleID() and handleSelect() in one function
|
||||
/// without changing their internal functionality, so
|
||||
/// it should work regardless of platform.
|
||||
void handleSelectEvent(U32 popID, U32 command);
|
||||
|
||||
virtual bool onMessageReceived(StringTableEntry queue, const char* event, const char* data );
|
||||
virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg );
|
||||
};
|
||||
|
||||
#endif // _POPUPMENU_H_
|
||||
208
Engine/source/platform/nativeDialogs/fileDialog.h
Normal file
208
Engine/source/platform/nativeDialogs/fileDialog.h
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _FILEDIALOG_H_
|
||||
#define _FILEDIALOG_H_
|
||||
#include "console/simBase.h"
|
||||
|
||||
// [03/14/07] The file dialogs need refactoring, and will be refactored in Jugg.
|
||||
// Things that might need to change:
|
||||
// - The interface is not in fact platform agnotsic, it is win32 oriented.
|
||||
// - Filter format is highly windows specific, and is a little fragile, both for
|
||||
// win32 and for other platforms.
|
||||
// - Platform specific path strings are exposed to the console, because the
|
||||
// protected validators save them as such.
|
||||
// - Several of the FDS_XXX values are not options we want to give the user, such
|
||||
// as NOT warning on file overwrite. The values FDS_OVERWRITEPROMPT,
|
||||
// FDS_MUSTEXIST, and FDS_CHANGEPATH are not good things to give the user.
|
||||
// - The Execute method is virtual for good reason. It should be implemented for
|
||||
// each subclass. If common behavior is needed for Execute(), it can be
|
||||
// factored out in hidden platform specific code.
|
||||
|
||||
|
||||
/// @defgroup SystemDialogs Using System Dialogs
|
||||
|
||||
/// @ingroup SystemDialogs
|
||||
/// FileDialogOpaqueData is both defined and implemented on a platform specific
|
||||
/// basis.
|
||||
class FileDialogOpaqueData;
|
||||
|
||||
/// @ingroup SystemDialogs
|
||||
/// @internal
|
||||
/// Platform Agnostic Structure for holding information about a file dialog.
|
||||
struct FileDialogData
|
||||
{
|
||||
|
||||
public:
|
||||
FileDialogData();
|
||||
~FileDialogData();
|
||||
|
||||
enum DialogStyle
|
||||
{
|
||||
FDS_OPEN = BIT(0),///< This is an open dialog.
|
||||
FDS_SAVE = BIT(1),///< This is a save dialog.
|
||||
FDS_OVERWRITEPROMPT = BIT(2),///< Can only be used in conjunction with style SaveDialog: prompt for a confirmation if a file will be overwritten.
|
||||
FDS_MUSTEXIST = BIT(3),///< The user may only select files that actually exist.
|
||||
FDS_MULTIPLEFILES = BIT(4),///< Can only be used in conjunction with style OpenDialog: allows selecting multiple files.
|
||||
FDS_CHANGEPATH = BIT(5),///< Change the current working path to the directory where the file(s) chosen by the user are.
|
||||
FDS_BROWSEFOLDER = BIT(6) ///< Select folders instead of files
|
||||
};
|
||||
U8 mStyle; ///< Specifies the Style of the File Dialog @see DialogStyle
|
||||
|
||||
StringTableEntry mFilters; ///< List of Filters pipe separated e.g. "BMP Files (*.bmp)|*.bmp|JPG Files (*.jpg)|*.jpg"
|
||||
//StringTableEntry mFiles; // this is never used ///< Should only be referenced when using dialogStyle OpenDialog AND MultipleFiles: List of Files returned pipe separated
|
||||
StringTableEntry mFile; ///< Should be referenced when dialogStyle MultipleFiles is NOT used: the file path of the user selected file.
|
||||
StringTableEntry mDefaultPath; ///< Default path of dialog
|
||||
StringTableEntry mDefaultFile; ///< Default selected file of dialog
|
||||
StringTableEntry mTitle; ///< Title to display in file dialog
|
||||
|
||||
FileDialogOpaqueData *mOpaqueData; ///< Stores platform specific info about the dialog
|
||||
|
||||
};
|
||||
|
||||
/// @ingroup SystemDialogs
|
||||
/// FileDialog is a platform agnostic dialog interface for querying the user for
|
||||
/// file locations. It is designed to be used through the exposed
|
||||
/// scripting interface.
|
||||
///
|
||||
/// FileDialog is the base class for Native File Dialog controls in Torque. It provides these
|
||||
/// basic areas of functionality:
|
||||
///
|
||||
/// - Inherits from SimObject and is exposed to the scripting interface
|
||||
/// - Provides blocking interface to allow instant return to script execution
|
||||
/// - Simple object configuration makes practical use easy and effective
|
||||
///
|
||||
/// @attention
|
||||
/// FileDialog is *NOT* intended to be used directly in script and is only exposed to script
|
||||
/// to expose generic file dialog attributes.
|
||||
/// @see OpenFileDialog for a practical example on opening a file
|
||||
/// @see SaveFileDialog for a practical example of saving a file
|
||||
///
|
||||
///
|
||||
/// @{
|
||||
class FileDialog : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
protected:
|
||||
FileDialogData mData; ///< Stores platform agnostic information about the dialogs properties
|
||||
bool mChangePath; ///< Exposed ChangePath Property
|
||||
bool mBoolTranslator; ///< Internally used to translate boolean values into their respective bits of dialog style
|
||||
public:
|
||||
|
||||
FileDialog();
|
||||
virtual ~FileDialog();
|
||||
DECLARE_CONOBJECT(FileDialog);
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
virtual bool Execute();
|
||||
|
||||
FileDialogData &getData() { return mData; };
|
||||
protected:
|
||||
/// @name FileDialog Properties
|
||||
/// @{
|
||||
/// @@property DefaultPath (String) : <i>Path to use as the default when the dialog is shown.</i>
|
||||
/// @code %fd.DefaultPath = "/source/myGameProject/data/images"; @endcode
|
||||
///
|
||||
/// @li @b ChangePath (bool) : <c>Will change the working path of the tools to the selected path when not canceled</c>
|
||||
/// @code %fd.ChangePath = true; // Change Working Path on Success @endcode
|
||||
/// @internal
|
||||
static bool setDefaultPath( void *object, const char *index, const char *data );
|
||||
static bool setDefaultFile( void *object, const char *index, const char *data );
|
||||
static bool setFilters( void *object, const char *index, const char *data );
|
||||
static bool setChangePath( void *object, const char *index, const char *data );
|
||||
static const char* getChangePath(void* obj, const char* data);
|
||||
///
|
||||
/// @}
|
||||
|
||||
static bool setFile( void *object, const char *index, const char *data );
|
||||
};
|
||||
/// @}
|
||||
|
||||
class OpenFileDialog : public FileDialog
|
||||
{
|
||||
typedef FileDialog Parent;
|
||||
|
||||
/// Field Values
|
||||
/// @{
|
||||
/// @internal
|
||||
bool mMustExist; ///< Corresponds to FDS_MUSTEXIST flag on the PlatformFileDlgData structure
|
||||
bool mMultipleFiles; ///< Corresponds to the FDS_MULTIPLEFILES flag on the PlatformFileDlgData structure
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
OpenFileDialog();
|
||||
virtual ~OpenFileDialog();
|
||||
|
||||
DECLARE_CONOBJECT(OpenFileDialog); /// @internal
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
protected:
|
||||
///
|
||||
/// @}
|
||||
|
||||
/// Must Exist Property
|
||||
static bool setMustExist( void *object, const char *index, const char *data );
|
||||
static const char*getMustExist(void* obj, const char* data);
|
||||
|
||||
/// Multiple Files Property
|
||||
static bool setMultipleFiles( void *object, const char *index, const char *data );
|
||||
static const char* getMultipleFiles(void* obj, const char* data);
|
||||
};
|
||||
|
||||
class OpenFolderDialog : public OpenFileDialog
|
||||
{
|
||||
typedef OpenFileDialog Parent;
|
||||
|
||||
public:
|
||||
StringTableEntry mMustExistInDir;
|
||||
|
||||
OpenFolderDialog();
|
||||
DECLARE_CONOBJECT(OpenFolderDialog);
|
||||
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
class SaveFileDialog : public FileDialog
|
||||
{
|
||||
typedef FileDialog Parent;
|
||||
|
||||
public:
|
||||
|
||||
SaveFileDialog();
|
||||
virtual ~SaveFileDialog();
|
||||
DECLARE_CONOBJECT(SaveFileDialog);
|
||||
|
||||
bool mOverwritePrompt;
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
protected:
|
||||
// Overwrite Prompt Property
|
||||
static bool setOverwritePrompt( void *object, const char *index, const char *data );
|
||||
static const char* getOverwritePrompt(void* obj, const char* data);
|
||||
|
||||
};
|
||||
|
||||
#endif // _FILEDIALOG_H_
|
||||
101
Engine/source/platform/nativeDialogs/msgBox.cpp
Normal file
101
Engine/source/platform/nativeDialogs/msgBox.cpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/module.h"
|
||||
#include "console/console.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "platform/nativeDialogs/msgBox.h"
|
||||
|
||||
|
||||
DefineEnumType( MBButtons );
|
||||
DefineEnumType( MBIcons );
|
||||
DefineEnumType( MBReturnVal );
|
||||
|
||||
|
||||
static const MBReturnVal gsOK = MROk;
|
||||
static const MBReturnVal gsCancel = MRCancel;
|
||||
static const MBReturnVal gsRetry = MRRetry;
|
||||
static const MBReturnVal gsDontSave = MRDontSave;
|
||||
|
||||
AFTER_MODULE_INIT( Sim )
|
||||
{
|
||||
#if !defined( _XBOX ) && !defined( TORQUE_DEDICATED )
|
||||
Con::addConstant( "$MROk", TypeS32, &gsOK, "Determines the ok button press state in a message box.\n"
|
||||
"@ingroup Platform" );
|
||||
Con::addConstant( "$MRCancel", TypeS32, &gsCancel, "Determines the cancel button press state in a message box.\n"
|
||||
"@ingroup Platform" );
|
||||
Con::addConstant( "$MRRetry", TypeS32, &gsRetry, "Determines the retry button press state in a message box.\n"
|
||||
"@ingroup Platform");
|
||||
Con::addConstant( "$MRDontSave", TypeS32, &gsDontSave, "Determines the don't save button press state in a message box.\n"
|
||||
"@ingroup Platform" );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ImplementEnumType( MBButtons,
|
||||
"Which buttons to display on a message box.\n\n"
|
||||
"@ingroup Platform" )
|
||||
{ MBOk, "Ok" },
|
||||
{ MBOkCancel, "OkCancel" },
|
||||
{ MBRetryCancel, "RetryCancel" },
|
||||
{ MBSaveDontSave, "SaveDontSave" }, // maps to yes/no on win, to save/discard on mac.
|
||||
{ MBSaveDontSaveCancel, "SaveDontSaveCancel" }, // maps to yes/no/cancel on win, to save/cancel/don'tsave on mac.
|
||||
EndImplementEnumType;
|
||||
|
||||
ImplementEnumType( MBIcons,
|
||||
"What icon to show on a message box.\n\n"
|
||||
"@ingroup Platform" )
|
||||
{ MIInformation, "Information" },// win: blue i, mac: app icon or talking head
|
||||
{ MIWarning, "Warning" }, // win & mac: yellow triangle with exclamation pt
|
||||
{ MIStop, "Stop" }, // win: red x, mac: app icon or stop icon, depending on version
|
||||
{ MIQuestion, "Question" }, // win: blue ?, mac: app icon
|
||||
EndImplementEnumType;
|
||||
|
||||
ImplementEnumType( MBReturnVal,
|
||||
"Return value for messageBox() indicating which button was pressed by the user.\n\n"
|
||||
"@ingroup Platform" )
|
||||
{ MROk, "OK" },
|
||||
{ MRCancel, "Cancelled" },
|
||||
{ MRRetry, "Retry" },
|
||||
{ MRDontSave, "DontSave" }
|
||||
EndImplementEnumType;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction( messageBox, S32, ( const char* title, const char* message, MBButtons buttons, MBIcons icons ), ( MBOkCancel, MIInformation ),
|
||||
"Display a modal message box using the platform's native message box implementation.\n\n"
|
||||
"@param title The title to display on the message box window.\n"
|
||||
"@param message The text message to display in the box.\n"
|
||||
"@param buttons Which buttons to put on the message box.\n"
|
||||
"@param icons Which icon to show next to the message.\n"
|
||||
"@return One of $MROK, $MRCancel, $MRRetry, and $MRDontSave identifying the button that the user pressed.\n"
|
||||
"@tsexample\n"
|
||||
"messageBox( \"Error\", \"\" );\n" //TODO
|
||||
"@endtsexample\n\n"
|
||||
"@ingroup Platform" )
|
||||
{
|
||||
return Platform::messageBox( title, message, buttons, icons );
|
||||
}
|
||||
62
Engine/source/platform/nativeDialogs/msgBox.h
Normal file
62
Engine/source/platform/nativeDialogs/msgBox.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MSGBOX_H_
|
||||
#define _MSGBOX_H_
|
||||
|
||||
|
||||
// [tom, 10/17/2006] Note: If you change either of these enums, make sure you
|
||||
// update the relevant code in the all the platform layers.
|
||||
|
||||
// [pauls, 3/20/2007] Reduced the available types of dialog boxes in order to
|
||||
// maintain a consistent but platform - appropriate look and feel in Torque.
|
||||
|
||||
enum MBButtons
|
||||
{
|
||||
MBOk,
|
||||
MBOkCancel,
|
||||
MBRetryCancel,
|
||||
MBSaveDontSave,
|
||||
MBSaveDontSaveCancel,
|
||||
};
|
||||
|
||||
enum MBIcons
|
||||
{
|
||||
MIWarning,
|
||||
MIInformation,
|
||||
MIQuestion,
|
||||
MIStop,
|
||||
};
|
||||
|
||||
enum MBReturnVal
|
||||
{
|
||||
MROk = 1, // Start from 1 to allow use of 0 for errors
|
||||
MRCancel,
|
||||
MRRetry,
|
||||
MRDontSave,
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern void initMessageBoxVars();
|
||||
|
||||
#endif // _MSGBOX_H_
|
||||
155
Engine/source/platform/platform.cpp
Normal file
155
Engine/source/platform/platform.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "platform/threads/mutex.h"
|
||||
#include "app/mainLoop.h"
|
||||
#include "platform/event.h"
|
||||
#include "platform/typetraits.h"
|
||||
|
||||
|
||||
const F32 TypeTraits< F32 >::MIN = - F32_MAX;
|
||||
const F32 TypeTraits< F32 >::MAX = F32_MAX;
|
||||
const F32 TypeTraits< F32 >::ZERO = 0;
|
||||
const F32 Float_Inf = std::numeric_limits< F32 >::infinity();
|
||||
|
||||
// The tools prefer to allow the CPU time to process
|
||||
#ifndef TORQUE_TOOLS
|
||||
S32 sgBackgroundProcessSleepTime = 25;
|
||||
#else
|
||||
S32 sgBackgroundProcessSleepTime = 200;
|
||||
#endif
|
||||
S32 sgTimeManagerProcessInterval = 1;
|
||||
|
||||
Vector<Platform::KeyboardInputExclusion> gKeyboardExclusionList;
|
||||
bool gInitKeyboardExclusionList = false;
|
||||
static bool gWebDeployment = false;
|
||||
|
||||
void Platform::initConsole()
|
||||
{
|
||||
Con::addVariable("$platform::backgroundSleepTime", TypeS32, &sgBackgroundProcessSleepTime, "Controls processor time usage when the game window is out of focus.\n"
|
||||
"@ingroup Platform\n");
|
||||
Con::addVariable("$platform::timeManagerProcessInterval", TypeS32, &sgTimeManagerProcessInterval, "Controls processor time usage when the game window is in focus.\n"
|
||||
"@ingroup Platform\n");
|
||||
}
|
||||
|
||||
S32 Platform::getBackgroundSleepTime()
|
||||
{
|
||||
return sgBackgroundProcessSleepTime;
|
||||
}
|
||||
|
||||
ConsoleToolFunction(restartInstance, void, 1, 1, "restartInstance()")
|
||||
{
|
||||
StandardMainLoop::setRestart(true);
|
||||
Platform::postQuitMessage( 0 );
|
||||
}
|
||||
|
||||
void Platform::clearKeyboardInputExclusion()
|
||||
{
|
||||
gKeyboardExclusionList.clear();
|
||||
gInitKeyboardExclusionList = true;
|
||||
}
|
||||
|
||||
void Platform::addKeyboardInputExclusion(const KeyboardInputExclusion &kie)
|
||||
{
|
||||
gKeyboardExclusionList.push_back(kie);
|
||||
}
|
||||
|
||||
const bool Platform::checkKeyboardInputExclusion(const InputEventInfo *info)
|
||||
{
|
||||
// Do one-time initialization of platform defaults.
|
||||
if(!gInitKeyboardExclusionList)
|
||||
{
|
||||
gInitKeyboardExclusionList = true;
|
||||
|
||||
// CodeReview Looks like we don't even need to do #ifdefs here since
|
||||
// things like cmd-tab don't appear on windows, and alt-tab is an unlikely
|
||||
// desired bind on other platforms - might be best to simply have a
|
||||
// global exclusion list and keep it standard on all platforms.
|
||||
// This might not be so, but it's the current assumption. [bjg 5/4/07]
|
||||
|
||||
// Alt-tab
|
||||
{
|
||||
KeyboardInputExclusion kie;
|
||||
kie.key = KEY_TAB;
|
||||
kie.andModifierMask = SI_ALT;
|
||||
addKeyboardInputExclusion(kie);
|
||||
}
|
||||
|
||||
// ... others go here...
|
||||
}
|
||||
|
||||
// Walk the list and look for matches.
|
||||
for(S32 i=0; i<gKeyboardExclusionList.size(); i++)
|
||||
{
|
||||
if(gKeyboardExclusionList[i].checkAgainstInput(info))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool Platform::KeyboardInputExclusion::checkAgainstInput( const InputEventInfo *info ) const
|
||||
{
|
||||
if(info->objType != SI_KEY)
|
||||
return false;
|
||||
|
||||
if(info->objInst != key)
|
||||
return false;
|
||||
|
||||
if((info->modifier & andModifierMask) != andModifierMask)
|
||||
return false;
|
||||
|
||||
if(info->modifier & !(info->modifier & orModifierMask))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
S32 Platform::compareModifiedTimes( const char *firstPath, const char *secondPath )
|
||||
{
|
||||
FileTime firstModTime;
|
||||
if ( !getFileTimes( firstPath, NULL, &firstModTime ) )
|
||||
return -1;
|
||||
|
||||
FileTime secondModTime;
|
||||
if ( !getFileTimes( secondPath, NULL, &secondModTime ) )
|
||||
return -1;
|
||||
|
||||
return compareFileTimes( firstModTime, secondModTime );
|
||||
}
|
||||
|
||||
bool Platform::getWebDeployment()
|
||||
{
|
||||
return gWebDeployment;
|
||||
}
|
||||
|
||||
void Platform::setWebDeployment(bool v)
|
||||
{
|
||||
gWebDeployment = v;
|
||||
}
|
||||
|
||||
|
||||
590
Engine/source/platform/platform.h
Normal file
590
Engine/source/platform/platform.h
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#define _PLATFORM_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef _TORQUECONFIG_H_
|
||||
#include "torqueConfig.h"
|
||||
#endif
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMASSERT_H_
|
||||
#include "platform/platformAssert.h"
|
||||
#endif
|
||||
#ifndef _MSGBOX_H_
|
||||
#include "platform/nativeDialogs/msgBox.h"
|
||||
#endif
|
||||
#ifndef _VERSION_H_
|
||||
#include "app/version.h"
|
||||
#endif
|
||||
#ifndef _TORQUE_STRING_H_
|
||||
#include "core/util/str.h"
|
||||
#endif
|
||||
#ifndef _TORQUE_SAFEDELETE_H_
|
||||
#include "core/util/safeDelete.h"
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <typeinfo>
|
||||
|
||||
/// Global processor identifiers.
|
||||
///
|
||||
/// @note These enums must be globally scoped so that they work with the inline assembly
|
||||
enum ProcessorType
|
||||
{
|
||||
// x86
|
||||
CPU_X86Compatible,
|
||||
CPU_Intel_Unknown,
|
||||
CPU_Intel_486,
|
||||
CPU_Intel_Pentium,
|
||||
CPU_Intel_PentiumMMX,
|
||||
CPU_Intel_PentiumPro,
|
||||
CPU_Intel_PentiumII,
|
||||
CPU_Intel_PentiumCeleron,
|
||||
CPU_Intel_PentiumIII,
|
||||
CPU_Intel_Pentium4,
|
||||
CPU_Intel_PentiumM,
|
||||
CPU_Intel_Core,
|
||||
CPU_Intel_Core2,
|
||||
CPU_Intel_Corei7Xeon, // Core i7 or Xeon
|
||||
CPU_AMD_K6,
|
||||
CPU_AMD_K6_2,
|
||||
CPU_AMD_K6_3,
|
||||
CPU_AMD_Athlon,
|
||||
CPU_AMD_Unknown,
|
||||
CPU_Cyrix_6x86,
|
||||
CPU_Cyrix_MediaGX,
|
||||
CPU_Cyrix_6x86MX,
|
||||
CPU_Cyrix_GXm, ///< Media GX w/ MMX
|
||||
CPU_Cyrix_Unknown,
|
||||
|
||||
// PowerPC
|
||||
CPU_PowerPC_Unknown,
|
||||
CPU_PowerPC_601,
|
||||
CPU_PowerPC_603,
|
||||
CPU_PowerPC_603e,
|
||||
CPU_PowerPC_603ev,
|
||||
CPU_PowerPC_604,
|
||||
CPU_PowerPC_604e,
|
||||
CPU_PowerPC_604ev,
|
||||
CPU_PowerPC_G3,
|
||||
CPU_PowerPC_G4,
|
||||
CPU_PowerPC_G4_7450,
|
||||
CPU_PowerPC_G4_7455,
|
||||
CPU_PowerPC_G4_7447,
|
||||
CPU_PowerPC_G5,
|
||||
|
||||
// Xenon
|
||||
CPU_Xenon,
|
||||
|
||||
};
|
||||
|
||||
/// Properties for CPU.
|
||||
enum ProcessorProperties
|
||||
{
|
||||
CPU_PROP_C = (1<<0), ///< We should use C fallback math functions.
|
||||
CPU_PROP_FPU = (1<<1), ///< Has an FPU. (It better!)
|
||||
CPU_PROP_MMX = (1<<2), ///< Supports MMX instruction set extension.
|
||||
CPU_PROP_3DNOW = (1<<3), ///< Supports AMD 3dNow! instruction set extension.
|
||||
CPU_PROP_SSE = (1<<4), ///< Supports SSE instruction set extension.
|
||||
CPU_PROP_RDTSC = (1<<5), ///< Supports Read Time Stamp Counter op.
|
||||
CPU_PROP_SSE2 = (1<<6), ///< Supports SSE2 instruction set extension.
|
||||
CPU_PROP_SSE3 = (1<<7), ///< Supports SSE3 instruction set extension.
|
||||
CPU_PROP_SSE3xt = (1<<8), ///< Supports extended SSE3 instruction set
|
||||
CPU_PROP_SSE4_1 = (1<<9), ///< Supports SSE4_1 instruction set extension.
|
||||
CPU_PROP_SSE4_2 = (1<<10), ///< Supports SSE4_2 instruction set extension.
|
||||
CPU_PROP_MP = (1<<11), ///< This is a multi-processor system.
|
||||
CPU_PROP_LE = (1<<12), ///< This processor is LITTLE ENDIAN.
|
||||
CPU_PROP_64bit = (1<<13), ///< This processor is 64-bit capable
|
||||
CPU_PROP_ALTIVEC = (1<<14), ///< Supports AltiVec instruction set extension (PPC only).
|
||||
};
|
||||
|
||||
/// Processor info manager.
|
||||
struct Processor
|
||||
{
|
||||
/// Gather processor state information.
|
||||
static void init();
|
||||
};
|
||||
|
||||
#if defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM)
|
||||
#define TORQUE_DEBUGBREAK() { asm ( "int 3"); }
|
||||
#elif defined (TORQUE_SUPPORTS_VC_INLINE_X86_ASM) // put this test second so that the __asm syntax doesn't break the Visual Studio Intellisense parser
|
||||
#define TORQUE_DEBUGBREAK() { __asm { int 3 }; }
|
||||
#else
|
||||
/// Macro to do in-line debug breaks, used for asserts. Does inline assembly when possible.
|
||||
#define TORQUE_DEBUGBREAK() Platform::debugBreak();
|
||||
#endif
|
||||
|
||||
/// Physical type of a drive.
|
||||
enum DriveType
|
||||
{
|
||||
DRIVETYPE_FIXED = 0, ///< Non-removable fixed drive.
|
||||
DRIVETYPE_REMOVABLE = 1, ///< Removable drive.
|
||||
DRIVETYPE_REMOTE = 2, ///< Networked/remote drive.
|
||||
DRIVETYPE_CDROM = 3, ///< CD-Rom.
|
||||
DRIVETYPE_RAMDISK = 4, ///< A ramdisk!
|
||||
DRIVETYPE_UNKNOWN = 5 ///< Don't know.
|
||||
};
|
||||
|
||||
// Some forward declares for later.
|
||||
class Point2I;
|
||||
template<class T> class Vector;
|
||||
template<typename Signature> class Signal;
|
||||
struct InputEventInfo;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
// Time
|
||||
struct LocalTime
|
||||
{
|
||||
U8 sec; ///< Seconds after minute (0-59)
|
||||
U8 min; ///< Minutes after hour (0-59)
|
||||
U8 hour; ///< Hours after midnight (0-23)
|
||||
U8 month; ///< Month (0-11; 0=january)
|
||||
U8 monthday; ///< Day of the month (1-31)
|
||||
U8 weekday; ///< Day of the week (0-6, 6=sunday)
|
||||
U16 year; ///< Current year minus 1900
|
||||
U16 yearday; ///< Day of year (0-365)
|
||||
bool isdst; ///< True if daylight savings time is active
|
||||
};
|
||||
|
||||
void getLocalTime(LocalTime &);
|
||||
|
||||
/// Converts the local time to a formatted string appropriate
|
||||
/// for the current platform.
|
||||
String localTimeToString( const LocalTime < );
|
||||
|
||||
U32 getTime();
|
||||
U32 getVirtualMilliseconds();
|
||||
|
||||
/// Returns the milliseconds since the system was started. You should
|
||||
/// not depend on this for high precision timing.
|
||||
/// @see PlatformTimer
|
||||
U32 getRealMilliseconds();
|
||||
|
||||
void advanceTime(U32 delta);
|
||||
S32 getBackgroundSleepTime();
|
||||
|
||||
// Platform control
|
||||
void init();
|
||||
void initConsole();
|
||||
void shutdown();
|
||||
void process();
|
||||
|
||||
// Math control state
|
||||
U32 getMathControlState();
|
||||
void setMathControlState(U32 state);
|
||||
void setMathControlStateKnown();
|
||||
|
||||
// Process control
|
||||
void sleep(U32 ms);
|
||||
bool excludeOtherInstances(const char *string);
|
||||
bool checkOtherInstances(const char *string);
|
||||
void restartInstance();
|
||||
void postQuitMessage(const U32 in_quitVal);
|
||||
void forceShutdown(S32 returnValue);
|
||||
|
||||
// Debug
|
||||
void outputDebugString(const char *string, ...);
|
||||
void debugBreak();
|
||||
|
||||
// Random
|
||||
float getRandom();
|
||||
|
||||
// Window state
|
||||
void setWindowLocked(bool locked);
|
||||
void minimizeWindow();
|
||||
//const Point2I &getWindowSize();
|
||||
void setWindowSize( U32 newWidth, U32 newHeight, bool fullScreen );
|
||||
void closeWindow();
|
||||
|
||||
// File stuff
|
||||
bool doCDCheck();
|
||||
StringTableEntry createPlatformFriendlyFilename(const char *filename);
|
||||
struct FileInfo
|
||||
{
|
||||
const char* pFullPath;
|
||||
const char* pFileName;
|
||||
U32 fileSize;
|
||||
};
|
||||
bool cdFileExists(const char *filePath, const char *volumeName, S32 serialNum);
|
||||
void fileToLocalTime(const FileTime &ft, LocalTime *lt);
|
||||
/// compare file times returns < 0 if a is earlier than b, >0 if b is earlier than a
|
||||
S32 compareFileTimes(const FileTime &a, const FileTime &b);
|
||||
bool stringToFileTime(const char * string, FileTime * time);
|
||||
bool fileTimeToString(FileTime * time, char * string, U32 strLen);
|
||||
|
||||
/// Compares the last modified time between two file paths. Returns < 0 if
|
||||
/// the first file is earlier than the second, > 0 if the second file is earlier
|
||||
/// than the first, and 0 if the files are equal.
|
||||
///
|
||||
/// If either of the files doesn't exist it returns -1.
|
||||
S32 compareModifiedTimes( const char *firstPath, const char *secondPath );
|
||||
|
||||
// Directory functions. Dump path returns false iff the directory cannot be
|
||||
// opened.
|
||||
|
||||
StringTableEntry getCurrentDirectory();
|
||||
bool setCurrentDirectory(StringTableEntry newDir);
|
||||
|
||||
StringTableEntry getTemporaryDirectory();
|
||||
StringTableEntry getTemporaryFileName();
|
||||
|
||||
/// Returns the filename of the torque executable.
|
||||
/// On Win32, this is the .exe file.
|
||||
/// On Mac, this is the .app/ directory bundle.
|
||||
StringTableEntry getExecutableName();
|
||||
/// Returns full pathname of the torque executable without filename
|
||||
StringTableEntry getExecutablePath();
|
||||
|
||||
/// Returns the full path to the directory that contains main.cs.
|
||||
/// Tools scripts are validated as such if they are in this directory or a
|
||||
/// subdirectory of this directory.
|
||||
StringTableEntry getMainDotCsDir();
|
||||
|
||||
/// Set main.cs directory. Used in runEntryScript()
|
||||
void setMainDotCsDir(const char *dir);
|
||||
|
||||
StringTableEntry getPrefsPath(const char *file = NULL);
|
||||
|
||||
char *makeFullPathName(const char *path, char *buffer, U32 size, const char *cwd = NULL);
|
||||
StringTableEntry stripBasePath(const char *path);
|
||||
bool isFullPath(const char *path);
|
||||
StringTableEntry makeRelativePathName(const char *path, const char *to);
|
||||
|
||||
String stripExtension( String fileName, Vector< String >& validExtensions );
|
||||
|
||||
bool dumpPath(const char *in_pBasePath, Vector<FileInfo>& out_rFileVector, S32 recurseDepth = -1);
|
||||
bool dumpDirectories( const char *path, Vector<StringTableEntry> &directoryVector, S32 depth = 0, bool noBasePath = false );
|
||||
bool hasSubDirectory( const char *pPath );
|
||||
bool getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime);
|
||||
bool isFile(const char *pFilePath);
|
||||
S32 getFileSize(const char *pFilePath);
|
||||
bool isDirectory(const char *pDirPath);
|
||||
bool isSubDirectory(const char *pParent, const char *pDir);
|
||||
|
||||
void addExcludedDirectory(const char *pDir);
|
||||
void clearExcludedDirectories();
|
||||
bool isExcludedDirectory(const char *pDir);
|
||||
|
||||
/// Given a directory path, create all necessary directories for that path to exist.
|
||||
bool createPath(const char *path); // create a directory path
|
||||
|
||||
// Alerts
|
||||
void AlertOK(const char *windowTitle, const char *message);
|
||||
bool AlertOKCancel(const char *windowTitle, const char *message);
|
||||
bool AlertRetry(const char *windowTitle, const char *message);
|
||||
|
||||
// Volumes
|
||||
struct VolumeInformation
|
||||
{
|
||||
StringTableEntry RootPath;
|
||||
StringTableEntry Name;
|
||||
StringTableEntry FileSystem;
|
||||
U32 SerialNumber;
|
||||
U32 Type;
|
||||
bool ReadOnly;
|
||||
};
|
||||
extern struct VolumeInformation *PVolumeInformation;
|
||||
|
||||
// Volume functions.
|
||||
void getVolumeNamesList( Vector<const char*>& out_rNameVector, bool bOnlyFixedDrives = false );
|
||||
void getVolumeInformationList( Vector<VolumeInformation>& out_rVolumeInfoVector, bool bOnlyFixedDrives = false );
|
||||
|
||||
struct SystemInfo_struct
|
||||
{
|
||||
struct Processor
|
||||
{
|
||||
ProcessorType type;
|
||||
const char* name;
|
||||
U32 mhz;
|
||||
bool isMultiCore;
|
||||
bool isHyperThreaded;
|
||||
U32 numLogicalProcessors;
|
||||
U32 numPhysicalProcessors;
|
||||
U32 numAvailableCores;
|
||||
U32 properties; // CPU type specific enum
|
||||
} processor;
|
||||
};
|
||||
extern Signal<void(void)> SystemInfoReady;
|
||||
extern SystemInfo_struct SystemInfo;
|
||||
|
||||
// Web page launch function:
|
||||
bool openWebBrowser( const char* webAddress );
|
||||
|
||||
// display Splash Window
|
||||
bool displaySplashWindow( );
|
||||
|
||||
void openFolder( const char* path );
|
||||
|
||||
// Open file at the OS level, according to registered file-types.
|
||||
void openFile( const char* path );
|
||||
|
||||
const char* getLoginPassword();
|
||||
bool setLoginPassword( const char* password );
|
||||
|
||||
const char* getClipboard();
|
||||
bool setClipboard(const char *text);
|
||||
|
||||
// User Specific Functions
|
||||
StringTableEntry getUserHomeDirectory();
|
||||
StringTableEntry getUserDataDirectory();
|
||||
bool getUserIsAdministrator();
|
||||
|
||||
// Displays a fancy platform specific message box
|
||||
S32 messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons = MBOkCancel, MBIcons icon = MIInformation);
|
||||
|
||||
/// Description of a keyboard input we want to ignore.
|
||||
struct KeyboardInputExclusion
|
||||
{
|
||||
KeyboardInputExclusion()
|
||||
{
|
||||
key = 0;
|
||||
orModifierMask = 0;
|
||||
andModifierMask = 0;
|
||||
}
|
||||
|
||||
/// The key code to ignore, e.g. KEY_TAB. If this and the other
|
||||
/// conditions match, ignore the key.
|
||||
S32 key;
|
||||
|
||||
/// if(modifiers | orModifierMask) and the other conditions match,
|
||||
/// ignore the key.
|
||||
U32 orModifierMask;
|
||||
|
||||
/// if((modifiers & andModifierMask) == andModifierMask) and the
|
||||
/// other conditions match, ignore the key stroke.
|
||||
U32 andModifierMask;
|
||||
|
||||
/// Based on the values above, determine if a given input event
|
||||
/// matchs this exclusion rule.
|
||||
const bool checkAgainstInput(const InputEventInfo *info) const;
|
||||
};
|
||||
|
||||
/// Reset the keyboard input exclusion list.
|
||||
void clearKeyboardInputExclusion();
|
||||
|
||||
/// Add a new keyboard exclusion.
|
||||
void addKeyboardInputExclusion(const KeyboardInputExclusion &kie);
|
||||
|
||||
/// Check if a given input event should be excluded.
|
||||
const bool checkKeyboardInputExclusion(const InputEventInfo *info);
|
||||
|
||||
|
||||
/// Set/Get whether this is a web deployment
|
||||
bool getWebDeployment();
|
||||
void setWebDeployment(bool v);
|
||||
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Unicode string conversions
|
||||
// UNICODE is a windows platform API switching flag. Don't define it on other platforms.
|
||||
#ifdef UNICODE
|
||||
#define dT(s) L##s
|
||||
#else
|
||||
#define dT(s) s
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Misc StdLib functions
|
||||
#define QSORT_CALLBACK FN_CDECL
|
||||
inline void dQsort(void *base, U32 nelem, U32 width, int (QSORT_CALLBACK *fcmp)(const void *, const void *))
|
||||
{
|
||||
qsort(base, nelem, width, fcmp);
|
||||
}
|
||||
|
||||
//-------------------------------------- Some all-around useful inlines and globals
|
||||
//
|
||||
|
||||
///@defgroup ObjTrickery Object Management Trickery
|
||||
///
|
||||
/// These functions are to construct and destruct objects in memory
|
||||
/// without causing a free or malloc call to occur. This is so that
|
||||
/// we don't have to worry about allocating, say, space for a hundred
|
||||
/// NetAddresses with a single malloc call, calling delete on a single
|
||||
/// NetAdress, and having it try to free memory out from under us.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Constructs an object that already has memory allocated for it.
|
||||
template <class T>
|
||||
inline T* constructInPlace(T* p)
|
||||
{
|
||||
return new ( p ) T;
|
||||
}
|
||||
template< class T >
|
||||
inline T* constructArrayInPlace( T* p, U32 num )
|
||||
{
|
||||
return new ( p ) T[ num ];
|
||||
}
|
||||
|
||||
/// Copy constructs an object that already has memory allocated for it.
|
||||
template <class T>
|
||||
inline T* constructInPlace(T* p, const T* copy)
|
||||
{
|
||||
return new ( p ) T( *copy );
|
||||
}
|
||||
|
||||
template <class T, class T2> inline T* constructInPlace(T* ptr, T2 t2)
|
||||
{
|
||||
return new ( ptr ) T( t2 );
|
||||
}
|
||||
|
||||
template <class T, class T2, class T3> inline T* constructInPlace(T* ptr, T2 t2, T3 t3)
|
||||
{
|
||||
return new ( ptr ) T( t2, t3 );
|
||||
}
|
||||
|
||||
template <class T, class T2, class T3, class T4> inline T* constructInPlace(T* ptr, T2 t2, T3 t3, T4 t4)
|
||||
{
|
||||
return new ( ptr ) T( t2, t3, t4 );
|
||||
}
|
||||
|
||||
template <class T, class T2, class T3, class T4, class T5> inline T* constructInPlace(T* ptr, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{
|
||||
return new ( ptr ) T( t2, t3, t4, t5 );
|
||||
}
|
||||
|
||||
/// Destructs an object without freeing the memory associated with it.
|
||||
template <class T>
|
||||
inline void destructInPlace(T* p)
|
||||
{
|
||||
p->~T();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// Memory functions
|
||||
|
||||
#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
|
||||
# define TORQUE_TMM_ARGS_DECL , const char* fileName, const U32 lineNum
|
||||
# define TORQUE_TMM_ARGS , fileName, lineNum
|
||||
# define TORQUE_TMM_LOC , __FILE__, __LINE__
|
||||
extern void* FN_CDECL operator new(dsize_t size, const char*, const U32);
|
||||
extern void* FN_CDECL operator new[](dsize_t size, const char*, const U32);
|
||||
extern void FN_CDECL operator delete(void* ptr);
|
||||
extern void FN_CDECL operator delete[](void* ptr);
|
||||
# define _new new(__FILE__, __LINE__)
|
||||
# define new _new
|
||||
#else
|
||||
# define TORQUE_TMM_ARGS_DECL
|
||||
# define TORQUE_TMM_ARGS
|
||||
# define TORQUE_TMM_LOC
|
||||
#endif
|
||||
|
||||
#define dMalloc(x) dMalloc_r(x, __FILE__, __LINE__)
|
||||
#define dRealloc(x, y) dRealloc_r(x, y, __FILE__, __LINE__)
|
||||
|
||||
extern void setBreakAlloc(dsize_t);
|
||||
extern void setMinimumAllocUnit(U32);
|
||||
extern void* dMalloc_r(dsize_t in_size, const char*, const dsize_t);
|
||||
extern void dFree(void* in_pFree);
|
||||
extern void* dRealloc_r(void* in_pResize, dsize_t in_size, const char*, const dsize_t);
|
||||
extern void* dRealMalloc(dsize_t);
|
||||
extern void dRealFree(void*);
|
||||
|
||||
extern void *dMalloc_aligned(dsize_t in_size, int alignment);
|
||||
extern void dFree_aligned(void *);
|
||||
|
||||
|
||||
inline void dFree( const void* p )
|
||||
{
|
||||
dFree( ( void* ) p );
|
||||
}
|
||||
|
||||
// Helper function to copy one array into another of different type
|
||||
template<class T,class S> void dCopyArray(T *dst, const S *src, dsize_t size)
|
||||
{
|
||||
for (dsize_t i = 0; i < size; i++)
|
||||
dst[i] = (T)src[i];
|
||||
}
|
||||
|
||||
// Special case of the above function when the arrays are the same type (use memcpy)
|
||||
template<class T> void dCopyArray(T *dst, const T *src, dsize_t size)
|
||||
{
|
||||
dMemcpy(dst, src, size * sizeof(T));
|
||||
}
|
||||
|
||||
extern void* dMemcpy(void *dst, const void *src, dsize_t size);
|
||||
extern void* dMemmove(void *dst, const void *src, dsize_t size);
|
||||
extern void* dMemset(void *dst, int c, dsize_t size);
|
||||
extern int dMemcmp(const void *ptr1, const void *ptr2, dsize_t size);
|
||||
|
||||
/// The dALIGN macro ensures the passed declaration is
|
||||
/// data aligned at 16 byte boundaries.
|
||||
#if defined( TORQUE_COMPILER_VISUALC )
|
||||
#define dALIGN( decl ) __declspec( align( 16 ) ) decl
|
||||
#define dALIGN_BEGIN __declspec( align( 16 ) )
|
||||
#define dALIGN_END
|
||||
#elif defined( TORQUE_COMPILER_GCC )
|
||||
#define dALIGN( decl ) decl __attribute__( ( aligned( 16 ) ) )
|
||||
#define dALIGN_BEGIN
|
||||
#define dALIGN_END __attribute__( ( aligned( 16 ) ) )
|
||||
#else
|
||||
#define dALIGN( decl ) decl
|
||||
#define dALIGN_BEGIN()
|
||||
#define dALIGN_END()
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// FileIO functions
|
||||
extern bool dFileDelete(const char *name);
|
||||
extern bool dFileRename(const char *oldName, const char *newName);
|
||||
extern bool dFileTouch(const char *name);
|
||||
extern bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite = true);
|
||||
|
||||
typedef void* FILE_HANDLE;
|
||||
enum DFILE_STATUS
|
||||
{
|
||||
DFILE_OK = 1
|
||||
};
|
||||
|
||||
extern FILE_HANDLE dOpenFileRead(const char *name, DFILE_STATUS &error);
|
||||
extern FILE_HANDLE dOpenFileReadWrite(const char *name, bool append, DFILE_STATUS &error);
|
||||
extern int dFileRead(FILE_HANDLE handle, U32 bytes, char *dst, DFILE_STATUS &error);
|
||||
extern int dFileWrite(FILE_HANDLE handle, U32 bytes, const char *dst, DFILE_STATUS &error);
|
||||
extern void dFileClose(FILE_HANDLE handle);
|
||||
|
||||
extern StringTableEntry osGetTemporaryDirectory();
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct Math
|
||||
{
|
||||
/// Initialize the math library with the appropriate libraries
|
||||
/// to support hardware acceleration features.
|
||||
///
|
||||
/// @param properties Leave zero to detect available hardware. Otherwise,
|
||||
/// pass CPU instruction set flags that you want to load
|
||||
/// support for.
|
||||
static void init(U32 properties = 0);
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
172
Engine/source/platform/platformAssert.cpp
Normal file
172
Engine/source/platform/platformAssert.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "console/console.h"
|
||||
|
||||
|
||||
//-------------------------------------- STATIC Declaration
|
||||
PlatformAssert *PlatformAssert::platformAssert = NULL;
|
||||
|
||||
//--------------------------------------
|
||||
PlatformAssert::PlatformAssert()
|
||||
{
|
||||
processing = false;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
PlatformAssert::~PlatformAssert()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void PlatformAssert::create( PlatformAssert* newAssertClass )
|
||||
{
|
||||
if (!platformAssert)
|
||||
platformAssert = newAssertClass ? newAssertClass : new PlatformAssert;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
void PlatformAssert::destroy()
|
||||
{
|
||||
if (platformAssert)
|
||||
delete platformAssert;
|
||||
platformAssert = NULL;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool PlatformAssert::displayMessageBox(const char *title, const char *message, bool retry)
|
||||
{
|
||||
if (retry)
|
||||
return Platform::AlertRetry(title, message);
|
||||
|
||||
Platform::AlertOK(title, message);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *typeName[] = { "Unknown", "Fatal-ISV", "Fatal", "Warning" };
|
||||
//------------------------------------------------------------------------------
|
||||
static bool askToEnterDebugger(const char* message )
|
||||
{
|
||||
static bool haveAsked = false;
|
||||
static bool useDebugger = true;
|
||||
if(!haveAsked )
|
||||
{
|
||||
static char tempBuff[1024];
|
||||
dSprintf( tempBuff, 1024, "Torque has encountered an assertion with message\n\n"
|
||||
"%s\n\n"
|
||||
"Would you like to use the debugger? If you cancel, you won't be asked"
|
||||
" again until you restart Torque.", message);
|
||||
|
||||
useDebugger = Platform::AlertOKCancel("Use debugger?", tempBuff );
|
||||
haveAsked = true;
|
||||
}
|
||||
return useDebugger;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
bool PlatformAssert::process(Type assertType,
|
||||
const char *filename,
|
||||
U32 lineNumber,
|
||||
const char *message)
|
||||
{
|
||||
// If we're somehow recursing, just die.
|
||||
if(processing)
|
||||
Platform::debugBreak();
|
||||
|
||||
processing = true;
|
||||
bool ret = true;
|
||||
|
||||
// always dump to the Assert to the Console
|
||||
if (Con::isActive())
|
||||
{
|
||||
if (assertType == Warning)
|
||||
Con::warnf(ConsoleLogEntry::Assert, "%s(%ld) : %s - %s", filename, lineNumber, typeName[assertType], message);
|
||||
else
|
||||
Con::errorf(ConsoleLogEntry::Assert, "%s(%ld) : %s - %s", filename, lineNumber, typeName[assertType], message);
|
||||
}
|
||||
|
||||
// if not a WARNING pop-up a dialog box
|
||||
if (assertType != Warning)
|
||||
{
|
||||
// used for processing navGraphs (an assert won't botch the whole build)
|
||||
if(Con::getBoolVariable("$FP::DisableAsserts", false) == true)
|
||||
Platform::forceShutdown(1);
|
||||
|
||||
char buffer[2048];
|
||||
dSprintf(buffer, 2048, "%s(%ld) : %s", filename, lineNumber, typeName[assertType] );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// In debug versions, allow a retry even for ISVs...
|
||||
bool retry = displayMessageBox(buffer, message, true);
|
||||
#else
|
||||
bool retry = displayMessageBox(buffer, message, ((assertType == Fatal) ? true : false) );
|
||||
#endif
|
||||
if(!retry)
|
||||
Platform::forceShutdown(1);
|
||||
|
||||
ret = askToEnterDebugger(message);
|
||||
}
|
||||
|
||||
processing = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool PlatformAssert::processingAssert()
|
||||
{
|
||||
return platformAssert ? platformAssert->processing : false;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool PlatformAssert::processAssert(Type assertType,
|
||||
const char *filename,
|
||||
U32 lineNumber,
|
||||
const char *message)
|
||||
{
|
||||
if (platformAssert)
|
||||
return platformAssert->process(assertType, filename, lineNumber, message);
|
||||
else // when platAssert NULL (during _start/_exit) try direct output...
|
||||
dPrintf("\n%s: (%s @ %ld) %s\n", typeName[assertType], filename, lineNumber, message);
|
||||
|
||||
// this could also be platform-specific: OutputDebugString on PC, DebugStr on Mac.
|
||||
// Will raw printfs do the job? In the worst case, it's a break-pointable line of code.
|
||||
// would have preferred Con but due to race conditions, it might not be around...
|
||||
// Con::errorf(ConsoleLogEntry::Assert, "%s: (%s @ %ld) %s", typeName[assertType], filename, lineNumber, message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
const char* avar(const char *message, ...)
|
||||
{
|
||||
static char buffer[4096];
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
dVsprintf(buffer, sizeof(buffer), message, args);
|
||||
return( buffer );
|
||||
}
|
||||
141
Engine/source/platform/platformAssert.h
Normal file
141
Engine/source/platform/platformAssert.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMASSERT_H_
|
||||
#define _PLATFORMASSERT_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
|
||||
class PlatformAssert
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Warning = 3,
|
||||
Fatal = 2,
|
||||
Fatal_ISV = 1
|
||||
};
|
||||
|
||||
private:
|
||||
static PlatformAssert *platformAssert;
|
||||
bool processing;
|
||||
|
||||
virtual bool displayMessageBox(const char *title, const char *message, bool retry);
|
||||
virtual bool process(Type assertType,
|
||||
const char* filename,
|
||||
U32 lineNumber,
|
||||
const char* message);
|
||||
|
||||
PlatformAssert();
|
||||
virtual ~PlatformAssert();
|
||||
|
||||
public:
|
||||
static void create( PlatformAssert* newAssertClass = NULL );
|
||||
static void destroy();
|
||||
static bool processAssert(Type assertType,
|
||||
const char* filename,
|
||||
U32 lineNumber,
|
||||
const char* message);
|
||||
static char *message(const char *message, ...);
|
||||
static bool processingAssert();
|
||||
};
|
||||
|
||||
|
||||
#ifdef TORQUE_ENABLE_ASSERTS
|
||||
/*!
|
||||
Assert that the statement x is true, and continue processing.
|
||||
|
||||
If the statment x is true, continue processing.
|
||||
|
||||
If the statement x is false, log the file and line where the assert occured,
|
||||
the message y and continue processing.
|
||||
|
||||
These asserts are only present in DEBUG builds.
|
||||
*/
|
||||
#define AssertWarn(x, y) \
|
||||
{ if ((x)==0) \
|
||||
::PlatformAssert::processAssert(::PlatformAssert::Warning, __FILE__, __LINE__, y); }
|
||||
|
||||
/*!
|
||||
Assert that the statement x is true, otherwise halt.
|
||||
|
||||
If the statement x is true, continue processing.
|
||||
|
||||
If the statement x is false, log the file and line where the assert occured,
|
||||
the message y and displaying a dialog containing the message y. The user then
|
||||
has the option to halt or continue causing the debugger to break.
|
||||
|
||||
These asserts are only present in DEBUG builds.
|
||||
|
||||
This assert is very useful for verifying data as well as function entry and
|
||||
exit conditions.
|
||||
*/
|
||||
#define AssertFatal(x, y) \
|
||||
{ if (((bool)(x))==(bool)0) \
|
||||
{ if ( ::PlatformAssert::processAssert(::PlatformAssert::Fatal, __FILE__, __LINE__, y) ) { ::Platform::debugBreak(); } } }
|
||||
|
||||
#else
|
||||
#define AssertFatal(x, y) { (void)sizeof(x); (void)sizeof(y); }
|
||||
#define AssertWarn(x, y) { (void)sizeof(x); (void)sizeof(y); }
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Assert (In Shipping Version) that the statement x is true, otherwise halt.
|
||||
|
||||
If the statement x is true, continue processing.
|
||||
|
||||
If the statement x is false, log the file and line where the assert occurred,
|
||||
the message y and exit the program displaying a dialog containing the message y.
|
||||
These asserts are present in both OPTIMIZED and DEBUG builds.
|
||||
|
||||
This assert should only be used for rare conditions where the application cannot continue
|
||||
execution without seg-faulting and you want to display a nice exit message.
|
||||
*/
|
||||
#define AssertISV(x, y) \
|
||||
{ if ((x)==0) \
|
||||
{ if ( ::PlatformAssert::processAssert(::PlatformAssert::Fatal_ISV, __FILE__, __LINE__, y) ) { ::Platform::debugBreak(); } } }
|
||||
|
||||
|
||||
/*!
|
||||
Sprintf style string formating into a fixed temporary buffer.
|
||||
@param in_msg sprintf style format string
|
||||
@returns pointer to fixed buffer containing formatted string
|
||||
|
||||
\b Example:
|
||||
\code
|
||||
U8 a = 5;
|
||||
S16 b = -10;
|
||||
char *output = avar("hello %s! a=%u, b=%d", "world");
|
||||
ouput = "hello world! a=5, b=-10"
|
||||
\endcode
|
||||
|
||||
@warning avar uses a static fixed buffer. Treat the buffer as volatile data
|
||||
and use it immediately. Other functions my use avar too and clobber the buffer.
|
||||
*/
|
||||
const char* avar(const char *in_msg, ...);
|
||||
|
||||
|
||||
|
||||
#endif // _PLATFORM_ASSERT_H_
|
||||
|
||||
269
Engine/source/platform/platformCPU.cpp
Normal file
269
Engine/source/platform/platformCPU.cpp
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformCPUCount.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "core/util/tSignal.h"
|
||||
|
||||
Signal<void(void)> Platform::SystemInfoReady;
|
||||
|
||||
enum CPUFlags
|
||||
{
|
||||
BIT_FPU = BIT(0),
|
||||
BIT_RDTSC = BIT(4),
|
||||
BIT_MMX = BIT(23),
|
||||
BIT_SSE = BIT(25),
|
||||
BIT_SSE2 = BIT(26),
|
||||
BIT_3DNOW = BIT(31),
|
||||
|
||||
// These use a different value for comparison than the above flags
|
||||
BIT_SSE3 = BIT(0),
|
||||
BIT_SSE3xt = BIT(9),
|
||||
BIT_SSE4_1 = BIT(19),
|
||||
BIT_SSE4_2 = BIT(20),
|
||||
};
|
||||
|
||||
// fill the specified structure with information obtained from asm code
|
||||
void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo,
|
||||
char* vendor, U32 processor, U32 properties, U32 properties2)
|
||||
{
|
||||
Platform::SystemInfo.processor.properties |= (properties & BIT_FPU) ? CPU_PROP_FPU : 0;
|
||||
Platform::SystemInfo.processor.properties |= (properties & BIT_RDTSC) ? CPU_PROP_RDTSC : 0;
|
||||
Platform::SystemInfo.processor.properties |= (properties & BIT_MMX) ? CPU_PROP_MMX : 0;
|
||||
|
||||
if (dStricmp(vendor, "GenuineIntel") == 0)
|
||||
{
|
||||
pInfo.properties |= (properties & BIT_SSE) ? CPU_PROP_SSE : 0;
|
||||
pInfo.properties |= (properties & BIT_SSE2) ? CPU_PROP_SSE2 : 0;
|
||||
pInfo.properties |= (properties2 & BIT_SSE3) ? CPU_PROP_SSE3 : 0;
|
||||
pInfo.properties |= (properties2 & BIT_SSE3xt) ? CPU_PROP_SSE3xt : 0;
|
||||
pInfo.properties |= (properties2 & BIT_SSE4_1) ? CPU_PROP_SSE4_1 : 0;
|
||||
pInfo.properties |= (properties2 & BIT_SSE4_2) ? CPU_PROP_SSE4_2 : 0;
|
||||
|
||||
pInfo.type = CPU_Intel_Unknown;
|
||||
// switch on processor family code
|
||||
switch ((processor >> 8) & 0x0f)
|
||||
{
|
||||
case 4:
|
||||
pInfo.type = CPU_Intel_486;
|
||||
pInfo.name = StringTable->insert("Intel 486 class");
|
||||
break;
|
||||
|
||||
// Pentium Family
|
||||
case 5:
|
||||
// switch on processor model code
|
||||
switch ((processor >> 4) & 0xf)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
pInfo.type = CPU_Intel_Pentium;
|
||||
pInfo.name = StringTable->insert("Intel Pentium");
|
||||
break;
|
||||
case 4:
|
||||
pInfo.type = CPU_Intel_PentiumMMX;
|
||||
pInfo.name = StringTable->insert("Intel Pentium MMX");
|
||||
break;
|
||||
default:
|
||||
pInfo.type = CPU_Intel_Pentium;
|
||||
pInfo.name = StringTable->insert( "Intel (unknown)" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Pentium Pro/II/II family
|
||||
case 6:
|
||||
{
|
||||
U32 extendedModel = ( processor & 0xf0000 ) >> 16;
|
||||
// switch on processor model code
|
||||
switch ((processor >> 4) & 0xf)
|
||||
{
|
||||
case 1:
|
||||
pInfo.type = CPU_Intel_PentiumPro;
|
||||
pInfo.name = StringTable->insert("Intel Pentium Pro");
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
pInfo.type = CPU_Intel_PentiumII;
|
||||
pInfo.name = StringTable->insert("Intel Pentium II");
|
||||
break;
|
||||
case 6:
|
||||
pInfo.type = CPU_Intel_PentiumCeleron;
|
||||
pInfo.name = StringTable->insert("Intel Pentium Celeron");
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 11:
|
||||
pInfo.type = CPU_Intel_PentiumIII;
|
||||
pInfo.name = StringTable->insert("Intel Pentium III");
|
||||
break;
|
||||
case 0xA:
|
||||
if( extendedModel == 1)
|
||||
{
|
||||
pInfo.type = CPU_Intel_Corei7Xeon;
|
||||
pInfo.name = StringTable->insert( "Intel Core i7 / Xeon" );
|
||||
}
|
||||
else
|
||||
{
|
||||
pInfo.type = CPU_Intel_PentiumIII;
|
||||
pInfo.name = StringTable->insert( "Intel Pentium III Xeon" );
|
||||
}
|
||||
break;
|
||||
case 0xD:
|
||||
if( extendedModel == 1 )
|
||||
{
|
||||
pInfo.type = CPU_Intel_Corei7Xeon;
|
||||
pInfo.name = StringTable->insert( "Intel Core i7 / Xeon" );
|
||||
}
|
||||
else
|
||||
{
|
||||
pInfo.type = CPU_Intel_PentiumM;
|
||||
pInfo.name = StringTable->insert( "Intel Pentium/Celeron M" );
|
||||
}
|
||||
break;
|
||||
case 0xE:
|
||||
pInfo.type = CPU_Intel_Core;
|
||||
pInfo.name = StringTable->insert( "Intel Core" );
|
||||
break;
|
||||
case 0xF:
|
||||
pInfo.type = CPU_Intel_Core2;
|
||||
pInfo.name = StringTable->insert( "Intel Core 2" );
|
||||
break;
|
||||
default:
|
||||
pInfo.type = CPU_Intel_PentiumPro;
|
||||
pInfo.name = StringTable->insert( "Intel (unknown)" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Pentium4 Family
|
||||
case 0xf:
|
||||
pInfo.type = CPU_Intel_Pentium4;
|
||||
pInfo.name = StringTable->insert( "Intel Pentium 4" );
|
||||
break;
|
||||
|
||||
default:
|
||||
pInfo.type = CPU_Intel_Unknown;
|
||||
pInfo.name = StringTable->insert( "Intel (unknown)" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
//--------------------------------------
|
||||
else
|
||||
if (dStricmp(vendor, "AuthenticAMD") == 0)
|
||||
{
|
||||
// AthlonXP processors support SSE
|
||||
pInfo.properties |= (properties & BIT_SSE) ? CPU_PROP_SSE : 0;
|
||||
pInfo.properties |= ( properties & BIT_SSE2 ) ? CPU_PROP_SSE2 : 0;
|
||||
pInfo.properties |= (properties & BIT_3DNOW) ? CPU_PROP_3DNOW : 0;
|
||||
// switch on processor family code
|
||||
switch ((processor >> 8) & 0xf)
|
||||
{
|
||||
// K6 Family
|
||||
case 5:
|
||||
// switch on processor model code
|
||||
switch ((processor >> 4) & 0xf)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
pInfo.type = CPU_AMD_K6_3;
|
||||
pInfo.name = StringTable->insert("AMD K5");
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
pInfo.type = CPU_AMD_K6;
|
||||
pInfo.name = StringTable->insert("AMD K6");
|
||||
break;
|
||||
case 8:
|
||||
pInfo.type = CPU_AMD_K6_2;
|
||||
pInfo.name = StringTable->insert("AMD K6-2");
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
pInfo.type = CPU_AMD_K6_3;
|
||||
pInfo.name = StringTable->insert("AMD K6-3");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Athlon Family
|
||||
case 6:
|
||||
pInfo.type = CPU_AMD_Athlon;
|
||||
pInfo.name = StringTable->insert("AMD Athlon");
|
||||
break;
|
||||
|
||||
default:
|
||||
pInfo.type = CPU_AMD_Unknown;
|
||||
pInfo.name = StringTable->insert("AMD (unknown)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
//--------------------------------------
|
||||
else
|
||||
if (dStricmp(vendor, "CyrixInstead") == 0)
|
||||
{
|
||||
switch (processor)
|
||||
{
|
||||
case 0x520:
|
||||
pInfo.type = CPU_Cyrix_6x86;
|
||||
pInfo.name = StringTable->insert("Cyrix 6x86");
|
||||
break;
|
||||
case 0x440:
|
||||
pInfo.type = CPU_Cyrix_MediaGX;
|
||||
pInfo.name = StringTable->insert("Cyrix Media GX");
|
||||
break;
|
||||
case 0x600:
|
||||
pInfo.type = CPU_Cyrix_6x86MX;
|
||||
pInfo.name = StringTable->insert("Cyrix 6x86mx/MII");
|
||||
break;
|
||||
case 0x540:
|
||||
pInfo.type = CPU_Cyrix_GXm;
|
||||
pInfo.name = StringTable->insert("Cyrix GXm");
|
||||
break;
|
||||
default:
|
||||
pInfo.type = CPU_Cyrix_Unknown;
|
||||
pInfo.name = StringTable->insert("Cyrix (unknown)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get multithreading caps.
|
||||
|
||||
CPUInfo::EConfig config = CPUInfo::CPUCount( pInfo.numLogicalProcessors, pInfo.numAvailableCores, pInfo.numPhysicalProcessors );
|
||||
pInfo.isHyperThreaded = CPUInfo::isHyperThreaded( config );
|
||||
pInfo.isMultiCore = CPUInfo::isMultiCore( config );
|
||||
|
||||
// Trigger the signal
|
||||
Platform::SystemInfoReady.trigger();
|
||||
}
|
||||
667
Engine/source/platform/platformCPUCount.cpp
Normal file
667
Engine/source/platform/platformCPUCount.cpp
Normal file
|
|
@ -0,0 +1,667 @@
|
|||
// Original code is:
|
||||
// Copyright (c) 2005 Intel Corporation
|
||||
// All Rights Reserved
|
||||
//
|
||||
// CPUCount.cpp : Detects three forms of hardware multi-threading support across IA-32 platform
|
||||
// The three forms of HW multithreading are: Multi-processor, Multi-core, and
|
||||
// HyperThreading Technology.
|
||||
// This application enumerates all the logical processors enabled by OS and BIOS,
|
||||
// determine the HW topology of these enabled logical processors in the system
|
||||
// using information provided by CPUID instruction.
|
||||
// A multi-processing system can support any combination of the three forms of HW
|
||||
// multi-threading support. The relevant topology can be identified using a
|
||||
// three level decomposition of the "initial APIC ID" into
|
||||
// Package_id, core_id, and SMT_id. Such decomposition provides a three-level map of
|
||||
// the topology of hardware resources and
|
||||
// allow multi-threaded software to manage shared hardware resources in
|
||||
// the platform to reduce resource contention
|
||||
|
||||
// Multicore detection algorithm for processor and cache topology requires
|
||||
// all leaf functions of CPUID instructions be available. System administrator
|
||||
// must ensure BIOS settings is not configured to restrict CPUID functionalities.
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformCPUCount.h"
|
||||
|
||||
// Consoles don't need this
|
||||
#if defined(TORQUE_OS_XENON) || defined(TORQUE_OS_PS3)
|
||||
namespace CPUInfo
|
||||
{
|
||||
|
||||
EConfig CPUCount(U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum)
|
||||
{
|
||||
TotAvailLogical = 6;
|
||||
TotAvailCore = 6;
|
||||
PhysicalNum = 3;
|
||||
|
||||
return CONFIG_MultiCoreAndHTEnabled;
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
#else
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
// The Linux source code listing can be compiled using Linux kernel verison 2.6
|
||||
// or higher (e.g. RH 4AS-2.8 using GCC 3.4.4).
|
||||
// Due to syntax variances of Linux affinity APIs with earlier kernel versions
|
||||
// and dependence on glibc library versions, compilation on Linux environment
|
||||
// with older kernels and compilers may require kernel patches or compiler upgrades.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#define DWORD unsigned long
|
||||
#elif defined( TORQUE_OS_WIN32 )
|
||||
#include <windows.h>
|
||||
#elif defined( TORQUE_OS_MAC )
|
||||
# include <sys/types.h>
|
||||
# include <sys/sysctl.h>
|
||||
#else
|
||||
#error Not implemented on platform.
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace CPUInfo {
|
||||
|
||||
#define HWD_MT_BIT 0x10000000 // EDX[28] Bit 28 is set if HT or multi-core is supported
|
||||
#define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical
|
||||
// processors per physical processor when execute cpuid with
|
||||
// eax set to 1
|
||||
#define NUM_CORE_BITS 0xFC000000 // EAX[31:26] Bit 26-31 in eax contains the number of cores minus one
|
||||
// per physical processor when execute cpuid with
|
||||
// eax set to 4.
|
||||
|
||||
|
||||
#define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique
|
||||
// initial APIC ID for the processor this code is running on.
|
||||
|
||||
|
||||
#ifndef TORQUE_OS_MAC
|
||||
static unsigned int CpuIDSupported(void);
|
||||
static unsigned int find_maskwidth(unsigned int);
|
||||
static unsigned int HWD_MTSupported(void);
|
||||
static unsigned int MaxLogicalProcPerPhysicalProc(void);
|
||||
static unsigned int MaxCorePerPhysicalProc(void);
|
||||
static unsigned char GetAPIC_ID(void);
|
||||
static unsigned char GetNzbSubID(unsigned char, unsigned char, unsigned char);
|
||||
#endif
|
||||
|
||||
static char g_s3Levels[2048];
|
||||
|
||||
#ifndef TORQUE_OS_MAC
|
||||
|
||||
//
|
||||
// CpuIDSupported will return 0 if CPUID instruction is unavailable. Otherwise, it will return
|
||||
// the maximum supported standard function.
|
||||
//
|
||||
static unsigned int CpuIDSupported(void)
|
||||
{
|
||||
unsigned int MaxInputValue;
|
||||
// If CPUID instruction is supported
|
||||
#ifdef TORQUE_COMPILER_GCC
|
||||
try
|
||||
{
|
||||
MaxInputValue = 0;
|
||||
// call cpuid with eax = 0
|
||||
asm
|
||||
(
|
||||
"pushl %%ebx\n\t"
|
||||
"xorl %%eax,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"popl %%ebx\n\t"
|
||||
: "=a" (MaxInputValue)
|
||||
:
|
||||
: "%ecx", "%edx"
|
||||
);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return(0); // cpuid instruction is unavailable
|
||||
}
|
||||
#elif defined( TORQUE_COMPILER_VISUALC )
|
||||
try
|
||||
{
|
||||
MaxInputValue = 0;
|
||||
// call cpuid with eax = 0
|
||||
__asm
|
||||
{
|
||||
xor eax, eax
|
||||
cpuid
|
||||
mov MaxInputValue, eax
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return(0); // cpuid instruction is unavailable
|
||||
}
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
|
||||
return MaxInputValue;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Function returns the maximum cores per physical package. Note that the number of
|
||||
// AVAILABLE cores per physical to be used by an application might be less than this
|
||||
// maximum value.
|
||||
//
|
||||
|
||||
static unsigned int MaxCorePerPhysicalProc(void)
|
||||
{
|
||||
|
||||
unsigned int Regeax = 0;
|
||||
|
||||
if (!HWD_MTSupported()) return (unsigned int) 1; // Single core
|
||||
#ifdef TORQUE_COMPILER_GCC
|
||||
{
|
||||
asm
|
||||
(
|
||||
"pushl %ebx\n\t"
|
||||
"xorl %eax, %eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"cmpl $4, %eax\n\t" // check if cpuid supports leaf 4
|
||||
"jl .single_core\n\t" // Single core
|
||||
"movl $4, %eax\n\t"
|
||||
"movl $0, %ecx\n\t" // start with index = 0; Leaf 4 reports
|
||||
"popl %ebx\n\t"
|
||||
); // at least one valid cache level
|
||||
asm
|
||||
(
|
||||
"cpuid"
|
||||
: "=a" (Regeax)
|
||||
:
|
||||
: "%ecx", "%edx"
|
||||
);
|
||||
asm
|
||||
(
|
||||
"jmp .multi_core\n"
|
||||
".single_core:\n\t"
|
||||
"xor %eax, %eax\n"
|
||||
".multi_core:"
|
||||
);
|
||||
}
|
||||
#elif defined( TORQUE_COMPILER_VISUALC )
|
||||
__asm
|
||||
{
|
||||
xor eax, eax
|
||||
cpuid
|
||||
cmp eax, 4 // check if cpuid supports leaf 4
|
||||
jl single_core // Single core
|
||||
mov eax, 4
|
||||
mov ecx, 0 // start with index = 0; Leaf 4 reports
|
||||
cpuid // at least one valid cache level
|
||||
mov Regeax, eax
|
||||
jmp multi_core
|
||||
|
||||
single_core:
|
||||
xor eax, eax
|
||||
|
||||
multi_core:
|
||||
|
||||
}
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
return (unsigned int)((Regeax & NUM_CORE_BITS) >> 26)+1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// The function returns 0 when the hardware multi-threaded bit is not set.
|
||||
//
|
||||
static unsigned int HWD_MTSupported(void)
|
||||
{
|
||||
|
||||
|
||||
unsigned int Regedx = 0;
|
||||
|
||||
|
||||
if ((CpuIDSupported() >= 1))
|
||||
{
|
||||
#ifdef TORQUE_COMPILER_GCC
|
||||
asm
|
||||
(
|
||||
"pushl %%ebx\n\t"
|
||||
"movl $1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"popl %%ebx\n\t"
|
||||
: "=d" (Regedx)
|
||||
:
|
||||
: "%eax","%ecx"
|
||||
);
|
||||
#elif defined( TORQUE_COMPILER_VISUALC )
|
||||
__asm
|
||||
{
|
||||
mov eax, 1
|
||||
cpuid
|
||||
mov Regedx, edx
|
||||
}
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
}
|
||||
|
||||
return (Regedx & HWD_MT_BIT);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Function returns the maximum logical processors per physical package. Note that the number of
|
||||
// AVAILABLE logical processors per physical to be used by an application might be less than this
|
||||
// maximum value.
|
||||
//
|
||||
static unsigned int MaxLogicalProcPerPhysicalProc(void)
|
||||
{
|
||||
|
||||
unsigned int Regebx = 0;
|
||||
|
||||
if (!HWD_MTSupported()) return (unsigned int) 1;
|
||||
#ifdef TORQUE_COMPILER_GCC
|
||||
asm
|
||||
(
|
||||
"movl $1,%%eax\n\t"
|
||||
"cpuid"
|
||||
: "=b" (Regebx)
|
||||
:
|
||||
: "%eax","%ecx","%edx"
|
||||
);
|
||||
#elif defined( TORQUE_COMPILER_VISUALC )
|
||||
__asm
|
||||
{
|
||||
mov eax, 1
|
||||
cpuid
|
||||
mov Regebx, ebx
|
||||
}
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
return (unsigned int) ((Regebx & NUM_LOGICAL_BITS) >> 16);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static unsigned char GetAPIC_ID(void)
|
||||
{
|
||||
|
||||
unsigned int Regebx = 0;
|
||||
#ifdef TORQUE_COMPILER_GCC
|
||||
asm
|
||||
(
|
||||
"movl $1, %%eax\n\t"
|
||||
"cpuid"
|
||||
: "=b" (Regebx)
|
||||
:
|
||||
: "%eax","%ecx","%edx"
|
||||
);
|
||||
|
||||
#elif defined( TORQUE_COMPILER_VISUALC )
|
||||
__asm
|
||||
{
|
||||
mov eax, 1
|
||||
cpuid
|
||||
mov Regebx, ebx
|
||||
}
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
|
||||
return (unsigned char) ((Regebx & INITIAL_APIC_ID_BITS) >> 24);
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Determine the width of the bit field that can represent the value count_item.
|
||||
//
|
||||
unsigned int find_maskwidth(unsigned int CountItem)
|
||||
{
|
||||
unsigned int MaskWidth,
|
||||
count = CountItem;
|
||||
#ifdef TORQUE_COMPILER_GCC
|
||||
asm
|
||||
(
|
||||
#ifdef __x86_64__ // define constant to compile
|
||||
"push %%rcx\n\t" // under 64-bit Linux
|
||||
"push %%rax\n\t"
|
||||
#else
|
||||
"pushl %%ecx\n\t"
|
||||
"pushl %%eax\n\t"
|
||||
#endif
|
||||
// "movl $count, %%eax\n\t" //done by Assembler below
|
||||
"xorl %%ecx, %%ecx"
|
||||
// "movl %%ecx, MaskWidth\n\t" //done by Assembler below
|
||||
: "=c" (MaskWidth)
|
||||
: "a" (count)
|
||||
// : "%ecx", "%eax" We don't list these as clobbered because we don't want the assembler
|
||||
//to put them back when we are done
|
||||
);
|
||||
asm
|
||||
(
|
||||
"decl %%eax\n\t"
|
||||
"bsrw %%ax,%%cx\n\t"
|
||||
"jz next\n\t"
|
||||
"incw %%cx\n\t"
|
||||
// "movl %%ecx, MaskWidth\n" //done by Assembler below
|
||||
: "=c" (MaskWidth)
|
||||
:
|
||||
);
|
||||
asm
|
||||
(
|
||||
"next:\n\t"
|
||||
#ifdef __x86_64__
|
||||
"pop %rax\n\t"
|
||||
"pop %rcx"
|
||||
#else
|
||||
"popl %eax\n\t"
|
||||
"popl %ecx"
|
||||
#endif
|
||||
);
|
||||
|
||||
#elif defined( TORQUE_COMPILER_VISUALC )
|
||||
__asm
|
||||
{
|
||||
mov eax, count
|
||||
mov ecx, 0
|
||||
mov MaskWidth, ecx
|
||||
dec eax
|
||||
bsr cx, ax
|
||||
jz next
|
||||
inc cx
|
||||
mov MaskWidth, ecx
|
||||
next:
|
||||
|
||||
}
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
return MaskWidth;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Extract the subset of bit field from the 8-bit value FullID. It returns the 8-bit sub ID value
|
||||
//
|
||||
static unsigned char GetNzbSubID(unsigned char FullID,
|
||||
unsigned char MaxSubIDValue,
|
||||
unsigned char ShiftCount)
|
||||
{
|
||||
unsigned int MaskWidth;
|
||||
unsigned char MaskBits;
|
||||
|
||||
MaskWidth = find_maskwidth((unsigned int) MaxSubIDValue);
|
||||
MaskBits = (0xff << ShiftCount) ^
|
||||
((unsigned char) (0xff << (ShiftCount + MaskWidth)));
|
||||
|
||||
return (FullID & MaskBits);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
EConfig CPUCount(U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum)
|
||||
{
|
||||
EConfig StatusFlag = CONFIG_UserConfigIssue;
|
||||
|
||||
g_s3Levels[0] = 0;
|
||||
TotAvailCore = 1;
|
||||
PhysicalNum = 1;
|
||||
|
||||
unsigned int numLPEnabled = 0;
|
||||
int MaxLPPerCore = 1;
|
||||
|
||||
#ifdef TORQUE_OS_MAC
|
||||
|
||||
//FIXME: This isn't a proper port but more or less just some sneaky cheating
|
||||
// to get around having to mess with yet another crap UNIX-style API. Seems
|
||||
// like there isn't a way to do this that's working across all OSX incarnations
|
||||
// and machine configurations anyway.
|
||||
|
||||
int numCPUs;
|
||||
int numPackages;
|
||||
|
||||
// Get the number of CPUs.
|
||||
|
||||
size_t len = sizeof( numCPUs );
|
||||
if( sysctlbyname( "hw.ncpu", &numCPUs, &len, 0, 0 ) == -1 )
|
||||
return CONFIG_UserConfigIssue;
|
||||
|
||||
// Get the number of packages.
|
||||
len = sizeof( numPackages );
|
||||
if( sysctlbyname( "hw.packages", &numPackages, &len, 0, 0 ) == -1 )
|
||||
return CONFIG_UserConfigIssue;
|
||||
|
||||
TotAvailCore = numCPUs;
|
||||
TotAvailLogical = numCPUs;
|
||||
PhysicalNum = numPackages;
|
||||
#else
|
||||
|
||||
U32 dwAffinityMask;
|
||||
int j = 0;
|
||||
unsigned char apicID, PackageIDMask;
|
||||
unsigned char tblPkgID[256], tblCoreID[256], tblSMTID[256];
|
||||
char tmp[256];
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
//we need to make sure that this process is allowed to run on
|
||||
//all of the logical processors that the OS itself can run on.
|
||||
//A process could acquire/inherit affinity settings that restricts the
|
||||
// current process to run on a subset of all logical processor visible to OS.
|
||||
|
||||
// Linux doesn't easily allow us to look at the Affinity Bitmask directly,
|
||||
// but it does provide an API to test affinity maskbits of the current process
|
||||
// against each logical processor visible under OS.
|
||||
int sysNumProcs = sysconf(_SC_NPROCESSORS_CONF); //This will tell us how many
|
||||
//CPUs are currently enabled.
|
||||
|
||||
//this will tell us which processors this process can run on.
|
||||
cpu_set_t allowedCPUs;
|
||||
sched_getaffinity(0, sizeof(allowedCPUs), &allowedCPUs);
|
||||
|
||||
for (int i = 0; i < sysNumProcs; i++ )
|
||||
{
|
||||
if ( CPU_ISSET(i, &allowedCPUs) == 0 )
|
||||
return CONFIG_UserConfigIssue;
|
||||
}
|
||||
#elif defined( TORQUE_OS_WIN32 )
|
||||
DWORD dwProcessAffinity, dwSystemAffinity;
|
||||
GetProcessAffinityMask(GetCurrentProcess(),
|
||||
&dwProcessAffinity,
|
||||
&dwSystemAffinity);
|
||||
if (dwProcessAffinity != dwSystemAffinity) // not all CPUs are enabled
|
||||
return CONFIG_UserConfigIssue;
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
|
||||
// Assume that cores within a package have the SAME number of
|
||||
// logical processors. Also, values returned by
|
||||
// MaxLogicalProcPerPhysicalProc and MaxCorePerPhysicalProc do not have
|
||||
// to be power of 2.
|
||||
|
||||
MaxLPPerCore = MaxLogicalProcPerPhysicalProc() / MaxCorePerPhysicalProc();
|
||||
dwAffinityMask = 1;
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
cpu_set_t currentCPU;
|
||||
while ( j < sysNumProcs )
|
||||
{
|
||||
CPU_ZERO(¤tCPU);
|
||||
CPU_SET(j, ¤tCPU);
|
||||
if ( sched_setaffinity (0, sizeof(currentCPU), ¤tCPU) == 0 )
|
||||
{
|
||||
sleep(0); // Ensure system to switch to the right CPU
|
||||
#elif defined( TORQUE_OS_WIN32 )
|
||||
while (dwAffinityMask && dwAffinityMask <= dwSystemAffinity)
|
||||
{
|
||||
if (SetThreadAffinityMask(GetCurrentThread(), dwAffinityMask))
|
||||
{
|
||||
Sleep(0); // Ensure system to switch to the right CPU
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
apicID = GetAPIC_ID();
|
||||
|
||||
|
||||
// Store SMT ID and core ID of each logical processor
|
||||
// Shift vlaue for SMT ID is 0
|
||||
// Shift value for core ID is the mask width for maximum logical
|
||||
// processors per core
|
||||
|
||||
tblSMTID[j] = GetNzbSubID(apicID, MaxLPPerCore, 0);
|
||||
tblCoreID[j] = GetNzbSubID(apicID,
|
||||
MaxCorePerPhysicalProc(),
|
||||
(unsigned char) find_maskwidth(MaxLPPerCore));
|
||||
|
||||
// Extract package ID, assume single cluster.
|
||||
// Shift value is the mask width for max Logical per package
|
||||
|
||||
PackageIDMask = (unsigned char) (0xff <<
|
||||
find_maskwidth(MaxLogicalProcPerPhysicalProc()));
|
||||
|
||||
tblPkgID[j] = apicID & PackageIDMask;
|
||||
sprintf(tmp," AffinityMask = %d; Initial APIC = %d; Physical ID = %d, Core ID = %d, SMT ID = %d\n",
|
||||
dwAffinityMask, apicID, tblPkgID[j], tblCoreID[j], tblSMTID[j]);
|
||||
strcat(g_s3Levels, tmp);
|
||||
|
||||
numLPEnabled ++; // Number of available logical processors in the system.
|
||||
|
||||
} // if
|
||||
|
||||
j++;
|
||||
dwAffinityMask = 1 << j;
|
||||
} // while
|
||||
|
||||
// restore the affinity setting to its original state
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
sched_setaffinity (0, sizeof(allowedCPUs), &allowedCPUs);
|
||||
sleep(0);
|
||||
#elif defined( TORQUE_OS_WIN32 )
|
||||
SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinity);
|
||||
Sleep(0);
|
||||
#else
|
||||
# error Not implemented.
|
||||
#endif
|
||||
TotAvailLogical = numLPEnabled;
|
||||
|
||||
//
|
||||
// Count available cores (TotAvailCore) in the system
|
||||
//
|
||||
unsigned char CoreIDBucket[256];
|
||||
DWORD ProcessorMask, pCoreMask[256];
|
||||
unsigned int i, ProcessorNum;
|
||||
|
||||
CoreIDBucket[0] = tblPkgID[0] | tblCoreID[0];
|
||||
ProcessorMask = 1;
|
||||
pCoreMask[0] = ProcessorMask;
|
||||
|
||||
for (ProcessorNum = 1; ProcessorNum < numLPEnabled; ProcessorNum++)
|
||||
{
|
||||
ProcessorMask <<= 1;
|
||||
for (i = 0; i < TotAvailCore; i++)
|
||||
{
|
||||
// Comparing bit-fields of logical processors residing in different packages
|
||||
// Assuming the bit-masks are the same on all processors in the system.
|
||||
if ((tblPkgID[ProcessorNum] | tblCoreID[ProcessorNum]) == CoreIDBucket[i])
|
||||
{
|
||||
pCoreMask[i] |= ProcessorMask;
|
||||
break;
|
||||
}
|
||||
|
||||
} // for i
|
||||
|
||||
if (i == TotAvailCore) // did not match any bucket. Start a new one.
|
||||
{
|
||||
CoreIDBucket[i] = tblPkgID[ProcessorNum] | tblCoreID[ProcessorNum];
|
||||
pCoreMask[i] = ProcessorMask;
|
||||
|
||||
TotAvailCore++; // Number of available cores in the system
|
||||
|
||||
}
|
||||
|
||||
} // for ProcessorNum
|
||||
|
||||
|
||||
//
|
||||
// Count physical processor (PhysicalNum) in the system
|
||||
//
|
||||
unsigned char PackageIDBucket[256];
|
||||
DWORD pPackageMask[256];
|
||||
|
||||
PackageIDBucket[0] = tblPkgID[0];
|
||||
ProcessorMask = 1;
|
||||
pPackageMask[0] = ProcessorMask;
|
||||
|
||||
for (ProcessorNum = 1; ProcessorNum < numLPEnabled; ProcessorNum++)
|
||||
{
|
||||
ProcessorMask <<= 1;
|
||||
for (i = 0; i < PhysicalNum; i++)
|
||||
{
|
||||
// Comparing bit-fields of logical processors residing in different packages
|
||||
// Assuming the bit-masks are the same on all processors in the system.
|
||||
if (tblPkgID[ProcessorNum]== PackageIDBucket[i])
|
||||
{
|
||||
pPackageMask[i] |= ProcessorMask;
|
||||
break;
|
||||
}
|
||||
|
||||
} // for i
|
||||
|
||||
if (i == PhysicalNum) // did not match any bucket. Start a new one.
|
||||
{
|
||||
PackageIDBucket[i] = tblPkgID[ProcessorNum];
|
||||
pPackageMask[i] = ProcessorMask;
|
||||
|
||||
PhysicalNum++; // Total number of physical processors in the system
|
||||
|
||||
}
|
||||
|
||||
} // for ProcessorNum
|
||||
#endif
|
||||
|
||||
//
|
||||
// Check to see if the system is multi-core
|
||||
// Check if the system is hyper-threading
|
||||
//
|
||||
if (TotAvailCore > PhysicalNum)
|
||||
{
|
||||
// Multi-core
|
||||
if (MaxLPPerCore == 1)
|
||||
StatusFlag = CONFIG_MultiCoreAndHTNotCapable;
|
||||
else if (numLPEnabled > TotAvailCore)
|
||||
StatusFlag = CONFIG_MultiCoreAndHTEnabled;
|
||||
else StatusFlag = CONFIG_MultiCoreAndHTDisabled;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single-core
|
||||
if (MaxLPPerCore == 1)
|
||||
StatusFlag = CONFIG_SingleCoreAndHTNotCapable;
|
||||
else if (numLPEnabled > TotAvailCore)
|
||||
StatusFlag = CONFIG_SingleCoreHTEnabled;
|
||||
else StatusFlag = CONFIG_SingleCoreHTDisabled;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return StatusFlag;
|
||||
}
|
||||
|
||||
} // namespace CPUInfo
|
||||
#endif
|
||||
75
Engine/source/platform/platformCPUCount.h
Normal file
75
Engine/source/platform/platformCPUCount.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_PLATFORM_PLATFORMCPUCOUNT_H_
|
||||
#define _TORQUE_PLATFORM_PLATFORMCPUCOUNT_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
namespace CPUInfo
|
||||
{
|
||||
enum EConfig
|
||||
{
|
||||
CONFIG_UserConfigIssue,
|
||||
CONFIG_SingleCoreHTEnabled,
|
||||
CONFIG_SingleCoreHTDisabled,
|
||||
CONFIG_SingleCoreAndHTNotCapable,
|
||||
CONFIG_MultiCoreAndHTNotCapable,
|
||||
CONFIG_MultiCoreAndHTEnabled,
|
||||
CONFIG_MultiCoreAndHTDisabled,
|
||||
};
|
||||
|
||||
inline bool isMultiCore( EConfig config )
|
||||
{
|
||||
switch( config )
|
||||
{
|
||||
case CONFIG_MultiCoreAndHTNotCapable:
|
||||
case CONFIG_MultiCoreAndHTEnabled:
|
||||
case CONFIG_MultiCoreAndHTDisabled:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isHyperThreaded( EConfig config )
|
||||
{
|
||||
switch( config )
|
||||
{
|
||||
case CONFIG_SingleCoreHTEnabled:
|
||||
case CONFIG_MultiCoreAndHTEnabled:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EConfig CPUCount( U32& totalAvailableLogical,
|
||||
U32& totalAvailableCores,
|
||||
U32& numPhysical );
|
||||
|
||||
} // namespace CPUInfo
|
||||
|
||||
#endif // _TORQUE_PLATFORM_PLATFORMCOUNT_H_
|
||||
|
||||
128
Engine/source/platform/platformCPUInfo.asm
Normal file
128
Engine/source/platform/platformCPUInfo.asm
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
;-----------------------------------------------------------------------------
|
||||
; Copyright (c) 2012 GarageGames, LLC
|
||||
;
|
||||
; Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
; of this software and associated documentation files (the "Software"), to
|
||||
; deal in the Software without restriction, including without limitation the
|
||||
; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
; sell copies of the Software, and to permit persons to whom the Software is
|
||||
; furnished to do so, subject to the following conditions:
|
||||
;
|
||||
; The above copyright notice and this permission notice shall be included in
|
||||
; all copies or substantial portions of the Software.
|
||||
;
|
||||
; 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 OR COPYRIGHT HOLDERS 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.
|
||||
;-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
segment .text
|
||||
|
||||
; syntax: export_fn <function name>
|
||||
%macro export_fn 1
|
||||
%ifidn __OUTPUT_FORMAT__, elf
|
||||
; No underscore needed for ELF object files
|
||||
global %1
|
||||
%1:
|
||||
%else
|
||||
global _%1
|
||||
_%1:
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; push registers
|
||||
%macro pushreg 0
|
||||
; pushad
|
||||
push ebx
|
||||
push ebp
|
||||
push esi
|
||||
push edi
|
||||
%endmacro
|
||||
|
||||
; pop registers
|
||||
%macro popreg 0
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebp
|
||||
pop ebx
|
||||
; popad
|
||||
%endmacro
|
||||
|
||||
; void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties);
|
||||
export_fn detectX86CPUInfo
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
pushreg
|
||||
|
||||
push edx
|
||||
push ecx
|
||||
pushfd
|
||||
pushfd ; save EFLAGS to stack
|
||||
pop eax ; move EFLAGS into EAX
|
||||
mov ebx, eax
|
||||
xor eax, 0x200000 ; flip bit 21
|
||||
push eax
|
||||
popfd ; restore EFLAGS
|
||||
pushfd
|
||||
pop eax
|
||||
cmp eax, ebx
|
||||
jz EXIT ; doesn't support CPUID instruction
|
||||
|
||||
;
|
||||
; get vendor information using CPUID eax == 0
|
||||
xor eax, eax
|
||||
cpuid
|
||||
|
||||
; store the vendor tag (12 bytes in ebx, edx, ecx) in the first parameter,
|
||||
; which should be a char[13]
|
||||
push eax ; save eax
|
||||
mov eax, [ebp+8] ; store the char* address in eax
|
||||
mov [eax], ebx ; move ebx into the first 4 bytes
|
||||
add eax, 4 ; advance the char* 4 bytes
|
||||
mov [eax], edx ; move edx into the next 4 bytes
|
||||
add eax, 4 ; advance the char* 4 bytes
|
||||
mov [eax], ecx ; move ecx into the last 4 bytes
|
||||
pop eax ; restore eax
|
||||
|
||||
; get generic extended CPUID info
|
||||
mov eax, 1
|
||||
cpuid ; eax=1, so cpuid queries feature information
|
||||
|
||||
and eax, 0x0fff3fff
|
||||
push ecx
|
||||
mov ecx, [ebp+12]
|
||||
mov [ecx], eax ; just store the model bits in processor param
|
||||
mov ecx, [ebp+16]
|
||||
mov [ecx], edx ; set properties param
|
||||
pop ecx
|
||||
|
||||
; want to check for 3DNow(tm).
|
||||
; need to see if extended cpuid functions present.
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000000
|
||||
jbe MAYBE_3DLATER
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
; 3DNow if bit 31 set -> put bit in our properties
|
||||
and edx, 0x80000000
|
||||
push eax
|
||||
mov eax, [ebp+16]
|
||||
or [eax], edx
|
||||
pop eax
|
||||
MAYBE_3DLATER:
|
||||
EXIT:
|
||||
popfd
|
||||
pop ecx
|
||||
pop edx
|
||||
|
||||
popreg
|
||||
|
||||
pop ebp
|
||||
ret
|
||||
72
Engine/source/platform/platformDlibrary.h
Normal file
72
Engine/source/platform/platformDlibrary.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef OS_DLIBRARY_H
|
||||
#define OS_DLIBRARY_H
|
||||
|
||||
#include "core/util/refBase.h"
|
||||
|
||||
// DLLs use the standard calling convention
|
||||
#define DLL_CALL __stdcall
|
||||
#define DLL_EXPORT_CALL __declspec(dllexport)
|
||||
#define DLL_IMPORT_CALL __declspec(dllimport)
|
||||
|
||||
// Export functions from the DLL
|
||||
#if defined(DLL_CODE)
|
||||
#define DLL_DECL DLL_EXPORT_CALL
|
||||
#else
|
||||
#define DLL_DECL DLL_IMPORT_CALL
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
///@defgroup KernelDLL Loadable Libraries
|
||||
/// Basic DLL handling and symbol resolving. When a library is first loaded
|
||||
/// it's "open" function will be called, and it's "close" function is called
|
||||
/// right before the library is unloaded.
|
||||
///@ingroup OsModule
|
||||
///@{
|
||||
|
||||
/// Dynamic Library
|
||||
/// Interface for library objects loaded using the loadLibrary() function.
|
||||
class DLibrary: public StrongRefBase
|
||||
{
|
||||
public:
|
||||
virtual ~DLibrary() {}
|
||||
virtual void *bind( const char *name ) = 0;
|
||||
};
|
||||
typedef StrongRefPtr<DLibrary> DLibraryRef;
|
||||
|
||||
/// Load a library
|
||||
/// Returns 0 if the library fails to load. Symbols are
|
||||
/// resolved through the DLibrary interface.
|
||||
DLibraryRef OsLoadLibrary( const char *file );
|
||||
|
||||
///@}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
531
Engine/source/platform/platformFileIO.cpp
Normal file
531
Engine/source/platform/platformFileIO.cpp
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "util/tempAlloc.h"
|
||||
#include "console/console.h"
|
||||
#include "core/stringTable.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
StringTableEntry Platform::getTemporaryDirectory()
|
||||
{
|
||||
StringTableEntry path = osGetTemporaryDirectory();
|
||||
|
||||
if(! Platform::isDirectory(path))
|
||||
path = Platform::getCurrentDirectory();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
ConsoleFunction(getTemporaryDirectory, const char *, 1, 1, "()"
|
||||
"@brief Returns the OS temporary directory, \"C:/Users/Mich/AppData/Local/Temp\" for example\n\n"
|
||||
"@note This can be useful to adhering to OS standards and practices, "
|
||||
"but not really used in Torque 3D right now.\n"
|
||||
"@note Be very careful when getting into OS level File I/O."
|
||||
"@return String containing path to OS temp directory\n"
|
||||
"@note This is legacy function brought over from TGB, and does not appear "
|
||||
"to have much use. Possibly deprecate?\n"
|
||||
"@ingroup FileSystem\n"
|
||||
"@internal")
|
||||
{
|
||||
return Platform::getTemporaryDirectory();
|
||||
}
|
||||
|
||||
StringTableEntry Platform::getTemporaryFileName()
|
||||
{
|
||||
char buf[512];
|
||||
StringTableEntry path = Platform::getTemporaryDirectory();
|
||||
|
||||
dSprintf(buf, sizeof(buf), "%s/tgb.%08x.%02x.tmp", path, Platform::getRealMilliseconds(), U32(Platform::getRandom() * 255));
|
||||
|
||||
// [tom, 9/7/2006] This shouldn't be needed, but just in case
|
||||
if(Platform::isFile(buf))
|
||||
return Platform::getTemporaryFileName();
|
||||
|
||||
return StringTable->insert(buf);
|
||||
}
|
||||
|
||||
ConsoleFunction(getTemporaryFileName, const char *, 1, 1, "()"
|
||||
"@brief Creates a name and extension for a potential temporary file\n\n"
|
||||
"This does not create the actual file. It simply creates a random name "
|
||||
"for a file that does not exist.\n\n"
|
||||
"@note This is legacy function brought over from TGB, and does not appear "
|
||||
"to have much use. Possibly deprecate?\n"
|
||||
"@ingroup FileSystem\n"
|
||||
"@internal")
|
||||
{
|
||||
return Platform::getTemporaryFileName();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static StringTableEntry sgMainCSDir = NULL;
|
||||
|
||||
StringTableEntry Platform::getMainDotCsDir()
|
||||
{
|
||||
if(sgMainCSDir == NULL)
|
||||
sgMainCSDir = Platform::getExecutablePath();
|
||||
|
||||
return sgMainCSDir;
|
||||
}
|
||||
|
||||
void Platform::setMainDotCsDir(const char *dir)
|
||||
{
|
||||
sgMainCSDir = StringTable->insert(dir);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef Vector<char*> CharVector;
|
||||
static CharVector gPlatformDirectoryExcludeList( __FILE__, __LINE__ );
|
||||
|
||||
void Platform::addExcludedDirectory(const char *pDir)
|
||||
{
|
||||
gPlatformDirectoryExcludeList.push_back(dStrdup(pDir));
|
||||
}
|
||||
|
||||
void Platform::clearExcludedDirectories()
|
||||
{
|
||||
while(gPlatformDirectoryExcludeList.size())
|
||||
{
|
||||
dFree(gPlatformDirectoryExcludeList.last());
|
||||
gPlatformDirectoryExcludeList.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
bool Platform::isExcludedDirectory(const char *pDir)
|
||||
{
|
||||
for(CharVector::iterator i=gPlatformDirectoryExcludeList.begin(); i!=gPlatformDirectoryExcludeList.end(); i++)
|
||||
if(!dStrcmp(pDir, *i))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void catPath(char *dst, const char *src, U32 len)
|
||||
{
|
||||
if(*dst != '/')
|
||||
{
|
||||
++dst; --len;
|
||||
*dst = '/';
|
||||
}
|
||||
|
||||
++dst; --len;
|
||||
|
||||
dStrncpy(dst, src, len);
|
||||
dst[len - 1] = 0;
|
||||
}
|
||||
|
||||
// converts the posix root path "/" to "c:/" for win32
|
||||
// FIXME: this is not ideal. the c: drive is not guaranteed to exist.
|
||||
#if defined(TORQUE_OS_WIN32)
|
||||
static inline void _resolveLeadingSlash(char* buf, U32 size)
|
||||
{
|
||||
if(buf[0] != '/')
|
||||
return;
|
||||
|
||||
AssertFatal(dStrlen(buf) + 2 < size, "Expanded path would be too long");
|
||||
dMemmove(buf + 2, buf, dStrlen(buf));
|
||||
buf[0] = 'c';
|
||||
buf[1] = ':';
|
||||
}
|
||||
#endif
|
||||
|
||||
static void makeCleanPathInPlace( char* path )
|
||||
{
|
||||
U32 pathDepth = 0;
|
||||
char* fromPtr = path;
|
||||
char* toPtr = path;
|
||||
|
||||
bool isAbsolute = false;
|
||||
if( *fromPtr == '/' )
|
||||
{
|
||||
fromPtr ++;
|
||||
toPtr ++;
|
||||
isAbsolute = true;
|
||||
}
|
||||
else if( fromPtr[ 0 ] != '\0' && fromPtr[ 1 ] == ':' )
|
||||
{
|
||||
toPtr += 3;
|
||||
fromPtr += 3;
|
||||
isAbsolute = true;
|
||||
}
|
||||
|
||||
while( *fromPtr )
|
||||
{
|
||||
if( fromPtr[ 0 ] == '.' && fromPtr[ 1 ] == '.' && fromPtr[ 2 ] == '/' )
|
||||
{
|
||||
// Back up from '../'
|
||||
|
||||
if( pathDepth > 0 )
|
||||
{
|
||||
pathDepth --;
|
||||
toPtr -= 2;
|
||||
while( toPtr >= path && *toPtr != '/' )
|
||||
toPtr --;
|
||||
toPtr ++;
|
||||
}
|
||||
else if( !isAbsolute )
|
||||
{
|
||||
dMemcpy( toPtr, fromPtr, 3 );
|
||||
toPtr += 3;
|
||||
}
|
||||
|
||||
fromPtr += 3;
|
||||
}
|
||||
else if( fromPtr[ 0 ] == '.' && fromPtr[ 1 ] == '/' )
|
||||
{
|
||||
// Ignore.
|
||||
fromPtr += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fromPtr[ 0 ] == '/' )
|
||||
pathDepth ++;
|
||||
|
||||
*toPtr ++ = *fromPtr ++;
|
||||
}
|
||||
}
|
||||
|
||||
*toPtr = '\0';
|
||||
}
|
||||
|
||||
char * Platform::makeFullPathName(const char *path, char *buffer, U32 size, const char *cwd /* = NULL */)
|
||||
{
|
||||
char bspath[1024];
|
||||
dStrncpy(bspath, path, sizeof(bspath));
|
||||
bspath[sizeof(bspath)-1] = 0;
|
||||
|
||||
for(S32 i = 0;i < dStrlen(bspath);++i)
|
||||
{
|
||||
if(bspath[i] == '\\')
|
||||
bspath[i] = '/';
|
||||
}
|
||||
|
||||
if(Platform::isFullPath(bspath))
|
||||
{
|
||||
// Already a full path
|
||||
#if defined(TORQUE_OS_WIN32)
|
||||
_resolveLeadingSlash(bspath, sizeof(bspath));
|
||||
#endif
|
||||
dStrncpy(buffer, bspath, size);
|
||||
buffer[size-1] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// [rene, 05/05/2008] Based on overall file handling in Torque, it does not seem to make
|
||||
// that much sense to me to base things off the current working directory here.
|
||||
|
||||
if(cwd == NULL)
|
||||
cwd = Con::isCurrentScriptToolScript() ? Platform::getMainDotCsDir() : Platform::getCurrentDirectory();
|
||||
|
||||
dStrncpy(buffer, cwd, size);
|
||||
buffer[size-1] = 0;
|
||||
|
||||
const char* defaultDir = Con::getVariable("defaultGame");
|
||||
|
||||
char *ptr = bspath;
|
||||
char *slash = NULL;
|
||||
char *endptr = buffer + dStrlen(buffer) - 1;
|
||||
|
||||
do
|
||||
{
|
||||
slash = dStrchr(ptr, '/');
|
||||
if(slash)
|
||||
{
|
||||
*slash = 0;
|
||||
|
||||
// Directory
|
||||
|
||||
if(dStrcmp(ptr, "..") == 0)
|
||||
{
|
||||
// Parent
|
||||
endptr = dStrrchr(buffer, '/');
|
||||
if (endptr)
|
||||
*endptr-- = 0;
|
||||
}
|
||||
else if(dStrcmp(ptr, ".") == 0)
|
||||
{
|
||||
// Current dir
|
||||
}
|
||||
else if(dStrcmp(ptr, "~") == 0)
|
||||
{
|
||||
catPath(endptr, defaultDir, size - (endptr - buffer));
|
||||
endptr += dStrlen(endptr) - 1;
|
||||
}
|
||||
else if(endptr)
|
||||
{
|
||||
catPath(endptr, ptr, size - (endptr - buffer));
|
||||
endptr += dStrlen(endptr) - 1;
|
||||
}
|
||||
|
||||
ptr = slash + 1;
|
||||
}
|
||||
else if(endptr)
|
||||
{
|
||||
// File
|
||||
|
||||
catPath(endptr, ptr, size - (endptr - buffer));
|
||||
endptr += dStrlen(endptr) - 1;
|
||||
}
|
||||
|
||||
} while(slash);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool Platform::isFullPath(const char *path)
|
||||
{
|
||||
// Quick way out
|
||||
if(path[0] == '/' || path[1] == ':')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Return "fileName" stripped of its extension. Only extensions contained
|
||||
/// in "validExtensions" will be stripped from the filename.
|
||||
///
|
||||
/// @note Extensions in "validExtension" should include the dot.
|
||||
String Platform::stripExtension( String fileName, Vector< String >& validExtensions )
|
||||
{
|
||||
// See if we have a valid extension to strip off
|
||||
String ext;
|
||||
S32 dotPos = fileName.find( '.', 0, String::Right );
|
||||
if( dotPos != String::NPos )
|
||||
ext = fileName.substr( dotPos );
|
||||
|
||||
U32 numValidExt = validExtensions.size();
|
||||
if( ext.isNotEmpty() && numValidExt )
|
||||
{
|
||||
bool validExt = false;
|
||||
for( U32 i = 0; i < numValidExt; i++ )
|
||||
{
|
||||
if( ext.equal( validExtensions[ i ], String::NoCase ) )
|
||||
{
|
||||
validExt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !validExt )
|
||||
ext = String::EmptyString;
|
||||
}
|
||||
|
||||
if( ext.isEmpty() )
|
||||
return fileName;
|
||||
else
|
||||
return fileName.substr( 0, fileName.length() - ext.length() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TODO: wow really shouldn't be adding everything to the string table, use the string class!
|
||||
StringTableEntry Platform::makeRelativePathName(const char *path, const char *to)
|
||||
{
|
||||
// Make sure 'to' is a proper absolute path terminated with a forward slash.
|
||||
|
||||
char buffer[ 2048 ];
|
||||
if( !to )
|
||||
{
|
||||
dSprintf( buffer, sizeof( buffer ), "%s/", Platform::getMainDotCsDir() );
|
||||
to = buffer;
|
||||
}
|
||||
else if( !Platform::isFullPath( to ) )
|
||||
{
|
||||
dSprintf( buffer, sizeof( buffer ), "%s/%s/", Platform::getMainDotCsDir(), to );
|
||||
makeCleanPathInPlace( buffer );
|
||||
to = buffer;
|
||||
}
|
||||
else if( to[ dStrlen( to ) - 1 ] != '/' )
|
||||
{
|
||||
U32 length = getMin( (U32)dStrlen( to ), sizeof( buffer ) - 2 );
|
||||
dMemcpy( buffer, to, length );
|
||||
buffer[ length ] = '/';
|
||||
buffer[ length + 1 ] = '\0';
|
||||
to = buffer;
|
||||
}
|
||||
|
||||
// If 'path' isn't absolute, make it now. Let's us use a single
|
||||
// absolute/absolute merge path from here on.
|
||||
|
||||
char buffer2[ 1024 ];
|
||||
if( !Platform::isFullPath( path ) )
|
||||
{
|
||||
dSprintf( buffer2, sizeof( buffer2 ), "%s/%s", Platform::getMainDotCsDir(), path );
|
||||
makeCleanPathInPlace( buffer2 );
|
||||
path = buffer2;
|
||||
}
|
||||
|
||||
// First, find the common prefix and see where 'path' branches off from 'to'.
|
||||
|
||||
const char *pathPtr, *toPtr, *branch = path;
|
||||
for(pathPtr = path, toPtr = to;*pathPtr && *toPtr && dTolower(*pathPtr) == dTolower(*toPtr);++pathPtr, ++toPtr)
|
||||
{
|
||||
if(*pathPtr == '/')
|
||||
branch = pathPtr;
|
||||
}
|
||||
|
||||
// If there's no common part, the two paths are on different drives and
|
||||
// there's nothing we can do.
|
||||
|
||||
if( pathPtr == path )
|
||||
return StringTable->insert( path );
|
||||
|
||||
// If 'path' and 'to' are identical (minus trailing slash or so), we can just return './'.
|
||||
|
||||
else if((*pathPtr == 0 || (*pathPtr == '/' && *(pathPtr + 1) == 0)) &&
|
||||
(*toPtr == 0 || (*toPtr == '/' && *(toPtr + 1) == 0)))
|
||||
{
|
||||
char* bufPtr = buffer;
|
||||
*bufPtr ++ = '.';
|
||||
|
||||
if(*pathPtr == '/' || *(pathPtr - 1) == '/')
|
||||
*bufPtr++ = '/';
|
||||
|
||||
*bufPtr = 0;
|
||||
return StringTable->insert(buffer);
|
||||
}
|
||||
|
||||
// If 'to' is a proper prefix of 'path', the remainder of 'path' is our relative path.
|
||||
|
||||
else if( *toPtr == '\0' && toPtr[ -1 ] == '/' )
|
||||
return StringTable->insert( pathPtr );
|
||||
|
||||
// Otherwise have to step up the remaining directories in 'to' and then
|
||||
// append the remainder of 'path'.
|
||||
|
||||
else
|
||||
{
|
||||
if((*pathPtr == 0 && *toPtr == '/') || (*toPtr == '/' && *pathPtr == 0))
|
||||
branch = pathPtr;
|
||||
|
||||
// Allocate a new temp so we aren't prone to buffer overruns.
|
||||
|
||||
TempAlloc< char > temp( dStrlen( toPtr ) + dStrlen( branch ) + 1 );
|
||||
char* bufPtr = temp;
|
||||
|
||||
// Figure out parent dirs
|
||||
|
||||
for(toPtr = to + (branch - path);*toPtr;++toPtr)
|
||||
{
|
||||
if(*toPtr == '/' && *(toPtr + 1) != 0)
|
||||
{
|
||||
*bufPtr++ = '.';
|
||||
*bufPtr++ = '.';
|
||||
*bufPtr++ = '/';
|
||||
}
|
||||
}
|
||||
*bufPtr = 0;
|
||||
|
||||
// Copy the rest
|
||||
if(*branch)
|
||||
dStrcpy(bufPtr, branch + 1);
|
||||
else
|
||||
*--bufPtr = 0;
|
||||
|
||||
return StringTable->insert( temp );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static StringTableEntry tryStripBasePath(const char *path, const char *base)
|
||||
{
|
||||
U32 len = dStrlen(base);
|
||||
if(dStrnicmp(path, base, len) == 0)
|
||||
{
|
||||
if(*(path + len) == '/') ++len;
|
||||
return StringTable->insert(path + len, true);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StringTableEntry Platform::stripBasePath(const char *path)
|
||||
{
|
||||
if(path == NULL)
|
||||
return NULL;
|
||||
|
||||
StringTableEntry str = tryStripBasePath(path, Platform::getMainDotCsDir());
|
||||
|
||||
if(str != NULL )
|
||||
return str;
|
||||
|
||||
str = tryStripBasePath(path, Platform::getCurrentDirectory());
|
||||
if(str != NULL )
|
||||
return str;
|
||||
|
||||
str = tryStripBasePath(path, Platform::getPrefsPath());
|
||||
if(str != NULL )
|
||||
return str;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
StringTableEntry Platform::getPrefsPath(const char *file /* = NULL */)
|
||||
{
|
||||
#ifndef TORQUE2D_TOOLS_FIXME
|
||||
return StringTable->insert(file ? file : "");
|
||||
#else
|
||||
char buf[1024];
|
||||
const char *company = Con::getVariable("$Game::CompanyName");
|
||||
if(company == NULL || *company == 0)
|
||||
company = "GarageGames";
|
||||
|
||||
const char *appName = Con::getVariable("$Game::GameName");
|
||||
if(appName == NULL || *appName == 0)
|
||||
appName = TORQUE_APP_NAME;
|
||||
|
||||
if(file)
|
||||
{
|
||||
if(dStrstr(file, ".."))
|
||||
{
|
||||
Con::errorf("getPrefsPath - filename cannot be relative");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dSprintf(buf, sizeof(buf), "%s/%s/%s/%s", Platform::getUserDataDirectory(), company, appName, file);
|
||||
}
|
||||
else
|
||||
dSprintf(buf, sizeof(buf), "%s/%s/%s", Platform::getUserDataDirectory(), company, appName);
|
||||
|
||||
return StringTable->insert(buf, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleFunction(getUserDataDirectory, const char*, 1, 1, "getUserDataDirectory()")
|
||||
{
|
||||
return Platform::getUserDataDirectory();
|
||||
}
|
||||
|
||||
ConsoleFunction(getUserHomeDirectory, const char*, 1, 1, "getUserHomeDirectory()")
|
||||
{
|
||||
return Platform::getUserHomeDirectory();
|
||||
}
|
||||
54
Engine/source/platform/platformFont.cpp
Normal file
54
Engine/source/platform/platformFont.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformFont.h"
|
||||
|
||||
|
||||
const char *getCharSetName(const U32 charSet)
|
||||
{
|
||||
switch(charSet)
|
||||
{
|
||||
case TGE_ANSI_CHARSET: return "ansi";
|
||||
case TGE_SYMBOL_CHARSET: return "symbol";
|
||||
case TGE_SHIFTJIS_CHARSET: return "shiftjis";
|
||||
case TGE_HANGEUL_CHARSET: return "hangeul";
|
||||
case TGE_HANGUL_CHARSET: return "hangul";
|
||||
case TGE_GB2312_CHARSET: return "gb2312";
|
||||
case TGE_CHINESEBIG5_CHARSET: return "chinesebig5";
|
||||
case TGE_OEM_CHARSET: return "oem";
|
||||
case TGE_JOHAB_CHARSET: return "johab";
|
||||
case TGE_HEBREW_CHARSET: return "hebrew";
|
||||
case TGE_ARABIC_CHARSET: return "arabic";
|
||||
case TGE_GREEK_CHARSET: return "greek";
|
||||
case TGE_TURKISH_CHARSET: return "turkish";
|
||||
case TGE_VIETNAMESE_CHARSET: return "vietnamese";
|
||||
case TGE_THAI_CHARSET: return "thai";
|
||||
case TGE_EASTEUROPE_CHARSET: return "easteurope";
|
||||
case TGE_RUSSIAN_CHARSET: return "russian";
|
||||
case TGE_MAC_CHARSET: return "mac";
|
||||
case TGE_BALTIC_CHARSET: return "baltic";
|
||||
}
|
||||
|
||||
AssertISV(false, "getCharSetName - unknown charset! Update table in platformString.cc!");
|
||||
return "";
|
||||
}
|
||||
97
Engine/source/platform/platformFont.h
Normal file
97
Engine/source/platform/platformFont.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
|
||||
#ifndef _PLATFORMFONT_H_
|
||||
#define _PLATFORMFONT_H_
|
||||
|
||||
// Charsets for fonts
|
||||
|
||||
// [tom, 7/27/2005] These are intended to map to their Win32 equivalents. This
|
||||
// enumeration may require changes to accommodate other platforms.
|
||||
enum FontCharset
|
||||
{
|
||||
TGE_ANSI_CHARSET = 0,
|
||||
TGE_SYMBOL_CHARSET,
|
||||
TGE_SHIFTJIS_CHARSET,
|
||||
TGE_HANGEUL_CHARSET,
|
||||
TGE_HANGUL_CHARSET,
|
||||
TGE_GB2312_CHARSET,
|
||||
TGE_CHINESEBIG5_CHARSET,
|
||||
TGE_OEM_CHARSET,
|
||||
TGE_JOHAB_CHARSET,
|
||||
TGE_HEBREW_CHARSET,
|
||||
TGE_ARABIC_CHARSET,
|
||||
TGE_GREEK_CHARSET,
|
||||
TGE_TURKISH_CHARSET,
|
||||
TGE_VIETNAMESE_CHARSET,
|
||||
TGE_THAI_CHARSET,
|
||||
TGE_EASTEUROPE_CHARSET,
|
||||
TGE_RUSSIAN_CHARSET,
|
||||
TGE_MAC_CHARSET,
|
||||
TGE_BALTIC_CHARSET
|
||||
};
|
||||
|
||||
extern const char *getCharSetName(const U32 charSet);
|
||||
|
||||
class PlatformFont
|
||||
{
|
||||
public:
|
||||
struct CharInfo
|
||||
{
|
||||
S16 bitmapIndex; ///< @note -1 indicates character is NOT to be
|
||||
/// rendered, i.e., \n, \r, etc.
|
||||
U32 xOffset; ///< x offset into bitmap sheet
|
||||
U32 yOffset; ///< y offset into bitmap sheet
|
||||
U32 width; ///< width of character (pixels)
|
||||
U32 height; ///< height of character (pixels)
|
||||
S32 xOrigin;
|
||||
S32 yOrigin;
|
||||
S32 xIncrement;
|
||||
U8 *bitmapData; ///< temp storage for bitmap data
|
||||
};
|
||||
|
||||
virtual ~PlatformFont() {}
|
||||
|
||||
/// Is the specified character valid for rendering?
|
||||
virtual bool isValidChar(const UTF16 ch) const = 0;
|
||||
virtual bool isValidChar(const UTF8 *str) const = 0;
|
||||
|
||||
virtual U32 getFontHeight() const = 0;
|
||||
virtual U32 getFontBaseLine() const = 0;
|
||||
|
||||
virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const = 0;
|
||||
virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const = 0;
|
||||
|
||||
/// This is just for createPlatformFont to call.
|
||||
///
|
||||
/// @todo Rethink this so we don't have a private public.
|
||||
virtual bool create(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET) = 0;
|
||||
static void enumeratePlatformFonts( Vector<StringTableEntry>& fonts, UTF16* fontFamily = NULL );
|
||||
};
|
||||
|
||||
extern PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET);
|
||||
|
||||
#endif // _PLATFORMFONT_H_
|
||||
134
Engine/source/platform/platformInput.h
Normal file
134
Engine/source/platform/platformInput.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMINPUT_H_
|
||||
#define _PLATFORMINPUT_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#include "platform/event.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
U8 TranslateOSKeyCode( U8 vcode );
|
||||
U8 TranslateKeyCodeToOS(U8 keycode);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
class InputDevice : public SimObject
|
||||
{
|
||||
protected:
|
||||
char mName[30];
|
||||
|
||||
public:
|
||||
struct ObjInfo
|
||||
{
|
||||
InputEventType mType;
|
||||
InputObjectInstances mInst;
|
||||
S32 mMin, mMax;
|
||||
};
|
||||
|
||||
inline const char* getDeviceName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
virtual bool process() = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class InputManager : public SimGroup
|
||||
{
|
||||
protected:
|
||||
bool mEnabled;
|
||||
|
||||
public:
|
||||
inline bool isEnabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
virtual bool enable() = 0;
|
||||
virtual void disable() = 0;
|
||||
virtual void process() = 0;
|
||||
};
|
||||
|
||||
enum KEY_STATE
|
||||
{
|
||||
STATE_LOWER,
|
||||
STATE_UPPER,
|
||||
STATE_GOOFY
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class Input
|
||||
{
|
||||
protected:
|
||||
static InputManager* smManager;
|
||||
|
||||
static bool smActive;
|
||||
|
||||
/// Current modifier keys.
|
||||
static U8 smModifierKeys;
|
||||
|
||||
static bool smLastKeyboardActivated;
|
||||
static bool smLastMouseActivated;
|
||||
static bool smLastJoystickActivated;
|
||||
|
||||
public:
|
||||
static void init();
|
||||
static void destroy();
|
||||
|
||||
static bool enable();
|
||||
static void disable();
|
||||
|
||||
static void activate();
|
||||
static void deactivate();
|
||||
|
||||
static U16 getAscii( U16 keyCode, KEY_STATE keyState );
|
||||
static U16 getKeyCode( U16 asciiCode );
|
||||
|
||||
static bool isEnabled();
|
||||
static bool isActive();
|
||||
|
||||
static void process();
|
||||
|
||||
static InputManager* getManager();
|
||||
|
||||
static U8 getModifierKeys() {return smModifierKeys;}
|
||||
static void setModifierKeys(U8 mod) {smModifierKeys = mod;}
|
||||
#ifdef LOG_INPUT
|
||||
static void log( const char* format, ... );
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_OS_XENON
|
||||
static S32 getLockedController();
|
||||
#endif
|
||||
|
||||
/// Global input routing JournaledSignal; post input events here for
|
||||
/// processing.
|
||||
static InputEvent smInputEvent;
|
||||
};
|
||||
|
||||
#endif // _H_PLATFORMINPUT_
|
||||
101
Engine/source/platform/platformIntrinsics.gcc.h
Normal file
101
Engine/source/platform/platformIntrinsics.gcc.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_
|
||||
#define _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_
|
||||
|
||||
/// @file
|
||||
/// Compiler intrinsics for GCC.
|
||||
|
||||
#ifdef TORQUE_OS_MAC
|
||||
#include <libkern/OSAtomic.h>
|
||||
#elif defined(TORQUE_OS_PS3)
|
||||
#include <cell/atomic.h>
|
||||
#endif
|
||||
|
||||
// Fetch-And-Add
|
||||
//
|
||||
// NOTE: These do not return the pre-add value because
|
||||
// not all platforms (damn you OSX) can do that.
|
||||
//
|
||||
inline void dFetchAndAdd( volatile U32& ref, U32 val )
|
||||
{
|
||||
#if defined(TORQUE_OS_PS3)
|
||||
cellAtomicAdd32( (std::uint32_t *)&ref, val );
|
||||
#elif !defined(TORQUE_OS_MAC)
|
||||
__sync_fetch_and_add( ( volatile long* ) &ref, val );
|
||||
#else
|
||||
OSAtomicAdd32( val, (int32_t* ) &ref);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void dFetchAndAdd( volatile S32& ref, S32 val )
|
||||
{
|
||||
#if defined(TORQUE_OS_PS3)
|
||||
cellAtomicAdd32( (std::uint32_t *)&ref, val );
|
||||
#elif !defined(TORQUE_OS_MAC)
|
||||
__sync_fetch_and_add( ( volatile long* ) &ref, val );
|
||||
#else
|
||||
OSAtomicAdd32( val, (int32_t* ) &ref);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compare-And-Swap
|
||||
|
||||
inline bool dCompareAndSwap( volatile U32& ref, U32 oldVal, U32 newVal )
|
||||
{
|
||||
// bool
|
||||
//OSAtomicCompareAndSwap32(int32_t oldValue, int32_t newValue, volatile int32_t *theValue);
|
||||
#if defined(TORQUE_OS_PS3)
|
||||
return ( cellAtomicCompareAndSwap32( (std::uint32_t *)&ref, newVal, oldVal ) == oldVal );
|
||||
#elif !defined(TORQUE_OS_MAC)
|
||||
return ( __sync_val_compare_and_swap( ( volatile long* ) &ref, oldVal, newVal ) == oldVal );
|
||||
#else
|
||||
return OSAtomicCompareAndSwap32(oldVal, newVal, (int32_t *) &ref);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool dCompareAndSwap( volatile U64& ref, U64 oldVal, U64 newVal )
|
||||
{
|
||||
#if defined(TORQUE_OS_PS3)
|
||||
return ( cellAtomicCompareAndSwap32( (std::uint32_t *)&ref, newVal, oldVal ) == oldVal );
|
||||
#elif !defined(TORQUE_OS_MAC)
|
||||
return ( __sync_val_compare_and_swap( ( volatile long long* ) &ref, oldVal, newVal ) == oldVal );
|
||||
#else
|
||||
return OSAtomicCompareAndSwap64(oldVal, newVal, (int64_t *) &ref);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// Performs an atomic read operation.
|
||||
inline U32 dAtomicRead( volatile U32 &ref )
|
||||
{
|
||||
#if defined(TORQUE_OS_PS3)
|
||||
return cellAtomicAdd32( (std::uint32_t *)&ref, 0 );
|
||||
#elif !defined(TORQUE_OS_MAC)
|
||||
return __sync_fetch_and_add( ( volatile long* ) &ref, 0 );
|
||||
#else
|
||||
return OSAtomicAdd32( 0, (int32_t* ) &ref);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_
|
||||
57
Engine/source/platform/platformIntrinsics.h
Normal file
57
Engine/source/platform/platformIntrinsics.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMINTRINSICS_H_
|
||||
#define _PLATFORMINTRINSICS_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
# include "platform/types.h"
|
||||
#endif
|
||||
|
||||
#if defined( TORQUE_COMPILER_VISUALC )
|
||||
# include "platform/platformIntrinsics.visualc.h"
|
||||
#elif defined ( TORQUE_COMPILER_GCC )
|
||||
# include "platform/platformIntrinsics.gcc.h"
|
||||
#else
|
||||
# error No intrinsics implemented for compiler.
|
||||
#endif
|
||||
|
||||
//TODO: 64bit safe
|
||||
|
||||
template< typename T >
|
||||
inline bool dCompareAndSwap( T* volatile& refPtr, T* oldPtr, T* newPtr )
|
||||
{
|
||||
return dCompareAndSwap( *reinterpret_cast< volatile U32* >( &refPtr ), ( U32 ) oldPtr, ( U32 ) newPtr );
|
||||
}
|
||||
|
||||
// Test-And-Set
|
||||
|
||||
inline bool dTestAndSet( volatile U32& ref )
|
||||
{
|
||||
return dCompareAndSwap( ref, 0, 1 );
|
||||
}
|
||||
inline bool dTestAndSet( volatile U64& ref )
|
||||
{
|
||||
return dCompareAndSwap( ref, 0, 1 );
|
||||
}
|
||||
|
||||
#endif // _PLATFORMINTRINSICS_H_
|
||||
76
Engine/source/platform/platformIntrinsics.visualc.h
Normal file
76
Engine/source/platform/platformIntrinsics.visualc.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_
|
||||
#define _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_
|
||||
|
||||
/// @file
|
||||
/// Compiler intrinsics for Visual C++.
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
# include <Xtl.h>
|
||||
# define _InterlockedExchangeAdd InterlockedExchangeAdd
|
||||
# define _InterlockedExchangeAdd64 InterlockedExchangeAdd64
|
||||
#else
|
||||
# include <intrin.h>
|
||||
#endif
|
||||
|
||||
// Fetch-And-Add
|
||||
//
|
||||
// NOTE: These do not return the pre-add value because
|
||||
// not all platforms (damn you OSX) can do that.
|
||||
//
|
||||
inline void dFetchAndAdd( volatile U32& ref, U32 val )
|
||||
{
|
||||
_InterlockedExchangeAdd( ( volatile long* ) &ref, val );
|
||||
}
|
||||
inline void dFetchAndAdd( volatile S32& ref, S32 val )
|
||||
{
|
||||
_InterlockedExchangeAdd( ( volatile long* ) &ref, val );
|
||||
}
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
// Not available on x86
|
||||
inline void dFetchAndAdd( volatile U64& ref, U64 val )
|
||||
{
|
||||
_InterlockedExchangeAdd64( ( volatile __int64* ) &ref, val );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compare-And-Swap
|
||||
|
||||
inline bool dCompareAndSwap( volatile U32& ref, U32 oldVal, U32 newVal )
|
||||
{
|
||||
return ( _InterlockedCompareExchange( ( volatile long* ) &ref, newVal, oldVal ) == oldVal );
|
||||
}
|
||||
inline bool dCompareAndSwap( volatile U64& ref, U64 oldVal, U64 newVal )
|
||||
{
|
||||
return ( _InterlockedCompareExchange64( ( volatile __int64* ) &ref, newVal, oldVal ) == oldVal );
|
||||
}
|
||||
|
||||
/// Performs an atomic read operation.
|
||||
inline U32 dAtomicRead( volatile U32 &ref )
|
||||
{
|
||||
return _InterlockedExchangeAdd( ( volatile long* )&ref, 0 );
|
||||
}
|
||||
|
||||
#endif // _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_
|
||||
1795
Engine/source/platform/platformMemory.cpp
Normal file
1795
Engine/source/platform/platformMemory.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
Engine/source/platform/platformMemory.h
Normal file
59
Engine/source/platform/platformMemory.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_PLATFORM_PLATFORMMEMORY_H_
|
||||
#define _TORQUE_PLATFORM_PLATFORMMEMORY_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
enum EFlag
|
||||
{
|
||||
FLAG_Debug,
|
||||
FLAG_Global,
|
||||
FLAG_Static
|
||||
};
|
||||
|
||||
struct Info
|
||||
{
|
||||
U32 mAllocNumber;
|
||||
U32 mAllocSize;
|
||||
const char* mFileName;
|
||||
U32 mLineNumber;
|
||||
bool mIsArray;
|
||||
bool mIsGlobal;
|
||||
bool mIsStatic;
|
||||
};
|
||||
|
||||
void checkPtr( void* ptr );
|
||||
void flagCurrentAllocs( EFlag flag = FLAG_Debug );
|
||||
void ensureAllFreed();
|
||||
void dumpUnflaggedAllocs(const char *file, EFlag flag = FLAG_Debug );
|
||||
S32 countUnflaggedAllocs(const char *file, S32 *outUnflaggedRealloc = NULL, EFlag flag = FLAG_Debug );
|
||||
dsize_t getMemoryUsed();
|
||||
dsize_t getMemoryAllocated();
|
||||
void getMemoryInfo( void* ptr, Info& info );
|
||||
void validate();
|
||||
}
|
||||
|
||||
#endif // _TORQUE_PLATFORM_PLATFORMMEMORY_H_
|
||||
1051
Engine/source/platform/platformNet.cpp
Normal file
1051
Engine/source/platform/platformNet.cpp
Normal file
File diff suppressed because it is too large
Load diff
150
Engine/source/platform/platformNet.h
Normal file
150
Engine/source/platform/platformNet.h
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_PLATFORMNET_H_
|
||||
#define _PLATFORM_PLATFORMNET_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/rawData.h"
|
||||
#include "core/util/journal/journaledSignal.h"
|
||||
|
||||
#ifndef MAXPACKETSIZE
|
||||
#define MAXPACKETSIZE 1500
|
||||
#endif
|
||||
|
||||
typedef int NetConnectionId;
|
||||
|
||||
/// Generic network address
|
||||
///
|
||||
/// This is used to represent IP addresses.
|
||||
struct NetAddress
|
||||
{
|
||||
int type; ///< Type of address (IPAddress currently)
|
||||
|
||||
/// Acceptable NetAddress types.
|
||||
enum
|
||||
{
|
||||
IPAddress,
|
||||
};
|
||||
|
||||
U8 netNum[4]; ///< For IP: sin_addr<br>
|
||||
U8 nodeNum[6]; ///< For IP: Not used.<br>
|
||||
U16 port; ///< For IP: sin_port<br>
|
||||
};
|
||||
|
||||
typedef S32 NetSocket;
|
||||
const NetSocket InvalidSocket = -1;
|
||||
|
||||
/// void event(NetSocket sock, U32 state)
|
||||
typedef JournaledSignal<void(NetSocket,U32)> ConnectionNotifyEvent;
|
||||
|
||||
/// void event(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress)
|
||||
typedef JournaledSignal<void(NetSocket,NetSocket,NetAddress)> ConnectionAcceptedEvent;
|
||||
|
||||
/// void event(NetSocket connection, RawData incomingData)
|
||||
typedef JournaledSignal<void(NetSocket,RawData)> ConnectionReceiveEvent;
|
||||
|
||||
/// void event(NetAddress originator, RawData incomingData)
|
||||
typedef JournaledSignal<void(NetAddress,RawData)> PacketReceiveEvent;
|
||||
|
||||
/// Platform-specific network operations.
|
||||
struct Net
|
||||
{
|
||||
enum Error
|
||||
{
|
||||
NoError,
|
||||
WrongProtocolType,
|
||||
InvalidPacketProtocol,
|
||||
WouldBlock,
|
||||
NotASocket,
|
||||
UnknownError
|
||||
};
|
||||
|
||||
enum ConnectionState {
|
||||
DNSResolved,
|
||||
DNSFailed,
|
||||
Connected,
|
||||
ConnectFailed,
|
||||
Disconnected
|
||||
};
|
||||
|
||||
enum Protocol
|
||||
{
|
||||
UDPProtocol,
|
||||
TCPProtocol
|
||||
};
|
||||
|
||||
static const int MaxPacketDataSize = MAXPACKETSIZE;
|
||||
|
||||
static ConnectionNotifyEvent smConnectionNotify;
|
||||
static ConnectionAcceptedEvent smConnectionAccept;
|
||||
static ConnectionReceiveEvent smConnectionReceive;
|
||||
static PacketReceiveEvent smPacketReceive;
|
||||
|
||||
static bool init();
|
||||
static void shutdown();
|
||||
|
||||
// Unreliable net functions (UDP)
|
||||
// sendto is for sending data
|
||||
// all incoming data comes in on packetReceiveEventType
|
||||
// App can only open one unreliable port... who needs more? ;)
|
||||
|
||||
static bool openPort(S32 connectPort, bool doBind = true);
|
||||
static NetSocket getPort();
|
||||
|
||||
static void closePort();
|
||||
static Error sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize);
|
||||
|
||||
// Reliable net functions (TCP)
|
||||
// all incoming messages come in on the Connected* events
|
||||
static NetSocket openListenPort(U16 port);
|
||||
static NetSocket openConnectTo(const char *stringAddress); // does the DNS resolve etc.
|
||||
static void closeConnectTo(NetSocket socket);
|
||||
static Error sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize);
|
||||
|
||||
static bool compareAddresses(const NetAddress *a1, const NetAddress *a2);
|
||||
static bool stringToAddress(const char *addressString, NetAddress *address);
|
||||
static void addressToString(const NetAddress *address, char addressString[256]);
|
||||
|
||||
// lower level socked based network functions
|
||||
static NetSocket openSocket();
|
||||
static Error closeSocket(NetSocket socket);
|
||||
|
||||
static Error send(NetSocket socket, const U8 *buffer, S32 bufferSize);
|
||||
static Error recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead);
|
||||
|
||||
static Error connect(NetSocket socket, const NetAddress *address);
|
||||
static Error listen(NetSocket socket, S32 maxConcurrentListens);
|
||||
static NetSocket accept(NetSocket acceptSocket, NetAddress *remoteAddress);
|
||||
|
||||
static Error bind(NetSocket socket, U16 port);
|
||||
static Error setBufferSize(NetSocket socket, S32 bufferSize);
|
||||
static Error setBroadcast(NetSocket socket, bool broadcastEnable);
|
||||
static Error setBlocking(NetSocket socket, bool blockingIO);
|
||||
|
||||
|
||||
private:
|
||||
static void process();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
187
Engine/source/platform/platformNetAsync.cpp
Normal file
187
Engine/source/platform/platformNetAsync.cpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platformNetAsync.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "platform/threads/threadPool.h"
|
||||
#include "console/console.h"
|
||||
|
||||
#if defined(TORQUE_OS_WIN32)
|
||||
# include <winsock.h>
|
||||
#elif defined(TORQUE_OS_XENON)
|
||||
# include <Xtl.h>
|
||||
#else
|
||||
# include <netdb.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
NetAsync gNetAsync;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// NetAsync::NameLookupRequest.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// internal structure for storing information about a name lookup request
|
||||
struct NetAsync::NameLookupRequest
|
||||
{
|
||||
NetSocket sock;
|
||||
char remoteAddr[4096];
|
||||
char out_h_addr[4096];
|
||||
int out_h_length;
|
||||
bool complete;
|
||||
|
||||
NameLookupRequest()
|
||||
{
|
||||
sock = InvalidSocket;
|
||||
remoteAddr[0] = 0;
|
||||
out_h_addr[0] = 0;
|
||||
out_h_length = -1;
|
||||
complete = false;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// NetAsync::NameLookupWorkItem.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Work item issued to the thread pool for each lookup request.
|
||||
|
||||
struct NetAsync::NameLookupWorkItem : public ThreadPool::WorkItem
|
||||
{
|
||||
typedef ThreadPool::WorkItem Parent;
|
||||
|
||||
NameLookupWorkItem( NameLookupRequest& request, ThreadPool::Context* context = 0 )
|
||||
: Parent( context ),
|
||||
mRequest( request )
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void execute()
|
||||
{
|
||||
#ifndef TORQUE_OS_XENON
|
||||
// do it
|
||||
struct hostent* hostent = gethostbyname(mRequest.remoteAddr);
|
||||
if (hostent == NULL)
|
||||
{
|
||||
// oh well! leave the lookup data unmodified (h_length) should
|
||||
// still be -1 from initialization
|
||||
mRequest.complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy the stuff we need from the hostent
|
||||
dMemset(mRequest.out_h_addr, 0,
|
||||
sizeof(mRequest.out_h_addr));
|
||||
dMemcpy(mRequest.out_h_addr, hostent->h_addr, hostent->h_length);
|
||||
|
||||
mRequest.out_h_length = hostent->h_length;
|
||||
mRequest.complete = true;
|
||||
}
|
||||
#else
|
||||
XNDNS *pxndns = NULL;
|
||||
HANDLE hEvent = CreateEvent(NULL, false, false, NULL);
|
||||
XNetDnsLookup(mRequest.remoteAddr, hEvent, &pxndns);
|
||||
|
||||
while(pxndns->iStatus == WSAEINPROGRESS)
|
||||
WaitForSingleObject(hEvent, INFINITE);
|
||||
|
||||
if(pxndns->iStatus == 0 && pxndns->cina > 0)
|
||||
{
|
||||
dMemset(mRequest.out_h_addr, 0, sizeof(mRequest.out_h_addr));
|
||||
|
||||
// This is a suspect section. I need to revisit. [2/22/2010 Pat]
|
||||
dMemcpy(mRequest.out_h_addr, pxndns->aina, sizeof(IN_ADDR));
|
||||
mRequest.out_h_length = sizeof(IN_ADDR);
|
||||
}
|
||||
|
||||
mRequest.complete = true;
|
||||
|
||||
XNetDnsRelease(pxndns);
|
||||
CloseHandle(hEvent);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
NameLookupRequest& mRequest;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// NetAsync.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
NetAsync::NetAsync()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mLookupRequests );
|
||||
}
|
||||
|
||||
void NetAsync::queueLookup(const char* remoteAddr, NetSocket socket)
|
||||
{
|
||||
// do we have it already?
|
||||
|
||||
unsigned int i = 0;
|
||||
for (i = 0; i < mLookupRequests.size(); ++i)
|
||||
{
|
||||
if (mLookupRequests[i].sock == socket)
|
||||
// found it. ignore more than one lookup at a time for a socket.
|
||||
return;
|
||||
}
|
||||
|
||||
// not found, so add it
|
||||
|
||||
mLookupRequests.increment();
|
||||
NameLookupRequest& lookupRequest = mLookupRequests.last();
|
||||
lookupRequest.sock = socket;
|
||||
dStrncpy(lookupRequest.remoteAddr, remoteAddr, sizeof(lookupRequest.remoteAddr));
|
||||
|
||||
ThreadSafeRef< NameLookupWorkItem > workItem( new NameLookupWorkItem( lookupRequest ) );
|
||||
ThreadPool::GLOBAL().queueWorkItem( workItem );
|
||||
}
|
||||
|
||||
bool NetAsync::checkLookup(NetSocket socket, char* out_h_addr,
|
||||
int* out_h_length, int out_h_addr_size)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
// search for the socket
|
||||
RequestIterator iter;
|
||||
for (iter = mLookupRequests.begin();
|
||||
iter != mLookupRequests.end();
|
||||
++iter)
|
||||
// if we found it and it is complete...
|
||||
if (socket == iter->sock && iter->complete)
|
||||
{
|
||||
// copy the lookup data to the callers parameters
|
||||
dMemcpy(out_h_addr, iter->out_h_addr, out_h_addr_size);
|
||||
*out_h_length = iter->out_h_length;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// we found the socket, so we are done with it. erase.
|
||||
if (found)
|
||||
mLookupRequests.erase(iter);
|
||||
|
||||
return found;
|
||||
}
|
||||
63
Engine/source/platform/platformNetAsync.h
Normal file
63
Engine/source/platform/platformNetAsync.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef PLATFORM_NET_ASYNC_H
|
||||
#define PLATFORM_NET_ASYNC_H
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformNet.h"
|
||||
#include "core/util/tVector.h"
|
||||
|
||||
// class for doing asynchronous network operations on unix (linux and
|
||||
// hopefully osx) platforms. right now it only implements dns lookups
|
||||
class NetAsync
|
||||
{
|
||||
private:
|
||||
struct NameLookupRequest;
|
||||
struct NameLookupWorkItem;
|
||||
|
||||
typedef Vector< NameLookupRequest > RequestVector;
|
||||
typedef RequestVector::iterator RequestIterator;
|
||||
|
||||
RequestVector mLookupRequests;
|
||||
|
||||
public:
|
||||
NetAsync();
|
||||
|
||||
// queue a DNS lookup. only one dns lookup can be queued per socket at
|
||||
// a time. subsequent queue request for the socket are ignored. use
|
||||
// checkLookup() to check the status of a request.
|
||||
void queueLookup(const char* remoteAddr, NetSocket socket);
|
||||
|
||||
// check on the status of a dns lookup for a socket. if the lookup is
|
||||
// not yet complete, the function will return false. if it is
|
||||
// complete, the function will return true, and out_h_addr and
|
||||
// out_h_length will be set appropriately. if out_h_length is -1, then
|
||||
// name could not be resolved. otherwise, it provides the number of
|
||||
// address bytes copied into out_h_addr.
|
||||
bool checkLookup(NetSocket socket, char* out_h_addr, int* out_h_length, int out_h_addr_size);
|
||||
};
|
||||
|
||||
// the global net async object
|
||||
extern NetAsync gNetAsync;
|
||||
|
||||
#endif
|
||||
291
Engine/source/platform/platformRedBook.cpp
Normal file
291
Engine/source/platform/platformRedBook.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "console/console.h"
|
||||
#include "platform/platformRedBook.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: RedBookDevice
|
||||
//------------------------------------------------------------------------------
|
||||
RedBookDevice::RedBookDevice()
|
||||
{
|
||||
mAcquired = false;
|
||||
mDeviceName = 0;
|
||||
}
|
||||
|
||||
RedBookDevice::~RedBookDevice()
|
||||
{
|
||||
delete [] mDeviceName;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: RedBook
|
||||
//------------------------------------------------------------------------------
|
||||
Vector<RedBookDevice *> RedBook::smDeviceList(__FILE__, __LINE__);
|
||||
RedBookDevice * RedBook::smCurrentDevice;
|
||||
char RedBook::smLastError[1024];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void RedBook::init()
|
||||
{
|
||||
}
|
||||
|
||||
void RedBook::destroy()
|
||||
{
|
||||
close();
|
||||
|
||||
for( Vector<RedBookDevice*>::iterator i = smDeviceList.begin( ); i != smDeviceList.end( ); i++ ) {
|
||||
delete *i;
|
||||
}
|
||||
|
||||
smDeviceList.clear( );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void RedBook::installDevice(RedBookDevice * device)
|
||||
{
|
||||
smDeviceList.push_back(device);
|
||||
}
|
||||
|
||||
RedBookDevice * RedBook::getCurrentDevice()
|
||||
{
|
||||
return(smCurrentDevice);
|
||||
}
|
||||
|
||||
U32 RedBook::getDeviceCount()
|
||||
{
|
||||
return(smDeviceList.size());
|
||||
}
|
||||
|
||||
const char * RedBook::getDeviceName(U32 idx)
|
||||
{
|
||||
if(idx >= getDeviceCount())
|
||||
{
|
||||
setLastError("Invalid device index");
|
||||
return("");
|
||||
}
|
||||
return(smDeviceList[idx]->mDeviceName);
|
||||
}
|
||||
|
||||
void RedBook::setLastError(const char * error)
|
||||
{
|
||||
if(!error || dStrlen(error) >= sizeof(smLastError))
|
||||
setLastError("Invalid error string passed");
|
||||
else
|
||||
dStrcpy(smLastError, error);
|
||||
}
|
||||
|
||||
const char * RedBook::getLastError()
|
||||
{
|
||||
return(smLastError);
|
||||
}
|
||||
|
||||
void RedBook::handleCallback(U32 type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case PlayFinished:
|
||||
Con::executef("RedBookCallback", "PlayFinished");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool RedBook::open(const char * deviceName)
|
||||
{
|
||||
if(!deviceName)
|
||||
{
|
||||
setLastError("Invalid device name");
|
||||
return(false);
|
||||
}
|
||||
|
||||
for(U32 i = 0; i < smDeviceList.size(); i++)
|
||||
if(!dStricmp(deviceName, smDeviceList[i]->mDeviceName))
|
||||
return(open(smDeviceList[i]));
|
||||
|
||||
setLastError("Failed to find device");
|
||||
return(false);
|
||||
}
|
||||
|
||||
bool RedBook::open(RedBookDevice * device)
|
||||
{
|
||||
if(!device)
|
||||
{
|
||||
setLastError("Invalid device passed");
|
||||
return(false);
|
||||
}
|
||||
|
||||
close();
|
||||
smCurrentDevice = device;
|
||||
return(smCurrentDevice->open());
|
||||
}
|
||||
|
||||
bool RedBook::close()
|
||||
{
|
||||
if(smCurrentDevice)
|
||||
{
|
||||
bool ret = smCurrentDevice->close();
|
||||
smCurrentDevice = 0;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
setLastError("No device is currently open");
|
||||
return(false);
|
||||
}
|
||||
|
||||
bool RedBook::play(U32 track)
|
||||
{
|
||||
if(!smCurrentDevice)
|
||||
{
|
||||
setLastError("No device is currently open");
|
||||
return(false);
|
||||
}
|
||||
return(smCurrentDevice->play(track));
|
||||
}
|
||||
|
||||
bool RedBook::stop()
|
||||
{
|
||||
if(!smCurrentDevice)
|
||||
{
|
||||
setLastError("No device is currently open");
|
||||
return(false);
|
||||
}
|
||||
return(smCurrentDevice->stop());
|
||||
}
|
||||
|
||||
bool RedBook::getTrackCount(U32 * trackCount)
|
||||
{
|
||||
if(!smCurrentDevice)
|
||||
{
|
||||
setLastError("No device is currently open");
|
||||
return(false);
|
||||
}
|
||||
return(smCurrentDevice->getTrackCount(trackCount));
|
||||
}
|
||||
|
||||
bool RedBook::getVolume(F32 * volume)
|
||||
{
|
||||
if(!smCurrentDevice)
|
||||
{
|
||||
setLastError("No device is currently open");
|
||||
return(false);
|
||||
}
|
||||
return(smCurrentDevice->getVolume(volume));
|
||||
}
|
||||
|
||||
bool RedBook::setVolume(F32 volume)
|
||||
{
|
||||
if(!smCurrentDevice)
|
||||
{
|
||||
setLastError("No device is currently open");
|
||||
return(false);
|
||||
}
|
||||
return(smCurrentDevice->setVolume(volume));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// console methods
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ConsoleFunctionGroupBegin( Redbook, "Control functions for Redbook audio (ie, CD audio).");
|
||||
|
||||
ConsoleFunction(redbookOpen, bool, 1, 2, "(string device=NULL)"
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
if(argc == 1)
|
||||
return(RedBook::open(RedBook::getDeviceName(0)));
|
||||
else
|
||||
return(RedBook::open(argv[1]));
|
||||
}
|
||||
|
||||
ConsoleFunction(redbookClose, bool, 1, 1, "Close the current Redbook device."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::close());
|
||||
}
|
||||
|
||||
ConsoleFunction( redbookPlay, bool, 2, 2, "(int track) Play the selected track."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::play(dAtoi(argv[1])));
|
||||
}
|
||||
|
||||
ConsoleFunction( redbookStop, bool, 1, 1, "Stop playing."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::stop());
|
||||
}
|
||||
ConsoleFunction(redbookGetTrackCount, S32, 1, 1, "Return the number of tracks."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
U32 trackCount;
|
||||
if(!RedBook::getTrackCount(&trackCount))
|
||||
return(0);
|
||||
return(trackCount);
|
||||
}
|
||||
|
||||
ConsoleFunction(redbookGetVolume, F32, 1, 1, "Get the volume."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
F32 vol;
|
||||
if(!RedBook::getVolume(&vol))
|
||||
return(0.f);
|
||||
else
|
||||
return(vol);
|
||||
}
|
||||
|
||||
ConsoleFunction(redbookSetVolume, bool, 2, 2, "(float volume) Set playback volume."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::setVolume(dAtof(argv[1])));
|
||||
}
|
||||
|
||||
ConsoleFunction( redbookGetDeviceCount, S32, 1, 1, "get the number of redbook devices."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::getDeviceCount());
|
||||
}
|
||||
|
||||
ConsoleFunction( redbookGetDeviceName, const char *, 2, 2, "(int index) Get name of specified Redbook device."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::getDeviceName(dAtoi(argv[1])));
|
||||
}
|
||||
|
||||
ConsoleFunction( redbookGetLastError, const char*, 1, 1, "Get a string explaining the last redbook error."
|
||||
"@brief Deprecated\n\n"
|
||||
"@internal")
|
||||
{
|
||||
return(RedBook::getLastError());
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( Redbook );
|
||||
87
Engine/source/platform/platformRedBook.h
Normal file
87
Engine/source/platform/platformRedBook.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMREDBOOK_H_
|
||||
#define _PLATFORMREDBOOK_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
class RedBookDevice
|
||||
{
|
||||
public:
|
||||
RedBookDevice();
|
||||
virtual ~RedBookDevice();
|
||||
|
||||
bool mAcquired;
|
||||
char * mDeviceName;
|
||||
|
||||
virtual bool open() = 0;
|
||||
virtual bool close() = 0;
|
||||
virtual bool play(U32) = 0;
|
||||
virtual bool stop() = 0;
|
||||
virtual bool getTrackCount(U32 *) = 0;
|
||||
virtual bool getVolume(F32 *) = 0;
|
||||
virtual bool setVolume(F32) = 0;
|
||||
};
|
||||
|
||||
class RedBook
|
||||
{
|
||||
private:
|
||||
static Vector<RedBookDevice *> smDeviceList;
|
||||
static RedBookDevice * smCurrentDevice;
|
||||
static char smLastError[];
|
||||
|
||||
public:
|
||||
enum {
|
||||
PlayFinished = 0,
|
||||
};
|
||||
static void handleCallback(U32);
|
||||
|
||||
static void init();
|
||||
static void destroy();
|
||||
|
||||
static void installDevice(RedBookDevice *);
|
||||
static U32 getDeviceCount();
|
||||
static const char * getDeviceName(U32);
|
||||
static RedBookDevice * getCurrentDevice();
|
||||
|
||||
static void setLastError(const char *);
|
||||
static const char * getLastError();
|
||||
|
||||
static bool open(const char *);
|
||||
static bool open(RedBookDevice *);
|
||||
static bool close();
|
||||
static bool play(U32);
|
||||
static bool stop();
|
||||
static bool getTrackCount(U32 *);
|
||||
static bool getVolume(F32 *);
|
||||
static bool setVolume(F32);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
55
Engine/source/platform/platformTLS.h
Normal file
55
Engine/source/platform/platformTLS.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMTLS_H_
|
||||
#define _PLATFORMTLS_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
struct PlatformThreadStorage;
|
||||
|
||||
/// Platform independent per-thread storage class.
|
||||
class ThreadStorage
|
||||
{
|
||||
enum
|
||||
{
|
||||
PlatformThreadStorageStorageSize = 32,
|
||||
};
|
||||
|
||||
PlatformThreadStorage *mThreadStorage;
|
||||
U8 mStorage[PlatformThreadStorageStorageSize];
|
||||
public:
|
||||
/// ThreadStorage constructor.
|
||||
ThreadStorage();
|
||||
/// ThreadStorage destructor.
|
||||
~ThreadStorage();
|
||||
|
||||
/// returns the per-thread stored void pointer for this ThreadStorage. The default value is NULL.
|
||||
void *get();
|
||||
/// sets the per-thread stored void pointer for this ThreadStorage object.
|
||||
void set(void *data);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
179
Engine/source/platform/platformTimer.cpp
Normal file
179
Engine/source/platform/platformTimer.cpp
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platformTimer.h"
|
||||
#include "core/util/journal/process.h"
|
||||
|
||||
void TimeManager::_updateTime()
|
||||
{
|
||||
// Calculate & filter time delta since last event.
|
||||
|
||||
// How long since last update?
|
||||
S32 delta = mTimer->getElapsedMs();
|
||||
|
||||
// Now - we want to try to sleep until the time threshold will hit.
|
||||
S32 msTillThresh = (mBackground ? mBackgroundThreshold : mForegroundThreshold) - delta;
|
||||
|
||||
if(msTillThresh > 0)
|
||||
{
|
||||
// There's some time to go, so let's sleep.
|
||||
Platform::sleep( msTillThresh );
|
||||
}
|
||||
|
||||
// Ok - let's grab the new elapsed and send that out.
|
||||
S32 finalDelta = mTimer->getElapsedMs();
|
||||
mTimer->reset();
|
||||
|
||||
timeEvent.trigger(finalDelta);
|
||||
}
|
||||
|
||||
TimeManager::TimeManager()
|
||||
{
|
||||
mBackground = false;
|
||||
mTimer = PlatformTimer::create();
|
||||
Process::notify(this, &TimeManager::_updateTime, PROCESS_TIME_ORDER);
|
||||
|
||||
mForegroundThreshold = 5;
|
||||
mBackgroundThreshold = 10;
|
||||
}
|
||||
|
||||
TimeManager::~TimeManager()
|
||||
{
|
||||
Process::remove(this, &TimeManager::_updateTime);
|
||||
delete mTimer;
|
||||
}
|
||||
|
||||
void TimeManager::setForegroundThreshold(const S32 msInterval)
|
||||
{
|
||||
AssertFatal(msInterval > 0, "TimeManager::setForegroundThreshold - should have at least 1 ms between time events to avoid math problems!");
|
||||
mForegroundThreshold = msInterval;
|
||||
}
|
||||
|
||||
const S32 TimeManager::getForegroundThreshold() const
|
||||
{
|
||||
return mForegroundThreshold;
|
||||
}
|
||||
|
||||
void TimeManager::setBackgroundThreshold(const S32 msInterval)
|
||||
{
|
||||
AssertFatal(msInterval > 0, "TimeManager::setBackgroundThreshold - should have at least 1 ms between time events to avoid math problems!");
|
||||
mBackgroundThreshold = msInterval;
|
||||
}
|
||||
|
||||
const S32 TimeManager::getBackgroundThreshold() const
|
||||
{
|
||||
return mBackgroundThreshold;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
#pragma message("Mac/Lunix will need to implement this or get unresolved externals.")
|
||||
#pragma message(" It was previously defined here with a Win32 ifdef which goes against")
|
||||
#pragma message(" how torque implements its platform agnostic systems - JDD")
|
||||
//PlatformTimer *PlatformTimer::create()
|
||||
//{
|
||||
// return new DefaultPlatformTimer();
|
||||
//}
|
||||
|
||||
PlatformTimer::PlatformTimer()
|
||||
{
|
||||
}
|
||||
|
||||
PlatformTimer::~PlatformTimer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Exposes PlatformTimer to script for when high precision is needed.
|
||||
|
||||
#include "core/util/tDictionary.h"
|
||||
#include "console/console.h"
|
||||
|
||||
class ScriptTimerMan
|
||||
{
|
||||
public:
|
||||
|
||||
ScriptTimerMan();
|
||||
~ScriptTimerMan();
|
||||
|
||||
S32 startTimer();
|
||||
S32 stopTimer( S32 id );
|
||||
|
||||
protected:
|
||||
|
||||
static S32 smNextId;
|
||||
|
||||
typedef Map<S32,PlatformTimer*> TimerMap;
|
||||
|
||||
TimerMap mTimers;
|
||||
};
|
||||
|
||||
S32 ScriptTimerMan::smNextId = 1;
|
||||
|
||||
ScriptTimerMan::ScriptTimerMan()
|
||||
{
|
||||
}
|
||||
|
||||
ScriptTimerMan::~ScriptTimerMan()
|
||||
{
|
||||
TimerMap::Iterator itr = mTimers.begin();
|
||||
|
||||
for ( ; itr != mTimers.end(); itr++ )
|
||||
delete itr->value;
|
||||
|
||||
mTimers.clear();
|
||||
}
|
||||
|
||||
S32 ScriptTimerMan::startTimer()
|
||||
{
|
||||
PlatformTimer *timer = PlatformTimer::create();
|
||||
mTimers.insert( smNextId, timer );
|
||||
smNextId++;
|
||||
return ( smNextId - 1 );
|
||||
}
|
||||
|
||||
S32 ScriptTimerMan::stopTimer( S32 id )
|
||||
{
|
||||
TimerMap::Iterator itr = mTimers.find( id );
|
||||
if ( itr == mTimers.end() )
|
||||
return -1;
|
||||
|
||||
PlatformTimer *timer = itr->value;
|
||||
S32 elapsed = timer->getElapsedMs();
|
||||
|
||||
mTimers.erase( itr );
|
||||
delete timer;
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
ScriptTimerMan gScriptTimerMan;
|
||||
|
||||
ConsoleFunction( startPrecisionTimer, S32, 1, 1, "startPrecisionTimer() - Create and start a high resolution platform timer. Returns the timer id." )
|
||||
{
|
||||
return gScriptTimerMan.startTimer();
|
||||
}
|
||||
|
||||
ConsoleFunction( stopPrecisionTimer, S32, 2, 2, "stopPrecisionTimer( S32 id ) - Stop and destroy timer with the passed id. Returns the elapsed milliseconds." )
|
||||
{
|
||||
return gScriptTimerMan.stopTimer( dAtoi( argv[1] ) );
|
||||
}
|
||||
111
Engine/source/platform/platformTimer.h
Normal file
111
Engine/source/platform/platformTimer.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_PLATFORMTIMER_H_
|
||||
#define _PLATFORM_PLATFORMTIMER_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/journal/journaledSignal.h"
|
||||
|
||||
/// Platform-specific timer class.
|
||||
///
|
||||
/// This exists primarily as support for the TimeManager, but may be useful
|
||||
/// elsewhere.
|
||||
class PlatformTimer
|
||||
{
|
||||
protected:
|
||||
PlatformTimer();
|
||||
public:
|
||||
virtual ~PlatformTimer();
|
||||
|
||||
/// Get the number of MS that have elapsed since creation or the last
|
||||
/// reset call.
|
||||
virtual const S32 getElapsedMs()=0;
|
||||
|
||||
/// Reset elapsed ms back to zero.
|
||||
virtual void reset()=0;
|
||||
|
||||
/// Create a new PlatformTimer.
|
||||
static PlatformTimer *create();
|
||||
};
|
||||
|
||||
/// Utility class to fire journalled time-delta events at regular intervals.
|
||||
///
|
||||
/// Most games and simulations need the ability to update their state based on
|
||||
/// a time-delta. However, tracking time accurately and sending out well-conditioned
|
||||
/// events (for instance, allowing no events with delta=0) tends to be platform
|
||||
/// specific. This class provides an abstraction around this platform mojo.
|
||||
///
|
||||
/// In addition, a well behaved application may want to alter how frequently
|
||||
/// it processes time advancement depending on its execution state. For instance,
|
||||
/// a game running in the background can significantly reduce CPU usage
|
||||
/// by only updating every 100ms, instead of trying to maintain a 1ms update
|
||||
/// update rate.
|
||||
class TimeManager
|
||||
{
|
||||
PlatformTimer *mTimer;
|
||||
S32 mForegroundThreshold, mBackgroundThreshold;
|
||||
bool mBackground;
|
||||
|
||||
void _updateTime();
|
||||
|
||||
public:
|
||||
|
||||
TimeManagerEvent timeEvent;
|
||||
|
||||
TimeManager();
|
||||
~TimeManager();
|
||||
|
||||
void setForegroundThreshold(const S32 msInterval);
|
||||
const S32 getForegroundThreshold() const;
|
||||
|
||||
void setBackgroundThreshold(const S32 msInterval);
|
||||
const S32 getBackgroundThreshold() const;
|
||||
|
||||
void setBackground(const bool isBackground) { mBackground = isBackground; };
|
||||
const bool getBackground() const { return mBackground; };
|
||||
|
||||
};
|
||||
|
||||
class DefaultPlatformTimer : public PlatformTimer
|
||||
{
|
||||
S32 mLastTime, mNextTime;
|
||||
|
||||
public:
|
||||
DefaultPlatformTimer()
|
||||
{
|
||||
mLastTime = mNextTime = Platform::getRealMilliseconds();
|
||||
}
|
||||
|
||||
const S32 getElapsedMs()
|
||||
{
|
||||
mNextTime = Platform::getRealMilliseconds();
|
||||
return (mNextTime - mLastTime);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
mLastTime = mNextTime;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
34
Engine/source/platform/platformVFS.h
Normal file
34
Engine/source/platform/platformVFS.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMVFS_H_
|
||||
#define _PLATFORMVFS_H_
|
||||
|
||||
namespace Zip
|
||||
{
|
||||
class ZipArchive;
|
||||
}
|
||||
|
||||
extern Zip::ZipArchive *openEmbeddedVFSArchive();
|
||||
extern void closeEmbeddedVFSArchive();
|
||||
|
||||
#endif // _PLATFORMVFS_H_
|
||||
99
Engine/source/platform/platformVideoInfo.cpp
Normal file
99
Engine/source/platform/platformVideoInfo.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platformVideoInfo.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
PlatformVideoInfo::PlatformVideoInfo()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mAdapters );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
PlatformVideoInfo::~PlatformVideoInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
bool PlatformVideoInfo::profileAdapters()
|
||||
{
|
||||
// Initialize the child class
|
||||
if( !_initialize() )
|
||||
return false;
|
||||
|
||||
mAdapters.clear();
|
||||
|
||||
// Query the number of adapters
|
||||
String tempString;
|
||||
|
||||
mAdapters.increment( 1 );
|
||||
//if( !_queryProperty( PVI_NumAdapters, 0, &tempString ) )
|
||||
// return false;
|
||||
|
||||
//mAdapters.increment( dAtoi( tempString ) );
|
||||
|
||||
U32 adapterNum = 0;
|
||||
for( Vector<PVIAdapter>::iterator itr = mAdapters.begin(); itr != mAdapters.end(); itr++ )
|
||||
{
|
||||
PVIAdapter &adapter = *itr;
|
||||
|
||||
|
||||
U32 querySuccessFlags = U32_MAX;
|
||||
AssertFatal( PVI_QueryCount < sizeof( querySuccessFlags ) * 8, "Not enough bits in query success mask." );
|
||||
querySuccessFlags -= ( ( 1 << PVI_QueryCount ) - 1 );
|
||||
|
||||
// Fill in adapter information
|
||||
#define _QUERY_MASK_HELPER( querytype, outstringaddr ) \
|
||||
querySuccessFlags |= ( _queryProperty( querytype, adapterNum, outstringaddr ) ? 1 << querytype : 0 )
|
||||
|
||||
_QUERY_MASK_HELPER( PVI_NumDevices, &tempString );
|
||||
adapter.numDevices = dAtoi( tempString );
|
||||
|
||||
_QUERY_MASK_HELPER( PVI_VRAM, &tempString );
|
||||
adapter.vram = dAtoi( tempString );
|
||||
|
||||
_QUERY_MASK_HELPER( PVI_Description, &adapter.description );
|
||||
_QUERY_MASK_HELPER( PVI_Name, &adapter.name );
|
||||
_QUERY_MASK_HELPER( PVI_ChipSet, &adapter.chipSet );
|
||||
_QUERY_MASK_HELPER( PVI_DriverVersion, &adapter.driverVersion );
|
||||
|
||||
#undef _QUERY_MASK_HELPER
|
||||
|
||||
// Test flags here for success
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const PlatformVideoInfo::PVIAdapter &PlatformVideoInfo::getAdapterInformation( const U32 adapterIndex ) const
|
||||
{
|
||||
AssertFatal( adapterIndex < mAdapters.size(), "Not that many adapters" );
|
||||
return mAdapters[adapterIndex];
|
||||
}
|
||||
99
Engine/source/platform/platformVideoInfo.h
Normal file
99
Engine/source/platform/platformVideoInfo.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_VIDEOINFO_H_
|
||||
#define _PLATFORM_VIDEOINFO_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/str.h"
|
||||
#include "core/util/tVector.h"
|
||||
|
||||
// The purpose of this class is to abstract the gathering of video adapter information.
|
||||
// This information is not specific to the API being used to do the rendering, or
|
||||
// the capabilities of that renderer. That information is queried in a different
|
||||
// class.
|
||||
|
||||
class PlatformVideoInfo
|
||||
{
|
||||
// # of devices
|
||||
// description
|
||||
// manufacturer
|
||||
// chip set
|
||||
// driver version
|
||||
// VRAM
|
||||
public:
|
||||
enum PVIQueryType
|
||||
{
|
||||
PVI_QueryStart = 0, ///< Start of the enum for looping
|
||||
|
||||
// The NumAdapters query is the only non-adapter specific query, the following
|
||||
// queries are all specific to an adapter.
|
||||
PVI_NumDevices = 0, ///< Number of sub adapters
|
||||
PVI_Description, ///< String description of the adapter
|
||||
PVI_Name, ///< Card name string
|
||||
PVI_ChipSet, ///< Chipset string
|
||||
PVI_DriverVersion, ///< Driver version string
|
||||
PVI_VRAM, ///< Dedicated video memory in megabytes
|
||||
|
||||
// Please add query types above this value
|
||||
PVI_QueryCount, ///< Counter so that this enum can be looped over
|
||||
PVI_NumAdapters, ///< Number of adapters on the system
|
||||
};
|
||||
|
||||
struct PVIAdapter
|
||||
{
|
||||
U32 numDevices;
|
||||
String description;
|
||||
String name;
|
||||
String chipSet;
|
||||
String driverVersion;
|
||||
U32 vram;
|
||||
};
|
||||
|
||||
private:
|
||||
Vector<PVIAdapter> mAdapters; ///< Vector of adapters
|
||||
|
||||
/// Signal handling method for GFX signals
|
||||
// bool processGFXSignal( GFXDevice::GFXDeviceEventType gfxEvent );
|
||||
|
||||
protected:
|
||||
/// This method will be called before any queries are made. All initialization,
|
||||
/// for example Win32 COM startup, should be done in this method. If the return
|
||||
/// value is false, no querys will be made.
|
||||
virtual bool _initialize() = 0;
|
||||
|
||||
/// This is the query method which subclasses must implement. The querys made
|
||||
/// are all found in the PVIQueryType enum. If NULL is specified for outValue,
|
||||
/// the sub class should simply return true if the query type is supported, and
|
||||
/// false otherwise.
|
||||
virtual bool _queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue ) = 0;
|
||||
|
||||
public:
|
||||
PlatformVideoInfo();
|
||||
virtual ~PlatformVideoInfo();
|
||||
|
||||
bool profileAdapters();
|
||||
|
||||
const PVIAdapter &getAdapterInformation( const U32 adapterIndex ) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
91
Engine/source/platform/platformVolume.cpp
Normal file
91
Engine/source/platform/platformVolume.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
|
||||
#include <sys/utime.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "platform/platformVolume.h"
|
||||
#include "core/util/zip/zipVolume.h"
|
||||
|
||||
using namespace Torque;
|
||||
using namespace Torque::FS;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
namespace FS
|
||||
{
|
||||
|
||||
bool MountDefaults()
|
||||
{
|
||||
String path = getAssetDir();
|
||||
|
||||
bool mounted = Mount( "game", createNativeFS( path ));
|
||||
|
||||
if ( !mounted )
|
||||
return false;
|
||||
|
||||
#ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
|
||||
// Note that the VirtualMountSystem must be enabled in volume.cpp for zip support to work.
|
||||
return MountZips("game");
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MountZips(const String &root)
|
||||
{
|
||||
Path basePath;
|
||||
basePath.setRoot(root);
|
||||
Vector<String> outList;
|
||||
|
||||
S32 num = FindByPattern(basePath, "*.zip", true, outList);
|
||||
if(num == 0)
|
||||
return true; // not an error
|
||||
|
||||
S32 mounted = 0;
|
||||
for(S32 i = 0;i < outList.size();++i)
|
||||
{
|
||||
String &zipfile = outList[i];
|
||||
mounted += (S32)Mount(root, new ZipFileSystem(zipfile, true));
|
||||
}
|
||||
|
||||
return mounted == outList.size();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool Touch( const Path &path )
|
||||
{
|
||||
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
|
||||
return( utime( path.getFullPath(), 0 ) != -1 );
|
||||
#else
|
||||
return( utimes( path.getFullPath(), NULL) == 0 ); // utimes returns 0 on success.
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace FS
|
||||
} // namespace Platform
|
||||
55
Engine/source/platform/platformVolume.h
Normal file
55
Engine/source/platform/platformVolume.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORMVOLUME_H_
|
||||
#define _PLATFORMVOLUME_H_
|
||||
|
||||
#include "core/volume.h"
|
||||
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
namespace FS
|
||||
{
|
||||
using namespace Torque;
|
||||
using namespace Torque::FS;
|
||||
|
||||
FileSystemRef createNativeFS( const String &volume );
|
||||
|
||||
String getAssetDir();
|
||||
|
||||
/// Mount default OS file systems.
|
||||
/// On POSIX environment this means mounting a root FileSystem "/", mounting
|
||||
/// the $HOME environment variable as the "home:/" file system and setting the
|
||||
/// current working directory to the current OS working directory.
|
||||
bool InstallFileSystems();
|
||||
|
||||
bool MountDefaults();
|
||||
bool MountZips(const String &root);
|
||||
|
||||
bool Touch( const Path &path );
|
||||
|
||||
} // Namespace FS
|
||||
} // Namespace Platform
|
||||
|
||||
#endif
|
||||
|
||||
760
Engine/source/platform/profiler.cpp
Normal file
760
Engine/source/platform/profiler.cpp
Normal file
|
|
@ -0,0 +1,760 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if defined(TORQUE_OS_WIN32)
|
||||
#include<Windows.h> // for SetThreadAffinityMask
|
||||
#endif
|
||||
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
#include <CoreServices/CoreServices.h> // For high resolution timer
|
||||
#endif
|
||||
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/stringTable.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "platform/threads/thread.h"
|
||||
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#ifdef TORQUE_ENABLE_PROFILER
|
||||
ProfilerRootData *ProfilerRootData::sRootList = NULL;
|
||||
Profiler *gProfiler = NULL;
|
||||
|
||||
// Uncomment the following line to enable a debugging aid for mismatched profiler blocks.
|
||||
//#define TORQUE_PROFILER_DEBUG
|
||||
|
||||
// Machinery to record the stack of node names, as a debugging aid to find
|
||||
// mismatched PROFILE_START and PROFILE_END blocks. We profile from the
|
||||
// beginning to catch profile block errors that occur when torque is starting up.
|
||||
#ifdef TORQUE_PROFILER_DEBUG
|
||||
Vector<StringTableEntry> gProfilerNodeStack;
|
||||
#define TORQUE_PROFILE_AT_ENGINE_START true
|
||||
#define PROFILER_DEBUG_PUSH_NODE( nodename ) \
|
||||
gProfilerNodeStack.push_back( nodename );
|
||||
#define PROFILER_DEBUG_POP_NODE() \
|
||||
gProfilerNodeStack.pop_back();
|
||||
#else
|
||||
#define TORQUE_PROFILE_AT_ENGINE_START false
|
||||
#define PROFILER_DEBUG_PUSH_NODE( nodename ) ;
|
||||
#define PROFILER_DEBUG_POP_NODE() ;
|
||||
#endif
|
||||
|
||||
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
|
||||
// platform specific get hires times...
|
||||
void startHighResolutionTimer(U32 time[2])
|
||||
{
|
||||
//time[0] = Platform::getRealMilliseconds();
|
||||
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
push edx
|
||||
push ecx
|
||||
rdtsc
|
||||
mov ecx, time
|
||||
mov DWORD PTR [ecx], eax
|
||||
mov DWORD PTR [ecx + 4], edx
|
||||
pop ecx
|
||||
pop edx
|
||||
pop eax
|
||||
}
|
||||
}
|
||||
|
||||
U32 endHighResolutionTimer(U32 time[2])
|
||||
{
|
||||
U32 ticks;
|
||||
//ticks = Platform::getRealMilliseconds() - time[0];
|
||||
//return ticks;
|
||||
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
push edx
|
||||
push ecx
|
||||
//db 0fh, 31h
|
||||
rdtsc
|
||||
mov ecx, time
|
||||
sub edx, DWORD PTR [ecx+4]
|
||||
sbb eax, DWORD PTR [ecx]
|
||||
mov DWORD PTR ticks, eax
|
||||
pop ecx
|
||||
pop edx
|
||||
pop eax
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
|
||||
#elif defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM)
|
||||
|
||||
// platform specific get hires times...
|
||||
void startHighResolutionTimer(U32 time[2])
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"rdtsc\n"
|
||||
: "=a" (time[0]), "=d" (time[1])
|
||||
);
|
||||
}
|
||||
|
||||
U32 endHighResolutionTimer(U32 time[2])
|
||||
{
|
||||
U32 ticks;
|
||||
__asm__ __volatile__(
|
||||
"rdtsc\n"
|
||||
"sub 0x4(%%ecx), %%edx\n"
|
||||
"sbb (%%ecx), %%eax\n"
|
||||
: "=a" (ticks) : "c" (time)
|
||||
);
|
||||
return ticks;
|
||||
}
|
||||
|
||||
#elif defined(TORQUE_OS_MAC)
|
||||
|
||||
|
||||
void startHighResolutionTimer(U32 time[2]) {
|
||||
UnsignedWide t;
|
||||
Microseconds(&t);
|
||||
time[0] = t.lo;
|
||||
time[1] = t.hi;
|
||||
}
|
||||
|
||||
U32 endHighResolutionTimer(U32 time[2]) {
|
||||
UnsignedWide t;
|
||||
Microseconds(&t);
|
||||
return t.lo - time[0];
|
||||
// given that we're returning a 32 bit integer, and this is unsigned subtraction...
|
||||
// it will just wrap around, we don't need the upper word of the time.
|
||||
// NOTE: the code assumes that more than 3 hrs will not go by between calls to startHighResolutionTimer() and endHighResolutionTimer().
|
||||
// I mean... that damn well better not happen anyway.
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void startHighResolutionTimer(U32 time[2])
|
||||
{
|
||||
}
|
||||
|
||||
U32 endHighResolutionTimer(U32 time[2])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Profiler::Profiler()
|
||||
{
|
||||
mMaxStackDepth = MaxStackDepth;
|
||||
mCurrentHash = 0;
|
||||
|
||||
mCurrentProfilerData = (ProfilerData *) malloc(sizeof(ProfilerData));
|
||||
mCurrentProfilerData->mRoot = NULL;
|
||||
mCurrentProfilerData->mNextForRoot = NULL;
|
||||
mCurrentProfilerData->mNextProfilerData = NULL;
|
||||
mCurrentProfilerData->mNextHash = NULL;
|
||||
mCurrentProfilerData->mParent = NULL;
|
||||
mCurrentProfilerData->mNextSibling = NULL;
|
||||
mCurrentProfilerData->mFirstChild = NULL;
|
||||
mCurrentProfilerData->mLastSeenProfiler = NULL;
|
||||
mCurrentProfilerData->mHash = 0;
|
||||
mCurrentProfilerData->mSubDepth = 0;
|
||||
mCurrentProfilerData->mInvokeCount = 0;
|
||||
mCurrentProfilerData->mTotalTime = 0;
|
||||
mCurrentProfilerData->mSubTime = 0;
|
||||
#ifdef TORQUE_ENABLE_PROFILE_PATH
|
||||
mCurrentProfilerData->mPath = "";
|
||||
#endif
|
||||
mRootProfilerData = mCurrentProfilerData;
|
||||
|
||||
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
|
||||
mCurrentProfilerData->mChildHash[i] = 0;
|
||||
|
||||
mProfileList = NULL;
|
||||
|
||||
mEnabled = TORQUE_PROFILE_AT_ENGINE_START;
|
||||
mNextEnable = TORQUE_PROFILE_AT_ENGINE_START;
|
||||
mStackDepth = 0;
|
||||
gProfiler = this;
|
||||
mDumpToConsole = false;
|
||||
mDumpToFile = false;
|
||||
mDumpFileName[0] = '\0';
|
||||
}
|
||||
|
||||
Profiler::~Profiler()
|
||||
{
|
||||
reset();
|
||||
free(mRootProfilerData);
|
||||
gProfiler = NULL;
|
||||
}
|
||||
|
||||
void Profiler::reset()
|
||||
{
|
||||
mEnabled = false; // in case we're in a profiler call.
|
||||
while(mProfileList)
|
||||
{
|
||||
free(mProfileList);
|
||||
mProfileList = NULL;
|
||||
}
|
||||
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
|
||||
{
|
||||
walk->mFirstProfilerData = 0;
|
||||
walk->mTotalTime = 0;
|
||||
walk->mSubTime = 0;
|
||||
walk->mTotalInvokeCount = 0;
|
||||
}
|
||||
mCurrentProfilerData = mRootProfilerData;
|
||||
mCurrentProfilerData->mNextForRoot = 0;
|
||||
mCurrentProfilerData->mFirstChild = 0;
|
||||
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
|
||||
mCurrentProfilerData->mChildHash[i] = 0;
|
||||
mCurrentProfilerData->mInvokeCount = 0;
|
||||
mCurrentProfilerData->mTotalTime = 0;
|
||||
mCurrentProfilerData->mSubTime = 0;
|
||||
mCurrentProfilerData->mSubDepth = 0;
|
||||
mCurrentProfilerData->mLastSeenProfiler = 0;
|
||||
}
|
||||
|
||||
static Profiler aProfiler; // allocate the global profiler
|
||||
|
||||
ProfilerRootData::ProfilerRootData(const char *name)
|
||||
{
|
||||
for(ProfilerRootData *walk = sRootList; walk; walk = walk->mNextRoot)
|
||||
if(!dStrcmp(walk->mName, name))
|
||||
AssertFatal( false, avar( "Duplicate profile name: %s", name ) );
|
||||
|
||||
mName = name;
|
||||
mNameHash = _StringTable::hashString(name);
|
||||
mNextRoot = sRootList;
|
||||
sRootList = this;
|
||||
mTotalTime = 0;
|
||||
mTotalInvokeCount = 0;
|
||||
mFirstProfilerData = NULL;
|
||||
mEnabled = true;
|
||||
}
|
||||
|
||||
void Profiler::validate()
|
||||
{
|
||||
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
|
||||
{
|
||||
for(ProfilerData *dp = walk->mFirstProfilerData; dp; dp = dp->mNextForRoot)
|
||||
{
|
||||
if(dp->mRoot != walk)
|
||||
Platform::debugBreak();
|
||||
// check if it's in the parent's list...
|
||||
ProfilerData *wk;
|
||||
for(wk = dp->mParent->mFirstChild; wk; wk = wk->mNextSibling)
|
||||
if(wk == dp)
|
||||
break;
|
||||
if(!wk)
|
||||
Platform::debugBreak();
|
||||
for(wk = dp->mParent->mChildHash[walk->mNameHash & (ProfilerData::HashTableSize - 1)] ;
|
||||
wk; wk = wk->mNextHash)
|
||||
if(wk == dp)
|
||||
break;
|
||||
if(!wk)
|
||||
Platform::debugBreak();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_ENABLE_PROFILE_PATH
|
||||
const char * Profiler::getProfilePath()
|
||||
{
|
||||
#ifdef TORQUE_MULTITHREAD
|
||||
// Ignore non-main-thread profiler activity.
|
||||
if( !ThreadManager::isMainThread() )
|
||||
return "[non-main thread]";
|
||||
#endif
|
||||
|
||||
return (mEnabled && mCurrentProfilerData) ? mCurrentProfilerData->mPath : "na";
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_ENABLE_PROFILE_PATH
|
||||
const char * Profiler::constructProfilePath(ProfilerData * pd)
|
||||
{
|
||||
if (pd->mParent)
|
||||
{
|
||||
const bool saveEnable = gProfiler->mEnabled;
|
||||
gProfiler->mEnabled = false;
|
||||
|
||||
const char * connector = " -> ";
|
||||
U32 len = dStrlen(pd->mParent->mPath);
|
||||
if (!len)
|
||||
connector = "";
|
||||
len += dStrlen(connector);
|
||||
len += dStrlen(pd->mRoot->mName);
|
||||
|
||||
U32 mark = FrameAllocator::getWaterMark();
|
||||
char * buf = (char*)FrameAllocator::alloc(len+1);
|
||||
dStrcpy(buf,pd->mParent->mPath);
|
||||
dStrcat(buf,connector);
|
||||
dStrcat(buf,pd->mRoot->mName);
|
||||
const char * ret = StringTable->insert(buf);
|
||||
FrameAllocator::setWaterMark(mark);
|
||||
|
||||
gProfiler->mEnabled = saveEnable;
|
||||
|
||||
return ret;
|
||||
}
|
||||
return "root";
|
||||
}
|
||||
#endif
|
||||
void Profiler::hashPush(ProfilerRootData *root)
|
||||
{
|
||||
#ifdef TORQUE_MULTITHREAD
|
||||
// Ignore non-main-thread profiler activity.
|
||||
if( !ThreadManager::isMainThread() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
mStackDepth++;
|
||||
PROFILER_DEBUG_PUSH_NODE(root->mName);
|
||||
AssertFatal(mStackDepth <= mMaxStackDepth,
|
||||
"Stack overflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs");
|
||||
if(!mEnabled)
|
||||
return;
|
||||
|
||||
ProfilerData *nextProfiler = NULL;
|
||||
if(!root->mEnabled || mCurrentProfilerData->mRoot == root)
|
||||
{
|
||||
mCurrentProfilerData->mSubDepth++;
|
||||
return;
|
||||
}
|
||||
|
||||
if(mCurrentProfilerData->mLastSeenProfiler &&
|
||||
mCurrentProfilerData->mLastSeenProfiler->mRoot == root)
|
||||
nextProfiler = mCurrentProfilerData->mLastSeenProfiler;
|
||||
|
||||
if(!nextProfiler)
|
||||
{
|
||||
// first see if it's in the hash table...
|
||||
U32 index = root->mNameHash & (ProfilerData::HashTableSize - 1);
|
||||
nextProfiler = mCurrentProfilerData->mChildHash[index];
|
||||
while(nextProfiler)
|
||||
{
|
||||
if(nextProfiler->mRoot == root)
|
||||
break;
|
||||
nextProfiler = nextProfiler->mNextHash;
|
||||
}
|
||||
if(!nextProfiler)
|
||||
{
|
||||
nextProfiler = (ProfilerData *) malloc(sizeof(ProfilerData));
|
||||
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
|
||||
nextProfiler->mChildHash[i] = 0;
|
||||
|
||||
nextProfiler->mRoot = root;
|
||||
nextProfiler->mNextForRoot = root->mFirstProfilerData;
|
||||
root->mFirstProfilerData = nextProfiler;
|
||||
|
||||
nextProfiler->mNextProfilerData = mProfileList;
|
||||
mProfileList = nextProfiler;
|
||||
|
||||
nextProfiler->mNextHash = mCurrentProfilerData->mChildHash[index];
|
||||
mCurrentProfilerData->mChildHash[index] = nextProfiler;
|
||||
|
||||
nextProfiler->mParent = mCurrentProfilerData;
|
||||
nextProfiler->mNextSibling = mCurrentProfilerData->mFirstChild;
|
||||
mCurrentProfilerData->mFirstChild = nextProfiler;
|
||||
nextProfiler->mFirstChild = NULL;
|
||||
nextProfiler->mLastSeenProfiler = NULL;
|
||||
nextProfiler->mHash = root->mNameHash;
|
||||
nextProfiler->mInvokeCount = 0;
|
||||
nextProfiler->mTotalTime = 0;
|
||||
nextProfiler->mSubTime = 0;
|
||||
nextProfiler->mSubDepth = 0;
|
||||
#ifdef TORQUE_ENABLE_PROFILE_PATH
|
||||
nextProfiler->mPath = constructProfilePath(nextProfiler);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
root->mTotalInvokeCount++;
|
||||
nextProfiler->mInvokeCount++;
|
||||
startHighResolutionTimer(nextProfiler->mStartTime);
|
||||
mCurrentProfilerData->mLastSeenProfiler = nextProfiler;
|
||||
mCurrentProfilerData = nextProfiler;
|
||||
}
|
||||
|
||||
void Profiler::enable(bool enabled)
|
||||
{
|
||||
mNextEnable = enabled;
|
||||
}
|
||||
|
||||
void Profiler::dumpToConsole()
|
||||
{
|
||||
mDumpToConsole = true;
|
||||
mDumpToFile = false;
|
||||
mDumpFileName[0] = '\0';
|
||||
}
|
||||
|
||||
void Profiler::dumpToFile(const char* fileName)
|
||||
{
|
||||
AssertFatal(dStrlen(fileName) < DumpFileNameLength, "Error, dump filename too long");
|
||||
mDumpToFile = true;
|
||||
mDumpToConsole = false;
|
||||
dStrcpy(mDumpFileName, fileName);
|
||||
}
|
||||
|
||||
void Profiler::hashPop(ProfilerRootData *expected)
|
||||
{
|
||||
#ifdef TORQUE_MULTITHREAD
|
||||
// Ignore non-main-thread profiler activity.
|
||||
if( !ThreadManager::isMainThread() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
mStackDepth--;
|
||||
PROFILER_DEBUG_POP_NODE();
|
||||
AssertFatal(mStackDepth >= 0, "Stack underflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs");
|
||||
if(mEnabled)
|
||||
{
|
||||
if(mCurrentProfilerData->mSubDepth)
|
||||
{
|
||||
mCurrentProfilerData->mSubDepth--;
|
||||
return;
|
||||
}
|
||||
|
||||
if(expected)
|
||||
{
|
||||
AssertISV(expected == mCurrentProfilerData->mRoot, "Profiler::hashPop - didn't get expected ProfilerRoot!");
|
||||
}
|
||||
|
||||
F64 fElapsed = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
|
||||
|
||||
mCurrentProfilerData->mTotalTime += fElapsed;
|
||||
mCurrentProfilerData->mParent->mSubTime += fElapsed; // mark it in the parent as well...
|
||||
mCurrentProfilerData->mRoot->mTotalTime += fElapsed;
|
||||
if(mCurrentProfilerData->mParent->mRoot)
|
||||
mCurrentProfilerData->mParent->mRoot->mSubTime += fElapsed; // mark it in the parent as well...
|
||||
|
||||
mCurrentProfilerData = mCurrentProfilerData->mParent;
|
||||
}
|
||||
if(mStackDepth == 0)
|
||||
{
|
||||
// apply the next enable...
|
||||
if(mDumpToConsole || mDumpToFile)
|
||||
{
|
||||
dump();
|
||||
startHighResolutionTimer(mCurrentProfilerData->mStartTime);
|
||||
}
|
||||
if(!mEnabled && mNextEnable)
|
||||
startHighResolutionTimer(mCurrentProfilerData->mStartTime);
|
||||
|
||||
#if defined(TORQUE_OS_WIN32)
|
||||
// The high performance counters under win32 are unreliable when running on multiple
|
||||
// processors. When the profiler is enabled, we restrict Torque to a single processor.
|
||||
if(mNextEnable != mEnabled)
|
||||
{
|
||||
|
||||
if(mNextEnable)
|
||||
{
|
||||
Con::warnf("Warning: forcing the Torque profiler thread to run only on cpu 1.");
|
||||
SetThreadAffinityMask(GetCurrentThread(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::warnf("Warning: the Torque profiler thread may now run on any cpu.");
|
||||
DWORD procMask;
|
||||
DWORD sysMask;
|
||||
GetProcessAffinityMask( GetCurrentProcess(), &procMask, &sysMask);
|
||||
SetThreadAffinityMask( GetCurrentThread(), procMask);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mEnabled = mNextEnable;
|
||||
}
|
||||
}
|
||||
|
||||
static S32 QSORT_CALLBACK rootDataCompare(const void *s1, const void *s2)
|
||||
{
|
||||
const ProfilerRootData *r1 = *((ProfilerRootData **) s1);
|
||||
const ProfilerRootData *r2 = *((ProfilerRootData **) s2);
|
||||
if((r2->mTotalTime - r2->mSubTime) > (r1->mTotalTime - r1->mSubTime))
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void profilerDataDumpRecurse(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime)
|
||||
{
|
||||
// dump out this one:
|
||||
Con::printf("%7.3f %7.3f %8d %s%s",
|
||||
100 * data->mTotalTime / totalTime,
|
||||
100 * (data->mTotalTime - data->mSubTime) / totalTime,
|
||||
data->mInvokeCount,
|
||||
buffer,
|
||||
data->mRoot ? data->mRoot->mName : "ROOT" );
|
||||
data->mTotalTime = 0;
|
||||
data->mSubTime = 0;
|
||||
data->mInvokeCount = 0;
|
||||
|
||||
buffer[bufferLen] = ' ';
|
||||
buffer[bufferLen+1] = ' ';
|
||||
buffer[bufferLen+2] = 0;
|
||||
// sort data's children...
|
||||
ProfilerData *list = NULL;
|
||||
while(data->mFirstChild)
|
||||
{
|
||||
ProfilerData *ins = data->mFirstChild;
|
||||
data->mFirstChild = ins->mNextSibling;
|
||||
ProfilerData **walk = &list;
|
||||
while(*walk && (*walk)->mTotalTime > ins->mTotalTime)
|
||||
walk = &(*walk)->mNextSibling;
|
||||
ins->mNextSibling = *walk;
|
||||
*walk = ins;
|
||||
}
|
||||
data->mFirstChild = list;
|
||||
while(list)
|
||||
{
|
||||
if(list->mInvokeCount)
|
||||
profilerDataDumpRecurse(list, buffer, bufferLen + 2, totalTime);
|
||||
list = list->mNextSibling;
|
||||
}
|
||||
buffer[bufferLen] = 0;
|
||||
}
|
||||
|
||||
static void profilerDataDumpRecurseFile(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime, FileStream& fws)
|
||||
{
|
||||
char pbuffer[256];
|
||||
dSprintf(pbuffer, 255, "%7.3f %7.3f %8d %s%s\n",
|
||||
100 * data->mTotalTime / totalTime,
|
||||
100 * (data->mTotalTime - data->mSubTime) / totalTime,
|
||||
data->mInvokeCount,
|
||||
buffer,
|
||||
data->mRoot ? data->mRoot->mName : "ROOT" );
|
||||
fws.write(dStrlen(pbuffer), pbuffer);
|
||||
data->mTotalTime = 0;
|
||||
data->mSubTime = 0;
|
||||
data->mInvokeCount = 0;
|
||||
|
||||
buffer[bufferLen] = ' ';
|
||||
buffer[bufferLen+1] = ' ';
|
||||
buffer[bufferLen+2] = 0;
|
||||
// sort data's children...
|
||||
ProfilerData *list = NULL;
|
||||
while(data->mFirstChild)
|
||||
{
|
||||
ProfilerData *ins = data->mFirstChild;
|
||||
data->mFirstChild = ins->mNextSibling;
|
||||
ProfilerData **walk = &list;
|
||||
while(*walk && (*walk)->mTotalTime > ins->mTotalTime)
|
||||
walk = &(*walk)->mNextSibling;
|
||||
ins->mNextSibling = *walk;
|
||||
*walk = ins;
|
||||
}
|
||||
data->mFirstChild = list;
|
||||
while(list)
|
||||
{
|
||||
if(list->mInvokeCount)
|
||||
profilerDataDumpRecurseFile(list, buffer, bufferLen + 2, totalTime, fws);
|
||||
list = list->mNextSibling;
|
||||
}
|
||||
buffer[bufferLen] = 0;
|
||||
}
|
||||
|
||||
void Profiler::dump()
|
||||
{
|
||||
bool enableSave = mEnabled;
|
||||
mEnabled = false;
|
||||
mStackDepth++;
|
||||
// may have some profiled calls... gotta turn em off.
|
||||
|
||||
Vector<ProfilerRootData *> rootVector;
|
||||
F64 totalTime = 0;
|
||||
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
|
||||
{
|
||||
totalTime += walk->mTotalTime - walk->mSubTime;
|
||||
rootVector.push_back(walk);
|
||||
}
|
||||
dQsort((void *) &rootVector[0], rootVector.size(), sizeof(ProfilerRootData *), rootDataCompare);
|
||||
|
||||
|
||||
if (mDumpToConsole == true)
|
||||
{
|
||||
Con::printf("Profiler Data Dump:");
|
||||
Con::printf("Ordered by non-sub total time -");
|
||||
Con::printf("%%NSTime %% Time Invoke # Name");
|
||||
for(U32 i = 0; i < rootVector.size(); i++)
|
||||
{
|
||||
Con::printf("%7.3f %7.3f %8d %s",
|
||||
100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime,
|
||||
100 * rootVector[i]->mTotalTime / totalTime,
|
||||
rootVector[i]->mTotalInvokeCount,
|
||||
rootVector[i]->mName);
|
||||
rootVector[i]->mTotalInvokeCount = 0;
|
||||
rootVector[i]->mTotalTime = 0;
|
||||
rootVector[i]->mSubTime = 0;
|
||||
}
|
||||
Con::printf("");
|
||||
Con::printf("Ordered by stack trace total time -");
|
||||
Con::printf("%% Time %% NSTime Invoke # Name");
|
||||
|
||||
mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
|
||||
|
||||
char depthBuffer[MaxStackDepth * 2 + 1];
|
||||
depthBuffer[0] = 0;
|
||||
profilerDataDumpRecurse(mCurrentProfilerData, depthBuffer, 0, totalTime);
|
||||
mEnabled = enableSave;
|
||||
mStackDepth--;
|
||||
}
|
||||
else if (mDumpToFile == true && mDumpFileName[0] != '\0')
|
||||
{
|
||||
FileStream fws;
|
||||
bool success = fws.open(mDumpFileName, Torque::FS::File::Write);
|
||||
AssertFatal(success, "Cannot write profile dump to specified file!");
|
||||
char buffer[1024];
|
||||
|
||||
dStrcpy(buffer, "Profiler Data Dump:\n");
|
||||
fws.write(dStrlen(buffer), buffer);
|
||||
dStrcpy(buffer, "Ordered by non-sub total time -\n");
|
||||
fws.write(dStrlen(buffer), buffer);
|
||||
dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n");
|
||||
fws.write(dStrlen(buffer), buffer);
|
||||
|
||||
for(U32 i = 0; i < rootVector.size(); i++)
|
||||
{
|
||||
dSprintf(buffer, 1023, "%7.3f %7.3f %8d %s\n",
|
||||
100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime,
|
||||
100 * rootVector[i]->mTotalTime / totalTime,
|
||||
rootVector[i]->mTotalInvokeCount,
|
||||
rootVector[i]->mName);
|
||||
fws.write(dStrlen(buffer), buffer);
|
||||
|
||||
rootVector[i]->mTotalInvokeCount = 0;
|
||||
rootVector[i]->mTotalTime = 0;
|
||||
rootVector[i]->mSubTime = 0;
|
||||
}
|
||||
dStrcpy(buffer, "\nOrdered by non-sub total time -\n");
|
||||
fws.write(dStrlen(buffer), buffer);
|
||||
dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n");
|
||||
fws.write(dStrlen(buffer), buffer);
|
||||
|
||||
mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
|
||||
|
||||
char depthBuffer[MaxStackDepth * 2 + 1];
|
||||
depthBuffer[0] = 0;
|
||||
profilerDataDumpRecurseFile(mCurrentProfilerData, depthBuffer, 0, totalTime, fws);
|
||||
mEnabled = enableSave;
|
||||
mStackDepth--;
|
||||
|
||||
fws.close();
|
||||
}
|
||||
|
||||
mDumpToConsole = false;
|
||||
mDumpToFile = false;
|
||||
mDumpFileName[0] = '\0';
|
||||
}
|
||||
|
||||
void Profiler::enableMarker(const char *marker, bool enable)
|
||||
{
|
||||
reset();
|
||||
U32 markerLen = dStrlen(marker);
|
||||
if(markerLen == 0)
|
||||
return;
|
||||
bool sn = marker[markerLen - 1] == '*';
|
||||
for(ProfilerRootData *data = ProfilerRootData::sRootList; data; data = data->mNextRoot)
|
||||
{
|
||||
if(sn)
|
||||
{
|
||||
if(!dStrncmp(marker, data->mName, markerLen - 1))
|
||||
data->mEnabled = enable;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!dStrcmp(marker, data->mName))
|
||||
data->mEnabled = enable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Console Functions.
|
||||
//=============================================================================
|
||||
// MARK: ---- Console Functions ----
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction( profilerMarkerEnable, void, ( const char* markerName, bool enable ), ( true ),
|
||||
"@brief Enable or disable a specific profile.\n\n"
|
||||
"@param enable Optional paramater to enable or disable the profile.\n"
|
||||
"@param markerName Name of a specific marker to enable or disable.\n"
|
||||
"@note Calling this function will first call profilerReset(), clearing all data from profiler. "
|
||||
"All profile markers are enabled by default.\n\n"
|
||||
"@ingroup Debugging")
|
||||
{
|
||||
if( gProfiler )
|
||||
gProfiler->enableMarker( markerName, enable );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction( profilerEnable, void, ( bool enable ),,
|
||||
"@brief Enables or disables the profiler.\n\n"
|
||||
"Data is only gathered while the profiler is enabled.\n\n"
|
||||
"@note Profiler is not available in shipping builds.\n"
|
||||
"T3D has predefined profiling areas surrounded by markers, "
|
||||
"but you may need to define additional markers (in C++) around areas you wish to profile,"
|
||||
" by using the PROFILE_START( markerName ); and PROFILE_END(); macros.\n\n"
|
||||
"@ingroup Debugging\n" )
|
||||
{
|
||||
if(gProfiler)
|
||||
gProfiler->enable(enable);
|
||||
}
|
||||
|
||||
DefineEngineFunction(profilerDump, void, (),,
|
||||
"@brief Dumps current profiling stats to the console window.\n\n"
|
||||
"@note Markers disabled with profilerMarkerEnable() will be skipped over. "
|
||||
"If the profiler is currently running, it will be disabled.\n"
|
||||
"@ingroup Debugging")
|
||||
{
|
||||
if(gProfiler)
|
||||
gProfiler->dumpToConsole();
|
||||
}
|
||||
|
||||
DefineEngineFunction( profilerDumpToFile, void, ( const char* fileName ),,
|
||||
"@brief Dumps current profiling stats to a file.\n\n"
|
||||
"@note If the profiler is currently running, it will be disabled.\n"
|
||||
"@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/). "
|
||||
"Will attempt to create the file if it does not already exist.\n"
|
||||
"@tsexample\n"
|
||||
"profilerDumpToFile( \"C:/Torque/log1.txt\" );\n"
|
||||
"@endtsexample\n\n"
|
||||
"@ingroup Debugging" )
|
||||
{
|
||||
if(gProfiler)
|
||||
gProfiler->dumpToFile(fileName);
|
||||
}
|
||||
|
||||
DefineEngineFunction( profilerReset, void, (),,
|
||||
"@brief Resets the profiler, clearing it of all its data.\n\n"
|
||||
"If the profiler is currently running, it will first be disabled. "
|
||||
"All markers will retain their current enabled/disabled status.\n\n"
|
||||
"@ingroup Debugging" )
|
||||
{
|
||||
if(gProfiler)
|
||||
gProfiler->reset();
|
||||
}
|
||||
|
||||
#endif
|
||||
194
Engine/source/platform/profiler.h
Normal file
194
Engine/source/platform/profiler.h
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PROFILER_H_
|
||||
#define _PROFILER_H_
|
||||
|
||||
#ifndef _TORQUECONFIG_H_
|
||||
#include "torqueConfig.h"
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_ENABLE_PROFILER
|
||||
|
||||
struct ProfilerData;
|
||||
struct ProfilerRootData;
|
||||
/// The Profiler is used to see how long a specific chunk of code takes to execute.
|
||||
/// All values outputted by the profiler are percentages of the time that it takes
|
||||
/// to run entire main loop.
|
||||
///
|
||||
/// First, you must #define TORQUE_ENABLE_PROFILER in profiler.h in order to
|
||||
/// active it. Examples of script use:
|
||||
/// @code
|
||||
/// //enables or disables profiling. Data is only gathered when the profiler is enabled.
|
||||
/// profilerEnable(bool enable);
|
||||
/// profilerReset(); //resets the data gathered by the profiler
|
||||
/// profilerDump(); //dumps all profiler data to the console
|
||||
/// profilerDumpToFile(string filename); //dumps all profiler data to a given file
|
||||
/// profilerMarkerEnable((string markerName, bool enable); //enables or disables a given profile tag
|
||||
/// @endcode
|
||||
///
|
||||
/// The C++ code side of the profiler uses pairs of PROFILE_START() and PROFILE_END().
|
||||
///
|
||||
/// When using these macros, make sure there is a PROFILE_END() for every PROFILE_START
|
||||
/// and a PROFILE_START() for every PROFILE_END(). It is fine to nest these macros, however,
|
||||
/// you must make sure that no matter what execution path the code takes, the PROFILE macros
|
||||
/// will be balanced.
|
||||
///
|
||||
/// The profiler can be used to locate areas of code that are slow or should be considered for
|
||||
/// optimization. Since it tracks the relative time of execution of that code to the execution
|
||||
/// of the main loop, it is possible to benchmark any given code to see if changes made will
|
||||
/// actually improve performance.
|
||||
///
|
||||
/// Here are some examples:
|
||||
/// @code
|
||||
/// PROFILE_START(TerrainRender);
|
||||
/// //some code here
|
||||
/// PROFILE_START(TerrainRenderGridSquare);
|
||||
/// //some code here
|
||||
/// PROFILE_END();
|
||||
/// //possibly some code here
|
||||
/// PROFILE_END();
|
||||
/// @endcode
|
||||
class Profiler
|
||||
{
|
||||
enum {
|
||||
MaxStackDepth = 256,
|
||||
DumpFileNameLength = 256
|
||||
};
|
||||
U32 mCurrentHash;
|
||||
|
||||
ProfilerData *mCurrentProfilerData;
|
||||
ProfilerData *mProfileList;
|
||||
ProfilerData *mRootProfilerData;
|
||||
|
||||
bool mEnabled;
|
||||
S32 mStackDepth;
|
||||
bool mNextEnable;
|
||||
U32 mMaxStackDepth;
|
||||
bool mDumpToConsole;
|
||||
bool mDumpToFile;
|
||||
char mDumpFileName[DumpFileNameLength];
|
||||
void dump();
|
||||
void validate();
|
||||
public:
|
||||
Profiler();
|
||||
~Profiler();
|
||||
|
||||
/// Reset the data in the profiler
|
||||
void reset();
|
||||
/// Dumps the profile to console
|
||||
void dumpToConsole();
|
||||
/// Dumps the profile data to a file
|
||||
/// @param fileName filename to dump data to
|
||||
void dumpToFile(const char *fileName);
|
||||
/// Enable profiling
|
||||
void enable(bool enabled);
|
||||
bool isEnabled() { return mNextEnable; }
|
||||
/// Helper function for macro definition PROFILE_START
|
||||
void hashPush(ProfilerRootData *data);
|
||||
/// Helper function for macro definition PROFILE_END
|
||||
void hashPop(ProfilerRootData *expected=NULL);
|
||||
/// Enable a profiler marker
|
||||
void enableMarker(const char *marker, bool enabled);
|
||||
#ifdef TORQUE_ENABLE_PROFILE_PATH
|
||||
/// Get current profile path
|
||||
const char * getProfilePath();
|
||||
/// Construct profile path of given profiler data
|
||||
const char * constructProfilePath(ProfilerData * pd);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Profiler *gProfiler;
|
||||
|
||||
struct ProfilerRootData
|
||||
{
|
||||
const char *mName;
|
||||
U32 mNameHash;
|
||||
ProfilerData *mFirstProfilerData;
|
||||
ProfilerRootData *mNextRoot;
|
||||
F64 mTotalTime;
|
||||
F64 mSubTime;
|
||||
U32 mTotalInvokeCount;
|
||||
bool mEnabled;
|
||||
|
||||
static ProfilerRootData *sRootList;
|
||||
|
||||
ProfilerRootData(const char *name);
|
||||
};
|
||||
|
||||
struct ProfilerData
|
||||
{
|
||||
ProfilerRootData *mRoot; ///< link to root node.
|
||||
ProfilerData *mNextForRoot; ///< links together all ProfilerData's for a particular root
|
||||
ProfilerData *mNextProfilerData; ///< links all the profilerDatas
|
||||
ProfilerData *mNextHash;
|
||||
ProfilerData *mParent;
|
||||
ProfilerData *mNextSibling;
|
||||
ProfilerData *mFirstChild;
|
||||
enum {
|
||||
HashTableSize = 32,
|
||||
};
|
||||
ProfilerData *mChildHash[HashTableSize];
|
||||
ProfilerData *mLastSeenProfiler;
|
||||
|
||||
U32 mHash;
|
||||
U32 mSubDepth;
|
||||
U32 mInvokeCount;
|
||||
U32 mStartTime[2];
|
||||
F64 mTotalTime;
|
||||
F64 mSubTime;
|
||||
#ifdef TORQUE_ENABLE_PROFILE_PATH
|
||||
const char * mPath;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#define PROFILE_START(name) \
|
||||
static ProfilerRootData pdata##name##obj (#name); \
|
||||
if(gProfiler) gProfiler->hashPush(& pdata##name##obj )
|
||||
|
||||
#define PROFILE_END() if(gProfiler) gProfiler->hashPop()
|
||||
|
||||
#define PROFILE_END_NAMED(name) if(gProfiler) gProfiler->hashPop(& pdata##name##obj)
|
||||
|
||||
class ScopedProfiler {
|
||||
public:
|
||||
ScopedProfiler(ProfilerRootData *data) {
|
||||
if (gProfiler) gProfiler->hashPush(data);
|
||||
}
|
||||
~ScopedProfiler() {
|
||||
if (gProfiler) gProfiler->hashPop();
|
||||
}
|
||||
};
|
||||
|
||||
#define PROFILE_SCOPE(name) \
|
||||
static ProfilerRootData pdata##name##obj (#name); \
|
||||
ScopedProfiler scopedProfiler##name##obj(&pdata##name##obj);
|
||||
|
||||
#else
|
||||
#define PROFILE_START(x)
|
||||
#define PROFILE_END()
|
||||
#define PROFILE_SCOPE(x)
|
||||
#define PROFILE_END_NAMED(x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
50
Engine/source/platform/test/testAlerts.cpp
Normal file
50
Engine/source/platform/test/testAlerts.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "unit/test.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateInteractiveTest(CheckPlatformAlerts, "Platform/Alerts")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
// Run through all the alert types.
|
||||
Platform::AlertOK("Test #1 - AlertOK", "This is a test of Platform::AlertOK. I am a blocking dialog with an OK button. Please hit OK to continue.");
|
||||
test(true, "AlertOK should return when the user clicks on it..."); // <-- gratuitous test point.
|
||||
|
||||
bool res;
|
||||
|
||||
res = Platform::AlertOKCancel("Test #2 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit Cancel to continue.");
|
||||
test(res==false,"AlertOKCancel - Didn't get cancel. User error, or just bad code?");
|
||||
|
||||
res = Platform::AlertOKCancel("Test #3 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit OK to continue.");
|
||||
test(res==true,"AlertOKCancel - Didn't get ok. User error, or just bad code?");
|
||||
|
||||
res = Platform::AlertRetry("Test #4 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Retry to continue.");
|
||||
test(res==true,"AlertRetry - Didn't get retry. User error, or just bad code?");
|
||||
|
||||
res = Platform::AlertRetry("Test #5 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Cancel to continue.");
|
||||
test(res==false,"AlertRetry - Didn't get cancel. User error, or just bad code?");
|
||||
}
|
||||
};
|
||||
151
Engine/source/platform/test/testAsyncPacketQueue.cpp
Normal file
151
Engine/source/platform/test/testAsyncPacketQueue.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "platform/async/asyncPacketQueue.h"
|
||||
#include "console/console.h"
|
||||
#include "core/util/tVector.h"
|
||||
|
||||
#ifndef TORQUE_SHIPPING
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
#define TEST( x ) test( ( x ), "FAIL: " #x )
|
||||
|
||||
CreateUnitTest( TestAsyncPacketQueue, "Platform/AsyncPacketQueue" )
|
||||
{
|
||||
struct Packet
|
||||
{
|
||||
typedef void Parent;
|
||||
|
||||
StringChar mChar;
|
||||
U32 mDuration;
|
||||
S32 mWriteIndex;
|
||||
U32 mWriteTime;
|
||||
|
||||
Packet() {}
|
||||
Packet( StringChar ch, U32 duration )
|
||||
: mChar( ch ), mDuration( duration ), mWriteIndex( -2 ), mWriteTime( 0 ) {}
|
||||
};
|
||||
|
||||
struct TimeSource
|
||||
{
|
||||
typedef void Parent;
|
||||
|
||||
U32 mStartTime;
|
||||
|
||||
TimeSource()
|
||||
: mStartTime( Platform::getRealMilliseconds() ) {}
|
||||
|
||||
U32 getPosition()
|
||||
{
|
||||
return ( Platform::getRealMilliseconds() - mStartTime );
|
||||
}
|
||||
};
|
||||
|
||||
struct Consumer : public IOutputStream< Packet* >
|
||||
{
|
||||
typedef IOutputStream< Packet* > Parent;
|
||||
|
||||
U32 mIndex;
|
||||
|
||||
Consumer()
|
||||
: mIndex( 0 ) {}
|
||||
|
||||
virtual void write( Packet* const* packets, U32 num )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
{
|
||||
Packet* p = packets[ i ];
|
||||
|
||||
Con::printf( "%c", p->mChar );
|
||||
|
||||
p->mWriteTime = Platform::getRealMilliseconds();
|
||||
p->mWriteIndex = mIndex;
|
||||
mIndex ++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void test1( bool dropPackets, U32 queueLength )
|
||||
{
|
||||
F32 factor = Con::getFloatVariable( "$testAsyncPacketQueue::timeFactor", 100.0f );
|
||||
String str = Con::getVariable( "$testAsyncPacketQueue::string" );
|
||||
if( str.isEmpty() )
|
||||
str = "This is a test string";
|
||||
|
||||
Vector< Packet > packets;
|
||||
for( U32 i = 0; i < str.size(); ++ i )
|
||||
packets.push_back( Packet( str[ i ], U32( Platform::getRandom() * factor ) ) );
|
||||
|
||||
U32 totalTime = 0;
|
||||
for( U32 i = 0; i < packets.size(); ++ i )
|
||||
totalTime += packets[ i ].mDuration;
|
||||
|
||||
TimeSource timeSource;
|
||||
Consumer consumer;
|
||||
AsyncPacketQueue< Packet*, TimeSource* > queue( queueLength, &timeSource, &consumer, totalTime, dropPackets );
|
||||
|
||||
U32 index = 0;
|
||||
while( !queue.isAtEnd() )
|
||||
{
|
||||
if( queue.needPacket()
|
||||
&& index < packets.size() )
|
||||
{
|
||||
|
||||
Packet* packet = &packets[ index ];
|
||||
index ++;
|
||||
|
||||
queue.submitPacket( packet, packet->mDuration );
|
||||
}
|
||||
}
|
||||
|
||||
U32 time = timeSource.mStartTime;
|
||||
S32 lastIndex = -1;
|
||||
for( U32 i = 0; i < packets.size(); ++ i )
|
||||
{
|
||||
TEST( ( packets[ i ].mWriteIndex == -2 && dropPackets ) // not written = dropped
|
||||
|| packets[ i ].mWriteIndex == lastIndex + 1 );
|
||||
|
||||
if( packets[ i ].mWriteIndex != -2 )
|
||||
lastIndex ++;
|
||||
|
||||
if( queueLength == 1 )
|
||||
TEST( packets[ i ].mWriteTime >= time || dropPackets ); // start time okay
|
||||
|
||||
time += packets[ i ].mDuration;
|
||||
if( dropPackets )
|
||||
TEST( packets[ i ].mWriteTime < time ); // end time okay (if not dropping)
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
test1( false, 1 );
|
||||
test1( true, 1 );
|
||||
|
||||
test1( false, 4 );
|
||||
test1( true, 4 );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // !TORQUE_SHIPPING
|
||||
124
Engine/source/platform/test/testBasicTypes.cpp
Normal file
124
Engine/source/platform/test/testBasicTypes.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/endian.h"
|
||||
#include "unit/test.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateUnitTest(CheckTypeSizes, "Platform/Types/Sizes")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
// Run through all the types and ensure they're the right size.
|
||||
|
||||
#define CheckType(typeName, expectedSize) \
|
||||
test( sizeof(typeName) == expectedSize, "Wrong size for a " #typeName ", expected " #expectedSize);
|
||||
|
||||
// One byte types.
|
||||
CheckType(bool, 1);
|
||||
CheckType(U8, 1);
|
||||
CheckType(S8, 1);
|
||||
CheckType(UTF8, 1);
|
||||
|
||||
// Two byte types.
|
||||
CheckType(U16, 2);
|
||||
CheckType(S16, 2);
|
||||
CheckType(UTF16, 2);
|
||||
|
||||
// Four byte types.
|
||||
CheckType(U32, 4);
|
||||
CheckType(S32, 4);
|
||||
CheckType(F32, 4);
|
||||
CheckType(UTF32, 4);
|
||||
|
||||
// Eight byte types.
|
||||
CheckType(U64, 8);
|
||||
CheckType(S64, 8);
|
||||
CheckType(F64, 8);
|
||||
|
||||
// 16 byte (128bit) types will go here, when we get some.
|
||||
#undef CheckType
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest(CheckEndianConversion, "Platform/Types/EndianRoundTrip")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
// Convenient and non-palindrome byte patterns to test with.
|
||||
const U16 U16Test = 0xA1B2;
|
||||
const S16 S16Test = 0x52A1;
|
||||
|
||||
const U32 U32Test = 0xA1B2C3D4;
|
||||
const S32 S32Test = 0xD4C3B2A1;
|
||||
const F32 F32Test = 1234.5678f;
|
||||
|
||||
//const U64 U64Test = 0xA1B2C3D4E3F2E10A;
|
||||
//const S64 S64Test = 0x1A2B3C4D3E2F1EA0;
|
||||
const F64 F64Test = 12345678.9101112131415;
|
||||
|
||||
// Run through all the conversions - bump stuff from host to little or big
|
||||
// endian and back again.
|
||||
#define CheckEndianRoundTrip(type, b_or_l) \
|
||||
test( type##Test == convert##b_or_l##EndianToHost(convertHostTo##b_or_l##Endian(type##Test)), "Failed to convert the " #type " test value to " #b_or_l " endian and back to host endian order.");
|
||||
|
||||
#define CheckTypeBothWays(type) \
|
||||
CheckEndianRoundTrip(type, B); \
|
||||
CheckEndianRoundTrip(type, L);
|
||||
|
||||
#define CheckIntsForBitSize(bits) \
|
||||
CheckTypeBothWays( U##bits ); \
|
||||
CheckTypeBothWays( S##bits );
|
||||
|
||||
// Don't check 8-bit types - they aren't affected by endian issues.
|
||||
|
||||
// Check the >1 byte int types, though.
|
||||
CheckIntsForBitSize(16);
|
||||
CheckIntsForBitSize(32);
|
||||
// CheckIntsForBitSize(64); // don't have convertHostToLEndian(const U64/S64) so this doesn't work
|
||||
|
||||
// And check the float types.
|
||||
CheckTypeBothWays(F32);
|
||||
CheckTypeBothWays(F64);
|
||||
|
||||
// We'd check 128bit types here, if we had any.
|
||||
|
||||
#undef CheckIntsForBitSize
|
||||
#undef CheckTypeBothWays
|
||||
#undef CheckEndianRoundTrip
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest(CheckEndianSwap, "Platform/Types/EndianSwap")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
U32 swap32 = 0xABCDEF12;
|
||||
U16 swap16 = 0xABCD;
|
||||
|
||||
test(endianSwap(swap32) == 0x12EFCDAB, "32 bit endianSwap should reverse byte order, but didn't.");
|
||||
test(endianSwap(swap16) == 0xCDAB, "16 bit endianSwap should reverse byte order, but didn't.");
|
||||
}
|
||||
};
|
||||
|
||||
150
Engine/source/platform/test/testFile.cpp
Normal file
150
Engine/source/platform/test/testFile.cpp
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/fileio.h"
|
||||
#include "unit/test.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "console/console.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateUnitTest(CheckFileListingAndExclusion, "File/ListDirectoryAndExclusions")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
// Just dump everything under the current directory. We should
|
||||
// find at least one file.
|
||||
|
||||
// Exclude .svn and CVS
|
||||
Platform::clearExcludedDirectories();
|
||||
Platform::addExcludedDirectory(".svn");
|
||||
Platform::addExcludedDirectory("CVS");
|
||||
|
||||
test(Platform::isExcludedDirectory("foo") == false, "Doesn't match list, shouldn't be excluded.");
|
||||
test(Platform::isExcludedDirectory(".svn") == true, "On list, should be excluded.");
|
||||
test(Platform::isExcludedDirectory("CVS") == true, "On list, should be excluded.");
|
||||
test(Platform::isExcludedDirectory(".svnCVS") == false, "Looks like a duck, but it shouldn't be excluded cuz it's distinct from all entries on the exclusion list.");
|
||||
|
||||
// Ok, now our exclusion list is setup, so let's dump some paths.
|
||||
Vector < Platform::FileInfo > pathInfo;
|
||||
Platform::dumpPath (Platform::getCurrentDirectory(), pathInfo, 2);
|
||||
|
||||
Con::printf("Dump of files in '%s', up to 2 levels deep...", Platform::getCurrentDirectory());
|
||||
for(S32 i=0; i<pathInfo.size(); i++)
|
||||
{
|
||||
Platform::FileInfo &file = pathInfo[i];
|
||||
|
||||
Con::printf(" %s (%s) %d bytes", file.pFullPath, file.pFileName, file.fileSize);
|
||||
}
|
||||
|
||||
test(pathInfo.size() > 0, "Should find at least SOMETHING in the current directory!");
|
||||
|
||||
// This'll nuke info if we run it in a live situation... so don't run unit
|
||||
// tests in a live situation. ;)
|
||||
Platform::clearExcludedDirectories();
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest(CheckFileTouchAndTime, "File/TouchAndTime")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
FileTime create[2], modify[2];
|
||||
|
||||
// Create a file and sleep for a second.
|
||||
File f;
|
||||
f.open("testTouch.file", File::WriteAppend);
|
||||
f.close();
|
||||
|
||||
Platform::sleep(2000);
|
||||
|
||||
// Touch a file and note its last-modified.
|
||||
dFileTouch("testTouch.file");
|
||||
test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist.");
|
||||
test(Platform::getFileTimes("testTouch.file", &create[0], &modify[0]), "Failed to get filetimes for a file we just created.");
|
||||
|
||||
// Sleep for a few seconds...
|
||||
Platform::sleep(5000);
|
||||
|
||||
// Touch it again, and compare the last-modifieds.
|
||||
test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist.");
|
||||
dFileTouch("testTouch.file");
|
||||
test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist.");
|
||||
test(Platform::getFileTimes("testTouch.file", &create[1], &modify[1]), "Failed to get filetimes for a file we just created.");
|
||||
|
||||
// Now compare the times...
|
||||
test(Platform::compareFileTimes(modify[0], modify[1]) < 0, "Timestamps are wrong - modify[0] should be before modify[1]!");
|
||||
|
||||
// This seems to fail even on a valid case...
|
||||
// test(Platform::compareFileTimes(create[0], create[1]) == 0, "Create timestamps should match - we didn't delete the file during this test.");
|
||||
|
||||
// Clean up..
|
||||
dFileDelete("testTouch.file");
|
||||
test(!Platform::isFile("testTouch.file"), "Somehow failed to delete our test file.");
|
||||
}
|
||||
};
|
||||
|
||||
// Mac has no implementations for these functions, so we 'def it out for now.
|
||||
#if 0
|
||||
CreateUnitTest(CheckVolumes, "File/Volumes")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
Con::printf("Dumping volumes by name:");
|
||||
|
||||
Vector<const char*> names;
|
||||
Platform::getVolumeNamesList(names);
|
||||
|
||||
test(names.size() > 0, "We should have at least one volume...");
|
||||
|
||||
for(S32 i=0; i<names.size(); i++)
|
||||
Con::printf(" %s", names[i]);
|
||||
|
||||
Con::printf("Dumping volume info:");
|
||||
|
||||
Vector<Platform::VolumeInformation> info;
|
||||
Platform::getVolumeInformationList(info);
|
||||
|
||||
test(names.size() == info.size(), "Got inconsistent number of volumes back from info vs. name list functions!");
|
||||
|
||||
for(S32 i=0; i<info.size(); i++)
|
||||
Con::printf(" %s rootPath = %s filesystem = %s ser. num. = %d type = %d readonly = %s",
|
||||
info[i].Name,
|
||||
info[i].RootPath,
|
||||
info[i].FileSystem,
|
||||
info[i].SerialNumber,
|
||||
info[i].Type,
|
||||
info[i].ReadOnly ? "true" : "false");
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
CreateUnitTest(CheckFileWriteAndRead, "File/ReadAndWrite")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
// Open a file, write some junk to it, close it,
|
||||
// check size is correct, and open it again.
|
||||
|
||||
}
|
||||
};
|
||||
194
Engine/source/platform/test/testNet.cpp
Normal file
194
Engine/source/platform/test/testNet.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platformNet.h"
|
||||
#include "unit/test.h"
|
||||
#include "core/util/journal/process.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateUnitTest( TestTCPRequest, "Platform/Net/TCPRequest")
|
||||
{
|
||||
NetSocket mSocket;
|
||||
S32 mDataRecved;
|
||||
|
||||
void handleNotify(NetSocket sock, U32 state)
|
||||
{
|
||||
// Only consider our own socket.
|
||||
if(mSocket != sock)
|
||||
return;
|
||||
|
||||
// Ok - what's the state? We do some dumb responses to given states
|
||||
// in order to fulfill the request.
|
||||
if(state == Net::Connected)
|
||||
{
|
||||
U8 reqBuffer[] = {
|
||||
"GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n"
|
||||
};
|
||||
|
||||
Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer));
|
||||
|
||||
test(e == Net::NoError, "Got an error sending our HTTP request!");
|
||||
}
|
||||
else if(state == Net::Disconnected)
|
||||
{
|
||||
Process::requestShutdown();
|
||||
mSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void handleReceive(NetSocket sock, RawData incomingData)
|
||||
{
|
||||
// Only consider our own socket.
|
||||
if(mSocket != sock)
|
||||
return;
|
||||
|
||||
char buff[4096];
|
||||
dMemcpy(buff, incomingData.data, incomingData.size);
|
||||
buff[incomingData.size] = 0;
|
||||
|
||||
UnitPrint("Got a message...\n");
|
||||
UnitPrint(buff);
|
||||
UnitPrint("------\n");
|
||||
|
||||
mDataRecved += incomingData.size;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
mSocket = InvalidSocket;
|
||||
mDataRecved = 0;
|
||||
|
||||
// Initialize networking - done by initLibraries currently
|
||||
//test(Net::init(), "Failed to initialize networking!");
|
||||
|
||||
// Hook into the signals.
|
||||
Net::smConnectionNotify. notify(this, &TestTCPRequest::handleNotify);
|
||||
Net::smConnectionReceive.notify(this, &TestTCPRequest::handleReceive);
|
||||
|
||||
// Open a TCP connection to garagegames.com
|
||||
mSocket = Net::openConnectTo("ip:72.246.107.193:80");
|
||||
|
||||
while(Process::processEvents())
|
||||
;
|
||||
|
||||
// Unhook from the signals.
|
||||
Net::smConnectionNotify. remove(this, &TestTCPRequest::handleNotify);
|
||||
Net::smConnectionReceive.remove(this, &TestTCPRequest::handleReceive);
|
||||
|
||||
test(mDataRecved > 0, "Didn't get any data back!");
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( TestTCPRequestJournal, "Platform/Net/JournalTCPRequest")
|
||||
{
|
||||
NetSocket mSocket;
|
||||
S32 mDataRecved;
|
||||
|
||||
void handleNotify(NetSocket sock, U32 state)
|
||||
{
|
||||
// Only consider our own socket.
|
||||
if(mSocket != sock)
|
||||
return;
|
||||
|
||||
// Ok - what's the state? We do some dumb responses to given states
|
||||
// in order to fulfill the request.
|
||||
if(state == Net::Connected)
|
||||
{
|
||||
U8 reqBuffer[] = {
|
||||
"GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n"
|
||||
};
|
||||
|
||||
Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer));
|
||||
|
||||
test(e == Net::NoError, "Got an error sending our HTTP request!");
|
||||
}
|
||||
else if(state == Net::Disconnected)
|
||||
{
|
||||
Process::requestShutdown();
|
||||
mSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void handleReceive(NetSocket sock, RawData incomingData)
|
||||
{
|
||||
// Only consider our own socket.
|
||||
if(mSocket != sock)
|
||||
return;
|
||||
|
||||
char buff[4096];
|
||||
dMemcpy(buff, incomingData.data, incomingData.size);
|
||||
buff[incomingData.size] = 0;
|
||||
|
||||
UnitPrint("Got a message...\n");
|
||||
UnitPrint(buff);
|
||||
UnitPrint("------\n");
|
||||
|
||||
mDataRecved += incomingData.size;
|
||||
}
|
||||
|
||||
void makeRequest()
|
||||
{
|
||||
mSocket = InvalidSocket;
|
||||
mDataRecved = 0;
|
||||
|
||||
// Initialize networking - done by initLibraries currently
|
||||
//test(Net::init(), "Failed to initialize networking!");
|
||||
|
||||
// Hook into the signals.
|
||||
Net::smConnectionNotify. notify(this, &TestTCPRequestJournal::handleNotify);
|
||||
Net::smConnectionReceive.notify(this, &TestTCPRequestJournal::handleReceive);
|
||||
|
||||
// Open a TCP connection to garagegames.com
|
||||
mSocket = Net::openConnectTo("ip:72.246.107.193:80");
|
||||
|
||||
// Let the callbacks enable things to process.
|
||||
while(Process::processEvents())
|
||||
;
|
||||
|
||||
// Unhook from the signals.
|
||||
Net::smConnectionNotify. remove(this, &TestTCPRequestJournal::handleNotify);
|
||||
Net::smConnectionReceive.remove(this, &TestTCPRequestJournal::handleReceive);
|
||||
|
||||
test(mDataRecved > 0, "Didn't get any data back!");
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
Journal::Record("journalTCP.jrn");
|
||||
|
||||
makeRequest();
|
||||
|
||||
S32 bytesRead = mDataRecved;
|
||||
|
||||
Journal::Stop();
|
||||
|
||||
Journal::Play("journalTCP.jrn");
|
||||
|
||||
makeRequest();
|
||||
|
||||
Journal::Stop();
|
||||
|
||||
test(bytesRead == mDataRecved, "Didn't get same data back from journal playback.");
|
||||
|
||||
}
|
||||
};
|
||||
85
Engine/source/platform/test/testThreadPool.cpp
Normal file
85
Engine/source/platform/test/testThreadPool.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "platform/threads/threadPool.h"
|
||||
#include "console/console.h"
|
||||
#include "core/util/tVector.h"
|
||||
|
||||
#ifndef TORQUE_SHIPPING
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
#define TEST( x ) test( ( x ), "FAIL: " #x )
|
||||
|
||||
// Simple test that creates and verifies an array of numbers using
|
||||
// thread pool work items.
|
||||
|
||||
CreateUnitTest( TestThreadPool, "Platform/ThreadPool/Simple" )
|
||||
{
|
||||
enum { DEFAULT_NUM_ITEMS = 4000 };
|
||||
|
||||
static Vector< U32 > results;
|
||||
|
||||
struct TestItem : public ThreadPool::WorkItem
|
||||
{
|
||||
typedef ThreadPool::WorkItem Parent;
|
||||
|
||||
U32 mIndex;
|
||||
|
||||
TestItem( U32 index )
|
||||
: mIndex( index ) {}
|
||||
|
||||
protected:
|
||||
virtual void execute()
|
||||
{
|
||||
results[ mIndex ] = mIndex;
|
||||
}
|
||||
};
|
||||
|
||||
void run()
|
||||
{
|
||||
U32 numItems = Con::getIntVariable( "$testThreadPool::numValues", DEFAULT_NUM_ITEMS );
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL();
|
||||
results.setSize( numItems );
|
||||
|
||||
for( U32 i = 0; i < numItems; ++ i )
|
||||
results[ i ] = U32( -1 );
|
||||
|
||||
for( U32 i = 0; i < numItems; ++ i )
|
||||
{
|
||||
ThreadSafeRef< TestItem > item( new TestItem( i ) );
|
||||
pool->queueWorkItem( item );
|
||||
}
|
||||
|
||||
pool->flushWorkItems();
|
||||
|
||||
for( U32 i = 0; i < numItems; ++ i )
|
||||
test( results[ i ] == i, "result mismatch" );
|
||||
|
||||
results.clear();
|
||||
}
|
||||
};
|
||||
|
||||
Vector< U32 > TestThreadPool::results( __FILE__, __LINE__ );
|
||||
|
||||
#endif // !TORQUE_SHIPPING
|
||||
403
Engine/source/platform/test/testThreadSafeDeque.cpp
Normal file
403
Engine/source/platform/test/testThreadSafeDeque.cpp
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "platform/threads/threadSafeDeque.h"
|
||||
#include "platform/threads/thread.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "console/console.h"
|
||||
|
||||
|
||||
#ifndef TORQUE_SHIPPING
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
#define TEST( x ) test( ( x ), "FAIL: " #x )
|
||||
#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
|
||||
|
||||
|
||||
// Test deque without concurrency.
|
||||
|
||||
CreateUnitTest( TestThreadSafeDequeSerial, "Platform/ThreadSafeDeque/Serial" )
|
||||
{
|
||||
void test1()
|
||||
{
|
||||
ThreadSafeDeque< char > deque;
|
||||
String str = "teststring";
|
||||
|
||||
for( U32 i = 0; i < str.length(); ++ i )
|
||||
deque.pushBack( str[ i ] );
|
||||
|
||||
TEST( !deque.isEmpty() );
|
||||
|
||||
for( U32 i = 0; i < str.length(); ++ i )
|
||||
{
|
||||
char ch;
|
||||
TEST( deque.tryPopFront( ch ) && ch == str[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
void test2()
|
||||
{
|
||||
ThreadSafeDeque< char > deque;
|
||||
String str = "teststring";
|
||||
|
||||
const char* p1 = str.c_str() + 4;
|
||||
const char* p2 = p1 + 1;
|
||||
while( *p2 )
|
||||
{
|
||||
deque.pushFront( *p1 );
|
||||
deque.pushBack( *p2 );
|
||||
|
||||
-- p1;
|
||||
++ p2;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
deque.dumpDebug();
|
||||
#endif
|
||||
|
||||
for( U32 i = 0; i < str.length(); ++ i )
|
||||
{
|
||||
char ch;
|
||||
TEST( deque.tryPopFront( ch ) && ch == str[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
void test3()
|
||||
{
|
||||
ThreadSafeDeque< char > deque;
|
||||
String str = "teststring";
|
||||
|
||||
const char* p1 = str.c_str() + 4;
|
||||
const char* p2 = p1 + 1;
|
||||
while( *p2 )
|
||||
{
|
||||
deque.pushFront( *p1 );
|
||||
deque.pushBack( *p2 );
|
||||
|
||||
-- p1;
|
||||
++ p2;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
deque.dumpDebug();
|
||||
#endif
|
||||
|
||||
for( S32 i = ( str.length() - 1 ); i >= 0; -- i )
|
||||
{
|
||||
char ch;
|
||||
TEST( deque.tryPopBack( ch ) && ch == str[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
void test4()
|
||||
{
|
||||
ThreadSafeDeque< char > deque;
|
||||
char ch;
|
||||
|
||||
TEST( deque.isEmpty() );
|
||||
|
||||
deque.pushFront( 'a' );
|
||||
TEST( !deque.isEmpty() );
|
||||
TEST( deque.tryPopFront( ch ) );
|
||||
TEST( ch == 'a' );
|
||||
|
||||
deque.pushBack( 'a' );
|
||||
TEST( !deque.isEmpty() );
|
||||
TEST( deque.tryPopFront( ch ) );
|
||||
TEST( ch == 'a' );
|
||||
|
||||
deque.pushBack( 'a' );
|
||||
TEST( !deque.isEmpty() );
|
||||
TEST( deque.tryPopBack( ch ) );
|
||||
TEST( ch == 'a' );
|
||||
|
||||
deque.pushFront( 'a' );
|
||||
TEST( !deque.isEmpty() );
|
||||
TEST( deque.tryPopBack( ch ) );
|
||||
TEST( ch == 'a' );
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
}
|
||||
};
|
||||
|
||||
// Test deque in a concurrent setting.
|
||||
|
||||
CreateUnitTest( TestThreadSafeDequeConcurrentSimple, "Platform/ThreadSafeDeque/ConcurrentSimple" )
|
||||
{
|
||||
public:
|
||||
typedef TestThreadSafeDequeConcurrentSimple TestType;
|
||||
|
||||
enum
|
||||
{
|
||||
DEFAULT_NUM_VALUES = 100000,
|
||||
};
|
||||
|
||||
struct Value : public ThreadSafeRefCount< Value >
|
||||
{
|
||||
U32 mIndex;
|
||||
U32 mTick;
|
||||
|
||||
Value() {}
|
||||
Value( U32 index, U32 tick )
|
||||
: mIndex( index ), mTick( tick ) {}
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< Value > ValueRef;
|
||||
|
||||
struct Deque : public ThreadSafeDeque< ValueRef >
|
||||
{
|
||||
typedef ThreadSafeDeque<ValueRef> Parent;
|
||||
|
||||
U32 mPushIndex;
|
||||
U32 mPopIndex;
|
||||
|
||||
Deque()
|
||||
: mPushIndex( 0 ), mPopIndex( 0 ) {}
|
||||
|
||||
void pushBack( const ValueRef& value )
|
||||
{
|
||||
AssertFatal( value->mIndex == mPushIndex, "index out of line" );
|
||||
mPushIndex ++;
|
||||
Parent::pushBack( value );
|
||||
}
|
||||
bool tryPopFront( ValueRef& outValue )
|
||||
{
|
||||
if( Parent::tryPopFront( outValue ) )
|
||||
{
|
||||
AssertFatal( outValue->mIndex == mPopIndex, "index out of line" );
|
||||
mPopIndex ++;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Deque mDeque;
|
||||
Vector< U32 > mValues;
|
||||
|
||||
struct ProducerThread : public Thread
|
||||
{
|
||||
ProducerThread( TestType* test )
|
||||
: Thread( 0, test ) {}
|
||||
|
||||
virtual void run( void* arg )
|
||||
{
|
||||
_setName( "ProducerThread" );
|
||||
Platform::outputDebugString( "Starting ProducerThread" );
|
||||
|
||||
TestType* test = ( TestType* ) arg;
|
||||
|
||||
for( U32 i = 0; i < test->mValues.size(); ++ i )
|
||||
{
|
||||
U32 tick = Platform::getRealMilliseconds();
|
||||
test->mValues[ i ] = tick;
|
||||
|
||||
ValueRef val = new Value( i, tick );
|
||||
test->mDeque.pushBack( val );
|
||||
}
|
||||
Platform::outputDebugString( "Stopping ProducerThread" );
|
||||
}
|
||||
};
|
||||
struct ConsumerThread : public Thread
|
||||
{
|
||||
ConsumerThread( TestType* test )
|
||||
: Thread( 0, test ) {}
|
||||
|
||||
virtual void run( void* arg )
|
||||
{
|
||||
_setName( "ConsumerThread" );
|
||||
Platform::outputDebugString( "Starting CosumerThread" );
|
||||
TestType* t = ( TestType* ) arg;
|
||||
|
||||
for( U32 i = 0; i < t->mValues.size(); ++ i )
|
||||
{
|
||||
ValueRef value;
|
||||
while( !t->mDeque.tryPopFront( value ) );
|
||||
|
||||
XTEST( t, value->mIndex == i );
|
||||
XTEST( t, t->mValues[ i ] == value->mTick );
|
||||
}
|
||||
Platform::outputDebugString( "Stopping ConsumerThread" );
|
||||
}
|
||||
};
|
||||
|
||||
void run()
|
||||
{
|
||||
U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES );
|
||||
mValues.setSize( numValues );
|
||||
|
||||
ProducerThread pThread( this );
|
||||
ConsumerThread cThread( this );
|
||||
|
||||
pThread.start();
|
||||
cThread.start();
|
||||
|
||||
pThread.join();
|
||||
cThread.join();
|
||||
|
||||
mValues.clear();
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( TestThreadSafeDequeConcurrent, "Platform/ThreadSafeDeque/Concurrent" )
|
||||
{
|
||||
public:
|
||||
typedef TestThreadSafeDequeConcurrent TestType;
|
||||
|
||||
enum
|
||||
{
|
||||
DEFAULT_NUM_VALUES = 100000,
|
||||
DEFAULT_NUM_CONSUMERS = 10,
|
||||
DEFAULT_NUM_PRODUCERS = 10
|
||||
};
|
||||
|
||||
struct Value : public ThreadSafeRefCount< Value >
|
||||
{
|
||||
U32 mIndex;
|
||||
U32 mTick;
|
||||
|
||||
Value() {}
|
||||
Value( U32 index, U32 tick )
|
||||
: mIndex( index ), mTick( tick ) {}
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< Value > ValueRef;
|
||||
|
||||
U32 mProducerIndex;
|
||||
U32 mConsumerIndex;
|
||||
ThreadSafeDeque< ValueRef > mDeque;
|
||||
Vector< U32 > mValues;
|
||||
|
||||
struct ProducerThread : public Thread
|
||||
{
|
||||
ProducerThread( TestType* test )
|
||||
: Thread( 0, test ) {}
|
||||
|
||||
virtual void run( void* arg )
|
||||
{
|
||||
_setName( "ProducerThread" );
|
||||
Platform::outputDebugString( "Starting ProducerThread" );
|
||||
TestType* test = ( TestType* ) arg;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
U32 index = test->mProducerIndex;
|
||||
if( index == test->mValues.size() )
|
||||
break;
|
||||
|
||||
if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) )
|
||||
{
|
||||
U32 tick = Platform::getRealMilliseconds();
|
||||
test->mValues[ index ] = tick;
|
||||
|
||||
ValueRef val = new Value( index, tick );
|
||||
test->mDeque.pushBack( val );
|
||||
}
|
||||
}
|
||||
Platform::outputDebugString( "Stopping ProducerThread" );
|
||||
}
|
||||
};
|
||||
struct ConsumerThread : public Thread
|
||||
{
|
||||
ConsumerThread( TestType* test )
|
||||
: Thread( 0, test ) {}
|
||||
|
||||
virtual void run( void* arg )
|
||||
{
|
||||
_setName( "ConsumerThread" );
|
||||
Platform::outputDebugString( "Starting ConsumerThread" );
|
||||
TestType* t = ( TestType* ) arg;
|
||||
|
||||
while( t->mConsumerIndex < t->mValues.size() )
|
||||
{
|
||||
ValueRef value;
|
||||
if( t->mDeque.tryPopFront( value ) )
|
||||
{
|
||||
dFetchAndAdd( t->mConsumerIndex, 1 );
|
||||
XTEST( t, t->mValues[ value->mIndex ] == value->mTick );
|
||||
t->mValues[ value->mIndex ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Platform::outputDebugString( "Stopping ConsumerThread" );
|
||||
}
|
||||
};
|
||||
|
||||
void run()
|
||||
{
|
||||
U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES );
|
||||
U32 numConsumers = Con::getIntVariable( "$testThreadSafeDeque::numConsumers", DEFAULT_NUM_CONSUMERS );
|
||||
U32 numProducers = Con::getIntVariable( "$testThreadSafeDeque::numProducers", DEFAULT_NUM_PRODUCERS );
|
||||
|
||||
mProducerIndex = 0;
|
||||
mConsumerIndex = 0;
|
||||
mValues.setSize( numValues );
|
||||
|
||||
U32 tick = Platform::getRealMilliseconds();
|
||||
for( U32 i = 0; i < numValues; ++ i )
|
||||
mValues[ i ] = tick;
|
||||
|
||||
Vector< ProducerThread* > producers;
|
||||
Vector< ConsumerThread* > consumers;
|
||||
|
||||
producers.setSize( numProducers );
|
||||
consumers.setSize( numConsumers );
|
||||
|
||||
for( U32 i = 0; i < numProducers; ++ i )
|
||||
{
|
||||
producers[ i ] = new ProducerThread( this );
|
||||
producers[ i ]->start();
|
||||
}
|
||||
for( U32 i = 0; i < numConsumers; ++ i )
|
||||
{
|
||||
consumers[ i ] = new ConsumerThread( this );
|
||||
consumers[ i ]->start();
|
||||
}
|
||||
|
||||
for( U32 i = 0; i < numProducers; ++ i )
|
||||
{
|
||||
producers[ i ]->join();
|
||||
delete producers[ i ];
|
||||
}
|
||||
for( U32 i = 0; i < numConsumers; ++ i )
|
||||
{
|
||||
consumers[ i ]->join();
|
||||
delete consumers[ i ];
|
||||
}
|
||||
|
||||
for( U32 i = 0; i < mValues.size(); ++ i )
|
||||
TEST( mValues[ i ] == 0 );
|
||||
|
||||
mValues.clear();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // !TORQUE_SHIPPING
|
||||
245
Engine/source/platform/test/testThreadSafePriorityQueue.cpp
Normal file
245
Engine/source/platform/test/testThreadSafePriorityQueue.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "platform/threads/threadSafePriorityQueue.h"
|
||||
#include "platform/threads/thread.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "console/console.h"
|
||||
|
||||
|
||||
#ifndef TORQUE_SHIPPING
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
#define TEST( x ) test( ( x ), "FAIL: " #x )
|
||||
#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
|
||||
|
||||
|
||||
// Test queue without concurrency.
|
||||
|
||||
CreateUnitTest( TestThreadSafePriorityQueueSerial, "Platform/ThreadSafePriorityQueue/Serial" )
|
||||
{
|
||||
struct Value
|
||||
{
|
||||
F32 mPriority;
|
||||
U32 mIndex;
|
||||
|
||||
Value() {}
|
||||
Value( F32 priority, U32 index )
|
||||
: mPriority( priority ), mIndex( index ) {}
|
||||
};
|
||||
|
||||
template< bool SORT_MIN_TO_MAX >
|
||||
void test1()
|
||||
{
|
||||
Vector< Value > values;
|
||||
|
||||
values.push_back( Value( 0.2f, 2 ) );
|
||||
values.push_back( Value( 0.7f, 7 ) );
|
||||
values.push_back( Value( 0.4f, 4 ) );
|
||||
values.push_back( Value( 0.6f, 6 ) );
|
||||
values.push_back( Value( 0.1f, 1 ) );
|
||||
values.push_back( Value( 0.5f, 5 ) );
|
||||
values.push_back( Value( 0.3f, 3 ) );
|
||||
values.push_back( Value( 0.8f, 8 ) );
|
||||
values.push_back( Value( 0.6f, 6 ) );
|
||||
values.push_back( Value( 0.9f, 9 ) );
|
||||
values.push_back( Value( 0.0f, 0 ) );
|
||||
|
||||
const S32 min = 0;
|
||||
const S32 max = 9;
|
||||
|
||||
ThreadSafePriorityQueue< U32, F32, SORT_MIN_TO_MAX > queue;
|
||||
|
||||
for( U32 i = 0; i < values.size(); ++ i )
|
||||
queue.insert( values[ i ].mPriority, values[ i ].mIndex );
|
||||
|
||||
TEST( !queue.isEmpty() );
|
||||
|
||||
S32 index;
|
||||
if( SORT_MIN_TO_MAX )
|
||||
index = min - 1;
|
||||
else
|
||||
index = max + 1;
|
||||
|
||||
for( U32 i = 0; i < values.size(); ++ i )
|
||||
{
|
||||
U32 value;
|
||||
TEST( queue.takeNext( value ) );
|
||||
|
||||
if( value != index )
|
||||
{
|
||||
if( SORT_MIN_TO_MAX )
|
||||
index ++;
|
||||
else
|
||||
index --;
|
||||
}
|
||||
|
||||
TEST( value == index );
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
test1< true >();
|
||||
test1< false >();
|
||||
}
|
||||
};
|
||||
|
||||
// Test queue with concurrency.
|
||||
|
||||
CreateUnitTest( TestThreadSafePriorityQueueConcurrent, "Platform/ThreadSafePriorityQueue/Concurrent" )
|
||||
{
|
||||
public:
|
||||
typedef TestThreadSafePriorityQueueConcurrent TestType;
|
||||
|
||||
enum
|
||||
{
|
||||
DEFAULT_NUM_VALUES = 100000,
|
||||
DEFAULT_NUM_CONSUMERS = 10,
|
||||
DEFAULT_NUM_PRODUCERS = 10
|
||||
};
|
||||
|
||||
struct Value : public ThreadSafeRefCount< Value >
|
||||
{
|
||||
U32 mIndex;
|
||||
F32 mPriority;
|
||||
bool mCheck;
|
||||
|
||||
Value() : mCheck( false ) {}
|
||||
Value( U32 index, F32 priority )
|
||||
: mIndex( index ), mPriority( priority ), mCheck( false ) {}
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< Value > ValueRef;
|
||||
|
||||
U32 mProducerIndex;
|
||||
U32 mConsumerIndex;
|
||||
ThreadSafePriorityQueue< ValueRef > mQueue;
|
||||
Vector< ValueRef > mValues;
|
||||
|
||||
struct ProducerThread : public Thread
|
||||
{
|
||||
ProducerThread( TestType* test )
|
||||
: Thread( 0, test ) {}
|
||||
|
||||
virtual void run( void* arg )
|
||||
{
|
||||
_setName( "ProducerThread" );
|
||||
Platform::outputDebugString( "Starting ProducerThread" );
|
||||
TestType* test = ( TestType* ) arg;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
U32 index = test->mProducerIndex;
|
||||
if( index == test->mValues.size() )
|
||||
break;
|
||||
|
||||
if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) )
|
||||
{
|
||||
F32 priority = Platform::getRandom();
|
||||
ValueRef val = new Value( index, priority );
|
||||
test->mValues[ index ] = val;
|
||||
test->mQueue.insert( priority, val );
|
||||
}
|
||||
}
|
||||
Platform::outputDebugString( "Stopping ProducerThread" );
|
||||
}
|
||||
};
|
||||
struct ConsumerThread : public Thread
|
||||
{
|
||||
ConsumerThread( TestType* test )
|
||||
: Thread( 0, test ) {}
|
||||
|
||||
virtual void run( void* arg )
|
||||
{
|
||||
_setName( "ConsumerThread" );
|
||||
Platform::outputDebugString( "Starting ConsumerThread" );
|
||||
TestType* t = ( TestType* ) arg;
|
||||
|
||||
while( t->mConsumerIndex < t->mValues.size() )
|
||||
{
|
||||
ValueRef value;
|
||||
if( t->mQueue.takeNext( value ) )
|
||||
{
|
||||
dFetchAndAdd( t->mConsumerIndex, 1 );
|
||||
XTEST( t, t->mValues[ value->mIndex ] == value );
|
||||
value->mCheck = true;
|
||||
}
|
||||
else
|
||||
Platform::sleep( 5 );
|
||||
}
|
||||
Platform::outputDebugString( "Stopping ConsumerThread" );
|
||||
}
|
||||
};
|
||||
|
||||
void run()
|
||||
{
|
||||
U32 numValues = Con::getIntVariable( "$testThreadSafePriorityQueue::numValues", DEFAULT_NUM_VALUES );
|
||||
U32 numConsumers = Con::getIntVariable( "$testThreadSafePriorityQueue::numConsumers", DEFAULT_NUM_CONSUMERS );
|
||||
U32 numProducers = Con::getIntVariable( "$testThreadSafePriorityQueue::numProducers", DEFAULT_NUM_PRODUCERS );
|
||||
|
||||
mProducerIndex = 0;
|
||||
mConsumerIndex = 0;
|
||||
mValues.setSize( numValues );
|
||||
|
||||
Vector< ProducerThread* > producers;
|
||||
Vector< ConsumerThread* > consumers;
|
||||
|
||||
producers.setSize( numProducers );
|
||||
consumers.setSize( numConsumers );
|
||||
|
||||
for( U32 i = 0; i < numProducers; ++ i )
|
||||
{
|
||||
producers[ i ] = new ProducerThread( this );
|
||||
producers[ i ]->start();
|
||||
}
|
||||
for( U32 i = 0; i < numConsumers; ++ i )
|
||||
{
|
||||
consumers[ i ] = new ConsumerThread( this );
|
||||
consumers[ i ]->start();
|
||||
}
|
||||
|
||||
for( U32 i = 0; i < numProducers; ++ i )
|
||||
{
|
||||
producers[ i ]->join();
|
||||
delete producers[ i ];
|
||||
}
|
||||
for( U32 i = 0; i < numConsumers; ++ i )
|
||||
{
|
||||
consumers[ i ]->join();
|
||||
delete consumers[ i ];
|
||||
}
|
||||
|
||||
for( U32 i = 0; i < mValues.size(); ++ i )
|
||||
{
|
||||
TEST( mValues[ i ] != NULL );
|
||||
if( mValues[ i ] != NULL )
|
||||
TEST( mValues[ i ]->mCheck );
|
||||
}
|
||||
|
||||
mValues.clear();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // !TORQUE_SHIPPING
|
||||
227
Engine/source/platform/test/testThreadSafeRefCount.cpp
Normal file
227
Engine/source/platform/test/testThreadSafeRefCount.cpp
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "platform/threads/threadSafeRefCount.h"
|
||||
#include "platform/threads/thread.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "console/console.h"
|
||||
|
||||
#ifndef TORQUE_SHIPPING
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
#define TEST( x ) test( ( x ), "FAIL: " #x )
|
||||
|
||||
CreateUnitTest( TestThreadSafeRefCountSerial, "Platform/ThreadSafeRefCount/Serial" )
|
||||
{
|
||||
struct TestObject : public ThreadSafeRefCount< TestObject >
|
||||
{
|
||||
static bool smDeleted;
|
||||
|
||||
TestObject()
|
||||
{
|
||||
smDeleted = false;
|
||||
}
|
||||
~TestObject()
|
||||
{
|
||||
smDeleted = true;
|
||||
}
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< TestObject > TestObjectRef;
|
||||
|
||||
void run()
|
||||
{
|
||||
TestObjectRef ref1 = new TestObject;
|
||||
TEST( !ref1->isShared() );
|
||||
TEST( ref1 != NULL );
|
||||
|
||||
TestObjectRef ref2 = ref1;
|
||||
TEST( ref1->isShared() );
|
||||
TEST( ref2->isShared() );
|
||||
TEST( ref1 == ref2 );
|
||||
|
||||
ref1 = NULL;
|
||||
TEST( !ref2->isShared() );
|
||||
|
||||
ref2 = NULL;
|
||||
TEST( TestObject::smDeleted );
|
||||
}
|
||||
};
|
||||
|
||||
bool TestThreadSafeRefCountSerial::TestObject::smDeleted;
|
||||
|
||||
CreateUnitTest( TestThreadSafeRefCountConcurrent, "Platform/ThreadSafeRefCount/Concurrent" )
|
||||
{
|
||||
public:
|
||||
typedef TestThreadSafeRefCountConcurrent TestType;
|
||||
enum
|
||||
{
|
||||
NUM_ADD_REFS_PER_THREAD = 1000,
|
||||
NUM_EXTRA_REFS_PER_THREAD = 1000,
|
||||
NUM_THREADS = 10
|
||||
};
|
||||
|
||||
class TestObject : public ThreadSafeRefCount< TestObject >
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
ThreadSafeRef< TestObject > mRef;
|
||||
|
||||
class TestThread : public Thread
|
||||
{
|
||||
public:
|
||||
TestType* mTest;
|
||||
Vector< ThreadSafeRef< TestObject > > mExtraRefs;
|
||||
|
||||
TestThread( TestType* test )
|
||||
: mTest( test ) {}
|
||||
|
||||
void run( void* arg )
|
||||
{
|
||||
if( !arg )
|
||||
{
|
||||
for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i )
|
||||
mTest->mRef->addRef();
|
||||
|
||||
mExtraRefs.setSize( NUM_EXTRA_REFS_PER_THREAD );
|
||||
for( U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; ++ i )
|
||||
mExtraRefs[ i ] = mTest->mRef;
|
||||
}
|
||||
else
|
||||
{
|
||||
mExtraRefs.clear();
|
||||
|
||||
for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i )
|
||||
mTest->mRef->release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void run()
|
||||
{
|
||||
mRef = new TestObject;
|
||||
TEST( mRef->getRefCount() == 2 ); // increments of 2
|
||||
|
||||
Vector< TestThread* > threads;
|
||||
threads.setSize( NUM_THREADS );
|
||||
|
||||
// Create threads.
|
||||
for( U32 i = 0; i < NUM_THREADS; ++ i )
|
||||
threads[ i ] = new TestThread( this );
|
||||
|
||||
// Run phase 1: create references.
|
||||
for( U32 i = 0; i < NUM_THREADS; ++ i )
|
||||
threads[ i ]->start( NULL );
|
||||
|
||||
// Wait for completion.
|
||||
for( U32 i = 0; i < NUM_THREADS; ++ i )
|
||||
threads[ i ]->join();
|
||||
|
||||
Con::printf( "REF: %i", mRef->getRefCount() );
|
||||
TEST( mRef->getRefCount() == 2 + ( ( NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD ) * NUM_THREADS * 2 ) );
|
||||
|
||||
// Run phase 2: release references.
|
||||
for( U32 i = 0; i < NUM_THREADS; ++ i )
|
||||
threads[ i ]->start( ( void* ) 1 );
|
||||
|
||||
// Wait for completion.
|
||||
for( U32 i = 0; i < NUM_THREADS; ++ i )
|
||||
{
|
||||
threads[ i ]->join();
|
||||
delete threads[ i ];
|
||||
}
|
||||
|
||||
TEST( mRef->getRefCount() == 2 ); // increments of two
|
||||
|
||||
mRef = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( TestThreadSafeRefCountTagging, "Platform/ThreadSafeRefCount/Tagging" )
|
||||
{
|
||||
struct TestObject : public ThreadSafeRefCount< TestObject > {};
|
||||
|
||||
typedef ThreadSafeRef< TestObject > TestObjectRef;
|
||||
|
||||
void run()
|
||||
{
|
||||
TestObjectRef ref;
|
||||
|
||||
TEST( !ref.isTagged() );
|
||||
TEST( !ref );
|
||||
TEST( !ref.ptr() );
|
||||
|
||||
TEST( ref.trySetFromTo( ref, NULL ) );
|
||||
TEST( !ref.isTagged() );
|
||||
|
||||
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) );
|
||||
TEST( ref.isTagged() );
|
||||
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) );
|
||||
TEST( ref.isTagged() );
|
||||
|
||||
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) );
|
||||
TEST( !ref.isTagged() );
|
||||
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) );
|
||||
TEST( !ref.isTagged() );
|
||||
|
||||
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) );
|
||||
TEST( ref.isTagged() );
|
||||
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) );
|
||||
TEST( ref.isTagged() );
|
||||
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfSet ) );
|
||||
|
||||
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) );
|
||||
TEST( !ref.isTagged() );
|
||||
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) );
|
||||
TEST( !ref.isTagged() );
|
||||
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfUnset ) );
|
||||
|
||||
TestObjectRef objectA = new TestObject;
|
||||
TestObjectRef objectB = new TestObject;
|
||||
|
||||
TEST( !objectA->isShared() );
|
||||
TEST( !objectB->isShared() );
|
||||
|
||||
ref = objectA;
|
||||
TEST( !ref.isTagged() );
|
||||
TEST( ref == objectA );
|
||||
TEST( ref == objectA.ptr() );
|
||||
TEST( objectA->isShared() );
|
||||
|
||||
TEST( ref.trySetFromTo( objectA, objectB, TestObjectRef::TAG_Set ) );
|
||||
TEST( ref.isTagged() );
|
||||
TEST( ref == objectB );
|
||||
TEST( ref == objectB.ptr() );
|
||||
TEST( objectB->isShared() );
|
||||
TEST( !objectA->isShared() );
|
||||
|
||||
TEST( ref.trySetFromTo( ref, objectA ) );
|
||||
TEST( ref.isTagged() );
|
||||
TEST( ref == objectA );
|
||||
TEST( ref == objectA.ptr() );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // !TORQUE_SHIPPING
|
||||
417
Engine/source/platform/test/testThreading.cpp
Normal file
417
Engine/source/platform/test/testThreading.cpp
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/threads/thread.h"
|
||||
#include "platform/threads/semaphore.h"
|
||||
#include "platform/threads/mutex.h"
|
||||
#include "unit/test.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "console/console.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
class ThreadTestHarness
|
||||
{
|
||||
U32 mStartTime, mEndTime, mCleanupTime;
|
||||
void (*mThreadBody)(void*);
|
||||
S32 mThreadCount;
|
||||
Thread **mThreads;
|
||||
|
||||
public:
|
||||
ThreadTestHarness()
|
||||
{
|
||||
mStartTime = mEndTime = mCleanupTime = 0;
|
||||
mThreadBody = NULL;
|
||||
mThreadCount = 1;
|
||||
mThreads = NULL;
|
||||
}
|
||||
|
||||
void startThreads(void (*threadBody)(void*), void *arg, U32 threadCount)
|
||||
{
|
||||
mThreadCount = threadCount;
|
||||
mThreadBody = threadBody;
|
||||
|
||||
// Start up threadCount threads...
|
||||
mThreads = new Thread*[threadCount];
|
||||
|
||||
mStartTime = Platform::getRealMilliseconds();
|
||||
|
||||
//Con::printf(" Running with %d threads...", threadCount);
|
||||
for(S32 i=0; i<mThreadCount; i++)
|
||||
{
|
||||
mThreads[i] = new Thread(threadBody, arg);
|
||||
mThreads[i]->start();
|
||||
}
|
||||
}
|
||||
|
||||
void waitForThreadExit(U32 checkFrequencyMs)
|
||||
{
|
||||
// And wait for them to complete.
|
||||
bool someAlive = true;
|
||||
S32 liveCount = mThreadCount;
|
||||
|
||||
while(someAlive)
|
||||
{
|
||||
//Con::printf(" - Sleeping for %dms with %d live threads.", checkFrequencyMs, liveCount);
|
||||
Platform::sleep(checkFrequencyMs);
|
||||
|
||||
someAlive = false;
|
||||
liveCount = 0;
|
||||
|
||||
for(S32 i=0; i<mThreadCount; i++)
|
||||
{
|
||||
if(!mThreads[i]->isAlive())
|
||||
continue;
|
||||
|
||||
someAlive = true;
|
||||
liveCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mEndTime = Platform::getRealMilliseconds();
|
||||
|
||||
// Clean up memory at this point.
|
||||
for(S32 i=0; i<mThreadCount; i++)
|
||||
delete mThreads[i];
|
||||
delete[] mThreads;
|
||||
|
||||
// Make sure we didn't take a long time to complete.
|
||||
mCleanupTime = Platform::getRealMilliseconds();
|
||||
|
||||
// And dump some stats.
|
||||
Con::printf(" Took approximately %dms (+/- %dms) to run %d threads, and %dms to cleanup.",
|
||||
(mEndTime - mStartTime),
|
||||
checkFrequencyMs,
|
||||
mThreadCount,
|
||||
mCleanupTime - mEndTime);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CreateUnitTest( ThreadSanityCheck, "Platform/Threads/BasicSanity")
|
||||
{
|
||||
const static S32 amountOfWork = 100;
|
||||
const static S32 numberOfThreads = 8;
|
||||
|
||||
static void threadBody(void *)
|
||||
{
|
||||
S32 work = 0x381f4fd3;
|
||||
// Spin on some work, then exit.
|
||||
for(S32 i=0; i<amountOfWork; i++)
|
||||
{
|
||||
// Do a little computation...
|
||||
work ^= (i + work | amountOfWork);
|
||||
|
||||
// And sleep a slightly variable bit.
|
||||
Platform::sleep(10 + ((work+i) % 10));
|
||||
}
|
||||
}
|
||||
|
||||
void runNThreads(S32 threadCount)
|
||||
{
|
||||
ThreadTestHarness tth;
|
||||
|
||||
tth.startThreads(&threadBody, NULL, threadCount);
|
||||
tth.waitForThreadExit(32);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for(S32 i=0; i<numberOfThreads; i++)
|
||||
runNThreads(i);
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( MutexStressTest, "Platform/Threads/MutexStress")
|
||||
{
|
||||
const static S32 numberOfLocks = 100;
|
||||
const static S32 numberOfThreads = 4;
|
||||
|
||||
void *mMutex;
|
||||
|
||||
static void threadBody(void *mutex)
|
||||
{
|
||||
// Acquire the mutex numberOfLocks times. Sleep for 1ms, acquire, sleep, release.
|
||||
S32 lockCount = numberOfLocks;
|
||||
while(lockCount--)
|
||||
{
|
||||
Platform::sleep(1);
|
||||
Mutex::lockMutex(mutex, true);
|
||||
Platform::sleep(1);
|
||||
Mutex::unlockMutex(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void runNThreads(S32 threadCount)
|
||||
{
|
||||
ThreadTestHarness tth;
|
||||
|
||||
mMutex = Mutex::createMutex();
|
||||
|
||||
tth.startThreads(&threadBody, mMutex, threadCount);
|
||||
|
||||
// We fudge the wait period to be about the expected time assuming
|
||||
// perfect execution speed.
|
||||
tth.waitForThreadExit(32); //threadCount * 2 * numberOfLocks + 100);
|
||||
|
||||
Mutex::destroyMutex(mMutex);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for(S32 i=0; i<numberOfThreads; i++)
|
||||
runNThreads(i);
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( MemoryStressTest, "Platform/Threads/MemoryStress")
|
||||
{
|
||||
const static S32 numberOfAllocs = 1000;
|
||||
const static S32 minAllocSize = 13;
|
||||
const static S32 maxAllocSize = 1024 * 1024;
|
||||
const static S32 numberOfThreads = 4;
|
||||
|
||||
void *mMutex;
|
||||
|
||||
// Cheap little RNG so we can vary our allocations more uniquely per thread.
|
||||
static U32 threadRandom(U32 &seed, U32 min, U32 max)
|
||||
{
|
||||
seed = (1664525 * seed + 1013904223);
|
||||
U32 res = seed;
|
||||
res %= (max - min);
|
||||
return res + min;
|
||||
}
|
||||
|
||||
static void threadBody(void *mutex)
|
||||
{
|
||||
// Acquire the mutex numberOfLocks times. Sleep for 1ms, acquire, sleep, release.
|
||||
S32 allocCount = numberOfAllocs;
|
||||
U32 seed = (U32)((U32)mutex + (U32)&allocCount);
|
||||
while(allocCount--)
|
||||
{
|
||||
U8 *mem = new U8[threadRandom(seed, minAllocSize, maxAllocSize)];
|
||||
delete[] mem;
|
||||
}
|
||||
}
|
||||
|
||||
void runNThreads(S32 threadCount)
|
||||
{
|
||||
ThreadTestHarness tth;
|
||||
|
||||
mMutex = Mutex::createMutex();
|
||||
|
||||
tth.startThreads(&threadBody, mMutex, threadCount);
|
||||
|
||||
// We fudge the wait period to be about the expected time assuming
|
||||
// perfect execution speed.
|
||||
tth.waitForThreadExit(32);
|
||||
|
||||
Mutex::destroyMutex(mMutex);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for(S32 i=0; i<numberOfThreads; i++)
|
||||
runNThreads(i);
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( ThreadGymnastics, "Platform/Threads/BasicSynchronization")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
// We test various scenarios wrt to locking and unlocking, in a single
|
||||
// thread, just to make sure our basic primitives are working in the
|
||||
// most basic case.
|
||||
|
||||
void *mutex1 = Mutex::createMutex();
|
||||
test(mutex1, "First Mutex::createMutex call failed - that's pretty bad!");
|
||||
|
||||
void *mutex2 = Mutex::createMutex();
|
||||
test(mutex2, "Second Mutex::createMutex call failed - that's pretty bad, too!");
|
||||
|
||||
test(Mutex::lockMutex(mutex1, false), "Nonblocking call to brand new mutex failed - should not be.");
|
||||
test(Mutex::lockMutex(mutex1, true), "Failed relocking a mutex from the same thread - should be able to do this.");
|
||||
|
||||
// Unlock & kill mutex 1
|
||||
Mutex::unlockMutex(mutex1);
|
||||
Mutex::unlockMutex(mutex1);
|
||||
Mutex::destroyMutex(mutex1);
|
||||
|
||||
// Kill mutex2, which was never touched.
|
||||
Mutex::destroyMutex(mutex2);
|
||||
|
||||
// Now we can test semaphores.
|
||||
Semaphore *sem1 = new Semaphore(1);
|
||||
Semaphore *sem2 = new Semaphore(1);
|
||||
|
||||
// Test that we can do non-blocking acquires that succeed.
|
||||
test(sem1->acquire(false), "Should succeed at acquiring a new semaphore with count 1.");
|
||||
test(sem2->acquire(false), "This one should succeed too, see previous test.");
|
||||
|
||||
// Test that we can do non-blocking acquires that fail.
|
||||
test(sem1->acquire(false)==false, "Should failed, as we've already got the sem.");
|
||||
sem1->release();
|
||||
test(sem2->acquire(false)==false, "Should also fail.");
|
||||
sem2->release();
|
||||
|
||||
// Test that we can do blocking acquires that succeed.
|
||||
test(sem1->acquire(true)==true, "Should succeed as we just released.");
|
||||
test(sem2->acquire(true)==true, "Should succeed as we just released.");
|
||||
|
||||
// Can't test blocking acquires that never happen... :)
|
||||
|
||||
// Clean up.
|
||||
delete sem1;
|
||||
delete sem2;
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( SemaphoreWaitTest, "Platform/Threads/SemaphoreWaitTest")
|
||||
{
|
||||
static void threadBody(void *self)
|
||||
{
|
||||
SemaphoreWaitTest *me = (SemaphoreWaitTest*)self;
|
||||
|
||||
// Wait for the semaphore to get released.
|
||||
me->mSemaphore->acquire();
|
||||
|
||||
// Increment the counter.
|
||||
Mutex::lockMutex(me->mMutex);
|
||||
me->mDoneCount++;
|
||||
Mutex::unlockMutex(me->mMutex);
|
||||
|
||||
// Signal back to the main thread we're done.
|
||||
me->mPostbackSemaphore->release();
|
||||
}
|
||||
|
||||
Semaphore *mSemaphore;
|
||||
Semaphore *mPostbackSemaphore;
|
||||
void *mMutex;
|
||||
U32 mDoneCount;
|
||||
|
||||
const static int csmThreadCount = 10;
|
||||
|
||||
void run()
|
||||
{
|
||||
ThreadTestHarness tth;
|
||||
|
||||
mDoneCount = 0;
|
||||
mSemaphore = new Semaphore(0);
|
||||
mPostbackSemaphore = new Semaphore(0);
|
||||
mMutex = Mutex::createMutex();
|
||||
|
||||
tth.startThreads(&threadBody, this, csmThreadCount);
|
||||
|
||||
Platform::sleep(500);
|
||||
|
||||
Mutex::lockMutex(mMutex);
|
||||
test(mDoneCount == 0, "no threads should have touched the counter yet.");
|
||||
Mutex::unlockMutex(mMutex);
|
||||
|
||||
// Let 500 come out.
|
||||
for(S32 i=0; i<csmThreadCount/2; i++)
|
||||
mSemaphore->release();
|
||||
|
||||
// And wait for 500 postbacks.
|
||||
for(S32 i=0; i<csmThreadCount/2; i++)
|
||||
mPostbackSemaphore->acquire();
|
||||
|
||||
Mutex::lockMutex(mMutex);
|
||||
test(mDoneCount == csmThreadCount / 2, "Didn't get expected number of done threads! (a)");
|
||||
Mutex::unlockMutex(mMutex);
|
||||
|
||||
// Ok, now do the rest.
|
||||
// Let 500 come out.
|
||||
for(S32 i=0; i<csmThreadCount/2; i++)
|
||||
mSemaphore->release();
|
||||
|
||||
// And wait for 500 postbacks.
|
||||
for(S32 i=0; i<csmThreadCount/2; i++)
|
||||
mPostbackSemaphore->acquire();
|
||||
|
||||
Mutex::lockMutex(mMutex);
|
||||
test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (b)");
|
||||
Mutex::unlockMutex(mMutex);
|
||||
|
||||
// Wait for the threads to exit - shouldn't have to wait ever though.
|
||||
tth.waitForThreadExit(10);
|
||||
|
||||
// Make sure no one touched our data after shutdown time.
|
||||
Mutex::lockMutex(mMutex);
|
||||
test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (c)");
|
||||
Mutex::unlockMutex(mMutex);
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest( MutexWaitTest, "Platform/Threads/MutexWaitTest")
|
||||
{
|
||||
static void threadBody(void *self)
|
||||
{
|
||||
MutexWaitTest *me = (MutexWaitTest*)self;
|
||||
|
||||
// Increment the counter. We'll block until the mutex
|
||||
// is open.
|
||||
Mutex::lockMutex(me->mMutex);
|
||||
me->mDoneCount++;
|
||||
Mutex::unlockMutex(me->mMutex);
|
||||
}
|
||||
|
||||
void *mMutex;
|
||||
U32 mDoneCount;
|
||||
|
||||
const static int csmThreadCount = 10;
|
||||
|
||||
void run()
|
||||
{
|
||||
mMutex = Mutex::createMutex();
|
||||
mDoneCount = 0;
|
||||
|
||||
// We lock the mutex before we create any threads, so that all the threads
|
||||
// block on the mutex. Then we unlock it and let them all work their way
|
||||
// through the increment.
|
||||
Mutex::lockMutex(mMutex);
|
||||
|
||||
ThreadTestHarness tth;
|
||||
tth.startThreads(&threadBody, this, csmThreadCount);
|
||||
|
||||
Platform::sleep(5000);
|
||||
|
||||
// Check count is still zero.
|
||||
test(mDoneCount == 0, "Uh oh - a thread somehow didn't get blocked by the locked mutex!");
|
||||
|
||||
// Open the flood gates...
|
||||
Mutex::unlockMutex(mMutex);
|
||||
|
||||
// Wait for the threads to all finish executing.
|
||||
tth.waitForThreadExit(10);
|
||||
|
||||
Mutex::lockMutex(mMutex);
|
||||
test(mDoneCount == csmThreadCount, "Hmm - all threads reported done, but we didn't get the expected count.");
|
||||
Mutex::unlockMutex(mMutex);
|
||||
|
||||
// Kill the mutex.
|
||||
Mutex::destroyMutex(mMutex);
|
||||
}
|
||||
};
|
||||
105
Engine/source/platform/test/testTimeManager.cpp
Normal file
105
Engine/source/platform/test/testTimeManager.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformTimer.h"
|
||||
#include "core/util/journal/journaledSignal.h"
|
||||
#include "core/util/journal/process.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
|
||||
#include "unit/test.h"
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateUnitTest(Check_advanceTime, "Platform/Time/advanceTime")
|
||||
{
|
||||
void run()
|
||||
{
|
||||
U32 time = Platform::getVirtualMilliseconds();
|
||||
Platform::advanceTime(10);
|
||||
U32 newTime = Platform::getVirtualMilliseconds();
|
||||
|
||||
test(newTime - time == 10, "Platform::advanceTime is borked, we advanced 10ms but didn't get a 10ms delta!");
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest(Check_platformSleep, "Platform/Time/Sleep")
|
||||
{
|
||||
const static S32 sleepTimeMs = 500;
|
||||
void run()
|
||||
{
|
||||
U32 start = Platform::getRealMilliseconds();
|
||||
Platform::sleep(sleepTimeMs);
|
||||
U32 end = Platform::getRealMilliseconds();
|
||||
|
||||
test(end - start >= sleepTimeMs, "We didn't sleep at least as long as we requested!");
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest(Check_timeManager, "Platform/Time/Manager")
|
||||
{
|
||||
void handleTimeEvent(S32 timeDelta)
|
||||
{
|
||||
mElapsedTime += timeDelta;
|
||||
mNumberCalls++;
|
||||
|
||||
if(mElapsedTime >= 1000)
|
||||
Process::requestShutdown();
|
||||
}
|
||||
|
||||
S32 mElapsedTime;
|
||||
S32 mNumberCalls;
|
||||
|
||||
void run()
|
||||
{
|
||||
mElapsedTime = mNumberCalls = 0;
|
||||
|
||||
// Initialize the time manager...
|
||||
TimeManager time;
|
||||
time.timeEvent.notify(this, &Check_timeManager::handleTimeEvent);
|
||||
|
||||
// Event loop till at least one second has passed.
|
||||
const U32 start = Platform::getRealMilliseconds();
|
||||
|
||||
while(Process::processEvents())
|
||||
{
|
||||
// If we go too long, kill it off...
|
||||
if(Platform::getRealMilliseconds() - start > 30*1000)
|
||||
{
|
||||
test(false, "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds.");
|
||||
Process::requestShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
const U32 end = Platform::getRealMilliseconds();
|
||||
|
||||
// Now, confirm we have approximately similar elapsed times.
|
||||
S32 elapsedRealTime = end - start;
|
||||
test(mAbs(elapsedRealTime - mElapsedTime) < 50, "Failed to elapse time to within the desired tolerance.");
|
||||
|
||||
test(mNumberCalls > 0, "Somehow got no event callbacks from TimeManager?");
|
||||
|
||||
Con::printf(" Got %d time events, and elapsed %dms from TimeManager, "
|
||||
"%dms according to Platform::getRealMilliseconds()",
|
||||
mNumberCalls, mElapsedTime, elapsedRealTime);
|
||||
}
|
||||
};
|
||||
129
Engine/source/platform/threads/mutex.h
Normal file
129
Engine/source/platform/threads/mutex.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/types.h"
|
||||
#include "platform/platformAssert.h"
|
||||
|
||||
#ifndef _PLATFORM_THREADS_MUTEX_H_
|
||||
#define _PLATFORM_THREADS_MUTEX_H_
|
||||
|
||||
// Forward ref used by platform code
|
||||
struct PlatformMutexData;
|
||||
|
||||
class Mutex
|
||||
{
|
||||
protected:
|
||||
PlatformMutexData *mData;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
virtual ~Mutex();
|
||||
|
||||
virtual bool lock(bool block = true);
|
||||
virtual void unlock();
|
||||
|
||||
// Old API so that we don't have to change a load of code
|
||||
static void *createMutex()
|
||||
{
|
||||
Mutex *mutex = new Mutex;
|
||||
return (void *)mutex;
|
||||
}
|
||||
|
||||
static void destroyMutex(void *mutex)
|
||||
{
|
||||
Mutex *realMutex = reinterpret_cast<Mutex *>(mutex);
|
||||
delete realMutex;
|
||||
}
|
||||
|
||||
static bool lockMutex(void *mutex, bool block = true)
|
||||
{
|
||||
Mutex *realMutex = reinterpret_cast<Mutex *>(mutex);
|
||||
return realMutex->lock(block);
|
||||
}
|
||||
|
||||
static void unlockMutex(void *mutex)
|
||||
{
|
||||
Mutex *realMutex = reinterpret_cast<Mutex *>(mutex);
|
||||
realMutex->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper for simplifying mutex locking code.
|
||||
///
|
||||
/// This class will automatically unlock a mutex that you've
|
||||
/// locked through it, saving you from managing a lot of complex
|
||||
/// exit cases. For instance:
|
||||
///
|
||||
/// @code
|
||||
/// MutexHandle handle;
|
||||
/// handle.lock(myMutex);
|
||||
///
|
||||
/// if(error1)
|
||||
/// return; // Auto-unlocked by handle if we leave here - normally would
|
||||
/// // leave the mutex locked, causing much pain later.
|
||||
///
|
||||
/// handle.unlock();
|
||||
/// @endcode
|
||||
class MutexHandle
|
||||
{
|
||||
private:
|
||||
void *mMutexPtr;
|
||||
|
||||
public:
|
||||
MutexHandle()
|
||||
: mMutexPtr(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~MutexHandle()
|
||||
{
|
||||
if(mMutexPtr)
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool lock(void *mutex, bool blocking=false)
|
||||
{
|
||||
AssertFatal(!mMutexPtr, "MutexHandle::lock - shouldn't be locking things twice!");
|
||||
|
||||
bool ret = Mutex::lockMutex(mutex, blocking);
|
||||
|
||||
if(ret)
|
||||
{
|
||||
// We succeeded, do book-keeping.
|
||||
mMutexPtr = mutex;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
if(mMutexPtr)
|
||||
{
|
||||
Mutex::unlockMutex(mMutexPtr);
|
||||
mMutexPtr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // _PLATFORM_THREADS_MUTEX_H_
|
||||
55
Engine/source/platform/threads/semaphore.h
Normal file
55
Engine/source/platform/threads/semaphore.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_THREAD_SEMAPHORE_H_
|
||||
#define _PLATFORM_THREAD_SEMAPHORE_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
// Forward ref used by platform code
|
||||
class PlatformSemaphore;
|
||||
|
||||
class Semaphore
|
||||
{
|
||||
protected:
|
||||
PlatformSemaphore *mData;
|
||||
|
||||
public:
|
||||
/// Create a semaphore. initialCount defaults to 1.
|
||||
Semaphore(S32 initialCount = 1);
|
||||
/// Delete a semaphore, ignoring it's count.
|
||||
~Semaphore();
|
||||
|
||||
/// Acquire the semaphore, decrementing its count.
|
||||
/// if the initial count is less than 1, block until it goes above 1, then acquire.
|
||||
/// Returns true if the semaphore was acquired, false if the semaphore could
|
||||
/// not be acquired and block was false.
|
||||
bool acquire(bool block = true, S32 timeoutMS = -1);
|
||||
|
||||
/// Release the semaphore, incrementing its count.
|
||||
/// Never blocks.
|
||||
void release();
|
||||
};
|
||||
|
||||
#endif
|
||||
245
Engine/source/platform/threads/thread.h
Normal file
245
Engine/source/platform/threads/thread.h
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLATFORM_THREADS_THREAD_H_
|
||||
#define _PLATFORM_THREADS_THREAD_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _PLATFORM_THREADS_MUTEX_H_
|
||||
#include "platform/threads/mutex.h"
|
||||
#endif
|
||||
#ifndef _TSINGLETON_H_
|
||||
#include "core/util/tSingleton.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Forward ref used by platform code
|
||||
class PlatformThreadData;
|
||||
|
||||
|
||||
// Typedefs
|
||||
typedef void (*ThreadRunFunction)(void *data);
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
typedef void Parent;
|
||||
|
||||
protected:
|
||||
PlatformThreadData* mData;
|
||||
|
||||
/// Used to signal threads need to stop.
|
||||
/// Threads set this flag to false in start()
|
||||
U32 shouldStop;
|
||||
|
||||
/// Set the name of this thread for identification in debuggers.
|
||||
/// Maybe a NOP on platforms that do not support this. Always a NOP
|
||||
/// in non-debug builds.
|
||||
void _setName( const char* name );
|
||||
|
||||
public:
|
||||
/// If set, the thread will delete itself once it has finished running.
|
||||
bool autoDelete;
|
||||
|
||||
/// Create a thread.
|
||||
/// @param func The starting function for the thread.
|
||||
/// @param arg Data to be passed to func, when the thread starts.
|
||||
/// @param start_thread Supported for compatibility. Must be false. Starting threads from
|
||||
/// within the constructor is not allowed anymore as the run() method is virtual.
|
||||
Thread(ThreadRunFunction func = 0, void *arg = 0, bool start_thread = false, bool autodelete = false);
|
||||
|
||||
/// Destroy a thread.
|
||||
/// The thread MUST be allowed to exit before it is destroyed.
|
||||
virtual ~Thread();
|
||||
|
||||
/// Start a thread.
|
||||
/// Sets shouldStop to false and calls run() in a new thread of execution.
|
||||
void start( void* arg = 0 );
|
||||
|
||||
/// Ask a thread to stop running.
|
||||
void stop()
|
||||
{
|
||||
shouldStop = true;
|
||||
}
|
||||
|
||||
/// Block until the thread stops running.
|
||||
/// @note Don't use this in combination with auto-deletion as otherwise the thread will kill
|
||||
/// itself while still executing the join() method on the waiting thread.
|
||||
bool join();
|
||||
|
||||
/// Threads may call checkForStop() periodically to check if they've been
|
||||
/// asked to stop. As soon as checkForStop() returns true, the thread should
|
||||
/// clean up and return.
|
||||
bool checkForStop()
|
||||
{
|
||||
return shouldStop;
|
||||
}
|
||||
|
||||
/// Run the Thread's entry point function.
|
||||
/// Override this method in a subclass of Thread to create threaded code in
|
||||
/// an object oriented way, and without passing a function ptr to Thread().
|
||||
/// Also, you can call this method directly to execute the thread's
|
||||
/// code in a non-threaded way.
|
||||
virtual void run(void *arg = 0);
|
||||
|
||||
/// Returns true if the thread is running.
|
||||
bool isAlive();
|
||||
|
||||
/// Returns the platform specific thread id for this thread.
|
||||
U32 getId();
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
class ThreadManager
|
||||
{
|
||||
Vector<Thread*> threadPool;
|
||||
Mutex poolLock;
|
||||
|
||||
struct MainThreadId
|
||||
{
|
||||
U32 mId;
|
||||
MainThreadId()
|
||||
{
|
||||
mId = ThreadManager::getCurrentThreadId();
|
||||
}
|
||||
U32 get()
|
||||
{
|
||||
// Okay, this is a bit soso. The main thread ID may get queried during
|
||||
// global ctor phase before MainThreadId's ctor ran. Since global
|
||||
// ctors will/should all run on the main thread, we can sort of safely
|
||||
// assume here that we can just query the current thread's ID.
|
||||
|
||||
if( !mId )
|
||||
mId = ThreadManager::getCurrentThreadId();
|
||||
return mId;
|
||||
}
|
||||
};
|
||||
|
||||
static MainThreadId smMainThreadId;
|
||||
|
||||
public:
|
||||
ThreadManager()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( threadPool );
|
||||
}
|
||||
|
||||
/// Return true if the caller is running on the main thread.
|
||||
static bool isMainThread();
|
||||
|
||||
/// Returns true if threadId is the same as the calling thread's id.
|
||||
static bool isCurrentThread(U32 threadId);
|
||||
|
||||
/// Returns true if the 2 thread ids represent the same thread. Some thread
|
||||
/// APIs return an opaque object as a thread id, so the == operator cannot
|
||||
/// reliably compare thread ids.
|
||||
// this comparator is needed by pthreads and ThreadManager.
|
||||
static bool compare(U32 threadId_1, U32 threadId_2);
|
||||
|
||||
/// Returns the platform specific thread id of the calling thread. Some
|
||||
/// platforms do not guarantee that this ID stays the same over the life of
|
||||
/// the thread, so use ThreadManager::compare() to compare thread ids.
|
||||
static U32 getCurrentThreadId();
|
||||
|
||||
/// Returns the platform specific thread id ot the main thread.
|
||||
static U32 getMainThreadId() { return smMainThreadId.get(); }
|
||||
|
||||
/// Each thread should add itself to the thread pool the first time it runs.
|
||||
static void addThread(Thread* thread)
|
||||
{
|
||||
ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance();
|
||||
manager.poolLock.lock();
|
||||
Thread *alreadyAdded = getThreadById(thread->getId());
|
||||
if(!alreadyAdded)
|
||||
manager.threadPool.push_back(thread);
|
||||
manager.poolLock.unlock();
|
||||
}
|
||||
|
||||
static void removeThread(Thread* thread)
|
||||
{
|
||||
ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance();
|
||||
manager.poolLock.lock();
|
||||
|
||||
U32 threadID = thread->getId();
|
||||
for(U32 i = 0;i < manager.threadPool.size();++i)
|
||||
{
|
||||
if( compare( manager.threadPool[i]->getId(), threadID ) )
|
||||
{
|
||||
manager.threadPool.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
manager.poolLock.unlock();
|
||||
}
|
||||
|
||||
/// Searches the pool of known threads for a thread whose id is equivalent to
|
||||
/// the given threadid. Compares thread ids with ThreadManager::compare().
|
||||
static Thread* getThreadById(U32 threadid)
|
||||
{
|
||||
AssertFatal(threadid != 0, "ThreadManager::getThreadById() Searching for a bad thread id.");
|
||||
Thread* ret = NULL;
|
||||
|
||||
ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance();
|
||||
manager.poolLock.lock();
|
||||
Vector<Thread*> &pool = manager.threadPool;
|
||||
for( S32 i = pool.size() - 1; i >= 0; i--)
|
||||
{
|
||||
Thread* p = pool[i];
|
||||
if(compare(p->getId(), threadid))
|
||||
{
|
||||
ret = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
manager.poolLock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Thread* getCurrentThread()
|
||||
{
|
||||
return getThreadById(ThreadManager::getCurrentThreadId());
|
||||
}
|
||||
|
||||
static const char* getSingletonName()
|
||||
{
|
||||
return "ThreadManager";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool ThreadManager::isMainThread()
|
||||
{
|
||||
return compare( ThreadManager::getCurrentThreadId(), smMainThreadId.get() );
|
||||
}
|
||||
|
||||
inline bool ThreadManager::isCurrentThread(U32 threadId)
|
||||
{
|
||||
U32 current = getCurrentThreadId();
|
||||
return compare(current, threadId);
|
||||
}
|
||||
|
||||
#endif // _PLATFORM_THREADS_THREAD_H_
|
||||
479
Engine/source/platform/threads/threadPool.cpp
Normal file
479
Engine/source/platform/threads/threadPool.cpp
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/threads/threadPool.h"
|
||||
#include "platform/threads/thread.h"
|
||||
#include "platform/platformCPUCount.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/util/tSingleton.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// ThreadPool::Context.
|
||||
//=============================================================================
|
||||
|
||||
ThreadPool::Context ThreadPool::Context::smRootContext( "ROOT", NULL, 1.0 );
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ThreadPool::Context::Context( const char* name, ThreadPool::Context* parent, F32 priorityBias )
|
||||
: mName( name ),
|
||||
mParent( parent ),
|
||||
mSibling( 0 ),
|
||||
mChildren( 0 ),
|
||||
mPriorityBias( priorityBias ),
|
||||
mAccumulatedPriorityBias( 0.0 )
|
||||
{
|
||||
if( parent )
|
||||
{
|
||||
mSibling = mParent->mChildren;
|
||||
mParent->mChildren = this;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ThreadPool::Context::~Context()
|
||||
{
|
||||
if( mParent )
|
||||
for( Context* context = mParent->mChildren, *prev = 0; context != 0; prev = context, context = context->mSibling )
|
||||
if( context == this )
|
||||
{
|
||||
if( !prev )
|
||||
mParent->mChildren = this->mSibling;
|
||||
else
|
||||
prev->mSibling = this->mSibling;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ThreadPool::Context* ThreadPool::Context::getChild( const char* name )
|
||||
{
|
||||
for( Context* child = getChildren(); child != 0; child = child->getSibling() )
|
||||
if( dStricmp( child->getName(), name ) == 0 )
|
||||
return child;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
F32 ThreadPool::Context::getAccumulatedPriorityBias()
|
||||
{
|
||||
if( !mAccumulatedPriorityBias )
|
||||
updateAccumulatedPriorityBiases();
|
||||
return mAccumulatedPriorityBias;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::Context::setPriorityBias( F32 value )
|
||||
{
|
||||
mPriorityBias = value;
|
||||
mAccumulatedPriorityBias = 0.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::Context::updateAccumulatedPriorityBiases()
|
||||
{
|
||||
// Update our own priority bias.
|
||||
|
||||
mAccumulatedPriorityBias = mPriorityBias;
|
||||
for( Context* context = getParent(); context != 0; context = context->getParent() )
|
||||
mAccumulatedPriorityBias *= context->getPriorityBias();
|
||||
|
||||
// Update our children.
|
||||
|
||||
for( Context* child = getChildren(); child != 0; child = child->getSibling() )
|
||||
child->updateAccumulatedPriorityBiases();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// ThreadPool::WorkItem.
|
||||
//=============================================================================
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::WorkItem::process()
|
||||
{
|
||||
execute();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
bool ThreadPool::WorkItem::isCancellationRequested()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
bool ThreadPool::WorkItem::cancellationPoint()
|
||||
{
|
||||
if( isCancellationRequested() )
|
||||
{
|
||||
onCancelled();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
F32 ThreadPool::WorkItem::getPriority()
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// ThreadPool::WorkItemWrapper.
|
||||
//=============================================================================
|
||||
|
||||
/// Value wrapper for work items while placed on priority queue.
|
||||
/// Conforms to interface dictated by ThreadSafePriorityQueueWithUpdate.
|
||||
///
|
||||
/// @see ThreadSafePriorityQueueWithUpdate
|
||||
/// @see ThreadPool::WorkItem
|
||||
///
|
||||
struct ThreadPool::WorkItemWrapper : public ThreadSafeRef< WorkItem >
|
||||
{
|
||||
typedef ThreadSafeRef< WorkItem > Parent;
|
||||
|
||||
WorkItemWrapper() {}
|
||||
WorkItemWrapper( WorkItem* item )
|
||||
: Parent( item ) {}
|
||||
|
||||
bool isAlive();
|
||||
F32 getPriority();
|
||||
};
|
||||
|
||||
inline bool ThreadPool::WorkItemWrapper::isAlive()
|
||||
{
|
||||
WorkItem* item = ptr();
|
||||
if( !item )
|
||||
return false;
|
||||
else if( item->isCancellationRequested() )
|
||||
{
|
||||
( *this ) = 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
inline F32 ThreadPool::WorkItemWrapper::getPriority()
|
||||
{
|
||||
WorkItem* item = ptr();
|
||||
AssertFatal( item != 0, "ThreadPool::WorkItemWrapper::getPriority - called on dead item" );
|
||||
|
||||
// Compute a scaled priority value based on the item's context.
|
||||
return ( item->getContext()->getAccumulatedPriorityBias() * item->getPriority() );
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// ThreadPool::WorkerThread.
|
||||
//=============================================================================
|
||||
|
||||
///
|
||||
///
|
||||
struct ThreadPool::WorkerThread : public Thread
|
||||
{
|
||||
WorkerThread( ThreadPool* pool, U32 index );
|
||||
|
||||
WorkerThread* getNext();
|
||||
virtual void run( void* arg = 0 );
|
||||
|
||||
private:
|
||||
U32 mIndex;
|
||||
ThreadPool* mPool;
|
||||
WorkerThread* mNext;
|
||||
};
|
||||
|
||||
ThreadPool::WorkerThread::WorkerThread( ThreadPool* pool, U32 index )
|
||||
: mPool( pool ),
|
||||
mIndex( index )
|
||||
{
|
||||
// Link us to the pool's thread list.
|
||||
|
||||
mNext = pool->mThreads;
|
||||
pool->mThreads = this;
|
||||
}
|
||||
|
||||
inline ThreadPool::WorkerThread* ThreadPool::WorkerThread::getNext()
|
||||
{
|
||||
return mNext;
|
||||
}
|
||||
|
||||
void ThreadPool::WorkerThread::run( void* arg )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
{
|
||||
// Set the thread's name for debugging.
|
||||
char buffer[ 2048 ];
|
||||
dSprintf( buffer, sizeof( buffer ), "ThreadPool(%s) WorkerThread %i", mPool->mName.c_str(), mIndex );
|
||||
_setName( buffer );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
// On Xbox 360 you must explicitly assign software threads to hardware threads.
|
||||
|
||||
// This will distribute job threads across the secondary CPUs leaving both
|
||||
// primary CPU cores available to the "main" thread. This will help prevent
|
||||
// more L2 thrashing of the main thread/core.
|
||||
static U32 sCoreAssignment = 2;
|
||||
XSetThreadProcessor( GetCurrentThread(), sCoreAssignment );
|
||||
sCoreAssignment = sCoreAssignment < 6 ? sCoreAssignment + 1 : 2;
|
||||
#endif
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if( checkForStop() )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' exits", getId() );
|
||||
#endif
|
||||
dFetchAndAdd( mPool->mNumThreads, ( U32 ) -1 );
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark us as potentially blocking.
|
||||
dFetchAndAdd( mPool->mNumThreadsReady, ( U32 ) -1 );
|
||||
|
||||
bool waitForSignal = false;
|
||||
{
|
||||
// Try to take an item from the queue. Do
|
||||
// this in a separate block, so we'll be
|
||||
// releasing the item after we have finished.
|
||||
|
||||
WorkItemWrapper workItem;
|
||||
if( mPool->mWorkItemQueue.takeNext( workItem ) )
|
||||
{
|
||||
// Mark us as non-blocking as this loop definitely
|
||||
// won't wait on the semaphore.
|
||||
dFetchAndAdd( mPool->mNumThreadsReady, 1 );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' takes item '0x%x'", getId(), *workItem );
|
||||
#endif
|
||||
workItem->process();
|
||||
}
|
||||
else
|
||||
waitForSignal = true;
|
||||
}
|
||||
|
||||
if( waitForSignal )
|
||||
{
|
||||
dFetchAndAdd( mPool->mNumThreadsAwake, ( U32 ) -1 );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' going to sleep", getId() );
|
||||
#endif
|
||||
mPool->mSemaphore.acquire();
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' waking up", getId() );
|
||||
#endif
|
||||
|
||||
dFetchAndAdd( mPool->mNumThreadsAwake, 1 );
|
||||
dFetchAndAdd( mPool->mNumThreadsReady, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// ThreadPool.
|
||||
//=============================================================================
|
||||
|
||||
bool ThreadPool::smForceAllMainThread;
|
||||
U32 ThreadPool::smMainThreadTimeMS;
|
||||
ThreadPool::QueueType ThreadPool::smMainThreadQueue;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ThreadPool::ThreadPool( const char* name, U32 numThreads )
|
||||
: mName( name ),
|
||||
mNumThreads( numThreads ),
|
||||
mNumThreadsAwake( 0 ),
|
||||
mThreads( 0 ),
|
||||
mSemaphore( 0 )
|
||||
{
|
||||
// Number of worker threads to create.
|
||||
|
||||
if( !mNumThreads )
|
||||
{
|
||||
// Use platformCPUInfo directly as in the case of the global pool,
|
||||
// Platform::SystemInfo will not yet have been initialized.
|
||||
|
||||
U32 numLogical;
|
||||
U32 numPhysical;
|
||||
U32 numCores;
|
||||
|
||||
CPUInfo::CPUCount( numLogical, numCores, numPhysical );
|
||||
|
||||
const U32 baseCount = getMax( numLogical, numCores );
|
||||
if( baseCount )
|
||||
mNumThreads = baseCount;
|
||||
else
|
||||
mNumThreads = 2;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[ThreadPool] spawning %i threads", mNumThreads );
|
||||
#endif
|
||||
|
||||
// Create the threads.
|
||||
|
||||
mNumThreadsAwake = mNumThreads;
|
||||
mNumThreadsReady = mNumThreads;
|
||||
for( U32 i = 0; i < mNumThreads; i ++ )
|
||||
{
|
||||
WorkerThread* thread = new WorkerThread( this, i );
|
||||
thread->start();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::shutdown()
|
||||
{
|
||||
const U32 numThreads = mNumThreads;
|
||||
|
||||
// Tell our worker threads to stop.
|
||||
|
||||
for( WorkerThread* thread = mThreads; thread != 0; thread = thread->getNext() )
|
||||
thread->stop();
|
||||
|
||||
// Release the semaphore as many times as there are threads.
|
||||
// Doing this separately guarantees we're not waking a thread
|
||||
// that hasn't been set its stop flag yet.
|
||||
|
||||
for( U32 n = 0; n < numThreads; ++ n )
|
||||
mSemaphore.release();
|
||||
|
||||
// Delete each worker thread. Wait until death as we're prone to
|
||||
// running into issues with decomposing work item lists otherwise.
|
||||
|
||||
for( WorkerThread* thread = mThreads; thread != 0; )
|
||||
{
|
||||
WorkerThread* next = thread->getNext();
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = next;
|
||||
}
|
||||
|
||||
mThreads = NULL;
|
||||
mNumThreads = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::queueWorkItem( WorkItem* item )
|
||||
{
|
||||
bool executeRightAway = ( getForceAllMainThread() );
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[ThreadPool] %s work item '0x%x'",
|
||||
( executeRightAway ? "executing" : "queuing" ),
|
||||
item );
|
||||
#endif
|
||||
|
||||
if( executeRightAway )
|
||||
item->process();
|
||||
else
|
||||
{
|
||||
// Put the item in the queue.
|
||||
|
||||
mWorkItemQueue.insert( item->getPriority(), item );
|
||||
|
||||
// Wake up some thread, if we need to.
|
||||
// Use the ready count here as the wake count does
|
||||
// not correctly protect the critical section in the
|
||||
// thread's run function. This may lead us to release
|
||||
// the semaphore more often than necessary, but it avoids
|
||||
// a race condition.
|
||||
|
||||
if( !dCompareAndSwap( mNumThreadsReady, mNumThreads, mNumThreads ) )
|
||||
mSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::flushWorkItems( S32 timeOut )
|
||||
{
|
||||
AssertFatal( mNumThreads, "ThreadPool::flushWorkItems() - no worker threads in pool" );
|
||||
|
||||
U32 endTime = 0;
|
||||
if( timeOut != -1 )
|
||||
endTime = Platform::getRealMilliseconds() + timeOut;
|
||||
|
||||
// Spinlock until the queue is empty.
|
||||
|
||||
while( !mWorkItemQueue.isEmpty() )
|
||||
{
|
||||
Platform::sleep( 25 );
|
||||
|
||||
// Stop if we have exceeded our processing time budget.
|
||||
|
||||
if( timeOut != -1
|
||||
&& Platform::getRealMilliseconds() >= endTime )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::queueWorkItemOnMainThread( WorkItem* item )
|
||||
{
|
||||
smMainThreadQueue.insert( item->getPriority(), item );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void ThreadPool::processMainThreadWorkItems()
|
||||
{
|
||||
AssertFatal( ThreadManager::isMainThread(),
|
||||
"ThreadPool::processMainThreadWorkItems - this function must only be called on the main thread" );
|
||||
|
||||
U32 timeLimit = ( Platform::getRealMilliseconds() + getMainThreadThresholdTimeMS() );
|
||||
|
||||
do
|
||||
{
|
||||
WorkItemWrapper item;
|
||||
if( !smMainThreadQueue.takeNext( item ) )
|
||||
break;
|
||||
else
|
||||
item->process();
|
||||
}
|
||||
while( Platform::getRealMilliseconds() < timeLimit );
|
||||
}
|
||||
398
Engine/source/platform/threads/threadPool.h
Normal file
398
Engine/source/platform/threads/threadPool.h
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _THREADPOOL_H_
|
||||
#define _THREADPOOL_H_
|
||||
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
#include "platform/threads/threadSafeRefCount.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEPRIORITYQUEUE_H_
|
||||
#include "platform/threads/threadSafePriorityQueue.h"
|
||||
#endif
|
||||
#ifndef _PLATFORM_THREAD_SEMAPHORE_H_
|
||||
#include "platform/threads/semaphore.h"
|
||||
#endif
|
||||
#ifndef _TSINGLETON_H_
|
||||
#include "core/util/tSingleton.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// @file
|
||||
/// Interface for an asynchronous work manager.
|
||||
|
||||
|
||||
/// Asynchronous work manager.
|
||||
///
|
||||
/// Thread pooling allows to submit work items for background execution.
|
||||
/// Each work item will be placed on a queue and, based on a total priority
|
||||
/// ordering, executed when it has the highest priority and a worker thread
|
||||
/// becomes available.
|
||||
///
|
||||
/// @note The global pool maintains the invariant that only the main thread
|
||||
/// may submit items in order to be able to flush the item queue reliably
|
||||
/// from the main thread itself. If other threads were issuing items to
|
||||
/// the queue, the queue may never empty out and the main thread will
|
||||
/// deadlock.
|
||||
///
|
||||
/// Flushing is the simplest method to guarantee that no asynchronous
|
||||
/// operation is pending in a specific case (deletion of the target object
|
||||
/// being the most common case). However, when possible, avoid this
|
||||
/// situation and design your work items to operate independently,
|
||||
/// e.g. by having only a single point of access to data that may have
|
||||
/// disappeared in the meantime and putting a check around that single
|
||||
/// access so that the item will silently die when its target object has
|
||||
/// disappeared.
|
||||
///
|
||||
/// The cleanest safe solution to this is to create a separate concurrently
|
||||
/// reference-counted structure that holds all interfacing state and
|
||||
/// functionality shared between a work item and its issueing code. This way
|
||||
/// the host object can safely disappear with the interfacing structure
|
||||
/// automatically being released once the last concurrent work item has been
|
||||
/// processed or discarded.
|
||||
///
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
|
||||
/// A ThreadPool context defines a logical context in which WorkItems are
|
||||
/// being executed. Their primary use is for biasing priorities of
|
||||
/// WorkItems.
|
||||
///
|
||||
/// Contexts are arranged in a tree hierarchy. Each parent node's priority
|
||||
/// bias scales all the priority biases underneath it.
|
||||
///
|
||||
/// Note that instances of this class are meant to be instantiated
|
||||
/// globally only.
|
||||
///
|
||||
class Context
|
||||
{
|
||||
protected:
|
||||
|
||||
/// Superordinate context; scales this context's priority bias.
|
||||
Context* mParent;
|
||||
|
||||
/// First child.
|
||||
Context* mChildren;
|
||||
|
||||
/// Next sibling in child chain.
|
||||
Context* mSibling;
|
||||
|
||||
/// Name of this context. Should be unique in parent namespace.
|
||||
const char* mName;
|
||||
|
||||
/// Priority scale factor of this context.
|
||||
F32 mPriorityBias;
|
||||
|
||||
/// Accumulated scale factor.
|
||||
F32 mAccumulatedPriorityBias;
|
||||
|
||||
/// The root context; does not modify priorities. All contexts should be direct or indirect children of this one.
|
||||
static Context smRootContext;
|
||||
|
||||
/// Recursively update cached accumulated priority biases.
|
||||
void updateAccumulatedPriorityBiases();
|
||||
|
||||
public:
|
||||
|
||||
Context( const char* name, Context* parent, F32 priorityBias );
|
||||
~Context();
|
||||
|
||||
/// Return the name of the worker threading context.
|
||||
const char* getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/// Return the context's own work item priority bias.
|
||||
F32 getPriorityBias() const
|
||||
{
|
||||
return mPriorityBias;
|
||||
}
|
||||
|
||||
/// Return the superordinate node to the current context.
|
||||
Context* getParent() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/// Return the next sibling to the current context.
|
||||
Context* getSibling() const
|
||||
{
|
||||
return mSibling;
|
||||
}
|
||||
|
||||
/// Return the first child context.
|
||||
Context* getChildren() const
|
||||
{
|
||||
return mChildren;
|
||||
}
|
||||
|
||||
/// Return the root context.
|
||||
static Context* ROOT_CONTEXT()
|
||||
{
|
||||
return &smRootContext;
|
||||
}
|
||||
|
||||
///
|
||||
F32 getAccumulatedPriorityBias();
|
||||
|
||||
///
|
||||
Context* getChild( const char* name );
|
||||
|
||||
///
|
||||
void setPriorityBias( F32 value );
|
||||
};
|
||||
|
||||
/// An action to execute on a worker thread from the pool.
|
||||
///
|
||||
/// Work items are concurrently reference-counted and will be
|
||||
/// automatically released once the last reference disappears.
|
||||
///
|
||||
class WorkItem : public ThreadSafeRefCount< WorkItem >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ThreadSafeRefCount< WorkItem > Parent;
|
||||
|
||||
protected:
|
||||
|
||||
/// The work context of this item.
|
||||
Context* mContext;
|
||||
|
||||
/// Mark a point in a work item's execution where the item can
|
||||
/// be safely cancelled.
|
||||
///
|
||||
/// This method should be called by subclasses' execute() methods
|
||||
/// whenever an item can be safely cancelled. When it returns true,
|
||||
/// the work item should exit from its execute() method.
|
||||
bool cancellationPoint();
|
||||
|
||||
/// Called when the item has been cancelled.
|
||||
virtual void onCancelled() {}
|
||||
|
||||
/// Execute the actions associated with this work item.
|
||||
/// This is the primary function to implement by subclasses.
|
||||
virtual void execute() = 0;
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new work item.
|
||||
///
|
||||
/// @param context The work context in which the item should be placed.
|
||||
/// If NULL, the root context will be used.
|
||||
WorkItem( Context* context = 0 )
|
||||
: mContext( context ? context : Context::ROOT_CONTEXT() )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~WorkItem() {}
|
||||
|
||||
/// Return the work context associated with the work item.
|
||||
inline Context* getContext() const
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
/// Process the work item.
|
||||
void process();
|
||||
|
||||
/// Return true if the work item should be cancelled.
|
||||
///
|
||||
/// This method can be overridden by subclasses. It's value will be
|
||||
/// checked each time cancellationPoint() is called. When it returns
|
||||
/// true, the item's process() method will exit automatically.
|
||||
///
|
||||
/// @return true, if item should be cancelled; default is false.
|
||||
/// @see ThreadPool::WorkItem::cancellationPoint
|
||||
virtual bool isCancellationRequested();
|
||||
|
||||
/// Return the item's base priority value.
|
||||
/// @return item priority; defaults to 1.0.
|
||||
virtual F32 getPriority();
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< WorkItem > WorkItemPtr;
|
||||
struct GlobalThreadPool;
|
||||
|
||||
protected:
|
||||
|
||||
struct WorkItemWrapper;
|
||||
struct WorkerThread;
|
||||
|
||||
friend struct WorkerThread; // mSemaphore, mNumThreadsAwake, mThreads
|
||||
|
||||
typedef ThreadSafePriorityQueueWithUpdate< WorkItemWrapper, F32 > QueueType;
|
||||
|
||||
/// Name of this pool. Mainly for debugging. Used to name worker threads.
|
||||
String mName;
|
||||
|
||||
/// Number of worker threads spawned by the pool.
|
||||
U32 mNumThreads;
|
||||
|
||||
/// Number of worker threads in non-sleeping state.
|
||||
U32 mNumThreadsAwake;
|
||||
|
||||
/// Number of worker threads guaranteed to be non-blocking.
|
||||
U32 mNumThreadsReady;
|
||||
|
||||
/// Semaphore used to wake up threads, if necessary.
|
||||
Semaphore mSemaphore;
|
||||
|
||||
/// Threaded priority queue for concurrent access by worker threads.
|
||||
QueueType mWorkItemQueue;
|
||||
|
||||
/// List of worker threads.
|
||||
WorkerThread* mThreads;
|
||||
|
||||
/// Force all work items to execute on main thread;
|
||||
/// turns this into a single-threaded system.
|
||||
/// Primarily useful to find whether malfunctions are caused
|
||||
/// by parallel execution or not.
|
||||
static bool smForceAllMainThread;
|
||||
|
||||
///
|
||||
static U32 smMainThreadTimeMS;
|
||||
|
||||
/// Work queue for main thread; can be used to ping back work items to
|
||||
/// main thread that need processing that can only happen on main thread.
|
||||
static QueueType smMainThreadQueue;
|
||||
|
||||
public:
|
||||
|
||||
/// Create a new thread pool with the given number of worker threads.
|
||||
///
|
||||
/// If numThreads is zero (the default), the number of threads created
|
||||
/// will be based on the number of CPU cores available.
|
||||
///
|
||||
/// @param numThreads Number of threads to create or zero for default.
|
||||
ThreadPool( const char* name, U32 numThreads = 0 );
|
||||
|
||||
~ThreadPool();
|
||||
|
||||
/// Manually shutdown threads outside of static destructors.
|
||||
void shutdown();
|
||||
|
||||
///
|
||||
void queueWorkItem( WorkItem* item );
|
||||
|
||||
///
|
||||
/// <em>For the global pool, it is very important to only ever call
|
||||
/// this function on the main thread and to let work items only ever
|
||||
/// come from the main thread. Otherwise this function has the potential
|
||||
/// of dead-locking as new work items may constantly be fed to the queue
|
||||
/// without it ever getting empty.</em>
|
||||
///
|
||||
/// @param timeOut Soft limit on the number of milliseconds to wait for
|
||||
/// the queue to flush out. -1 = infinite.
|
||||
void flushWorkItems( S32 timeOut = -1 );
|
||||
|
||||
/// Add a work item to the main thread's work queue.
|
||||
///
|
||||
/// The main thread's work queue will be processed each frame using
|
||||
/// a set timeout to limit the work being done. Nonetheless, work
|
||||
/// items will not be suspended in-midst of processing, so make sure
|
||||
/// that whatever work you issue to the main thread is light work
|
||||
/// or you may see short hangs in gameplay.
|
||||
///
|
||||
/// To reiterate this: any code executed through this interface directly
|
||||
/// adds to frame processing time on the main thread.
|
||||
///
|
||||
/// This method *may* (and is meant to) be called from threads
|
||||
/// other than the main thread.
|
||||
static void queueWorkItemOnMainThread( WorkItem* item );
|
||||
|
||||
/// Process work items waiting on the main thread's work queue.
|
||||
///
|
||||
/// There is a soft limit imposed on the time this method is allowed
|
||||
/// to run so as to balance frame-to-frame load. However, work
|
||||
/// items, once their processing is initiated, will not be suspended
|
||||
/// and will run for as long as they take to complete, so make sure
|
||||
/// individual items perform as little work as necessary.
|
||||
///
|
||||
/// @see ThreadPool::getMainThreadThesholdTimeMS
|
||||
static void processMainThreadWorkItems();
|
||||
|
||||
/// Return the interval in which item priorities are updated on the queue.
|
||||
/// @return update interval in milliseconds.
|
||||
U32 getQueueUpdateInterval() const
|
||||
{
|
||||
return mWorkItemQueue.getUpdateInterval();
|
||||
}
|
||||
|
||||
/// Return the priority increment applied to work items on each passing of the update interval.
|
||||
F32 getQueueTimeBasedPriorityBoost() const
|
||||
{
|
||||
return mWorkItemQueue.getTimeBasedPriorityBoost();
|
||||
}
|
||||
|
||||
/// Set the update interval of the work item queue to the given value.
|
||||
/// @param milliSeconds Time between updates in milliseconds.
|
||||
void setQueueUpdateInterval( U32 milliSeconds )
|
||||
{
|
||||
mWorkItemQueue.setUpdateInterval( milliSeconds );
|
||||
}
|
||||
|
||||
/// Set the priority increment applied to work items on each update interval.
|
||||
/// @param value Priority increment. Set to zero to deactivate.
|
||||
void setQueueTimeBasedPriorityBoost( F32 value )
|
||||
{
|
||||
mWorkItemQueue.setTimeBasedPriorityBoost( value );
|
||||
}
|
||||
|
||||
///
|
||||
static U32& getMainThreadThresholdTimeMS()
|
||||
{
|
||||
return smMainThreadTimeMS;
|
||||
}
|
||||
|
||||
///
|
||||
static bool& getForceAllMainThread()
|
||||
{
|
||||
return smForceAllMainThread;
|
||||
}
|
||||
|
||||
/// Return the global thread pool singleton.
|
||||
static ThreadPool& GLOBAL();
|
||||
};
|
||||
|
||||
typedef ThreadPool::Context ThreadContext;
|
||||
typedef ThreadPool::WorkItem ThreadWorkItem;
|
||||
|
||||
|
||||
struct ThreadPool::GlobalThreadPool : public ThreadPool, public ManagedSingleton< GlobalThreadPool >
|
||||
{
|
||||
typedef ThreadPool Parent;
|
||||
|
||||
GlobalThreadPool()
|
||||
: Parent( "GLOBAL" ) {}
|
||||
|
||||
// For ManagedSingleton.
|
||||
static const char* getSingletonName() { return "GlobalThreadPool"; }
|
||||
};
|
||||
|
||||
inline ThreadPool& ThreadPool::GLOBAL()
|
||||
{
|
||||
return *( GlobalThreadPool::instance() );
|
||||
}
|
||||
|
||||
#endif // !_THREADPOOL_H_
|
||||
357
Engine/source/platform/threads/threadPoolAsyncIO.h
Normal file
357
Engine/source/platform/threads/threadPoolAsyncIO.h
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _THREADPOOLASYNCIO_H_
|
||||
#define _THREADPOOLASYNCIO_H_
|
||||
|
||||
#ifndef _THREADPOOL_H_
|
||||
# include "platform/threads/threadPool.h"
|
||||
#endif
|
||||
#ifndef _RAWDATA_H_
|
||||
# include "core/util/rawData.h"
|
||||
#endif
|
||||
#ifndef _TSTREAM_H_
|
||||
# include "core/stream/tStream.h"
|
||||
#endif
|
||||
|
||||
|
||||
//RDTODO: I/O error handling
|
||||
|
||||
/// @file
|
||||
/// Thread pool work items for asynchronous stream I/O.
|
||||
/// Through the use of stream filters, this can be basically used for any
|
||||
/// type of asynchronous stream processing.
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// AsyncIOItem.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Abstract superclass of async I/O work items.
|
||||
///
|
||||
/// Supports both offset-based stream I/O as well as I/O on streams with
|
||||
/// implicit positions. Note that if you use the latter type, make sure
|
||||
/// that no other thread is messing with the stream at the same time or
|
||||
/// chaos will ensue.
|
||||
///
|
||||
/// @param T Type of elements being streamed.
|
||||
template< typename T, class Stream >
|
||||
class AsyncIOItem : public ThreadPool::WorkItem
|
||||
{
|
||||
public:
|
||||
|
||||
typedef WorkItem Parent;
|
||||
typedef T ValueType;
|
||||
typedef RawDataT< ValueType > BufferType;
|
||||
typedef U32 OffsetType;
|
||||
typedef Stream StreamType;
|
||||
|
||||
protected:
|
||||
|
||||
/// Buffer keeping/receiving the data elements.
|
||||
BufferType mBuffer;
|
||||
|
||||
/// The stream to read from/write to.
|
||||
StreamType* mStream;
|
||||
|
||||
/// Number of elements to read from/write to the stream.
|
||||
U32 mNumElements;
|
||||
|
||||
/// Offset in "mBuffer" from where to read/where to start writing to.
|
||||
U32 mOffsetInBuffer;
|
||||
|
||||
/// Offset in stream from where to read/where to write to.
|
||||
/// @note This is only meaningful if the stream is an offset I/O
|
||||
/// stream. For a stream that is can do both types of I/O,
|
||||
/// explicit offsets are preferred and this value is used.
|
||||
OffsetType mOffsetInStream;
|
||||
|
||||
///
|
||||
ValueType* getBufferPtr()
|
||||
{
|
||||
return &getBuffer().data[ getOffsetInBuffer() ];
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
/// If the stream uses implicit positioning, then the supplied "offsetInStream"
|
||||
/// is meaningless and ignored.
|
||||
AsyncIOItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
|
||||
ThreadContext* context = 0 )
|
||||
: Parent( context ),
|
||||
mStream( stream ),
|
||||
mNumElements( numElements ),
|
||||
mOffsetInStream( offsetInStream ),
|
||||
mOffsetInBuffer( 0 ) {}
|
||||
|
||||
/// Construct a read item on "stream" that stores data into the given "buffer".
|
||||
///
|
||||
AsyncIOItem( StreamType* stream, BufferType& buffer, U32 offsetInBuffer,
|
||||
U32 numElements, OffsetType offsetInStream, bool takeOwnershipOfBuffer = true,
|
||||
ThreadContext* context = 0 )
|
||||
: Parent( context ),
|
||||
mStream( stream ),
|
||||
mBuffer( buffer ),
|
||||
mNumElements( numElements ),
|
||||
mOffsetInStream( offsetInStream ),
|
||||
mOffsetInBuffer( offsetInBuffer )
|
||||
{
|
||||
if( takeOwnershipOfBuffer )
|
||||
mBuffer.ownMemory = true;
|
||||
}
|
||||
|
||||
/// Return the stream being written to/read from.
|
||||
StreamType* getStream()
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
|
||||
/// Return the data buffer being written to/read from.
|
||||
/// @note This may not yet have been allocated.
|
||||
BufferType& getBuffer()
|
||||
{
|
||||
return mBuffer;
|
||||
|
||||
}
|
||||
|
||||
/// Return the number of elements involved in the transfer.
|
||||
U32 getNumElements()
|
||||
{
|
||||
return mNumElements;
|
||||
}
|
||||
|
||||
/// Return the position in the data buffer at which to start the transfer.
|
||||
U32 getOffsetInBuffer()
|
||||
{
|
||||
return mOffsetInBuffer;
|
||||
}
|
||||
|
||||
/// Return the position in the stream at which to start the transfer.
|
||||
/// @note Only meaningful for streams that support offset I/O.
|
||||
OffsetType getOffsetInStream()
|
||||
{
|
||||
return mOffsetInStream;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// AsyncReadItem.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//RDTODO: error handling
|
||||
/// Work item to asynchronously read from a stream.
|
||||
///
|
||||
/// The given stream type may implement any of the input stream
|
||||
/// interfaces. Preference is given to IAsyncInputStream, then to
|
||||
/// IOffsetInputStream, and only if none of these are implemented
|
||||
/// IInputStream is used.
|
||||
///
|
||||
/// For IAsyncInputStreams, the async read operation is issued immediately
|
||||
/// on the constructing thread and then picked up on the worker thread.
|
||||
/// This ensures optimal use of concurrency.
|
||||
|
||||
template< typename T, class Stream = IOffsetInputStream< T > >
|
||||
class AsyncReadItem : public AsyncIOItem< T, Stream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncIOItem< T, Stream > Parent;
|
||||
typedef typename Parent::StreamType StreamType;
|
||||
typedef typename Parent::OffsetType OffsetType;
|
||||
typedef typename Parent::BufferType BufferType;
|
||||
typedef typename Parent::ValueType ValueType;
|
||||
|
||||
/// Construct a read item that reads "numElements" at "offsetInStream"
|
||||
/// from "stream".
|
||||
///
|
||||
/// Since with this constructor no data buffer is supplied, it will be
|
||||
/// dynamically allocated by the read() method. Note that this only makes
|
||||
/// sense if this class is subclassed and processing is done on the buffer
|
||||
/// after it has been read.
|
||||
///
|
||||
/// @param stream The stream to read from.
|
||||
/// @param numElement The number of elements to read from the stream.
|
||||
/// @param offsetInStream The offset at which to read from the stream;
|
||||
/// ignored if the stream uses implicit positioning
|
||||
/// @param context The tread pool context to place the item into.
|
||||
AsyncReadItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
|
||||
ThreadContext* context = 0 )
|
||||
: Parent( stream, numElements, offsetInStream, context )
|
||||
{
|
||||
_prep();
|
||||
}
|
||||
|
||||
AsyncReadItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
|
||||
BufferType& buffer, bool takeOwnershipOfBuffer = false,
|
||||
U32 offsetInBuffer = 0, ThreadContext* context = 0 )
|
||||
: Parent( stream, buffer, offsetInBuffer, numElements, offsetInStream, takeOwnershipOfBuffer, context )
|
||||
{
|
||||
_prep();
|
||||
}
|
||||
|
||||
/// @return The number of elements actually read from the stream.
|
||||
U32 getNumElementsRead()
|
||||
{
|
||||
return mNumElementsRead;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// Handle of asynchronous stream read, if we are using an async interface.
|
||||
void* mAsyncHandle;
|
||||
|
||||
/// After the read operation has completed, this holds the number of
|
||||
/// elements actually read from the stream.
|
||||
U32 mNumElementsRead;
|
||||
|
||||
virtual void execute();
|
||||
|
||||
void _allocBuffer()
|
||||
{
|
||||
if( !this->getBuffer().data )
|
||||
this->getBuffer().alloc( this->getNumElements() );
|
||||
}
|
||||
|
||||
void _prep()
|
||||
{
|
||||
IAsyncInputStream< T >* s = dynamic_cast< IAsyncInputStream< T >* >( this->getStream() );
|
||||
if( s )
|
||||
{
|
||||
_allocBuffer();
|
||||
mAsyncHandle = s->issueReadAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() );
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions to differentiate between stream types.
|
||||
|
||||
void _read( IInputStream< T >* stream )
|
||||
{
|
||||
mNumElementsRead = stream->read( this->getBufferPtr(), this->getNumElements() );
|
||||
}
|
||||
void _read( IOffsetInputStream< T >* stream )
|
||||
{
|
||||
mNumElementsRead = stream->readAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() );
|
||||
}
|
||||
void _read( IAsyncInputStream< T >* stream )
|
||||
{
|
||||
stream->tryCompleteReadAt( mAsyncHandle, mNumElementsRead, true );
|
||||
}
|
||||
};
|
||||
|
||||
template< typename T, class Stream >
|
||||
void AsyncReadItem< T, Stream >::execute()
|
||||
{
|
||||
_allocBuffer();
|
||||
|
||||
// Read the data. Do a dynamic cast for any of the
|
||||
// interfaces we prefer.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
StreamType* stream = this->getStream();
|
||||
if( dynamic_cast< IAsyncInputStream< T >* >( stream ) )
|
||||
_read( ( IAsyncInputStream< T >* ) stream );
|
||||
else if( dynamic_cast< IOffsetInputStream< T >* >( stream ) )
|
||||
_read( ( IOffsetInputStream< T >* ) stream );
|
||||
else
|
||||
_read( stream );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// AsyncWriteItem.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Work item for writing to an output stream.
|
||||
///
|
||||
/// The stream being written to may implement any of the given output stream
|
||||
/// interfaces. Preference is given to IAsyncOutputStream, then to
|
||||
/// IOffsetOutputStream, and only if none of these is implemented IOutputStream
|
||||
/// is used.
|
||||
///
|
||||
/// A useful feature is to yield ownership of the data buffer to the
|
||||
/// write item. This way, this can be pretty much used in a fire-and-forget
|
||||
/// manner where after submission, no further synchronization happens
|
||||
/// between the client and the work item.
|
||||
///
|
||||
/// @note Be aware that if writing to an output stream that has an implicit
|
||||
/// position property, multiple concurrent writes will interfere with each other.
|
||||
template< typename T, class Stream = IOffsetOutputStream< T > >
|
||||
class AsyncWriteItem : public AsyncIOItem< T, Stream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncIOItem< T, Stream > Parent;
|
||||
typedef typename Parent::StreamType StreamType;
|
||||
typedef typename Parent::OffsetType OffsetType;
|
||||
typedef typename Parent::BufferType BufferType;
|
||||
typedef typename Parent::ValueType ValueType;
|
||||
|
||||
AsyncWriteItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
|
||||
BufferType& buffer, bool takeOwnershipOfBuffer = true,
|
||||
U32 offsetInBuffer = 0, ThreadContext* context = 0 )
|
||||
: Parent( stream, buffer, offsetInBuffer, numElements, offsetInStream, takeOwnershipOfBuffer, context )
|
||||
{
|
||||
_prep( stream );
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// Handle of asynchronous write operation, if the stream implements IAsyncOutputStream.
|
||||
void* mAsyncHandle;
|
||||
|
||||
virtual void execute();
|
||||
|
||||
void _prep( StreamType* stream )
|
||||
{
|
||||
IAsyncOutputStream< T >* s = dynamic_cast< IAsyncOutputStream< T >* >( stream );
|
||||
if( s )
|
||||
mAsyncHandle = s->issueWriteAt( this->getOffset(), this->getBufferPtr(), this->getNumElements() );
|
||||
}
|
||||
|
||||
void _write( IOutputStream< T >* stream )
|
||||
{
|
||||
stream->write( this->getBufferPtr(), this->getNumElements() );
|
||||
}
|
||||
void _write( IOffsetOutputStream< T >* stream )
|
||||
{
|
||||
stream->writeAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() );
|
||||
}
|
||||
void _write( IAsyncOutputStream< T >* stream )
|
||||
{
|
||||
stream->tryCompleteWriteAt( mAsyncHandle, true );
|
||||
}
|
||||
};
|
||||
|
||||
template< typename T, class Stream >
|
||||
void AsyncWriteItem< T, Stream >::execute()
|
||||
{
|
||||
if( this->cancellationPoint() ) return;
|
||||
|
||||
StreamType* stream = this->getStream();
|
||||
if( dynamic_cast< IAsyncOutputStream< T >* >( stream ) )
|
||||
_write( ( IAsyncOutputStream< T >* ) stream );
|
||||
if( dynamic_cast< IOffsetOutputStream< T >* >( stream ) )
|
||||
_write( ( IOffsetOutputStream< T >* ) stream );
|
||||
else
|
||||
_write( stream );
|
||||
}
|
||||
|
||||
#endif // _THREADPOOLASYNCIO_H_
|
||||
474
Engine/source/platform/threads/threadSafeDeque.h
Normal file
474
Engine/source/platform/threads/threadSafeDeque.h
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _THREADSAFEDEQUE_H_
|
||||
#define _THREADSAFEDEQUE_H_
|
||||
|
||||
#ifndef _THREADSAFEFREELIST_H_
|
||||
# include "platform/threads/threadSafeFreeList.h"
|
||||
#endif
|
||||
|
||||
#include "platform/tmm_off.h"
|
||||
|
||||
|
||||
/// Fast, lock-free double-ended queue for concurrent access.
|
||||
///
|
||||
/// @param T Type of list elements; must have default contructor.
|
||||
template< typename T >
|
||||
class ThreadSafeDeque
|
||||
{
|
||||
// Lock-free deques using just single-word atomic writes are
|
||||
// very tricky as each pointer update must immediately result
|
||||
// in a fully valid list state. The idea here is to maintain the
|
||||
// deque's head and tail pointers unreliably but otherwise keep a
|
||||
// regular double-linked list (since we never insert nodes in the
|
||||
// middle, single-word writes are all we need).
|
||||
//
|
||||
// Deletions are a bit less straightforward and require the threads
|
||||
// to work cooperatively. Since failure of a pointer update depends
|
||||
// on the deletion state, the deletion flag has to be encoded into
|
||||
// the link fields. However, as there are two link fields this creates
|
||||
// two independent deletion flags for each single node, one on the
|
||||
// next link and one on the prev link.
|
||||
//
|
||||
// This will not lead to a problem, though, as it only becomes relevant
|
||||
// when there is only a single value in the list which, even if the
|
||||
// respective node gets both deleted and appended/prepended a new node,
|
||||
// will result in a valid list state.
|
||||
|
||||
|
||||
public:
|
||||
|
||||
typedef T ValueType;
|
||||
|
||||
protected:
|
||||
|
||||
class Node;
|
||||
class DeleteNode;
|
||||
typedef ThreadSafeRef< Node > NodeRef;
|
||||
|
||||
/// List node.
|
||||
class Node : public ThreadSafeFreeListNode< Node, DeleteNode >
|
||||
{
|
||||
public:
|
||||
|
||||
friend class DeleteNode; // mFreeList;
|
||||
typedef typename ThreadSafeDeque< T >::ValueType ValueType;
|
||||
|
||||
/// Thread claim flag. This is to prevent two threads who concurrently
|
||||
/// do a tryPopFront() and tryPopBack() respectively on a deque with just
|
||||
/// a single node to both claim and return the same value (which would happen
|
||||
/// without the flag as otherwise both threads would use two different
|
||||
/// deletion bits for claiming the node).
|
||||
U32 mIsClaimed;
|
||||
|
||||
/// Link to the freelist that the node has been
|
||||
/// allocated from.
|
||||
ThreadSafeFreeList< Node >& mFreeList;
|
||||
|
||||
/// Value contained in the node.
|
||||
ValueType mValue;
|
||||
|
||||
/// Reference to next node and deletion bit.
|
||||
NodeRef mNext;
|
||||
|
||||
/// Reference to previous node and deletion bit.
|
||||
NodeRef mPrev;
|
||||
|
||||
/// Construct an unlinked node allocated from "freeList".
|
||||
Node( ThreadSafeFreeList< Node >& freeList, const ValueType& value )
|
||||
: mIsClaimed( 0 ), mFreeList( freeList ), mValue( value ) {}
|
||||
};
|
||||
|
||||
class DeleteNode
|
||||
{
|
||||
public:
|
||||
template< typename N >
|
||||
static void destroy( N* ptr )
|
||||
{
|
||||
AssertFatal( ptr->mIsClaimed,
|
||||
"ThreadSafeDeque::DeleteNode::destroy() - deleting unclaimed node" );
|
||||
destructInPlace( ptr );
|
||||
ptr->mFreeList.free( ptr );
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
S32 mNumValues;
|
||||
#endif
|
||||
|
||||
/// Reference to the head node.
|
||||
NodeRef mHead;
|
||||
|
||||
///
|
||||
NodeRef mTail;
|
||||
|
||||
/// Free list for list nodes.
|
||||
ThreadSafeFreeList< Node > mFreeList;
|
||||
|
||||
/// @return the leftmost node in the list.
|
||||
/// @note Updates the list state and may purge deleted nodes.
|
||||
NodeRef getHead();
|
||||
|
||||
/// @return the rightmost node in the list.
|
||||
/// @note Updates the list state and may purge deleted nodes.
|
||||
NodeRef getTail();
|
||||
|
||||
public:
|
||||
|
||||
/// Construct an empty deque.
|
||||
ThreadSafeDeque()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
mNumValues = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
~ThreadSafeDeque()
|
||||
{
|
||||
ValueType value;
|
||||
while( tryPopFront( value ) );
|
||||
AssertFatal( isEmpty(), "ThreadSafeDeque::~ThreadSafeDeque() - not empty" );
|
||||
}
|
||||
|
||||
/// @return true if the queue is empty.
|
||||
bool isEmpty()
|
||||
{
|
||||
return ( !getHead() && !getTail() );
|
||||
}
|
||||
|
||||
/// Prepend the given value to the list.
|
||||
void pushFront( const ValueType& value );
|
||||
|
||||
/// Append the given value to the list.
|
||||
void pushBack( const ValueType& value );
|
||||
|
||||
/// Try to take the leftmost value from the deque.
|
||||
/// Fails if the deque is empty at the time the method tries to
|
||||
/// take a node from the list.
|
||||
bool tryPopFront( ValueType& outValue );
|
||||
|
||||
/// Try to take the rightmost value from the deque.
|
||||
/// Fails if the deque is empty at the time the method tries to
|
||||
/// take a node from the list.
|
||||
bool tryPopBack( ValueType& outValue );
|
||||
|
||||
void dumpDebug()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
Platform::outputDebugString( "[ThreadSafeDeque] numValues=%i", mNumValues );
|
||||
mFreeList.dumpDebug();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// The getHead() and getTail() code here is pretty much brute-force in order
|
||||
// to keep the complexities of synchronizing it bounded. We just let each
|
||||
// thread work as if it is the only thread but require each one to start from
|
||||
// scratch on each iteration.
|
||||
|
||||
template< typename T >
|
||||
typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getHead()
|
||||
{
|
||||
// Find leftmost node.
|
||||
|
||||
NodeRef result;
|
||||
while( 1 )
|
||||
{
|
||||
// Iterate through to leftmost node.
|
||||
|
||||
{
|
||||
NodeRef head = mHead;
|
||||
while( head != NULL )
|
||||
{
|
||||
NodeRef prev = head->mPrev;
|
||||
if( prev != NULL )
|
||||
mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset );
|
||||
else
|
||||
break;
|
||||
|
||||
head = mHead;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out dead nodes at front of list.
|
||||
|
||||
{
|
||||
NodeRef head = mHead;
|
||||
if( head && head->mPrev.isTagged() )
|
||||
{
|
||||
NodeRef next = head->mNext;
|
||||
|
||||
mHead.trySetFromTo( head, next, NodeRef::TAG_Unset );
|
||||
mTail.trySetFromTo( head, next, NodeRef::TAG_Unset );
|
||||
|
||||
if( next != NULL )
|
||||
next->mPrev.trySetFromTo( head, NULL );
|
||||
|
||||
head->mNext.trySetFromTo( next, NULL, NodeRef::TAG_Set );
|
||||
|
||||
continue; // Restart.
|
||||
}
|
||||
}
|
||||
|
||||
// Try head.
|
||||
|
||||
NodeRef head = mHead;
|
||||
if( head != NULL && !head->mPrev.isTagged() )
|
||||
{
|
||||
result = head;
|
||||
break;
|
||||
}
|
||||
|
||||
// Try tail.
|
||||
|
||||
if( !head )
|
||||
{
|
||||
head = mTail;
|
||||
if( !head )
|
||||
break;
|
||||
}
|
||||
|
||||
// Update head.
|
||||
|
||||
NodeRef prev = head->mPrev;
|
||||
if( head->mPrev != NULL )
|
||||
{
|
||||
if( !mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset ) )
|
||||
mHead.trySetFromTo( NULL, prev );
|
||||
}
|
||||
else
|
||||
mHead.trySetFromTo( NULL, head );
|
||||
}
|
||||
|
||||
AssertFatal( !result.isTagged(), "ThreadSafeDeque::getHead() - head got tagged" );
|
||||
return result;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getTail()
|
||||
{
|
||||
// Find rightmost node.
|
||||
|
||||
NodeRef result;
|
||||
while( 1 )
|
||||
{
|
||||
// Iterate through to rightmost node.
|
||||
|
||||
{
|
||||
NodeRef tail = mTail;
|
||||
while( tail != NULL )
|
||||
{
|
||||
NodeRef next = tail->mNext;
|
||||
if( next != NULL )
|
||||
mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset );
|
||||
else
|
||||
break;
|
||||
|
||||
tail = mTail;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out dead nodes at tail of list.
|
||||
|
||||
{
|
||||
NodeRef tail = mTail;
|
||||
if( tail != NULL && tail->mNext.isTagged() )
|
||||
{
|
||||
NodeRef prev = tail->mPrev;
|
||||
|
||||
mHead.trySetFromTo( tail, prev, NodeRef::TAG_Unset );
|
||||
mTail.trySetFromTo( tail, prev, NodeRef::TAG_Unset );
|
||||
|
||||
if( prev != NULL )
|
||||
prev->mNext.trySetFromTo( tail, NULL );
|
||||
|
||||
tail->mPrev.trySetFromTo( prev, NULL, NodeRef::TAG_Set );
|
||||
|
||||
continue; // Restart.
|
||||
}
|
||||
}
|
||||
|
||||
// Try tail.
|
||||
|
||||
NodeRef tail = mTail;
|
||||
if( tail != NULL && !tail->mNext.isTagged() )
|
||||
{
|
||||
result = tail;
|
||||
break;
|
||||
}
|
||||
|
||||
// Try head.
|
||||
|
||||
if( !tail )
|
||||
{
|
||||
tail = mHead;
|
||||
if( !tail )
|
||||
break;
|
||||
}
|
||||
|
||||
// Update tail.
|
||||
|
||||
NodeRef next = tail->mNext;
|
||||
if( next != NULL )
|
||||
{
|
||||
if( !mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset ) )
|
||||
mTail.trySetFromTo( NULL, next );
|
||||
}
|
||||
else
|
||||
mTail.trySetFromTo( NULL, tail );
|
||||
}
|
||||
|
||||
AssertFatal( !result.isTagged(), "ThreadSafeDeque::getTail() - tail got tagged" );
|
||||
return result;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void ThreadSafeDeque< T >::pushFront( const ValueType& value )
|
||||
{
|
||||
NodeRef nextNode;
|
||||
NodeRef newNode;
|
||||
|
||||
NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
nextNode = getHead();
|
||||
if( !nextNode )
|
||||
{
|
||||
newNode->mNext = NULL;
|
||||
if( mHead.trySetFromTo( NULL, newNode ) )
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
newNode->mNext = nextNode;
|
||||
if( nextNode->mPrev.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumValues, 1 );
|
||||
#endif
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void ThreadSafeDeque< T >::pushBack( const ValueType& value )
|
||||
{
|
||||
NodeRef prevNode;
|
||||
NodeRef newNode;
|
||||
|
||||
NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
prevNode = getTail();
|
||||
if( !prevNode )
|
||||
{
|
||||
newNode->mPrev = NULL;
|
||||
if( mHead.trySetFromTo( NULL, newNode ) ) // use head so we synchronize with pushFront
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
newNode->mPrev = prevNode;
|
||||
if( prevNode->mNext.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumValues, 1 );
|
||||
#endif
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
bool ThreadSafeDeque< T >::tryPopFront( ValueType& outValue )
|
||||
{
|
||||
NodeRef oldHead;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
oldHead = getHead();
|
||||
if( !oldHead )
|
||||
return false;
|
||||
|
||||
// Try to claim the node.
|
||||
|
||||
if( oldHead->mPrev.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) )
|
||||
{
|
||||
if( dCompareAndSwap( oldHead->mIsClaimed, 0, 1 ) )
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
outValue = oldHead->mValue;
|
||||
oldHead = NULL;
|
||||
|
||||
// Cleanup.
|
||||
getHead();
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumValues, -1 );
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
bool ThreadSafeDeque< T >::tryPopBack( ValueType& outValue )
|
||||
{
|
||||
NodeRef oldTail;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
oldTail = getTail();
|
||||
if( !oldTail )
|
||||
return false;
|
||||
|
||||
// Try to claim the node.
|
||||
|
||||
if( oldTail->mNext.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) )
|
||||
{
|
||||
if( dCompareAndSwap( oldTail->mIsClaimed, 0, 1 ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outValue = oldTail->mValue;
|
||||
oldTail = NULL;
|
||||
|
||||
// Cleanup.
|
||||
getTail();
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumValues, -1 );
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#include "platform/tmm_on.h"
|
||||
|
||||
#endif // _THREADSAFEDEQUE_H_
|
||||
192
Engine/source/platform/threads/threadSafeFreeList.h
Normal file
192
Engine/source/platform/threads/threadSafeFreeList.h
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _THREADSAFEFREELIST_H_
|
||||
#define _THREADSAFEFREELIST_H_
|
||||
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
# include "platform/threads/threadSafeRefCount.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMINTRINSICS_H_
|
||||
# include "platform/platformIntrinsics.h"
|
||||
#endif
|
||||
|
||||
#include "platform/tmm_off.h"
|
||||
|
||||
|
||||
/// @file
|
||||
/// Lock-free freelists for concurrent access.
|
||||
|
||||
|
||||
/// Freelist for re-using allocations in a concurrent setting.
|
||||
///
|
||||
/// @note Make sure that there are no more allocations in use
|
||||
/// when the free list is destructed.
|
||||
/// @note Allocated instances come with a reference already counted
|
||||
/// on the instance.
|
||||
///
|
||||
/// @param T Type of elements to allocate; must be derived from
|
||||
/// ThreadSafeRefCount and have at least define one additional
|
||||
/// pointer-sized field.
|
||||
template< class T >
|
||||
class ThreadSafeFreeList
|
||||
{
|
||||
protected:
|
||||
|
||||
T* mFreeList;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
S32 mNumNodesTotal;
|
||||
S32 mNumNodesFree;
|
||||
#endif
|
||||
|
||||
T*& getNext( T* ptr )
|
||||
{
|
||||
return *( ( T** ) &( ( U8* ) ptr )[ sizeof( T ) - sizeof( T* ) ] );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Create the freelist.
|
||||
///
|
||||
/// @param numPreAlloc Number of instances to pre-allocate.
|
||||
ThreadSafeFreeList( U32 numPreAlloc = 0 )
|
||||
: mFreeList( 0 )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
mNumNodesTotal = 0;
|
||||
mNumNodesFree = 0;
|
||||
#endif
|
||||
|
||||
for( U32 i = 0; i < numPreAlloc; ++ i )
|
||||
free( alloc() );
|
||||
}
|
||||
|
||||
~ThreadSafeFreeList()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertWarn( mNumNodesTotal == mNumNodesFree,
|
||||
"ThreadSafeFreeList::~ThreadSafeFreeList() - still got live instances" );
|
||||
#endif
|
||||
|
||||
// Destroy remaining nodes. Not synchronized. We assume all
|
||||
// concurrent processing to have finished.
|
||||
|
||||
while( mFreeList )
|
||||
{
|
||||
T* next = getNext( mFreeList );
|
||||
dFree( mFreeList );
|
||||
mFreeList = next;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return memory for a new instance.
|
||||
void* alloc()
|
||||
{
|
||||
T* ptr;
|
||||
while( 1 )
|
||||
{
|
||||
ptr = ThreadSafeRef< T >::safeRead( mFreeList );
|
||||
if( !ptr )
|
||||
{
|
||||
ptr = ( T* ) dMalloc( sizeof( T ) );
|
||||
dMemset( ptr, 0, sizeof( T ) );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumNodesTotal, 1 );
|
||||
#endif
|
||||
|
||||
ptr->addRef();
|
||||
break;
|
||||
}
|
||||
else if( dCompareAndSwap( mFreeList, ptr, getNext( ptr ) ) )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumNodesFree, -1 );
|
||||
#endif
|
||||
|
||||
ptr->clearLowestBit();
|
||||
break;
|
||||
}
|
||||
else
|
||||
ptr->release();
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// Return the memory allocated to the given instance to the freelist.
|
||||
void free( void* ptr )
|
||||
{
|
||||
AssertFatal( ptr, "ThreadSafeFreeList::free() - got a NULL pointer" );
|
||||
T* node = ( T* ) ptr;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
T* list = mFreeList;
|
||||
getNext( node ) = list;
|
||||
if( dCompareAndSwap( mFreeList, list, node ) )
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
dFetchAndAdd( mNumNodesFree, 1 );
|
||||
#endif
|
||||
}
|
||||
|
||||
void dumpDebug()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
Platform::outputDebugString( "[ThreadSafeFreeList] total=%i, free=%i",
|
||||
mNumNodesTotal, mNumNodesFree );
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/// Baseclass for objects allocated from ThreadSafeFreeLists.
|
||||
template< class T, class DeletePolicy = DeleteSingle >
|
||||
class ThreadSafeFreeListNode : public ThreadSafeRefCount< T, DeletePolicy >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ThreadSafeRefCount< T, DeletePolicy > Parent;
|
||||
|
||||
ThreadSafeFreeListNode()
|
||||
: Parent( false ) {}
|
||||
|
||||
static void* operator new( size_t size, ThreadSafeFreeList< T >& freeList )
|
||||
{
|
||||
AssertFatal( size <= sizeof( T ),
|
||||
"ThreadSafeFreeListNode::new() - size exceeds limit of freelist" );
|
||||
TORQUE_UNUSED( size );
|
||||
return freeList.alloc();
|
||||
}
|
||||
static void operator delete( void* ptr, ThreadSafeFreeList< T >& freeList )
|
||||
{
|
||||
freeList.free( ptr );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#include "platform/tmm_on.h"
|
||||
|
||||
#endif // _THREADSAFEFREELIST_H_
|
||||
740
Engine/source/platform/threads/threadSafePriorityQueue.h
Normal file
740
Engine/source/platform/threads/threadSafePriorityQueue.h
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _THREADSAFEPRIORITYQUEUE_H_
|
||||
#define _THREADSAFEPRIORITYQUEUE_H_
|
||||
|
||||
#ifndef _PLATFORMINTRINSICS_H_
|
||||
#include "platform/platformIntrinsics.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
#include "platform/threads/threadSafeRefCount.h"
|
||||
#endif
|
||||
#ifndef _TYPETRAITS_H_
|
||||
#include "platform/typetraits.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Disable TMM's new operator grabbing.
|
||||
#include "platform/tmm_off.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
/// @file
|
||||
/// Template code for an efficient thread-safe priority queue
|
||||
/// implementation. There are two alternative implementations to
|
||||
/// choose from: ThreadSafePriorityQueue and ThreadSafePriorityQueueWithUpdate
|
||||
/// where the latter adds concurrent status updates of queue items to
|
||||
/// the former implementation.
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ThreadSafePriorityQueue.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Fast, lock-free priority queue implementation for concurrent access.
|
||||
///
|
||||
/// Equal priorities are allowed and are placed <em>before</em> existing items of
|
||||
/// identical priority in the queue.
|
||||
///
|
||||
/// Based on (but with significant deviations from) "Fast and Lock-Free Concurrent
|
||||
/// Priority Queues for Multi-Thread Systems" by Hakan Sundell and Philippas Tsigas.
|
||||
/// Parts of the skiplist code is based on work by William Pugh.
|
||||
///
|
||||
/// @param T The item value type. Must have a default constructor.
|
||||
/// @param K The priority key type. Must be comparable, have a default constructor,
|
||||
/// and be a valid template parameter to TypeTraits.
|
||||
/// @param SORT_MIN_TO_MAX If true, the queue sorts from minimum to maximum priority or
|
||||
/// the reverse if false.
|
||||
/// @param MAX_LEVEL The number of levels a node can have at most.
|
||||
/// @param PROBABILISTIC_BIAS The probabilistic level distribution factor for
|
||||
/// the skiplist. Multiplied by 100 and turned into int to conform to restrictions
|
||||
/// on non-type template parameters.
|
||||
///
|
||||
/// @see TypeTraits
|
||||
|
||||
template< typename T, typename K = F32, bool SORT_MIN_TO_MAX = false, U32 MAX_LEVEL = 4, U32 PROBABILISTIC_BIAS = 50 >
|
||||
struct ThreadSafePriorityQueue
|
||||
{
|
||||
typedef T ValueType;
|
||||
typedef K KeyType;
|
||||
|
||||
enum { MAX_LEVEL_CONST = MAX_LEVEL };
|
||||
|
||||
ThreadSafePriorityQueue();
|
||||
|
||||
bool isEmpty();
|
||||
void insert( KeyType priority, const T& value );
|
||||
bool takeNext( T& outValue, KeyType upToPriority = ( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN ) );
|
||||
|
||||
protected:
|
||||
struct Node;
|
||||
typedef ThreadSafeRef< Node > NodePtr;
|
||||
friend class ThreadSafeRefCount< Node >;
|
||||
friend struct DeleteSingle;
|
||||
|
||||
/// A queue node.
|
||||
///
|
||||
/// Nodes are reference-counted to coordinate memory management
|
||||
/// between the different threads. Reclamation happens on the
|
||||
/// thread that releases the last reference.
|
||||
///
|
||||
/// Reference-counting and deletion requests are kept separate.
|
||||
/// A given node is marked for deletion and will then have its references
|
||||
/// progressively disappear and eventually be reclaimed once the
|
||||
/// reference count drops to zero.
|
||||
///
|
||||
/// Note that 'Next' references are released by the destructor which
|
||||
/// is only called when the reference count to the node itself drops to
|
||||
/// zero. This is to avoid threads getting trapped in a node with no
|
||||
/// link out.
|
||||
|
||||
struct Node : public ThreadSafeRefCount< Node >
|
||||
{
|
||||
typedef ThreadSafeRefCount< Node > Parent;
|
||||
|
||||
Node( KeyType priority, const ValueType& value );
|
||||
~Node();
|
||||
|
||||
KeyType getPriority() { return mPriority; }
|
||||
ValueType& getValue() { return mValue; }
|
||||
U32 getLevel();
|
||||
NodePtr& getNext( U32 level );
|
||||
|
||||
bool isMarkedForDeletion();
|
||||
bool tryMarkForDeletion();
|
||||
|
||||
void clearValue() { mValue = ValueType(); }
|
||||
|
||||
static U32 randomLevel();
|
||||
|
||||
void* operator new( size_t size, S32 level = -1 );
|
||||
void operator delete( void* ptr );
|
||||
|
||||
private:
|
||||
KeyType mPriority; ///< Priority key.
|
||||
U32 mLevel; ///< Level count and deletion bit (highest).
|
||||
ValueType mValue;
|
||||
Node* mNext[ 1 ]; ///< Variable-sized array of next pointers.
|
||||
|
||||
struct FreeList
|
||||
{
|
||||
bool mDestroyed;
|
||||
Node* mNodes;
|
||||
|
||||
~FreeList();
|
||||
};
|
||||
|
||||
static FreeList smFreeLists[ MAX_LEVEL ];
|
||||
};
|
||||
|
||||
NodePtr mHead; ///< Artificial head node.
|
||||
NodePtr mTail; ///< Artificial tail node.
|
||||
|
||||
void readNext( NodePtr& refPrev, NodePtr& refNext, U32 level );
|
||||
void scan( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority );
|
||||
void scanFromHead( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority );
|
||||
void insert( KeyType priority, const T& value, NodePtr& outResult );
|
||||
void helpDelete();
|
||||
};
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
typename ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::FreeList ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::smFreeLists[ MAX_LEVEL ];
|
||||
|
||||
/// Construct an empty queue.
|
||||
///
|
||||
/// Internally, this creates a head node with maximal priority and a tail node with minimal priority,
|
||||
/// both at maximum level.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::ThreadSafePriorityQueue()
|
||||
{
|
||||
NodePtr::unsafeWrite( mHead, new ( MAX_LEVEL - 1 )
|
||||
Node( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MIN : TypeTraits< KeyType >::MAX, ValueType() ) );
|
||||
NodePtr::unsafeWrite( mTail, new ( MAX_LEVEL - 1 )
|
||||
Node( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN, ValueType() ) );
|
||||
|
||||
for( U32 level = 0; level < MAX_LEVEL; level ++ )
|
||||
mHead->getNext( level ) = mTail;
|
||||
}
|
||||
|
||||
/// Return true if the queue does not currently contain an item.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::isEmpty()
|
||||
{
|
||||
return ( mHead->getNext( 0 ) == mTail );
|
||||
}
|
||||
|
||||
/// Insert the given value into the queue at the place determined by the given priority.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value )
|
||||
{
|
||||
NodePtr result;
|
||||
insert( priority, value, result );
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value, NodePtr& outResult )
|
||||
{
|
||||
// Create a new node at a random level.
|
||||
|
||||
outResult = NULL;
|
||||
NodePtr::unsafeWrite( outResult, new Node( priority, value ) );
|
||||
U32 resultNodeLevel = outResult->getLevel();
|
||||
|
||||
// Link up all the levels. Do this bottom-up instead of
|
||||
// top-down (as would be the right way for a skiplist) so
|
||||
// that our list state always remains valid. If going top-down,
|
||||
// we'll insert nodes with NULL pointers at their lower levels.
|
||||
|
||||
U32 currentLevel = 0;
|
||||
do
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
NodePtr nextNode;
|
||||
NodePtr prevNode;
|
||||
|
||||
scanFromHead( prevNode, nextNode, currentLevel, priority );
|
||||
|
||||
outResult->getNext( currentLevel ) = nextNode;
|
||||
if( prevNode->getNext( currentLevel ).trySetFromTo( nextNode, outResult, NodePtr::TAG_FailIfSet ) )
|
||||
break;
|
||||
else
|
||||
outResult->getNext( currentLevel ) = 0;
|
||||
}
|
||||
|
||||
currentLevel ++;
|
||||
}
|
||||
while( currentLevel <= resultNodeLevel
|
||||
&& !outResult->isMarkedForDeletion() ); // No point linking up remaining levels if another thread already took this node.
|
||||
}
|
||||
|
||||
/// Take the item with the highest priority from the queue.
|
||||
///
|
||||
/// @param outValue Reference to where the resulting value should be stored.
|
||||
/// @param upToPriority Priority limit (inclusive) up to which items are taken from the queue.
|
||||
/// @return true if there was a matching item in the queue.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::takeNext( T& outValue, KeyType upToPriority )
|
||||
{
|
||||
// Iterate through to the first unmarked node.
|
||||
|
||||
NodePtr prevNode = mHead;
|
||||
while( 1 )
|
||||
{
|
||||
NodePtr node;
|
||||
readNext( prevNode, node, 0 );
|
||||
|
||||
if( node == mTail )
|
||||
return false; // End reached.
|
||||
|
||||
bool priorityThresholdReached = SORT_MIN_TO_MAX
|
||||
? ( upToPriority >= node->getPriority() )
|
||||
: ( upToPriority <= node->getPriority() );
|
||||
|
||||
if( !priorityThresholdReached )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
// Try to mark the node for deletion. Only if that succeeds, taking the
|
||||
// node was a success and we can return. If it fails, spin and try again.
|
||||
|
||||
if( node->tryMarkForDeletion() )
|
||||
{
|
||||
helpDelete();
|
||||
|
||||
// Node is now off the list and will disappear as soon as
|
||||
// all references held by threads (including this one)
|
||||
// go out of scope.
|
||||
|
||||
outValue = node->getValue();
|
||||
node->clearValue();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the given references to the next non-deleted node at the given level.
|
||||
/// refPrev will be updated to reference the immediate predecessor of the next
|
||||
/// node returned. Note that this can be a node in deleted state.
|
||||
///
|
||||
/// @param refPrev Reference to a node of which the successor node should be
|
||||
/// returned. Updated to immediate predecessor of refNext on return.
|
||||
/// @param refNext Reference to update to refer to next non-deleted node on
|
||||
/// the given level.
|
||||
/// @param level Skiplist level to operate on.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::readNext( NodePtr& refPrev, NodePtr& refNext, U32 level )
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
refNext = refPrev->getNext( level );
|
||||
AssertFatal( refNext != NULL, "ThreadSafePriorityQueue::readNext() - next is NULL" );
|
||||
if( !refNext->isMarkedForDeletion() || refNext == mTail )
|
||||
break;
|
||||
|
||||
refPrev = refNext;
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan for the position at which to insert a node of the given priority.
|
||||
/// Upon return, the position between refPrev and refNext is the one to insert at.
|
||||
///
|
||||
/// @param refPrev position at which to start scanning; updated to match insert position.
|
||||
/// @param refNext
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::scan( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority )
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
readNext( refPrev, refNext, level );
|
||||
if( refNext == mTail
|
||||
|| ( SORT_MIN_TO_MAX
|
||||
? ( refNext->getPriority() > priority )
|
||||
: ( refNext->getPriority() < priority ) ) )
|
||||
break;
|
||||
|
||||
refPrev = refNext;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::scanFromHead( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority )
|
||||
{
|
||||
// Purge dead nodes at left end of queue so
|
||||
// we don't get stuck hitting the same node
|
||||
// in deletable state over and over again.
|
||||
helpDelete();
|
||||
|
||||
S32 currentLevel = MAX_LEVEL - 1;
|
||||
refPrev = mHead;
|
||||
do
|
||||
{
|
||||
scan( refPrev, refNext, currentLevel, priority );
|
||||
currentLevel --;
|
||||
}
|
||||
while( currentLevel >= S32( level ) );
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::helpDelete()
|
||||
{
|
||||
// Clean out all the references from head.
|
||||
// Spin over a given reference on each level until head
|
||||
// clearly refers to a node in non-deletable state. This
|
||||
// makes this code work cooperatively with other threads
|
||||
// doing takeNexts on prior or later nodes while also
|
||||
// guaranteeing that all next pointers to us will eventually
|
||||
// disappear.
|
||||
//
|
||||
// Note that this is *the only place* where we will be cleaning
|
||||
// out our lists.
|
||||
|
||||
S32 level = MAX_LEVEL - 1;
|
||||
do
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
NodePtr ptr = mHead->getNext( level );
|
||||
if( !ptr->isMarkedForDeletion() )
|
||||
break;
|
||||
else
|
||||
{
|
||||
NodePtr& next = ptr->getNext( level );
|
||||
next.setTag();
|
||||
mHead->getNext( level ).trySetFromTo( ptr, next, NodePtr::TAG_Unset );
|
||||
AssertFatal( next->getRefCount() >= 2, "ThreadSafePriorityQueue::helpDelete() - invalid refcount" );
|
||||
}
|
||||
}
|
||||
|
||||
level --;
|
||||
}
|
||||
while( level >= 0 );
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::Node( KeyType priority, const ValueType& value )
|
||||
: Parent( false ),
|
||||
mPriority( priority ),
|
||||
mValue( value )
|
||||
{
|
||||
dMemset( mNext, 0, sizeof( Node* ) * ( getLevel() + 1 ) );
|
||||
|
||||
// Level is already set by the allocation routines.
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::~Node()
|
||||
{
|
||||
for( U32 level = 0; level < ( getLevel() + 1 ); level ++ )
|
||||
getNext( level ) = NULL;
|
||||
}
|
||||
|
||||
/// Return the skip list level the node is at.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline U32 ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::getLevel()
|
||||
{
|
||||
// Mask out the deletion request bit.
|
||||
|
||||
return ( mLevel & 0x7FFFFFFF );
|
||||
}
|
||||
|
||||
/// Return the successor node at the given level.
|
||||
/// @param level The level of the desired successor node; must be within the node's level bounds.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline typename ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::NodePtr& ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::getNext( U32 level )
|
||||
{
|
||||
return *reinterpret_cast< NodePtr* >( &mNext[ level ] );
|
||||
}
|
||||
|
||||
/// Return true if the node is marked to be deleted.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::isMarkedForDeletion()
|
||||
{
|
||||
return ( mLevel & 0x80000000 );
|
||||
}
|
||||
|
||||
/// Attempt to mark the node for deletion. If the mark bit has not yet been set
|
||||
/// and setting it on the current thread succeeds, returns true.
|
||||
///
|
||||
/// @return true, if the marking succeeded.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
inline bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::tryMarkForDeletion()
|
||||
{
|
||||
U32 oldVal = mLevel & 0x7FFFFFFF;
|
||||
U32 newVal = oldVal | 0x80000000;
|
||||
|
||||
return ( dCompareAndSwap( mLevel, oldVal, newVal ) );
|
||||
}
|
||||
|
||||
/// Choose a random level.
|
||||
///
|
||||
/// The chosen level depends on the given PROBABILISTIC_BIAS and MAX_LEVEL,
|
||||
/// but is not affected by the actual number of nodes in a queue.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
U32 ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::randomLevel()
|
||||
{
|
||||
U32 level = 0;
|
||||
while( Platform::getRandom() < ( ( ( F32 ) PROBABILISTIC_BIAS ) / 100 ) && level < ( MAX_LEVEL - 1 ) )
|
||||
level ++;
|
||||
return level;
|
||||
}
|
||||
|
||||
/// Allocate a new node.
|
||||
/// The node comes with a reference count of one and its level already set.
|
||||
///
|
||||
/// @param level The level to allocate the node at. If this is -1, a random level is chosen.
|
||||
/// @return a new node.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void* ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::operator new( size_t size, S32 level )
|
||||
{
|
||||
if( level == -1 )
|
||||
level = randomLevel();
|
||||
|
||||
Node* node = 0;
|
||||
while( 1 )
|
||||
{
|
||||
// Try to take a node from the freelist. If there's none,
|
||||
// allocate a new one.
|
||||
|
||||
if( !smFreeLists[ level ].mDestroyed )
|
||||
node = Node::safeRead( smFreeLists[ level ].mNodes );
|
||||
|
||||
if( !node )
|
||||
{
|
||||
node = ( Node* ) dMalloc( sizeof( Node ) + sizeof( Node* ) * level );
|
||||
dMemset( node, 0, sizeof( Node ) );
|
||||
node->mLevel = level;
|
||||
node->addRef();
|
||||
break;
|
||||
}
|
||||
else if( dCompareAndSwap( smFreeLists[ level ].mNodes, node, node->mNext[ 0 ] ) )
|
||||
{
|
||||
node->clearLowestBit();
|
||||
break;
|
||||
}
|
||||
else
|
||||
node->release(); // Other thread was quicker than us; release.
|
||||
}
|
||||
|
||||
AssertFatal( node->getRefCount() != 0, "ThreadSafePriorityQueue::new Node() - invalid refcount" );
|
||||
AssertFatal( ( node->getRefCount() % 2 ) == 0, "ThreadSafePriorityQueue::new Node() - invalid refcount" );
|
||||
return node;
|
||||
}
|
||||
|
||||
/// Reclaim a node.
|
||||
///
|
||||
/// @param node The node to reclaim. Must refer to a Node instance.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::operator delete( void* ptr )
|
||||
{
|
||||
//TODO: limit number of nodes kept
|
||||
|
||||
Node* node = ( Node* ) ptr;
|
||||
U32 level = node->getLevel();
|
||||
node->mLevel = level; // Reset the node's deletion bit.
|
||||
|
||||
while( !smFreeLists[ level ].mDestroyed )
|
||||
{
|
||||
// Put the node on the freelist.
|
||||
|
||||
Node* freeList = smFreeLists[ level ].mNodes;
|
||||
node->mNext[ 0 ] = freeList;
|
||||
|
||||
if( dCompareAndSwap( smFreeLists[ level ].mNodes, freeList, node ) )
|
||||
{
|
||||
node = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( node )
|
||||
dFree( node );
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::FreeList::~FreeList()
|
||||
{
|
||||
mDestroyed = true;
|
||||
while( mNodes )
|
||||
{
|
||||
//FIXME: could leak some bytes under unfortunate circumstances (this in
|
||||
// combination with mDestroyed is a dependent write)
|
||||
|
||||
Node* next = mNodes;
|
||||
if( dCompareAndSwap( mNodes, next, next->mNext[ 0 ] ) )
|
||||
dFree( next );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ThreadSafePriorityQueueWithUpdate.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Fast, lock-free priority queue implementation for concurrent access that
|
||||
/// performs dynamic re-prioritization of items.
|
||||
///
|
||||
/// Within the bounds of a set update interval UPDATE_INTERVAL, the takeNext
|
||||
/// method is guaranteed to always return the item that has the highest priority
|
||||
/// at the time the method is called rather than at the time items were inserted
|
||||
/// into the queue.
|
||||
///
|
||||
/// Values placed on the queue must implement the following interface:
|
||||
///
|
||||
/// @code
|
||||
/// template< typename K >
|
||||
/// struct IThreadSafePriorityQueueItem
|
||||
/// {
|
||||
/// // Default constructor.
|
||||
/// IThreadSafePriorityQueueItem();
|
||||
///
|
||||
/// // Return the current priority.
|
||||
/// // This must run normally even if the item is already dead.
|
||||
/// K getPriority();
|
||||
///
|
||||
/// // Return true if the item is still meant to be waiting in the queue.
|
||||
/// bool isAlive();
|
||||
/// };
|
||||
/// @endcode
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX = false, U32 MAX_LEVEL = 4, U32 PROBABILISTIC_BIAS = 50 >
|
||||
struct ThreadSafePriorityQueueWithUpdate : public ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >
|
||||
{
|
||||
|
||||
typedef T ValueType;
|
||||
typedef K KeyType;
|
||||
|
||||
enum { DEFAULT_UPDATE_INTERVAL = 256 };
|
||||
|
||||
ThreadSafePriorityQueueWithUpdate( U32 updateInterval = DEFAULT_UPDATE_INTERVAL );
|
||||
|
||||
void insert( KeyType priority, const T& value );
|
||||
bool takeNext( T& outValue, KeyType upToPriority = ( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN ) );
|
||||
|
||||
U32 getUpdateInterval() const;
|
||||
void setUpdateInterval( U32 value );
|
||||
|
||||
KeyType getTimeBasedPriorityBoost() const;
|
||||
void setTimeBasedPriorityBoost( KeyType value );
|
||||
|
||||
void updatePriorities();
|
||||
|
||||
protected:
|
||||
typedef ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS > Parent;
|
||||
typedef U32 TickType;
|
||||
typedef typename Parent::NodePtr NodePtr;
|
||||
|
||||
U32 mUpdateInterval;
|
||||
KeyType mPriorityBoost; ///< If this is non-zero, priorities will be boosted by this amount each update. This can be used to prevent constant high-priority inserts to starve low-priority items already in the queue.
|
||||
|
||||
/// Work queue for node updates.
|
||||
ThreadSafePriorityQueue< NodePtr, TickType, true, MAX_LEVEL, PROBABILISTIC_BIAS > mUpdateQueue;
|
||||
|
||||
TickType getTick() { return Platform::getRealMilliseconds(); }
|
||||
};
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::ThreadSafePriorityQueueWithUpdate( U32 updateInterval )
|
||||
: mUpdateInterval( updateInterval ),
|
||||
mPriorityBoost( TypeTraits< KeyType >::ZERO )
|
||||
{
|
||||
}
|
||||
|
||||
/// Return the current update interval in milliseconds.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
U32 ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::getUpdateInterval() const
|
||||
{
|
||||
return mUpdateInterval;
|
||||
}
|
||||
|
||||
/// Set update interval of queue to given value.
|
||||
///
|
||||
/// <em>Call this method on the main thread only.</em>
|
||||
///
|
||||
/// @param value Time between priority updates in milliseconds.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::setUpdateInterval( U32 value )
|
||||
{
|
||||
mUpdateInterval = value;
|
||||
}
|
||||
|
||||
/// Return the delta to apply to priorities on each update.
|
||||
/// Set to zero to deactivate time-based priority adjustments.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
K ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::getTimeBasedPriorityBoost() const
|
||||
{
|
||||
return mPriorityBoost;
|
||||
}
|
||||
|
||||
/// Set the delta for time-based priority adjustments to the given value.
|
||||
///
|
||||
/// <em>Call this method on the main thread only.</em>
|
||||
///
|
||||
/// @param value The new priority adjustment value.
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::setTimeBasedPriorityBoost( KeyType value )
|
||||
{
|
||||
mPriorityBoost = value;
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value )
|
||||
{
|
||||
NodePtr node;
|
||||
Parent::insert( priority, value, node );
|
||||
mUpdateQueue.insert( getTick() + getUpdateInterval(), node );
|
||||
}
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
bool ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::takeNext( T& outValue, KeyType upToPriority )
|
||||
{
|
||||
updatePriorities();
|
||||
|
||||
bool result = false;
|
||||
do
|
||||
{
|
||||
result = Parent::takeNext( outValue, upToPriority );
|
||||
}
|
||||
while( result && !outValue.isAlive() );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
|
||||
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::updatePriorities()
|
||||
{
|
||||
TickType currentTime = getTick();
|
||||
U32 numNodesUpdated = 0;
|
||||
U32 numNodesDead = 0;
|
||||
U32 numNodesChanged = 0;
|
||||
|
||||
NodePtr node;
|
||||
while( mUpdateQueue.takeNext( node, currentTime ) )
|
||||
{
|
||||
numNodesUpdated ++;
|
||||
|
||||
// Since we're updating nodes on the update queue only periodically,
|
||||
// their associated values or main queue nodes may have died in the
|
||||
// meantime. If so, we just discard them here.
|
||||
|
||||
if( node->getValue().isAlive()
|
||||
&& !node->isMarkedForDeletion() )
|
||||
{
|
||||
KeyType newPriority = node->getValue().getPriority() + getTimeBasedPriorityBoost();
|
||||
if( newPriority != node->getPriority() )
|
||||
{
|
||||
// Node is outdated. Reinsert with new priority and mark the
|
||||
// old node for deletion.
|
||||
|
||||
insert( newPriority, node->getValue() );
|
||||
node->tryMarkForDeletion();
|
||||
numNodesChanged ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Node is still current. Just move to end.
|
||||
|
||||
mUpdateQueue.insert( currentTime + getUpdateInterval(), node );
|
||||
}
|
||||
}
|
||||
else
|
||||
numNodesDead ++;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
if( numNodesUpdated )
|
||||
Platform::outputDebugString( "[ThreadSafePriorityQueueWithUpdate] updated %i nodes (%i changed, %i dead)",
|
||||
numNodesUpdated, numNodesChanged, numNodesDead );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Re-enable TMM if necessary.
|
||||
#include "platform/tmm_on.h"
|
||||
|
||||
#undef DEBUG_SPEW
|
||||
|
||||
#endif // !_THREADSAFEPRIORITYQUEUE_H_
|
||||
380
Engine/source/platform/threads/threadSafeRefCount.h
Normal file
380
Engine/source/platform/threads/threadSafeRefCount.h
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
#define _THREADSAFEREFCOUNT_H_
|
||||
|
||||
#ifndef _PLATFORMINTRINSICS_H_
|
||||
# include "platform/platformIntrinsics.h"
|
||||
#endif
|
||||
#ifndef _TYPETRAITS_H_
|
||||
# include "platform/typetraits.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// @file
|
||||
/// Templated code for concurrent reference-counting.
|
||||
///
|
||||
/// Part of this code is based on work by J.D. Valois, Michael M. Maged,
|
||||
/// and Scott L. Michael.
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ThreadSafeRefCount.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Baseclass for concurrently reference-counted objects.
|
||||
///
|
||||
/// @note NOTE that freshly instantiated objects start out with a reference
|
||||
/// count of ZERO! Depending on how this class is used, this may not
|
||||
/// be desirable, so override this behavior in constructors if necessary.
|
||||
///
|
||||
/// @param T the class being reference counted; this is passed to this class,
|
||||
/// so it can call the correct destructor without having to force users
|
||||
/// to have virtual methods
|
||||
|
||||
template< class T, class DeletePolicy = DeleteSingle >
|
||||
class ThreadSafeRefCount
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
|
||||
ThreadSafeRefCount()
|
||||
: mRefCount( 0 ) {}
|
||||
ThreadSafeRefCount( bool noSet ) {}
|
||||
|
||||
bool isShared() const;
|
||||
U32 getRefCount() const;
|
||||
void addRef();
|
||||
void release();
|
||||
void clearLowestBit();
|
||||
static T* safeRead( T* const volatile& refPtr );
|
||||
|
||||
protected:
|
||||
|
||||
U32 mRefCount; ///< Reference count and claim bit. Note that this increments in steps of two.
|
||||
|
||||
static U32 decrementAndTestAndSet( U32& refCount );
|
||||
};
|
||||
|
||||
/// @return true if the object is referenced by more than a single
|
||||
/// reference.
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
inline bool ThreadSafeRefCount< T, DeletePolicy >::isShared() const
|
||||
{
|
||||
return ( mRefCount > 3 );
|
||||
}
|
||||
|
||||
/// Get the current reference count. This method is mostly meant for
|
||||
/// debugging and should not normally be used.
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
inline U32 ThreadSafeRefCount< T, DeletePolicy >::getRefCount() const
|
||||
{
|
||||
return mRefCount;
|
||||
}
|
||||
|
||||
/// Increase the reference count on the object.
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
inline void ThreadSafeRefCount< T, DeletePolicy >::addRef()
|
||||
{
|
||||
dFetchAndAdd( mRefCount, 2 );
|
||||
}
|
||||
|
||||
/// Decrease the object's reference count and delete the object, if the count
|
||||
/// drops to zero and claiming the object by the current thread succeeds.
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
inline void ThreadSafeRefCount< T, DeletePolicy >::release()
|
||||
{
|
||||
AssertFatal( mRefCount != 0, "ThreadSafeRefCount::release() - refcount of zero" );
|
||||
if( decrementAndTestAndSet( mRefCount ) != 0 )
|
||||
DeletePolicy::destroy( ( T* ) this );
|
||||
}
|
||||
|
||||
/// Dereference a reference-counted pointer in a multi-thread safe way.
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
T* ThreadSafeRefCount< T, DeletePolicy >::safeRead( T* const volatile& refPtr )
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
// Support tagged pointers here.
|
||||
|
||||
T* ptr = TypeTraits< T* >::getUntaggedPtr( refPtr );
|
||||
if( !ptr )
|
||||
return 0;
|
||||
|
||||
ptr->addRef();
|
||||
if( ptr == TypeTraits< T* >::getUntaggedPtr( refPtr ) )
|
||||
return ptr;
|
||||
else
|
||||
ptr->release();
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrement the given reference count. Return 1 if the count dropped to zero
|
||||
/// and the claim bit has been successfully set; return 0 otherwise.
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
U32 ThreadSafeRefCount< T, DeletePolicy >::decrementAndTestAndSet( U32& refCount )
|
||||
{
|
||||
U32 oldVal;
|
||||
U32 newVal;
|
||||
|
||||
do
|
||||
{
|
||||
oldVal = refCount;
|
||||
newVal = oldVal - 2;
|
||||
|
||||
AssertFatal( oldVal >= 2,
|
||||
"ThreadSafeRefCount::decrementAndTestAndSet() - invalid refcount" );
|
||||
|
||||
if( newVal == 0 )
|
||||
newVal = 1;
|
||||
}
|
||||
while( !dCompareAndSwap( refCount, oldVal, newVal ) );
|
||||
|
||||
return ( ( oldVal - newVal ) & 1 );
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
template< class T, class DeletePolicy >
|
||||
inline void ThreadSafeRefCount< T, DeletePolicy >::clearLowestBit()
|
||||
{
|
||||
AssertFatal( mRefCount % 2 != 0, "ThreadSafeRefCount::clearLowestBit() - invalid refcount" );
|
||||
|
||||
U32 oldVal;
|
||||
U32 newVal;
|
||||
|
||||
do
|
||||
{
|
||||
oldVal = mRefCount;
|
||||
newVal = oldVal - 1;
|
||||
}
|
||||
while( !dCompareAndSwap( mRefCount, oldVal, newVal ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ThreadSafeRef.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Reference to a concurrently reference-counted object.
|
||||
///
|
||||
/// This class takes care of the reference-counting as well as protecting
|
||||
/// the reference itself from concurrent operations.
|
||||
///
|
||||
/// Tagging allows the pointer contained in the reference to be flagged.
|
||||
/// Tag state is preserved through updates to the reference.
|
||||
///
|
||||
/// @note If you directly assign a freshly created object with a reference
|
||||
/// count of zero to a ThreadSafeRef, make absolutely sure the ThreadSafeRef
|
||||
/// is accessed only by a single thread. Otherwise there's a risk of the
|
||||
/// object being released and freed in midst of trying to set the reference.
|
||||
template< class T >
|
||||
class ThreadSafeRef
|
||||
{
|
||||
public:
|
||||
|
||||
enum ETag
|
||||
{
|
||||
TAG_PreserveOld, ///< Preserve existing tagging state when changing pointer.
|
||||
TAG_PreserveNew, ///< Preserve tagging state of new pointer when changing pointer.
|
||||
TAG_Set, ///< Set tag when changing pointer; okay if already set.
|
||||
TAG_Unset, ///< Unset tag when changing pointer; okay if already unset.
|
||||
TAG_SetOrFail, ///< Set tag when changing pointer; fail if already set.
|
||||
TAG_UnsetOrFail, ///< Unset tag when changing pointer; fail if already unset.
|
||||
TAG_FailIfSet, ///< Fail changing pointer when currently tagged.
|
||||
TAG_FailIfUnset ///< Fail changing pointer when currently untagged.
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< T > ThisType;
|
||||
|
||||
ThreadSafeRef() : mPtr( 0 ) {}
|
||||
ThreadSafeRef( T* ptr ) : mPtr( ThreadSafeRefCount< T >::safeRead( ptr ) ) {}
|
||||
ThreadSafeRef( const ThisType& ref ) : mPtr( ThreadSafeRefCount< T >::safeRead( ref.mPtr ) ) {}
|
||||
~ThreadSafeRef()
|
||||
{
|
||||
T* ptr = NULL;
|
||||
while( !trySetFromTo( mPtr, ptr ) );
|
||||
}
|
||||
|
||||
T* ptr() const { return getUntaggedPtr( mPtr ) ; }
|
||||
void setTag() { while( !trySetFromTo( mPtr, mPtr, TAG_Set ) ); }
|
||||
bool isTagged() const { return isTaggedPtr( mPtr ); }
|
||||
bool trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag = TAG_PreserveOld );
|
||||
bool trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld );
|
||||
bool trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld );
|
||||
static void unsafeWrite( ThisType& ref, T* ptr );
|
||||
static T* safeRead( T* const volatile& refPtr ) { return ThreadSafeRefCount< T >::safeRead( refPtr ); }
|
||||
|
||||
bool operator ==( T* ptr ) const;
|
||||
bool operator ==( const ThisType& ref ) const;
|
||||
bool operator !=( T* ptr ) const { return !( *this == ptr ); }
|
||||
bool operator !=( const ThisType& ref ) const { return !( *this == ref ); }
|
||||
ThisType& operator =( T* ptr );
|
||||
ThisType& operator =( const ThisType& ref );
|
||||
|
||||
bool operator !() const { return ( ptr() == 0 ); }
|
||||
T& operator *() const { return *ptr(); }
|
||||
T* operator ->() const { return ptr(); }
|
||||
operator T*() const { return ptr(); }
|
||||
|
||||
protected:
|
||||
|
||||
T* volatile mPtr;
|
||||
|
||||
static bool isTaggedPtr( T* ptr ) { return TypeTraits< T* >::isTaggedPtr( ptr ); }
|
||||
static T* getTaggedPtr( T* ptr ) { return TypeTraits< T* >::getTaggedPtr( ptr ); }
|
||||
static T* getUntaggedPtr( T* ptr ) { return TypeTraits< T* >::getUntaggedPtr( ptr ); }
|
||||
};
|
||||
|
||||
/// Update the reference from pointing to oldVal to point to newVal.
|
||||
/// Do so in a thread-safe way.
|
||||
///
|
||||
/// This operation will only succeed, if, when doing the pointer-swapping,
|
||||
/// the reference still points to oldVal. If, however, the reference
|
||||
/// has been changed in the meantime by another thread, the operation will
|
||||
/// fail.
|
||||
///
|
||||
/// @param oldVal The pointer assumed to currently be contained in this ThreadSafeRef.
|
||||
/// @param newVal The pointer to store in this ThreadSafeRef.
|
||||
/// @param tag Operation to perform on the reference's tag field.
|
||||
///
|
||||
/// @return true, if the reference now points to newVal.
|
||||
|
||||
template< class T >
|
||||
bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag )
|
||||
{
|
||||
bool setTag = false;
|
||||
bool getTag = false;
|
||||
bool isTagged = isTaggedPtr( oldVal );
|
||||
|
||||
switch( tag )
|
||||
{
|
||||
case TAG_PreserveOld: setTag = isTaggedPtr( oldVal ); break;
|
||||
case TAG_PreserveNew: setTag = isTaggedPtr( newVal ); break;
|
||||
case TAG_Set: setTag = true; break;
|
||||
case TAG_Unset: setTag = false; break;
|
||||
case TAG_SetOrFail: setTag = true; getTag = true; break;
|
||||
case TAG_UnsetOrFail: setTag = false; getTag = true; break;
|
||||
case TAG_FailIfSet: if( isTagged ) return false; break;
|
||||
case TAG_FailIfUnset: if( !isTagged ) return false; break;
|
||||
}
|
||||
|
||||
T* newValPtr = ( setTag
|
||||
? getTaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) )
|
||||
: getUntaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) ) );
|
||||
|
||||
if( dCompareAndSwap( mPtr,
|
||||
( getTag
|
||||
? ( setTag
|
||||
? getUntaggedPtr( oldVal )
|
||||
: getTaggedPtr( oldVal ) )
|
||||
: oldVal ),
|
||||
newValPtr ) )
|
||||
{
|
||||
if( getUntaggedPtr( oldVal ) )
|
||||
getUntaggedPtr( oldVal )->release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( getUntaggedPtr( newValPtr ) )
|
||||
getUntaggedPtr( newValPtr )->release();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag )
|
||||
{
|
||||
return trySetFromTo( oldVal, newVal.mPtr, tag );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline bool ThreadSafeRef< T >::trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag )
|
||||
{
|
||||
return trySetFromTo( oldVal.mPtr, newVal.mPtr, tag );
|
||||
}
|
||||
|
||||
/// Update ref to point to ptr but <em>do not</em> release an existing
|
||||
/// reference held by ref nor do the operation in a thread-safe way.
|
||||
///
|
||||
/// This method is <em>only</em> for when you absolutely know that your
|
||||
/// thread is the only thread operating on a reference <em>and</em> you
|
||||
/// are keeping track of reference counts yourself.
|
||||
///
|
||||
/// @param ref The reference to update.
|
||||
/// @param ptr The new pointer to store in ref.
|
||||
|
||||
template< class T >
|
||||
inline void ThreadSafeRef< T >::unsafeWrite( ThisType& ref, T* ptr )
|
||||
{
|
||||
ref.mPtr = ptr;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline bool ThreadSafeRef< T >::operator ==( T* p ) const
|
||||
{
|
||||
return ( ptr() == p );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline bool ThreadSafeRef< T >::operator ==( const ThisType& ref ) const
|
||||
{
|
||||
return ( ptr() == ref.ptr() );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline ThreadSafeRef< T >& ThreadSafeRef< T >::operator =( T* ptr )
|
||||
{
|
||||
while( !trySetFromTo( mPtr, ptr, TAG_PreserveNew ) );
|
||||
return *this;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline ThreadSafeRef< T >& ThreadSafeRef< T >::operator =( const ThisType& ref )
|
||||
{
|
||||
while( !trySetFromTo( mPtr, ref, TAG_PreserveNew ) );
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
struct TypeTraits< ThreadSafeRef< T > > : public TypeTraits< T* > {};
|
||||
template< typename T >
|
||||
inline T& Deref( ThreadSafeRef< T >& ref )
|
||||
{
|
||||
return *ref;
|
||||
}
|
||||
template< typename T >
|
||||
inline T& Deref( const ThreadSafeRef< T >& ref )
|
||||
{
|
||||
return *ref;
|
||||
}
|
||||
|
||||
#endif // _THREADSAFEREFCOUNT_H_
|
||||
23
Engine/source/platform/tmm_off.h
Normal file
23
Engine/source/platform/tmm_off.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#undef new
|
||||
25
Engine/source/platform/tmm_on.h
Normal file
25
Engine/source/platform/tmm_on.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef TORQUE_DISABLE_MEMORY_MANAGER
|
||||
# define new _new
|
||||
#endif
|
||||
98
Engine/source/platform/types.codewarrior.h
Normal file
98
Engine/source/platform/types.codewarrior.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef INCLUDED_TYPES_CODEWARRIOR_H
|
||||
#define INCLUDED_TYPES_CODEWARRIOR_H
|
||||
|
||||
#pragma once
|
||||
|
||||
// If using the IDE detect if DEBUG build was requested
|
||||
#if __ide_target("Torque-W32-Debug")
|
||||
#define TORQUE_DEBUG
|
||||
#elif __ide_target("Torque-MacCarb-Debug")
|
||||
#define TORQUE_DEBUG
|
||||
#elif __ide_target("Torque-MacX-Debug")
|
||||
#define TORQUE_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Types
|
||||
typedef signed long long S64; ///< Compiler independent Signed 64-bit integer
|
||||
typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer
|
||||
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Compiler Version
|
||||
#define TORQUE_COMPILER_CODEWARRIOR __MWERKS__
|
||||
|
||||
#define TORQUE_COMPILER_STRING "CODEWARRIOR"
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the Operating System
|
||||
#if defined(_WIN32)
|
||||
# define TORQUE_OS_STRING "Win32"
|
||||
# define TORQUE_OS_WIN32
|
||||
# include "platform/types.win32.h"
|
||||
|
||||
#elif defined(macintosh) || defined(__APPLE__)
|
||||
# define TORQUE_OS_STRING "Mac"
|
||||
# define TORQUE_OS_MAC
|
||||
# if defined(__MACH__)
|
||||
# define TORQUE_OS_MAC
|
||||
# endif
|
||||
# include "platform/types.ppc.h"
|
||||
#else
|
||||
# error "CW: Unsupported Operating System"
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the CPU
|
||||
#if defined(_M_IX86)
|
||||
# define TORQUE_CPU_STRING "x86"
|
||||
# define TORQUE_CPU_X86
|
||||
# define TORQUE_LITTLE_ENDIAN
|
||||
# define TORQUE_SUPPORTS_NASM
|
||||
# define TORQUE_SUPPORTS_VC_INLINE_X86_ASM
|
||||
|
||||
// Compiling with the CW IDE we cannot use NASM :(
|
||||
# if __ide_target("Torque-W32-Debug")
|
||||
# undef TORQUE_SUPPORTS_NASM
|
||||
# elif __ide_target("Torque-W32-Release")
|
||||
# undef TORQUE_SUPPORTS_NASM
|
||||
# endif
|
||||
|
||||
#elif defined(__POWERPC__)
|
||||
# define TORQUE_CPU_STRING "PowerPC"
|
||||
# define TORQUE_CPU_PPC
|
||||
# define TORQUE_BIG_ENDIAN
|
||||
|
||||
#else
|
||||
# error "CW: Unsupported Target CPU"
|
||||
#endif
|
||||
|
||||
|
||||
#endif // INCLUDED_TYPES_CODEWARRIOR_H
|
||||
|
||||
163
Engine/source/platform/types.gcc.h
Normal file
163
Engine/source/platform/types.gcc.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESGCC_H
|
||||
#define _TYPESGCC_H
|
||||
|
||||
|
||||
// For additional information on GCC predefined macros
|
||||
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp.html
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Types
|
||||
typedef signed long long S64;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Compiler Version
|
||||
#define TORQUE_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the compiler string
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
# define TORQUE_COMPILER_STRING "GCC (MinGW)"
|
||||
# define TORQUE_COMPILER_MINGW
|
||||
#elif defined(__CYGWIN__)
|
||||
# define TORQUE_COMPILER_STRING "GCC (Cygwin)"
|
||||
# define TORQUE_COMPILER_MINGW
|
||||
#else
|
||||
# define TORQUE_COMPILER_STRING "GCC "
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the Operating System
|
||||
#if defined(__WIN32__) || defined(_WIN32)
|
||||
# define TORQUE_OS_STRING "Win32"
|
||||
# define TORQUE_OS_WIN32
|
||||
# define TORQUE_SUPPORTS_NASM
|
||||
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
|
||||
# include "platform/types.win32.h"
|
||||
|
||||
#elif defined(SN_TARGET_PS3)
|
||||
# define TORQUE_OS_STRING "PS3"
|
||||
# define TORQUE_OS_PS3
|
||||
# include "platform/types.posix.h"
|
||||
|
||||
#elif defined(linux)
|
||||
# define TORQUE_OS_STRING "Linux"
|
||||
# define TORQUE_OS_LINUX
|
||||
# define TORQUE_SUPPORTS_NASM
|
||||
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
|
||||
# include "platform/types.posix.h"
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
# define TORQUE_OS_STRING "OpenBSD"
|
||||
# define TORQUE_OS_OPENBSD
|
||||
# define TORQUE_SUPPORTS_NASM
|
||||
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
|
||||
# include "platform/types.posix.h"
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
# define TORQUE_OS_STRING "FreeBSD"
|
||||
# define TORQUE_OS_FREEBSD
|
||||
# define TORQUE_SUPPORTS_NASM
|
||||
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
|
||||
# include "platform/types.posix.h"
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
# define TORQUE_OS_STRING "MacOS X"
|
||||
# define TORQUE_OS_MAC
|
||||
# include "platform/types.mac.h"
|
||||
# if defined(i386)
|
||||
// Disabling ASM on XCode for shared library build code relocation issues
|
||||
// This could be reconfigured for static builds, though minimal impact
|
||||
//# define TORQUE_SUPPORTS_NASM
|
||||
# endif
|
||||
#else
|
||||
# error "GCC: Unsupported Operating System"
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the CPU
|
||||
#if defined(i386)
|
||||
# define TORQUE_CPU_STRING "Intel x86"
|
||||
# define TORQUE_CPU_X86
|
||||
# define TORQUE_LITTLE_ENDIAN
|
||||
|
||||
#elif defined(__ppc__)
|
||||
# define TORQUE_CPU_STRING "PowerPC"
|
||||
# define TORQUE_CPU_PPC
|
||||
# define TORQUE_BIG_ENDIAN
|
||||
|
||||
#elif defined(SN_TARGET_PS3)
|
||||
# define TORQUE_CPU_STRING "PowerPC"
|
||||
# define TORQUE_CPU_PPC
|
||||
# define TORQUE_BIG_ENDIAN
|
||||
|
||||
#else
|
||||
# error "GCC: Unsupported Target CPU"
|
||||
#endif
|
||||
|
||||
#ifndef Offset
|
||||
/// Offset macro:
|
||||
/// Calculates the location in memory of a given member x of class cls from the
|
||||
/// start of the class. Need several definitions to account for various
|
||||
/// flavors of GCC.
|
||||
|
||||
// now, for each compiler type, define the Offset macros that should be used.
|
||||
// The Engine code usually uses the Offset macro, but OffsetNonConst is needed
|
||||
// when a variable is used in the indexing of the member field (see
|
||||
// TSShapeConstructor::initPersistFields for an example)
|
||||
|
||||
// compiler is non-GCC, or gcc < 3
|
||||
#if (__GNUC__ < 3)
|
||||
#define Offset(x, cls) _Offset_Normal(x, cls)
|
||||
#define OffsetNonConst(x, cls) _Offset_Normal(x, cls)
|
||||
|
||||
// compiler is GCC 3 with minor version less than 4
|
||||
#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
|
||||
#define Offset(x, cls) _Offset_Variant_1(x, cls)
|
||||
#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls)
|
||||
|
||||
// compiler is GCC 3 with minor version greater than 4
|
||||
#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)
|
||||
#include <stddef.h>
|
||||
#define Offset(x, cls) _Offset_Variant_2(x, cls)
|
||||
#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls)
|
||||
|
||||
// compiler is GCC 4
|
||||
#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 4)
|
||||
#include <stddef.h>
|
||||
#define Offset(x, cls) _Offset_Normal(x, cls)
|
||||
#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // INCLUDED_TYPES_GCC_H
|
||||
|
||||
299
Engine/source/platform/types.h
Normal file
299
Engine/source/platform/types.h
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#define _TORQUE_TYPES_H_
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Basic Types...
|
||||
|
||||
typedef signed char S8; ///< Compiler independent Signed Char
|
||||
typedef unsigned char U8; ///< Compiler independent Unsigned Char
|
||||
|
||||
typedef signed short S16; ///< Compiler independent Signed 16-bit short
|
||||
typedef unsigned short U16; ///< Compiler independent Unsigned 16-bit short
|
||||
|
||||
typedef signed int S32; ///< Compiler independent Signed 32-bit integer
|
||||
typedef unsigned int U32; ///< Compiler independent Unsigned 32-bit integer
|
||||
|
||||
typedef float F32; ///< Compiler independent 32-bit float
|
||||
typedef double F64; ///< Compiler independent 64-bit float
|
||||
|
||||
struct EmptyType {}; ///< "Null" type used by templates
|
||||
|
||||
#define TORQUE_UNUSED(var) (void)var
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------- String Types
|
||||
|
||||
typedef char UTF8; ///< Compiler independent 8 bit Unicode encoded character
|
||||
typedef unsigned short UTF16; ///< Compiler independent 16 bit Unicode encoded character
|
||||
typedef unsigned int UTF32; ///< Compiler independent 32 bit Unicode encoded character
|
||||
|
||||
typedef const char* StringTableEntry;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Type constants...
|
||||
#define __EQUAL_CONST_F F32(0.000001) ///< Constant float epsilon used for F32 comparisons
|
||||
|
||||
extern const F32 Float_Inf;
|
||||
static const F32 Float_One = F32(1.0); ///< Constant float 1.0
|
||||
static const F32 Float_Half = F32(0.5); ///< Constant float 0.5
|
||||
static const F32 Float_Zero = F32(0.0); ///< Constant float 0.0
|
||||
static const F32 Float_Pi = F32(3.14159265358979323846); ///< Constant float PI
|
||||
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); ///< Constant float 2*PI
|
||||
static const F32 Float_InversePi = F32(1.0 / 3.14159265358979323846); ///< Constant float 1 / PI
|
||||
static const F32 Float_HalfPi = F32(0.5 * 3.14159265358979323846); ///< Constant float 1/2 * PI
|
||||
static const F32 Float_2InversePi = F32(2.0 / 3.14159265358979323846);///< Constant float 2 / PI
|
||||
static const F32 Float_Inverse2Pi = F32(0.5 / 3.14159265358979323846);///< Constant float 0.5 / PI
|
||||
|
||||
static const F32 Float_Sqrt2 = F32(1.41421356237309504880f); ///< Constant float sqrt(2)
|
||||
static const F32 Float_SqrtHalf = F32(0.7071067811865475244008443f); ///< Constant float sqrt(0.5)
|
||||
|
||||
static const S8 S8_MIN = S8(-128); ///< Constant Min Limit S8
|
||||
static const S8 S8_MAX = S8(127); ///< Constant Max Limit S8
|
||||
static const U8 U8_MAX = U8(255); ///< Constant Max Limit U8
|
||||
|
||||
static const S16 S16_MIN = S16(-32768); ///< Constant Min Limit S16
|
||||
static const S16 S16_MAX = S16(32767); ///< Constant Max Limit S16
|
||||
static const U16 U16_MAX = U16(65535); ///< Constant Max Limit U16
|
||||
|
||||
static const S32 S32_MIN = S32(-2147483647 - 1); ///< Constant Min Limit S32
|
||||
static const S32 S32_MAX = S32(2147483647); ///< Constant Max Limit S32
|
||||
static const U32 U32_MAX = U32(0xffffffff); ///< Constant Max Limit U32
|
||||
|
||||
static const F32 F32_MIN = F32(1.175494351e-38F); ///< Constant Min Limit F32
|
||||
static const F32 F32_MAX = F32(3.402823466e+38F); ///< Constant Max Limit F32
|
||||
|
||||
// define all the variants of Offset that we might use
|
||||
#define _Offset_Normal(x, cls) ((dsize_t)((const char *)&(((cls *)1)->x)-(const char *)1))
|
||||
#define _Offset_Variant_1(x, cls) ((int)(&((cls *)1)->x) - 1)
|
||||
#define _Offset_Variant_2(x, cls) offsetof(cls, x) // also requires #include <stddef.h>
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the compiler being used
|
||||
|
||||
// PC-lint
|
||||
#if defined(_lint)
|
||||
# include "platform/types.lint.h"
|
||||
// Metrowerks CodeWarrior
|
||||
#elif defined(__MWERKS__)
|
||||
# include "platform/types.codewarrior.h"
|
||||
// Microsoft Visual C++/Visual.NET
|
||||
#elif defined(_MSC_VER)
|
||||
# include "platform/types.visualc.h"
|
||||
// GNU GCC
|
||||
#elif defined(__GNUC__)
|
||||
# include "platform/types.gcc.h"
|
||||
#else
|
||||
# error "Unknown Compiler"
|
||||
#endif
|
||||
|
||||
/// Integral type matching the host's memory address width.
|
||||
#ifdef TORQUE_64BITS
|
||||
typedef U64 MEM_ADDRESS;
|
||||
#else
|
||||
typedef U32 MEM_ADDRESS;
|
||||
#endif
|
||||
|
||||
//-------------------------------------- Some all-around useful inlines and globals
|
||||
//
|
||||
|
||||
/// Returns power of 2 number which is as small as possible but
|
||||
/// still greater than or equal to input number. Note: returns 0
|
||||
/// for an input of 0 even though that is not a power of 2.
|
||||
/// @param num Any U32
|
||||
inline U32 getNextPow2(U32 num)
|
||||
{
|
||||
// Taken from: http://graphics.stanford.edu/~seander/bithacks.html
|
||||
|
||||
num--;
|
||||
num |= num >> 1;
|
||||
num |= num >> 2;
|
||||
num |= num >> 4;
|
||||
num |= num >> 8;
|
||||
num |= num >> 16;
|
||||
num++;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/// Return integer log2 of input number (rounding down). So, e.g.,
|
||||
/// getBinLog2(7) == 2 whereas getBinLog2(8) == 3. If known
|
||||
/// @param num Any U32
|
||||
/// @param knownPow2 Is num a known power of 2?
|
||||
inline U32 getBinLog2(U32 num, bool knownPow2 = false)
|
||||
{
|
||||
// Taken from: http://graphics.stanford.edu/~seander/bithacks.html
|
||||
|
||||
static const U32 MultiplyDeBruijnBitPosition[32] =
|
||||
{
|
||||
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
|
||||
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
|
||||
};
|
||||
|
||||
if (!knownPow2)
|
||||
{
|
||||
num |= num >> 1; // first round down to power of 2
|
||||
num |= num >> 2;
|
||||
num |= num >> 4;
|
||||
num |= num >> 8;
|
||||
num |= num >> 16;
|
||||
num = (num >> 1) + 1;
|
||||
}
|
||||
|
||||
return MultiplyDeBruijnBitPosition[(num * 0x077CB531UL) >> 27];
|
||||
}
|
||||
|
||||
/// Determines if the given U32 is some 2^n
|
||||
/// @param num Any U32
|
||||
/// @returns true if in_num is a power of two, otherwise false
|
||||
inline bool isPow2(const U32 num)
|
||||
{
|
||||
return (num & (num - 1)) == 0;
|
||||
}
|
||||
|
||||
/// Determines the binary logarithm of the next greater power of two of the input number.
|
||||
inline U32 getNextBinLog2(U32 number)
|
||||
{
|
||||
return getBinLog2(number) + (isPow2(number) ? 0 : 1);
|
||||
}
|
||||
|
||||
//----------------Many versions of min and max-------------
|
||||
//---not using template functions because MS VC++ chokes---
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline U32 getMin(U32 a, U32 b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline U16 getMin(U16 a, U16 b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline U8 getMin(U8 a, U8 b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline S32 getMin(S32 a, S32 b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline S16 getMin(S16 a, S16 b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline S8 getMin(S8 a, S8 b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline float getMin(float a, float b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the lesser of the two parameters: a & b.
|
||||
inline double getMin(double a, double b)
|
||||
{
|
||||
return a>b ? b : a;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline U32 getMax(U32 a, U32 b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline U16 getMax(U16 a, U16 b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline U8 getMax(U8 a, U8 b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline S32 getMax(S32 a, S32 b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline S16 getMax(S16 a, S16 b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline S8 getMax(S8 a, S8 b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline float getMax(float a, float b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
/// Returns the greater of the two parameters: a & b.
|
||||
inline double getMax(double a, double b)
|
||||
{
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
//-------------------------------------- Use this instead of Win32 FOURCC()
|
||||
// macro...
|
||||
//
|
||||
#define makeFourCCTag(ch0, ch1, ch2, ch3) \
|
||||
(((U32(ch0) & 0xFF) << 0) | \
|
||||
((U32(ch1) & 0xFF) << 8) | \
|
||||
((U32(ch2) & 0xFF) << 16) | \
|
||||
((U32(ch3) & 0xFF) << 24) )
|
||||
|
||||
#define makeFourCCString(ch0, ch1, ch2, ch3) { ch0, ch1, ch2, ch3 }
|
||||
|
||||
#define BIT(x) (1 << (x)) ///< Returns value with bit x set (2^x)
|
||||
|
||||
#if defined(TORQUE_OS_WIN32)
|
||||
#define STDCALL __stdcall
|
||||
#else
|
||||
#define STDCALL
|
||||
#endif
|
||||
|
||||
#endif //_TORQUE_TYPES_H_
|
||||
58
Engine/source/platform/types.lint.h
Normal file
58
Engine/source/platform/types.lint.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef TORQUE_TYPES_LINT_H_
|
||||
#define TORQUE_TYPES_LINT_H_
|
||||
|
||||
typedef signed long long S64;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
typedef unsigned int dsize_t;
|
||||
|
||||
struct FileTime
|
||||
{
|
||||
U32 v1;
|
||||
U32 v2;
|
||||
};
|
||||
|
||||
#define TORQUE_OS_STRING "Lint"
|
||||
#define TORQUE_CPU_STRING "x86"
|
||||
#define TORQUE_LITTLE_ENDIAN
|
||||
#define TORQUE_SUPPORTS_NASM
|
||||
#define TORQUE_SUPPORTS_VC_INLINE_X86_ASM
|
||||
#define TORQUE_OS_WIN32
|
||||
#define TORQUE_COMPILER_VISUALC 1500
|
||||
|
||||
#ifndef FN_CDECL
|
||||
#define FN_CDECL
|
||||
#endif
|
||||
|
||||
#ifndef Offset
|
||||
#define Offset(x, cls) _Offset_Normal(x, cls)
|
||||
#define OffsetNonConst(x, cls) _Offset_Normal(x, cls)
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
51
Engine/source/platform/types.mac.h
Normal file
51
Engine/source/platform/types.mac.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESPPC_H_
|
||||
#define _TYPESPPC_H_
|
||||
|
||||
///< Calling convention
|
||||
#define FN_CDECL
|
||||
#define STDCALL
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef unsigned long dsize_t;
|
||||
|
||||
|
||||
/** Platform dependent file date-time structure. The defination of this structure
|
||||
* will likely be different for each OS platform.
|
||||
* On the PPC is a 64-bit structure for storing the date/time for a file
|
||||
*/
|
||||
|
||||
// 64-bit structure for storing the date/time for a file
|
||||
// The date and time, specified in seconds since the unix epoch.
|
||||
// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes.
|
||||
typedef U64 FileTime;
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL (0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_TYPESPPC_H_
|
||||
46
Engine/source/platform/types.posix.h
Normal file
46
Engine/source/platform/types.posix.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESPOSIX_H_
|
||||
#define _TYPESPOSIX_H_
|
||||
|
||||
|
||||
#define FN_CDECL ///< Calling convention
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef unsigned int dsize_t;
|
||||
|
||||
|
||||
/** Platform dependent file date-time structure. The defination of this structure
|
||||
* will likely be different for each OS platform.
|
||||
*/
|
||||
typedef S32 FileTime;
|
||||
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL (0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_TYPESPOSIX_H_
|
||||
50
Engine/source/platform/types.ppc.h
Normal file
50
Engine/source/platform/types.ppc.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESPPC_H_
|
||||
#define _TYPESPPC_H_
|
||||
|
||||
///< Calling convention
|
||||
#define FN_CDECL
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef unsigned long dsize_t;
|
||||
|
||||
|
||||
/** Platform dependent file date-time structure. The defination of this structure
|
||||
* will likely be different for each OS platform.
|
||||
* On the PPC is a 64-bit structure for storing the date/time for a file
|
||||
*/
|
||||
|
||||
// 64-bit structure for storing the date/time for a file
|
||||
// The date and time, specified in seconds since the unix epoch.
|
||||
// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes.
|
||||
typedef U64 FileTime;
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL (0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_TYPESPPC_H_
|
||||
99
Engine/source/platform/types.visualc.h
Normal file
99
Engine/source/platform/types.visualc.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef INCLUDED_TYPES_VISUALC_H
|
||||
#define INCLUDED_TYPES_VISUALC_H
|
||||
|
||||
|
||||
// For more information on VisualC++ predefined macros
|
||||
// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q65472
|
||||
|
||||
//--------------------------------------
|
||||
// Types
|
||||
typedef signed _int64 S64;
|
||||
typedef unsigned _int64 U64;
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Compiler Version
|
||||
#define TORQUE_COMPILER_VISUALC _MSC_VER
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the compiler string
|
||||
#if _MSC_VER < 1200
|
||||
// No support for old compilers
|
||||
# error "VC: Minimum VisualC++ 6.0 or newer required"
|
||||
#else _MSC_VER >= 1200
|
||||
# define TORQUE_COMPILER_STRING "VisualC++"
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the Operating System
|
||||
#if _XBOX_VER >= 200
|
||||
# define TORQUE_OS_STRING "Xenon"
|
||||
# ifndef TORQUE_OS_XENON
|
||||
# define TORQUE_OS_XENON
|
||||
# endif
|
||||
# include "platform/types.xenon.h"
|
||||
#elif defined( _XBOX_VER )
|
||||
# define TORQUE_OS_STRING "Xbox"
|
||||
# define TORQUE_OS_XBOX
|
||||
# include "platform/types.win32.h"
|
||||
#elif defined(_WIN32)
|
||||
# define TORQUE_OS_STRING "Win32"
|
||||
# define TORQUE_OS_WIN32
|
||||
# include "platform/types.win32.h"
|
||||
#else
|
||||
# error "VC: Unsupported Operating System"
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Identify the CPU
|
||||
#if defined(_M_IX86)
|
||||
# define TORQUE_CPU_STRING "x86"
|
||||
# define TORQUE_CPU_X86
|
||||
# define TORQUE_LITTLE_ENDIAN
|
||||
# define TORQUE_SUPPORTS_NASM
|
||||
# define TORQUE_SUPPORTS_VC_INLINE_X86_ASM
|
||||
#elif defined(TORQUE_OS_XENON)
|
||||
# define TORQUE_CPU_STRING "ppc"
|
||||
# define TORQUE_CPU_PPC
|
||||
# define TORQUE_BIG_ENDIAN
|
||||
#else
|
||||
# error "VC: Unsupported Target CPU"
|
||||
#endif
|
||||
|
||||
#ifndef FN_CDECL
|
||||
# define FN_CDECL __cdecl ///< Calling convention
|
||||
#endif
|
||||
|
||||
#define for if(false) {} else for ///< Hack to work around Microsoft VC's non-C++ compliance on variable scoping
|
||||
|
||||
// disable warning caused by memory layer
|
||||
// see msdn.microsoft.com "Compiler Warning (level 1) C4291" for more details
|
||||
#pragma warning(disable: 4291)
|
||||
|
||||
|
||||
#endif // INCLUDED_TYPES_VISUALC_H
|
||||
|
||||
49
Engine/source/platform/types.win32.h
Normal file
49
Engine/source/platform/types.win32.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESWIN32_H_
|
||||
#define _TYPESWIN32_H_
|
||||
|
||||
|
||||
#define FN_CDECL __cdecl ///< Calling convention
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef unsigned int dsize_t;
|
||||
|
||||
|
||||
/// Platform dependent file date-time structure. The definition of this structure
|
||||
/// will likely be different for each OS platform.
|
||||
struct FileTime
|
||||
{
|
||||
U32 v1;
|
||||
U32 v2;
|
||||
};
|
||||
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_TYPESWIN32_H_
|
||||
49
Engine/source/platform/types.xenon.h
Normal file
49
Engine/source/platform/types.xenon.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESXENON_H_
|
||||
#define _TYPESXENON_H_
|
||||
|
||||
///< Calling convention
|
||||
#ifdef FN_CDECL
|
||||
# undef FN_CDECL
|
||||
#endif
|
||||
#define FN_CDECL __cdecl
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef size_t dsize_t;
|
||||
|
||||
struct FileTime
|
||||
{
|
||||
U32 v1;
|
||||
U32 v2;
|
||||
};
|
||||
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL (0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_TYPESXENON_H_
|
||||
80
Engine/source/platform/typesLinux.h
Normal file
80
Engine/source/platform/typesLinux.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESLINUX_H_
|
||||
#define _TYPESLINUX_H_
|
||||
|
||||
/* eek. */
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
#define FN_CDECL
|
||||
|
||||
typedef signed char S8;
|
||||
typedef unsigned char U8;
|
||||
|
||||
typedef signed short S16;
|
||||
typedef unsigned short U16;
|
||||
|
||||
typedef signed int S32;
|
||||
typedef unsigned int U32;
|
||||
|
||||
typedef signed long long S64;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
typedef float F32;
|
||||
typedef double F64;
|
||||
|
||||
typedef unsigned int dsize_t;
|
||||
|
||||
typedef const char* StringTableEntry;
|
||||
|
||||
typedef S32 FileTime;
|
||||
|
||||
#define __EQUAL_CONST_F F32(0.000001)
|
||||
|
||||
static const F32 Float_One = F32(1.0);
|
||||
static const F32 Float_Half = F32(0.5);
|
||||
static const F32 Float_Zero = F32(0.0);
|
||||
static const F32 Float_Pi = F32(3.14159265358979323846);
|
||||
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846);
|
||||
|
||||
static const S8 S8_MIN = S8(-128);
|
||||
static const S8 S8_MAX = S8(127);
|
||||
static const U8 U8_MAX = U8(255);
|
||||
|
||||
static const S16 S16_MIN = S16(-32768);
|
||||
static const S16 S16_MAX = S16(32767);
|
||||
static const U16 U16_MAX = U16(65535);
|
||||
|
||||
static const S32 S32_MIN = S32(-2147483647 - 1);
|
||||
static const S32 S32_MAX = S32(2147483647);
|
||||
static const U32 U32_MAX = U32(0xffffffff);
|
||||
|
||||
static const F32 F32_MAX = F32(3.402823466e+38F);
|
||||
static const F32 F32_MIN = F32(1.175494351e-38F);
|
||||
|
||||
|
||||
#endif
|
||||
99
Engine/source/platform/typesPPC.h
Normal file
99
Engine/source/platform/typesPPC.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPES_PPC_H_
|
||||
#define _TYPES_PPC_H_
|
||||
|
||||
// We have to check this. Since every file will eventually wind up including
|
||||
// this header, but not every header includes a windows or system header...
|
||||
//
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
// Let's just have this in a nice central location. Again, since every file
|
||||
// will wind up including this file, we can affect compilation most effectively
|
||||
// from this location.
|
||||
//
|
||||
#define PLATFORM_BIG_ENDIAN
|
||||
|
||||
#define FN_CDECL
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Basic Types...
|
||||
|
||||
typedef signed char S8;
|
||||
typedef unsigned char U8;
|
||||
|
||||
typedef signed short S16;
|
||||
typedef unsigned short U16;
|
||||
|
||||
typedef signed int S32;
|
||||
typedef unsigned int U32;
|
||||
|
||||
typedef signed long long S64;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
typedef float F32;
|
||||
typedef double F64;
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef unsigned long dsize_t;
|
||||
|
||||
typedef const char* StringTableEntry;
|
||||
|
||||
// 64-bit structure for storing the date/time for a file
|
||||
// The date and time, specified in seconds since the unix epoch.
|
||||
// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes.
|
||||
typedef U64 FileTime;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Type constants...
|
||||
#define __EQUAL_CONST_F F32(0.000001)
|
||||
|
||||
static const F32 Float_One = F32(1.0);
|
||||
static const F32 Float_Half = F32(0.5);
|
||||
static const F32 Float_Zero = F32(0.0);
|
||||
static const F32 Float_Pi = F32(3.14159265358979323846);
|
||||
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846);
|
||||
|
||||
static const S8 S8_MIN = S8(-128);
|
||||
static const S8 S8_MAX = S8(127);
|
||||
static const U8 U8_MAX = U8(255);
|
||||
|
||||
static const S16 S16_MIN = S16(-32768);
|
||||
static const S16 S16_MAX = S16(32767);
|
||||
static const U16 U16_MAX = U16(65535);
|
||||
|
||||
static const S32 S32_MIN = S32(-2147483647 - 1);
|
||||
static const S32 S32_MAX = S32(2147483647);
|
||||
static const U32 U32_MAX = U32(0xffffffff);
|
||||
|
||||
static const F32 F32_MAX = F32(3.402823466e+38F);
|
||||
static const F32 F32_MIN = F32(1.175494351e-38F);
|
||||
|
||||
|
||||
#endif //_TYPES_PPC_H_
|
||||
123
Engine/source/platform/typesWin32.h
Normal file
123
Engine/source/platform/typesWin32.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESWIN32_H_
|
||||
#define _TYPESWIN32_H_
|
||||
|
||||
// We have to check this. Since every file will eventually wind up including
|
||||
// this header, but not every header includes a windows or system header...
|
||||
//
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
// Let's just have this in a nice central location. Again, since every file
|
||||
// will wind up including this file, we can affect compilation most effectively
|
||||
// from this location.
|
||||
//
|
||||
#define PLATFORM_LITTLE_ENDIAN ///< Signals this platfrom is Little Endian
|
||||
|
||||
|
||||
#define FN_CDECL __cdecl ///< Calling convention
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Basic Types...
|
||||
|
||||
typedef signed char S8; ///< Compiler independent Signed Char
|
||||
typedef unsigned char U8; ///< Compiler independent Unsigned Char
|
||||
|
||||
typedef signed short S16; ///< Compiler independent Signed 16-bit short
|
||||
typedef unsigned short U16; ///< Compiler independent Unsigned 16-bit short
|
||||
|
||||
typedef signed int S32; ///< Compiler independent Signed 32-bit integer
|
||||
typedef unsigned int U32; ///< Compiler independent Unsigned 32-bit integer
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
typedef signed __int64 S64; ///< Compiler independent Signed 64-bit integer
|
||||
typedef unsigned __int64 U64; ///< Compiler independent Unsigned 64-bit integer
|
||||
|
||||
#elif defined(__MWERKS__) // This has to go before MSC_VER since CodeWarrior defines MSC_VER too
|
||||
typedef signed long long S64; ///< Compiler independent Signed 64-bit integer
|
||||
typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
typedef signed _int64 S64; ///< Compiler independent Signed 64-bit integer
|
||||
typedef unsigned _int64 U64; ///< Compiler independent Unsigned 64-bit integer
|
||||
#pragma warning(disable: 4291) // disable warning caused by memory layer...
|
||||
#pragma warning(disable: 4996) // turn off "deprecation" warnings
|
||||
|
||||
#else
|
||||
typedef signed long long S64; ///< Compiler independent Signed 64-bit integer
|
||||
typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer
|
||||
#endif
|
||||
|
||||
typedef float F32; ///< Compiler independent 32-bit float
|
||||
typedef double F64; ///< Compiler independent 64-bit float
|
||||
|
||||
// size_t is needed to overload new
|
||||
// size_t tends to be OS and compiler specific and may need to
|
||||
// be if/def'ed in the future
|
||||
typedef unsigned int dsize_t;
|
||||
|
||||
typedef const char* StringTableEntry;
|
||||
|
||||
/* Platform dependent file date-time structure. The defination of this structure
|
||||
* will likely be different for each OS platform.
|
||||
*/
|
||||
struct FileTime
|
||||
{
|
||||
U32 v1;
|
||||
U32 v2;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Type constants...
|
||||
#define __EQUAL_CONST_F F32(0.000001) ///< Constant float epsilon used for F32 comparisons
|
||||
|
||||
static const F32 Float_One = F32(1.0); ///< Constant float 1.0
|
||||
static const F32 Float_Half = F32(0.5); ///< Constant float 0.5
|
||||
static const F32 Float_Zero = F32(0.0); ///< Constant float 0.0
|
||||
static const F32 Float_Pi = F32(3.14159265358979323846); ///< Constant float PI
|
||||
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); ///< Constant float 2*PI
|
||||
|
||||
static const S8 S8_MIN = S8(-128); ///< Constant Min Limit S8
|
||||
static const S8 S8_MAX = S8(127); ///< Constant Max Limit S8
|
||||
static const U8 U8_MAX = U8(255); ///< Constant Max Limit U8
|
||||
|
||||
static const S16 S16_MIN = S16(-32768); ///< Constant Min Limit S16
|
||||
static const S16 S16_MAX = S16(32767); ///< Constant Max Limit S16
|
||||
static const U16 U16_MAX = U16(65535); ///< Constant Max Limit U16
|
||||
|
||||
static const S32 S32_MIN = S32(-2147483647 - 1); ///< Constant Min Limit S32
|
||||
static const S32 S32_MAX = S32(2147483647); ///< Constant Max Limit S32
|
||||
static const U32 U32_MAX = U32(0xffffffff); ///< Constant Max Limit U32
|
||||
|
||||
static const F32 F32_MIN = F32(1.175494351e-38F); ///< Constant Min Limit F32
|
||||
static const F32 F32_MAX = F32(3.402823466e+38F); ///< Constant Max Limit F32
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define for if(false) {} else for ///< Hack to work around Microsoft VC's non-C++ compliance on variable scoping
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_NTYPES_H_
|
||||
80
Engine/source/platform/typesX86UNIX.h
Normal file
80
Engine/source/platform/typesX86UNIX.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPESX86UNIX_H_
|
||||
#define _TYPESX86UNIX_H_
|
||||
|
||||
/* eek. */
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
#define FN_CDECL
|
||||
|
||||
typedef signed char S8;
|
||||
typedef unsigned char U8;
|
||||
|
||||
typedef signed short S16;
|
||||
typedef unsigned short U16;
|
||||
|
||||
typedef signed int S32;
|
||||
typedef unsigned int U32;
|
||||
|
||||
typedef signed long long S64;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
typedef float F32;
|
||||
typedef double F64;
|
||||
|
||||
typedef unsigned int dsize_t;
|
||||
|
||||
typedef const char* StringTableEntry;
|
||||
|
||||
typedef S32 FileTime;
|
||||
|
||||
#define __EQUAL_CONST_F F32(0.000001)
|
||||
|
||||
static const F32 Float_One = F32(1.0);
|
||||
static const F32 Float_Half = F32(0.5);
|
||||
static const F32 Float_Zero = F32(0.0);
|
||||
static const F32 Float_Pi = F32(3.14159265358979323846);
|
||||
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846);
|
||||
|
||||
static const S8 S8_MIN = S8(-128);
|
||||
static const S8 S8_MAX = S8(127);
|
||||
static const U8 U8_MAX = U8(255);
|
||||
|
||||
static const S16 S16_MIN = S16(-32768);
|
||||
static const S16 S16_MAX = S16(32767);
|
||||
static const U16 U16_MAX = U16(65535);
|
||||
|
||||
static const S32 S32_MIN = S32(-2147483647 - 1);
|
||||
static const S32 S32_MAX = S32(2147483647);
|
||||
static const U32 U32_MAX = U32(0xffffffff);
|
||||
|
||||
static const F32 F32_MAX = F32(3.402823466e+38F);
|
||||
static const F32 F32_MIN = F32(1.175494351e-38F);
|
||||
|
||||
|
||||
#endif
|
||||
425
Engine/source/platform/typetraits.h
Normal file
425
Engine/source/platform/typetraits.h
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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 OR COPYRIGHT HOLDERS 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TYPETRAITS_H_
|
||||
#define _TYPETRAITS_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
# include "platform/platform.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// @file
|
||||
/// Template definitions for introspecting type properties.
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Type Predicating.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct TrueType {};
|
||||
struct FalseType {};
|
||||
|
||||
template< typename T >
|
||||
inline bool IsTrueType()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
template<>
|
||||
inline bool IsTrueType< TrueType >()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool IsFalseType()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
template<>
|
||||
inline bool IsFalseType< FalseType >()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template< typename T, typename IfTrue, typename IfFalse >
|
||||
struct IfTrueType : public IfFalse {};
|
||||
template< typename IfTrue, typename IfFalse >
|
||||
struct IfTrueType< TrueType, IfTrue, IfFalse > : public IfTrue {};
|
||||
|
||||
template< typename T, typename IfTrue, typename IfFalse >
|
||||
struct IfFalseType : public IfTrue {};
|
||||
template< typename IfTrue, typename IfFalse >
|
||||
struct IfFalseType< FalseType, IfTrue, IfFalse > : public IfFalse {};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Construct.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct _ConstructDefault
|
||||
{
|
||||
template< typename T >
|
||||
static T single()
|
||||
{
|
||||
return T();
|
||||
}
|
||||
template< typename T, typename A >
|
||||
static T single( A a )
|
||||
{
|
||||
return T( a );
|
||||
}
|
||||
template< typename T, typename A, typename B >
|
||||
static T single( A a, B b )
|
||||
{
|
||||
return T( a, b );
|
||||
}
|
||||
template< typename T >
|
||||
static void array( T* ptr, U32 num )
|
||||
{
|
||||
constructArrayInPlace< T >( ptr, num );
|
||||
}
|
||||
template< typename T, typename A >
|
||||
static void array( T* ptr, U32 num, A a )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
ptr[ i ] = single< T >( a );
|
||||
}
|
||||
};
|
||||
struct _ConstructPrim
|
||||
{
|
||||
template< typename T >
|
||||
static T single()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
template< typename T, typename A >
|
||||
static T single( T a )
|
||||
{
|
||||
return a;
|
||||
}
|
||||
template< typename T >
|
||||
static void array( T* ptr, U32 num )
|
||||
{
|
||||
dMemset( ptr, 0, num * sizeof( T ) );
|
||||
}
|
||||
template< typename T, typename A >
|
||||
static void array( T* ptr, U32 num, T a )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
ptr[ i ] = a;
|
||||
}
|
||||
};
|
||||
struct _ConstructPtr
|
||||
{
|
||||
template< typename T >
|
||||
static T* single()
|
||||
{
|
||||
return new T;
|
||||
}
|
||||
template< typename T, typename A >
|
||||
static T* single( A a )
|
||||
{
|
||||
return new T( a );
|
||||
}
|
||||
template< typename T, typename A, typename B >
|
||||
static T* single( A a, B b )
|
||||
{
|
||||
return new T( a, b );
|
||||
}
|
||||
template< typename T >
|
||||
static void array( T** ptr, U32 num )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
ptr[ i ] = single< T >();
|
||||
}
|
||||
template< typename T, typename A >
|
||||
static void array( T** ptr, U32 num, A a )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
ptr[ i ] = single< T >( a );
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Destruct.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct _DestructDefault
|
||||
{
|
||||
template< typename T >
|
||||
static void single( T& val )
|
||||
{
|
||||
val.~T();
|
||||
}
|
||||
template< typename T >
|
||||
static void array( T* ptr, U32 num )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
single< T >( ptr[ i ] );
|
||||
}
|
||||
};
|
||||
struct _DestructPrim
|
||||
{
|
||||
template< typename T >
|
||||
static void single( T& val ) {}
|
||||
template< typename T >
|
||||
static void array( T* ptr, U32 num ) {}
|
||||
};
|
||||
struct _DestructPtr
|
||||
{
|
||||
template< typename T >
|
||||
static void single( T*& val )
|
||||
{
|
||||
delete val;
|
||||
val = NULL;
|
||||
}
|
||||
template< typename T >
|
||||
static void array( T* ptr, U32 num )
|
||||
{
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
single< T >( ptr[ i ] );
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// TypeTraits.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template< typename T >
|
||||
struct _TypeTraits
|
||||
{
|
||||
typedef T BaseType;
|
||||
typedef const T ConstType;
|
||||
typedef _ConstructDefault Construct;
|
||||
typedef _DestructDefault Destruct;
|
||||
};
|
||||
template< typename T >
|
||||
struct _TypeTraits< T* >
|
||||
{
|
||||
typedef T BaseType;
|
||||
typedef const T ConstType;
|
||||
typedef _ConstructPtr Construct;
|
||||
typedef _DestructPtr Destruct;
|
||||
|
||||
template< typename A >
|
||||
static bool isTaggedPtr( A* ptr ) { return ( U32( ptr ) & 0x1 ); } //TODO: 64bits
|
||||
template< typename A >
|
||||
static A* getTaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) | 0x1 ); } //TODO: 64bits
|
||||
template< typename A >
|
||||
static A* getUntaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) & 0xFFFFFFFE ); } //TODO: 64bit
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct TypeTraits : public TypeTraits< typename T::Parent >
|
||||
{
|
||||
typedef T BaseType;
|
||||
typedef const T ConstType;
|
||||
};
|
||||
template< typename T >
|
||||
struct TypeTraits< T* > : public TypeTraits< typename T::Parent* >
|
||||
{
|
||||
typedef T BaseType;
|
||||
typedef const T ConstType;
|
||||
};
|
||||
template< typename T >
|
||||
struct TypeTraits< T* const > : public TypeTraits< typename T::Parent* >
|
||||
{
|
||||
typedef T BaseType;
|
||||
typedef const T ConstType;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< void > : public _TypeTraits< void > {};
|
||||
template<>
|
||||
struct TypeTraits< void* > : public _TypeTraits< void* > {};
|
||||
template<>
|
||||
struct TypeTraits< void* const > : public _TypeTraits< void* > {};
|
||||
|
||||
// Type traits for primitive types.
|
||||
|
||||
template<>
|
||||
struct TypeTraits< bool > : public _TypeTraits< bool >
|
||||
{
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< S8 > : public _TypeTraits< S8 >
|
||||
{
|
||||
static const S8 MIN = S8_MIN;
|
||||
static const S8 MAX = S8_MAX;
|
||||
static const S8 ZERO = 0;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< U8 > : public _TypeTraits< U8 >
|
||||
{
|
||||
static const U8 MIN = 0;
|
||||
static const U8 MAX = U8_MAX;
|
||||
static const U8 ZERO = 0;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< S16 > : public _TypeTraits< S16 >
|
||||
{
|
||||
static const S16 MIN = S16_MIN;
|
||||
static const S16 MAX = S16_MAX;
|
||||
static const S16 ZERO = 0;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< U16 > : public _TypeTraits< U16 >
|
||||
{
|
||||
static const U16 MIN = 0;
|
||||
static const U16 MAX = U16_MAX;
|
||||
static const U16 ZERO = 0;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< S32 > : public _TypeTraits< S32 >
|
||||
{
|
||||
static const S32 MIN = S32_MIN;
|
||||
static const S32 MAX = S32_MAX;
|
||||
static const S32 ZERO = 0;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< U32 > : public _TypeTraits< U32 >
|
||||
{
|
||||
static const U32 MIN = 0;
|
||||
static const U32 MAX = U32_MAX;
|
||||
static const U32 ZERO = 0;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
template<>
|
||||
struct TypeTraits< F32 > : public _TypeTraits< F32 >
|
||||
{
|
||||
static const F32 MIN;
|
||||
static const F32 MAX;
|
||||
static const F32 ZERO;
|
||||
typedef _ConstructPrim Construct;
|
||||
typedef _DestructPrim Destruct;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Utilities.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template< typename T >
|
||||
inline T constructSingle()
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType Type;
|
||||
typedef typename TypeTraits< T >::Construct Construct;
|
||||
return Construct::template single< Type >();
|
||||
}
|
||||
template< typename T, typename A >
|
||||
inline T constructSingle( A a )
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType BaseType;
|
||||
typedef typename TypeTraits< T >::Construct Construct;
|
||||
return Construct::template single< BaseType >( a );
|
||||
}
|
||||
template< typename T, typename A, typename B >
|
||||
inline T constructSingle( A a, B b )
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType BaseType;
|
||||
typedef typename TypeTraits< T >::Construct Construct;
|
||||
return Construct::template single< BaseType >( a, b );
|
||||
}
|
||||
template< typename T >
|
||||
inline void constructArray( T* ptr, U32 num )
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType BaseType;
|
||||
typedef typename TypeTraits< T >::Construct Construct;
|
||||
Construct::template array< BaseType >( ptr, num );
|
||||
}
|
||||
template< typename T, typename A >
|
||||
inline void constructArray( T* ptr, U32 num, A a )
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType BaseType;
|
||||
typedef typename TypeTraits< T >::Construct Construct;
|
||||
Construct::template array< BaseType >( ptr, num, a );
|
||||
}
|
||||
template< typename T >
|
||||
inline void destructSingle( T& val )
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType BaseType;
|
||||
typedef typename TypeTraits< T >::Destruct Destruct;
|
||||
Destruct::template single< BaseType >( val );
|
||||
}
|
||||
template< typename T >
|
||||
inline void destructArray( T* ptr, U32 num )
|
||||
{
|
||||
typedef typename TypeTraits< T >::BaseType BaseType;
|
||||
typedef typename TypeTraits< T >::Destruct Destruct;
|
||||
Destruct::template array< BaseType >( ptr, num );
|
||||
}
|
||||
|
||||
template< typename T>
|
||||
inline T& Deref( T& val )
|
||||
{
|
||||
return val;
|
||||
}
|
||||
template< typename T >
|
||||
inline T& Deref( T* ptr )
|
||||
{
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/// Delete a single object policy.
|
||||
struct DeleteSingle
|
||||
{
|
||||
template<class T>
|
||||
static void destroy(T *ptr) { delete ptr; }
|
||||
};
|
||||
|
||||
/// Delete an array of objects policy.
|
||||
struct DeleteArray
|
||||
{
|
||||
template<class T>
|
||||
static void destroy(T *ptr) { delete [] ptr; }
|
||||
};
|
||||
|
||||
///
|
||||
template< typename T >
|
||||
struct ValueHolder
|
||||
{
|
||||
T value;
|
||||
|
||||
ValueHolder( const T& value )
|
||||
: value( value ) {}
|
||||
|
||||
operator T() const { return value; }
|
||||
};
|
||||
template<>
|
||||
struct ValueHolder< void >
|
||||
{
|
||||
ValueHolder() {}
|
||||
};
|
||||
|
||||
#endif // _TYPETRAITS_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue