6e8fbca745
match the genesis editor version 1.3.0.653.
373 lines
11 KiB
C++
373 lines
11 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2009, Radon Labs GmbH
|
|
Copyright (c) 2011-2013,WebJet Business Division,CYOU
|
|
|
|
http://www.genesis-3d.com.cn
|
|
|
|
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 "stdneb.h"
|
|
#include "framesync/framesynchandlerthread.h"
|
|
#include "framesync/framesynctimer.h"
|
|
#include "threading/interlocked.h"
|
|
|
|
namespace FrameSync
|
|
{
|
|
__ImplementClass(FrameSync::FrameSyncHandlerThread, 'FSHT', Messaging::HandlerThreadBase);
|
|
__ImplementImageSingleton(FrameSync::FrameSyncHandlerThread);
|
|
|
|
using namespace Util;
|
|
using namespace Threading;
|
|
using namespace Messaging;
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
FrameSyncHandlerThread::FrameSyncHandlerThread() :
|
|
lockStepMode(0),
|
|
frameCount(0),
|
|
curTime(0.0),
|
|
fixedFrameTime(0.0),
|
|
fixedFrameTimeMode(0),
|
|
realTime(0.0)
|
|
{
|
|
__ConstructImageSingleton;
|
|
this->threadBarrier.Setup(NumSyncThreads);
|
|
this->msgQueue.SetSignalOnEnqueueEnabled(false);
|
|
this->masterTimer.Start();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
FrameSyncHandlerThread::~FrameSyncHandlerThread()
|
|
{
|
|
__DestructImageSingleton;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Enter lock-step mode, this method must only be called from the
|
|
slave thread.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::EnterLockStepMode()
|
|
{
|
|
if (0 == this->lockStepMode)
|
|
{
|
|
Interlocked::Exchange(&this->lockStepMode, 1);
|
|
this->ArriveAtSyncPoint(false);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Leave lock-step mode, this method must only be called from the
|
|
slave thread.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::LeaveLockStepMode()
|
|
{
|
|
if (1 == this->lockStepMode)
|
|
{
|
|
this->ArriveAtSyncPoint(false);
|
|
Interlocked::Exchange(&this->lockStepMode, 0);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
This is the central sync point. Every thread will call this method
|
|
at the start of a new frame to synchronize with the other lock-step
|
|
frames. Once all threads have arrived, the master time will be advanced
|
|
and the double-buffered message queues will be flipped.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::ArriveAtSyncPoint(bool masterThread)
|
|
{
|
|
if (0 != this->lockStepMode)
|
|
{
|
|
// lock-step mode enabled
|
|
if (this->threadBarrier.Arrive())
|
|
{
|
|
// all thread have arrived, perform sync actions and signal other
|
|
// threads to continue
|
|
this->frameCritSect.Enter();
|
|
this->SyncUpdateTime();
|
|
this->SyncFlipMessageQueue();
|
|
this->frameCritSect.Leave();
|
|
this->threadBarrier.SignalContinue();
|
|
}
|
|
else
|
|
{
|
|
// other threads still need to finish their frame, wait until they arrive
|
|
this->threadBarrier.Wait();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// lock-step mode disabled, the master thread is allowed to run,
|
|
// while client threads wait for the master thread's sync point
|
|
if (masterThread)
|
|
{
|
|
this->frameCritSect.Enter();
|
|
this->SyncUpdateTime();
|
|
this->SyncFlipMessageQueue();
|
|
this->frameSyncEvent.Signal();
|
|
this->frameCritSect.Leave();
|
|
}
|
|
else
|
|
{
|
|
this->frameSyncEvent.Wait();
|
|
}
|
|
}
|
|
|
|
// update the thread-local FrameSyncTimer object (if exists)
|
|
this->SyncUpdateThreadLocalFrameSyncTimer();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Start the master timer. This method may be called from any of the
|
|
sync-threads, thus we need to protect with a critical section.
|
|
DO NOT CALL FREQUENTLY!
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::StartMasterTime()
|
|
{
|
|
this->frameCritSect.Enter();
|
|
this->masterTimer.Start();
|
|
this->frameCritSect.Leave();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Stop the master timer, may be called from any of the sync-threads.
|
|
DO NOT CALL FREQUENTLY!
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::StopMasterTime()
|
|
{
|
|
this->frameCritSect.Enter();
|
|
this->masterTimer.Stop();
|
|
this->frameCritSect.Leave();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Reset the master timer, may be called from any of the sync-threads.
|
|
DO NOT CALL FREQUENTLY!
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::ResetMasterTime()
|
|
{
|
|
this->frameCritSect.Enter();
|
|
this->masterTimer.Reset();
|
|
this->frameCritSect.Leave();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Test if the master time is currently running. May be called from any
|
|
of the sync-threads.
|
|
DO NOT CALL FREQUENTLY!
|
|
*/
|
|
bool
|
|
FrameSyncHandlerThread::IsMasterTimeRunning() const
|
|
{
|
|
this->frameCritSect.Enter();
|
|
bool isRunning = this->masterTimer.Running();
|
|
this->frameCritSect.Leave();
|
|
return isRunning;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Safely get the current frame time. This is only used for first-time
|
|
init of FrameSyncTimer objects
|
|
DO NOT CALL FREQUENTLY!
|
|
*/
|
|
Timing::Time
|
|
FrameSyncHandlerThread::SafeGetMasterTime() const
|
|
{
|
|
this->frameCritSect.Enter();
|
|
Timing::Time t = this->curTime;
|
|
this->frameCritSect.Leave();
|
|
return t;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Update the master time. Since this method is called during the
|
|
sync point, we don't need thread-synchronization.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::SyncUpdateTime()
|
|
{
|
|
Timing::Time timeDelta(0);
|
|
Timing::Time masterTime = this->masterTimer.GetTime();
|
|
if (1 == this->fixedFrameTimeMode)
|
|
{
|
|
// in fixed frame time mode increase current time
|
|
// by a fixed value
|
|
timeDelta = this->fixedFrameTime;
|
|
}
|
|
else
|
|
{
|
|
// in normal mode increase current time by the time passed
|
|
// since the last SyncUpdateTime call
|
|
timeDelta = masterTime - this->realTime;
|
|
}
|
|
this->curTime += timeDelta;
|
|
this->realTime = masterTime;
|
|
Interlocked::Increment(this->frameCount);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Checks if the current thread has a FrameSyncTimer singleton, and if
|
|
yes, update its time. Call this method from the frame-sync-point.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::SyncUpdateThreadLocalFrameSyncTimer()
|
|
{
|
|
if (FrameSyncTimer::HasInstance())
|
|
{
|
|
FrameSyncTimer::Instance()->Update(this->curTime);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Flip the message queues, this is called from the sync point.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::SyncFlipMessageQueue()
|
|
{
|
|
this->msgQueue.DequeueAll(this->msgArray);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Add a message to the producer queue, since we're using double buffering
|
|
so we don't need thread-synchronization.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::AddMessage(const GPtr<Message>& msg)
|
|
{
|
|
this->msgQueue.Enqueue(msg);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Cancel a pending message from the producer queue. If the message is
|
|
already in the consumer queue, then it's too late to cancel the
|
|
message!
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::CancelMessage(const GPtr<Message>& msg)
|
|
{
|
|
this->msgQueue.EraseMatchingElements(msg);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Wait for a message to be handled.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::WaitForMessage(const GPtr<Message>& msg)
|
|
{
|
|
n_assert(!this->lockStepMode);
|
|
while (!msg->Handled())
|
|
{
|
|
this->ArriveAtSyncPoint(false);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
The central message processing loop.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::DoWork()
|
|
{
|
|
this->ThreadOpenHandlers();
|
|
do
|
|
{
|
|
bool msgHandled = false;
|
|
|
|
// update state of deferred messages
|
|
msgHandled = this->ThreadUpdateDeferredMessages();
|
|
|
|
// arrive at sync point as master thread
|
|
this->ArriveAtSyncPoint(true);
|
|
|
|
// process messages
|
|
msgHandled |= this->ThreadHandleMessages(this->msgArray);
|
|
this->msgArray.Clear();
|
|
|
|
// signal if at least one message has been handled
|
|
if (msgHandled)
|
|
{
|
|
this->ThreadSignalMessageHandled();
|
|
}
|
|
|
|
// do per-frame update on attached handlers
|
|
this->ThreadUpdateHandlers();
|
|
Thread::YieldThread();
|
|
}
|
|
while (!this->ThreadStopRequested());
|
|
|
|
// cleanup and exit thread
|
|
this->ThreadDiscardDeferredMessages();
|
|
this->ThreadCloseHandlers();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Start the fixed frame time mode. In this mode the current time is
|
|
increased by a fixed value every frame.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::StartFixedFrameTime(Timing::Time frameTime)
|
|
{
|
|
if (0 == this->fixedFrameTimeMode)
|
|
{
|
|
Interlocked::Exchange(&this->fixedFrameTimeMode, 1);
|
|
this->frameCritSect.Enter();
|
|
this->fixedFrameTime = frameTime;
|
|
this->frameCritSect.Leave();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Stop the fixed frame time mode.
|
|
*/
|
|
void
|
|
FrameSyncHandlerThread::StopFixedFrameTime()
|
|
{
|
|
if (1 == this->fixedFrameTimeMode)
|
|
{
|
|
Interlocked::Exchange(&this->fixedFrameTimeMode, 0);
|
|
}
|
|
}
|
|
|
|
} // namespace FrameSync
|