This commit is contained in:
gallaert
2019-07-13 18:12:22 +01:00
73 changed files with 3052 additions and 1199 deletions

View File

@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
# include <winsock2.h> /* includes <windows.h> */
# include <iphlpapi.h> /* for dns server addresses etc */
#else
@@ -53,7 +53,7 @@ static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
dns_add_srch(ctx, srch);
}
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
#ifndef NO_IPHLPAPI
/* Apparently, some systems does not have proper headers for IPHLPAIP to work.
@@ -217,7 +217,7 @@ int dns_init(struct dns_ctx *ctx, int do_open) {
ctx = &dns_defctx;
dns_reset(ctx);
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
if (dns_initns_iphlpapi(ctx) != 0)
dns_initns_registry(ctx);
/*XXX WINDOWS: probably good to get default domain and search list too...

View File

@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
# include <winsock2.h> /* includes <windows.h> */
# include <ws2tcpip.h> /* needed for struct in6_addr */
#else
@@ -392,7 +392,7 @@ dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) {
}
static unsigned dns_nonrandom_32(void) {
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft.dwLowDateTime;
@@ -551,7 +551,7 @@ int dns_open(struct dns_ctx *ctx) {
ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
return -1;
}
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
{ unsigned long on = 1;
if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
closesocket(sock);
@@ -991,7 +991,7 @@ again: /* receive the reply */
* or remote. On local errors, we should stop, while
* remote errors should be ignored (for now anyway).
*/
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
if (WSAGetLastError() == WSAEWOULDBLOCK)
#else
if (errno == EAGAIN)

View File

@@ -27,7 +27,7 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#ifndef WINDOWS
#if !defined(_WINDOWS) && !defined(WINDOWS)
# include <sys/types.h>
# include <netinet/in.h>
#endif

View File

@@ -150,7 +150,7 @@ endif()
if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} DIRECTORY)
if (CMAKE_CL_64)
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
else (CMAKE_CL_64)
@@ -198,7 +198,7 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
# if this variable was not set by the user, set it to 3rdparty root's
# parent dir, which is the normal location for people using our
# windows-3rd-party repo
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
get_filename_component(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} DIRECTORY)
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
endif()

View File

@@ -80,7 +80,7 @@ if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
set(_boostConfig "BoostTestTargetsDynamic.h")
endif()
endif()
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
configure_file("${_moddir}/${_boostConfig}"
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
COPYONLY)

View File

@@ -30,12 +30,12 @@ function(copy_resources_to_build_tree _target)
endif()
get_target_property(_path ${_target} LOCATION)
get_filename_component(_path "${_path}" PATH)
get_filename_component(_path "${_path}" DIRECTORY)
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
get_target_property(_path${_config} ${_target} LOCATION_${_config})
get_filename_component(_path${_config} "${_path${_config}}" PATH)
get_filename_component(_path${_config} "${_path${_config}}" DIRECTORY)
add_custom_command(TARGET ${_target}
POST_BUILD
COMMAND

View File

@@ -6,6 +6,7 @@ foreach( mylibfolder
bvh
debug
embedded_resources
emesary
ephemeris
io
magvar

View File

@@ -28,42 +28,39 @@ namespace simgear
namespace canvas
{
class CanvasMgr:
public PropertyBasedMgr
{
public:
class CanvasMgr : public PropertyBasedMgr
{
public:
/**
* @param node Root node of branch used to control canvasses
*/
CanvasMgr(SGPropertyNode_ptr node);
/**
* @param node Root node of branch used to control canvasses
*/
CanvasMgr(SGPropertyNode_ptr node);
/**
* Create a new canvas
*
* @param name Name of the new canvas
*/
CanvasPtr createCanvas(const std::string& name = "");
/**
* Create a new canvas
*
* @param name Name of the new canvas
*/
CanvasPtr createCanvas(const std::string& name = "");
/**
* Get ::Canvas by index
*
* @param index Index of texture node in /canvas/by-index/
*/
CanvasPtr getCanvas(size_t index) const;
/**
* Get ::Canvas by index
*
* @param index Index of texture node in /canvas/by-index/
*/
CanvasPtr getCanvas(size_t index) const;
/**
* Get ::Canvas by name
*
* @param name Value of child node "name" in
* /canvas/by-index/texture[i]/name
*/
CanvasPtr getCanvas(const std::string& name) const;
/**
* Get ::Canvas by name
*
* @param name Value of child node "name" in
* /canvas/by-index/texture[i]/name
*/
CanvasPtr getCanvas(const std::string& name) const;
protected:
void elementCreated(PropertyBasedElementPtr element) override;
};
protected:
void elementCreated(PropertyBasedElementPtr element) override;
};
} // namespace canvas
} // namespace simgear

View File

@@ -425,8 +425,11 @@ public:
return;
}
m_startupLogging = on;
m_startupEntries.clear();
{
SGGuard<SGMutex> g(m_lock);
m_startupLogging = on;
m_startupEntries.clear();
}
}
virtual void run()
@@ -438,13 +441,14 @@ public:
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
return;
}
if (m_startupLogging) {
// save to the startup list for not-yet-added callbacks to
// pull down on startup
m_startupEntries.push_back(entry);
{
SGGuard<SGMutex> g(m_lock);
if (m_startupLogging) {
// save to the startup list for not-yet-added callbacks to
// pull down on startup
m_startupEntries.push_back(entry);
}
}
// submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority,
@@ -455,14 +459,16 @@ public:
bool stop()
{
SGGuard<SGMutex> g(m_lock);
if (!m_isRunning) {
return false;
}
{
SGGuard<SGMutex> g(m_lock);
if (!m_isRunning) {
return false;
}
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
}
join();
m_isRunning = false;

View File

@@ -0,0 +1,33 @@
include (SimGearComponent)
set(HEADERS
Emesary.hxx
INotification.hxx
IReceiver.hxx
ITransmitter.hxx
ReceiptStatus.hxx
Transmitter.hxx
notifications.hxx
)
set(SOURCES
Emesary.cxx
)
simgear_component(emesary emesary "${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(test_emesary test_emesary.cxx)
set_target_properties(test_emesary PROPERTIES
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
target_link_libraries(test_emesary ${TEST_LIBS})
add_test(emesary ${EXECUTABLE_OUTPUT_PATH}/test_emesary)
endif(ENABLE_TESTS)

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : Emesary main.
* : This only needs to instance the GlobalTransmitter as all of the
* : logic is in the header files (by design)
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002
*
* Version : $Header: $
*
* Copyright © 2002 Richard Harrison All Rights Reserved.
*
*---------------------------------------------------------------------------*/
#include "simgear/emesary/Emesary.hxx"
namespace simgear
{
namespace Emesary
{
}
}

View File

@@ -0,0 +1,48 @@
#ifndef EMESARY_hxx
#define EMESARY_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : Provides generic inter-object communication. For an object to receive a message it
* : must first register with a Transmitter, such as GlobalTransmitter, and implement the
* : IReceiver interface. That's it.
* : To send a message use a Transmitter with an object. That's all there is to it.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
#include <typeinfo>
#include "ReceiptStatus.hxx"
#include "INotification.hxx"
#include "IReceiver.hxx"
#include "ITransmitter.hxx"
#include "Transmitter.hxx"
#include <simgear/structure/Singleton.hxx>
namespace simgear
{
namespace Emesary
{
class GlobalTransmitter : public simgear::Singleton<Transmitter>
{
public:
GlobalTransmitter()
{
}
virtual ~GlobalTransmitter() {}
};
}
}
#endif

View File

@@ -0,0 +1,54 @@
#ifndef INOTIFICATION_hxx
#define INOTIFICATION_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Notification base class
*
* File Type : Implementation File
*
* Description : Base class (interface) for all Notifications.
* : This is also compatible with the usual implementation of how we
* : implement queued notifications.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base class) for all notifications.
class INotification
{
public:
// text representation of notification type. must be unique across all notifications
virtual const char *GetType() = 0;
/// Used to control the sending of notifications. If this returns false then the Transmitter
/// should not send this notification.
virtual bool IsReadyToSend() { return true; }
/// Used to control the timeout. If this notification has timed out - then the processor is entitled
/// to true.
virtual bool IsTimedOut() { return false; }
/// when this notification has completed the processing recipient must set this to true.
/// the processing recipient is responsible for follow on notifications.
/// a notification can remain as complete until the transmit queue decides to remove it from the queue.
/// there is no requirement that elements are removed immediately upon completion merely that once complete
/// the transmitter should not notify any more elements.
/// The current notification loop may be completed - following the usual convention unless Completed or Abort
/// is returned as the status.
virtual bool IsComplete() { return true; }
};
}
}
#endif

View File

@@ -0,0 +1,47 @@
#ifndef IRECEIVER_hxx
#define IRECEIVER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Receiver base class
*
* File Type : Implementation File
*
* Description : Base class for all recipients.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base class) for a recipeint.
class IReceiver
{
public:
/// Receive notification - must be implemented
virtual ReceiptStatus Receive(INotification& message) = 0;
/// Called when registered at a transmitter
virtual void OnRegisteredAtTransmitter(class Transmitter *p)
{
}
/// Called when de-registered at a transmitter
virtual void OnDeRegisteredAtTransmitter(class Transmitter *p)
{
}
};
}
}
#endif

View File

@@ -0,0 +1,52 @@
#ifndef ITRANSMITTER_hxx
#define ITRANSMITTER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Base class for all transmitters.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base clasee) for a transmitter.
/// Transmits Message derived objects. Each instance of this class provides a
/// event/databus to which any number of receivers can attach to.
class ITransmitter
{
public:
// Registers a recipient to receive message from this transmitter
virtual void Register(IReceiver& R) = 0;
// Removes a recipient from from this transmitter
virtual void DeRegister(IReceiver& R) = 0;
//Notify all registered recipients. Stop when receipt status of abort or finished are received.
//The receipt status from this method will be
// - OK > message handled
// - Fail > message not handled. A status of Abort from a recipient will result in our status
// being fail as Abort means that the message was not and cannot be handled, and
// allows for usages such as access controls.
virtual ReceiptStatus NotifyAll(INotification& M) = 0;
/// number of recipients
virtual int Count() = 0;
};
}
}
#endif

View File

@@ -0,0 +1,54 @@
#ifndef RECEIPTSTATUS_hxx
#define RECEIPTSTATUS_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Defines the receipt status that can be returned from
* : a receive method.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
enum ReceiptStatus
{
/// Processing completed successfully
ReceiptStatusOK = 0,
/// Individual item failure
ReceiptStatusFail = 1,
/// Fatal error; stop processing any further recipieints of this message. Implicitly fail
ReceiptStatusAbort = 2,
/// Definitive completion - do not send message to any further recipieints
ReceiptStatusFinished = 3,
/// Return value when method doesn't process a message.
ReceiptStatusNotProcessed = 4,
/// Message has been sent but the return status cannot be determined as it has not been processed by the recipient.
/// e.g. a queue or outgoing bridge
ReceiptStatusPending = 5,
/// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further
/// e.g. a point to point forwarding bridge
ReceiptStatusPendingFinished = 6,
};
}
}
#endif

View File

@@ -0,0 +1,203 @@
#ifndef TRANSMITTER_hxx
#define TRANSMITTER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Defines the receipt status that can be returned from
* : a receive method.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
#include <algorithm>
#include <string>
#include <list>
#include <set>
#include <vector>
#include <atomic>
#include <simgear/threads/SGThread.hxx>
namespace simgear
{
namespace Emesary
{
// Implementation of a ITransmitter
class Transmitter : public ITransmitter
{
protected:
typedef std::list<IReceiver *> RecipientList;
RecipientList recipient_list;
RecipientList deleted_recipients;
int CurrentRecipientIndex = 0;
SGMutex _lock;
std::atomic<int> receiveDepth;
std::atomic<int> sentMessageCount;
void UnlockList()
{
_lock.unlock();
}
void LockList()
{
_lock.lock();
}
public:
Transmitter() : receiveDepth(0), sentMessageCount(0)
{
}
virtual ~Transmitter()
{
}
// Registers an object to receive messsages from this transmitter.
// This object is added to the top of the list of objects to be notified. This is deliberate as
// the sequence of registration and message receipt can influence the way messages are processing
// when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the
// most recently registered recipients should process the messages/events first.
virtual void Register(IReceiver& r)
{
LockList();
recipient_list.push_back(&r);
r.OnRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
deleted_recipients.remove(&r);
UnlockList();
}
// Removes an object from receving message from this transmitter
virtual void DeRegister(IReceiver& R)
{
LockList();
//printf("Remove %x\n", &R);
if (recipient_list.size())
{
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
{
recipient_list.remove(&R);
R.OnDeRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end())
deleted_recipients.push_back(&R);
}
}
UnlockList();
}
// Notify all registered recipients. Stop when receipt status of abort or finished are received.
// The receipt status from this method will be
// - OK > message handled
// - Fail > message not handled. A status of Abort from a recipient will result in our status
// being fail as Abort means that the message was not and cannot be handled, and
// allows for usages such as access controls.
virtual ReceiptStatus NotifyAll(INotification& M)
{
ReceiptStatus return_status = ReceiptStatusNotProcessed;
sentMessageCount++;
try
{
LockList();
if (receiveDepth == 0)
deleted_recipients.clear();
receiveDepth++;
std::vector<IReceiver*> temp(recipient_list.size());
int idx = 0;
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
{
temp[idx++] = *i;
}
UnlockList();
int tempSize = temp.size();
for (int index = 0; index < tempSize; index++)
{
IReceiver* R = temp[index];
LockList();
if (deleted_recipients.size())
{
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
{
UnlockList();
continue;
}
}
UnlockList();
if (R)
{
ReceiptStatus rstat = R->Receive(M);
switch (rstat)
{
case ReceiptStatusFail:
return_status = ReceiptStatusFail;
break;
case ReceiptStatusPending:
return_status = ReceiptStatusPending;
break;
case ReceiptStatusPendingFinished:
return rstat;
case ReceiptStatusNotProcessed:
break;
case ReceiptStatusOK:
if (return_status == ReceiptStatusNotProcessed)
return_status = rstat;
break;
case ReceiptStatusAbort:
return ReceiptStatusAbort;
case ReceiptStatusFinished:
return ReceiptStatusOK;
}
}
}
}
catch (...)
{
throw;
// return_status = ReceiptStatusAbort;
}
receiveDepth--;
return return_status;
}
// number of currently registered recipients
virtual int Count()
{
LockList();
return recipient_list.size();
UnlockList();
}
// number of sent messages.
int SentMessageCount()
{
return sentMessageCount;
}
// ascertain if a receipt status can be interpreted as failure.
static bool Failed(ReceiptStatus receiptStatus)
{
//
// failed is either Fail or Abort.
// NotProcessed isn't a failure because it hasn't been processed.
return receiptStatus == ReceiptStatusFail
|| receiptStatus == ReceiptStatusAbort;
}
};
}
}
#endif

View File

@@ -0,0 +1,68 @@
#ifndef NOTIFICATIONS_hxx
#define NOTIFICATIONS_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : simgear notifications
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017
*
* Version : $Header: $
*
* Copyright <20> 2002 - 2017 Richard Harrison All Rights Reserved.
*
*---------------------------------------------------------------------------*/
#include "INotification.hxx"
namespace simgear
{
namespace Notifications
{
class MainLoopNotification : public simgear::Emesary::INotification
{
public:
enum Type { Started, Stopped, Begin, End };
MainLoopNotification(Type v) : _type(v) {}
virtual Type GetValue() { return _type; }
virtual const char *GetType() { return "MainLoop"; }
protected:
Type _type;
};
class NasalGarbageCollectionConfigurationNotification : public simgear::Emesary::INotification
{
public:
NasalGarbageCollectionConfigurationNotification(bool canWait, bool active) : CanWait(canWait), Active(active) {}
virtual bool GetCanWait() { return CanWait; }
virtual bool GetActive() { return Active; }
virtual const char *GetType() { return "NasalGarbageCollectionConfiguration"; }
virtual bool SetWait(bool wait) {
if (wait == CanWait)
return false;
CanWait = wait;
return true;
}
virtual bool SetActive(bool active) {
if (active == Active)
return false;
Active = active;
return true;
}
public:
bool CanWait;
bool Active;
};
}
}
#endif

View File

@@ -0,0 +1,126 @@
////////////////////////////////////////////////////////////////////////
// Test harness for Emesary.
////////////////////////////////////////////////////////////////////////
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <iostream>
#include <simgear/emesary/Emesary.hxx>
using std::cout;
using std::cerr;
using std::endl;
std::atomic<int> nthread {0};
std::atomic<int> noperations {0};
const int MaxIterations = 9999;
class TestThreadNotification : public simgear::Emesary::INotification
{
protected:
const char *baseValue;
public:
TestThreadNotification(const char *v) : baseValue(v) {}
virtual const char* GetType () { return baseValue; }
};
class TestThreadRecipient : public simgear::Emesary::IReceiver
{
public:
TestThreadRecipient() : receiveCount(0)
{
}
std::atomic<int> receiveCount;
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
{
if (n.GetType() == (const char*)this)
{
TestThreadNotification *tn = dynamic_cast<TestThreadNotification *>(&n);
receiveCount++;
TestThreadNotification onwardNotification("AL");
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(onwardNotification);
return simgear::Emesary::ReceiptStatusOK;
}
return simgear::Emesary::ReceiptStatusOK;
}
};
class EmesaryTestThread : public SGThread
{
protected:
virtual void run() {
int threadId = nthread.fetch_add(1);
//System.Threading.Interlocked.Increment(ref nthread);
//var rng = new Random();
TestThreadRecipient r;
char temp[100];
sprintf(temp, "Notif %d", threadId);
printf("starting thread %s\n", temp);
TestThreadNotification tn((const char*)&r);
for (int i = 0; i < MaxIterations; i++)
{
simgear::Emesary::GlobalTransmitter::instance()->Register(r);
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(tn);
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(r);
//System.Threading.Thread.Sleep(rng.Next(MaxSleep));
noperations++;
}
printf("%s invocations %d\n", temp, (int)r.receiveCount);
printf("finish thread %s\n", temp);
}
};
class EmesaryTest
{
public:
void Emesary_MultiThreadTransmitterTest()
{
int num_threads = 12;
std::list<EmesaryTestThread*> threads;
for (int i = 0; i < num_threads; i++)
{
EmesaryTestThread *thread = new EmesaryTestThread();
threads.push_back(thread);
thread->start();
}
for (std::list<EmesaryTestThread*>::iterator i = threads.begin(); i != threads.end(); i++)
{
(*i)->join();
}
}
};
void testEmesaryThreaded()
{
TestThreadRecipient r;
TestThreadNotification tn((const char*)&r);
simgear::Emesary::GlobalTransmitter::instance()->Register(r);
for (int i = 0; i < MaxIterations*MaxIterations; i++)
{
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(tn);
//System.Threading.Thread.Sleep(rng.Next(MaxSleep));
noperations++;
}
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(r);
printf("invocations %d\n", simgear::Emesary::GlobalTransmitter::instance()->SentMessageCount());
EmesaryTest t;
t.Emesary_MultiThreadTransmitterTest();
}
int main(int ac, char ** av)
{
testEmesaryThreaded();
std::cout << "all tests passed" << std::endl;
return 0;
}

View File

@@ -19,6 +19,7 @@ set(HEADERS
tabbed_values.hxx
texcoord.hxx
test_macros.hxx
lru_cache.hxx
)
set(SOURCES

View File

@@ -30,11 +30,16 @@
// code can register another one immediately without worrying about
// timer aliasing.
class SGInterpolator : public SGSubsystem {
class SGInterpolator : public SGSubsystem
{
public:
SGInterpolator() { _list = 0; }
virtual void init() {}
virtual void update(double delta_time_sec);
// Subsystem API.
void update(double delta_time_sec) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "interpolator"; }
// Simple method that interpolates a double property value from
// its current value (default of zero) to the specified target

173
simgear/misc/lru_cache.hxx Normal file
View File

@@ -0,0 +1,173 @@
///@file
/// Compare lists and get differences
//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//
///
// Changes Copyright (C) 2019 Richard Harrison (rjh@zaretto.com)
//
// As the boost licence is lax and permissive see
// (https://www.gnu.org/licenses/license-list.en.html#boost)
// any changes to this module are covered under the GPL
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef LRU_CACHE_HXX_
#define LRU_CACHE_HXX_
#include <boost/function.hpp>
#include <map>
#include <list>
#include <utility>
#include <boost/optional.hpp>
#include <simgear/threads/SGThread.hxx>
namespace simgear
{
// a cache which evicts the least recently used item when it is full
template<class Key, class Value>
class lru_cache
{
public:
SGMutex _mutex;
typedef Key key_type;
typedef Value value_type;
typedef std::list<key_type> list_type;
typedef std::map<
key_type,
std::pair<value_type, typename list_type::iterator>
> map_type;
lru_cache(size_t capacity)
: m_capacity(capacity)
{
}
~lru_cache()
{
}
size_t size() const
{
return m_map.size();
}
size_t capacity() const
{
return m_capacity;
}
bool empty() const
{
return m_map.empty();
}
bool contains(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
return m_map.find(key) != m_map.end();
}
void insert(const key_type &key, const value_type &value)
{
SGGuard<SGMutex> scopeLock(_mutex);
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// insert item into the cache, but first check if it is full
if (size() >= m_capacity) {
// cache is full, evict the least recently used item
evict();
}
// insert the new item
m_list.push_front(key);
m_map[key] = std::make_pair(value, m_list.begin());
}
}
boost::optional<key_type> findValue(const std::string &requiredValue)
{
SGGuard<SGMutex> scopeLock(_mutex);
for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it)
if (it->second.first == requiredValue)
return it->first;
return boost::none;
}
boost::optional<value_type> get(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
// lookup value in the cache
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// value not in cache
return boost::none;
}
// return the value, but first update its place in the most
// recently used list
typename list_type::iterator j = i->second.second;
if (j != m_list.begin()) {
// move item to the front of the most recently used list
m_list.erase(j);
m_list.push_front(key);
// update iterator in map
j = m_list.begin();
const value_type &value = i->second.first;
m_map[key] = std::make_pair(value, j);
// return the value
return value;
}
else {
// the item is already at the front of the most recently
// used list so just return it
return i->second.first;
}
}
void clear()
{
SGGuard<SGMutex> scopeLock(_mutex);
m_map.clear();
m_list.clear();
}
private:
void evict()
{
SGGuard<SGMutex> scopeLock(_mutex);
// evict item from the end of most recently used list
typename list_type::iterator i = --m_list.end();
m_map.erase(*i);
m_list.erase(i);
}
private:
map_type m_map;
list_type m_list;
size_t m_capacity;
};
} // namespace simgear
#endif /* SG_LISTDIFF_HXX_ */

View File

@@ -157,7 +157,7 @@ static void initContext(naContext c)
c->error[0] = 0;
c->userData = 0;
}
#define BASE_SIZE 256000
static void initGlobals()
{
int i;
@@ -168,10 +168,10 @@ static void initGlobals()
globals->sem = naNewSem();
globals->lock = naNewLock();
globals->allocCount = 256; // reasonable starting value
globals->allocCount = BASE_SIZE; // reasonable starting value
for(i=0; i<NUM_NASAL_TYPES; i++)
naGC_init(&(globals->pools[i]), i);
globals->deadsz = 256;
globals->deadsz = BASE_SIZE;
globals->ndead = 0;
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
@@ -833,9 +833,13 @@ naRef naGetSourceFile(naContext ctx, int frame)
{
naRef f;
frame = findFrame(ctx, &ctx, frame);
f = ctx->fStack[frame].func;
f = PTR(f).func->code;
return PTR(f).code->srcFile;
if (frame >= 0) {
f = ctx->fStack[frame].func;
f = PTR(f).func->code;
if (!IS_NIL(f) && PTR(f).code)
return PTR(f).code->srcFile;
}
return naNil();
}
char* naGetError(naContext ctx)

View File

@@ -5,6 +5,7 @@ set(HEADERS
Ghost.hxx
NasalCallContext.hxx
NasalContext.hxx
NasalEmesaryInterface.hxx
NasalHash.hxx
NasalMe.hxx
NasalMethodHolder.hxx

View File

@@ -0,0 +1,123 @@
#ifndef NASALEMESARYINTERFACE_INCLUDED
#define NASALEMESARYINTERFACE_INCLUDED 1
// Nasal Emesary receipient interface.
//
// Copyright (C) 2019 Richard Harrison rjh@zaretto.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/emesary/Emesary.hxx>
#include <simgear/emesary/notifications.hxx>
#include <boost/function.hpp>
#include <simgear/debug/logstream.hxx>
#include <simgear/threads/SGThread.hxx>
#include <mutex>
#include <condition_variable>
#include <atomic>
namespace nasal
{
extern"C" {
extern int GCglobalAlloc();
extern int naGarbageCollect();
// these are used by the detailed debug in the Nasal GC.
SGTimeStamp global_timestamp;
void global_stamp() {
global_timestamp.stamp();
}
extern int global_elapsedUSec()
{
return global_timestamp.elapsedUSec();
}
}
class ThreadedGarbageCollector : public SGExclusiveThread {
public:
ThreadedGarbageCollector() : SGExclusiveThread() {}
virtual ~ThreadedGarbageCollector() {}
virtual int process(){
return naGarbageCollect();
}
};
class NasalMainLoopRecipient : public simgear::Emesary::IReceiver {
public:
NasalMainLoopRecipient() : receiveCount(0) {
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
SG_LOG(SG_NASAL, SG_INFO, "NasalMainLoopRecipient created");
}
virtual ~NasalMainLoopRecipient() {
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this);
}
std::atomic<int> receiveCount;
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
{
simgear::Notifications::MainLoopNotification *mln = dynamic_cast<simgear::Notifications::MainLoopNotification *>(&n);
if (mln) {
switch (mln->GetValue()) {
case simgear::Notifications::MainLoopNotification::Type::Begin:
if (gct.is_running()) {
if (Active && CanWait)
gct.awaitCompletion();
else
gct.clearAwaitCompletionTime();
}
break;
case simgear::Notifications::MainLoopNotification::Type::End:
if (Active) {
if (gct.is_running())
gct.release();
}
break;
case simgear::Notifications::MainLoopNotification::Type::Started:
gct.ensure_running();
break;
case simgear::Notifications::MainLoopNotification::Type::Stopped:
gct.terminate();
break;
}
return simgear::Emesary::ReceiptStatusOK;
}
auto *gccn = dynamic_cast<simgear::Notifications::NasalGarbageCollectionConfigurationNotification *>(&n);
if (gccn) {
CanWait = gccn->GetCanWait();
Active = gccn->GetActive();
return simgear::Emesary::ReceiptStatusOK;
}
return simgear::Emesary::ReceiptStatusNotProcessed;
}
protected:
bool CanWait;
bool Active;
ThreadedGarbageCollector gct;
};
} // namespace nasal
#endif

View File

@@ -19,6 +19,7 @@
#include "to_nasal_helper.hxx"
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalEmesaryInterface.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/misc/sg_path.hxx>
@@ -27,6 +28,19 @@
namespace nasal
{
// create single instance of the main loop recipient for Nasal - this will self register at the
// global transmitter - and that's all that is needed to link up the background GC to the main
// loop in FG that will send out the MainLoop notifications.
//class NasalMainLoopRecipientSingleton : public simgear::Singleton<NasalMainLoopRecipient>
//{
//public:
// NasalMainLoopRecipientSingleton()
// {
// }
// virtual ~NasalMainLoopRecipientSingleton() {}
//};
NasalMainLoopRecipient mrl;
//----------------------------------------------------------------------------
naRef to_nasal_helper(naContext c, const std::string& str)
{

View File

@@ -1,7 +1,6 @@
#include "nasal.h"
#include "data.h"
#include "code.h"
#define MIN_BLOCK_SIZE 32
static void reap(struct naPool* p);
@@ -12,14 +11,17 @@ struct Block {
char* block;
struct Block* next;
};
// Must be called with the giant exclusive lock!
static void freeDead()
extern void global_stamp();
extern int global_elapsedUSec();
static int freeDead()
{
int i;
for(i=0; i<globals->ndead; i++)
naFree(globals->deadBlocks[i]);
globals->ndead = 0;
return i;
}
static void marktemps(struct Context* c)
@@ -31,50 +33,127 @@ static void marktemps(struct Context* c)
mark(r);
}
}
//#define GC_DETAIL_DEBUG
static int __elements_visited = 0;
static int gc_busy=0;
// Must be called with the big lock!
static void garbageCollect()
{
if (gc_busy)
return;
gc_busy = 1;
int i;
struct Context* c;
globals->allocCount = 0;
c = globals->allContexts;
while(c) {
for(i=0; i<NUM_NASAL_TYPES; i++)
#if GC_DETAIL_DEBUG
int ctxc = 0;
__elements_visited = 0;
int st = global_elapsedUSec();
int et = 0;
int stel = __elements_visited;
int eel = 0;
#endif
c = globals->allContexts;
while (c) {
#if GC_DETAIL_DEBUG
ctxc++;
#endif
for (i = 0; i < NUM_NASAL_TYPES; i++)
c->nfree[i] = 0;
for(i=0; i < c->fTop; i++) {
for (i = 0; i < c->fTop; i++) {
mark(c->fStack[i].func);
mark(c->fStack[i].locals);
}
for(i=0; i < c->opTop; i++)
for (i = 0; i < c->opTop; i++)
mark(c->opStack[i]);
mark(c->dieArg);
marktemps(c);
c = c->nextAll;
}
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
printf("--> garbageCollect(#e%-5d): %-4d ", eel, et);
#endif
mark(globals->save);
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
printf("s(%5d) %-5d ", eel, et);
#endif
mark(globals->save_hash);
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
printf("h(%5d) %-5d ", eel, et);
#endif
mark(globals->symbols);
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
//printf("sy(%5d) %-4d ", eel, et);
#endif
mark(globals->meRef);
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
//printf("me(%5d) %-5d ", eel, et);
#endif
mark(globals->argRef);
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
//printf("ar(%5d) %-5d ", eel, et);
#endif
mark(globals->parentsRef);
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
eel = __elements_visited - stel; stel = __elements_visited;
#endif
//printf(" ev[%3d] %-5d", eel, et);
// Finally collect all the freed objects
for(i=0; i<NUM_NASAL_TYPES; i++)
for (i = 0; i < NUM_NASAL_TYPES; i++) {
reap(&(globals->pools[i]));
}
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
printf(" >> reap %-5d", et);
#endif
// Make enough space for the dead blocks we need to free during
// execution. This works out to 1 spot for every 2 live objects,
// which should be limit the number of bottleneck operations
// without imposing an undue burden of extra "freeable" memory.
if(globals->deadsz < globals->allocCount) {
globals->deadsz = globals->allocCount;
if(globals->deadsz < 256) globals->deadsz = 256;
if(globals->deadsz < 256000) globals->deadsz = 256000;
naFree(globals->deadBlocks);
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
}
globals->needGC = 0;
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
printf(">> %-5d ", et);
#endif
gc_busy = 0;
}
void naModLock()
@@ -104,6 +183,7 @@ void naModUnlock()
// you think about it).
static void bottleneck()
{
global_stamp();
struct Globals* g = globals;
g->bottleneck = 1;
while(g->bottleneck && g->waitCount < g->nThreads - 1) {
@@ -111,12 +191,39 @@ static void bottleneck()
UNLOCK(); naSemDown(g->sem); LOCK();
g->waitCount--;
}
#if GC_DETAIL_DEBUG
printf("GC: wait %2d ", global_elapsedUSec());
#endif
if(g->waitCount >= g->nThreads - 1) {
freeDead();
if(g->needGC) garbageCollect();
int fd = freeDead();
#if GC_DETAIL_DEBUG
printf("--> freedead (%5d) : %5d", fd, global_elapsedUSec());
#endif
if(g->needGC)
garbageCollect();
if(g->waitCount) naSemUp(g->sem, g->waitCount);
g->bottleneck = 0;
}
#if GC_DETAIL_DEBUG
printf(" :: finished: %5d\n", global_elapsedUSec());
#endif
}
static void bottleneckFreeDead()
{
global_stamp();
struct Globals* g = globals;
g->bottleneck = 1;
while (g->bottleneck && g->waitCount < g->nThreads - 1) {
g->waitCount++;
UNLOCK(); naSemDown(g->sem); LOCK();
g->waitCount--;
}
if (g->waitCount >= g->nThreads - 1) {
freeDead();
if (g->waitCount) naSemUp(g->sem, g->waitCount);
g->bottleneck = 0;
}
}
void naGC()
@@ -127,6 +234,29 @@ void naGC()
UNLOCK();
naCheckBottleneck();
}
int naGarbageCollect()
{
int rv = 1;
LOCK();
//
// The number here is again based on observation - if this is too low then the inline GC will be used
// which is fine occasionally.
// So what we're doing by checking the global alloc is to see if GC is likely required during the next frame and if
// so we pre-empt this by doing it now.
// GC can typically take between 5ms and 50ms (F-15, FG1000 PFD & MFD, Advanced weather) - but usually it is completed
// prior to the start of the next frame.
globals->needGC = nasal_globals->allocCount < 23000;
if (globals->needGC)
bottleneck();
else {
bottleneckFreeDead();
rv = 0;
}
UNLOCK();
naCheckBottleneck();
return rv;
}
void naCheckBottleneck()
{
@@ -207,7 +337,9 @@ static int poolsize(struct naPool* p)
while(b) { total += b->size; b = b->next; }
return total;
}
int GCglobalAlloc() {
return globals->allocCount;
}
struct naObj** naGC_get(struct naPool* p, int n, int* nout)
{
struct naObj** result;
@@ -215,6 +347,9 @@ struct naObj** naGC_get(struct naPool* p, int n, int* nout)
LOCK();
while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) {
globals->needGC = 1;
#if GC_DETAIL_DEBUG
printf("++");
#endif
bottleneck();
}
if(p->nfree == 0)
@@ -248,7 +383,7 @@ static void mark(naRef r)
if(PTR(r).obj->mark == 1)
return;
__elements_visited++;
PTR(r).obj->mark = 1;
switch(PTR(r).obj->type) {
case T_VEC: markvec(r); break;
@@ -306,11 +441,14 @@ static void reap(struct naPool* p)
// Allocate more if necessary (try to keep 25-50% of the objects
// available)
if(p->nfree < total/4) {
// This was changed (2019.2) to allocate in larger blocks
// previously it used total/4 and used/2 now we
// use total/2 and used / 1
if (p->nfree < total / 2) {
int used = total - p->nfree;
int avail = total - used;
int need = used/2 - avail;
if(need > 0)
int need = used / 1 - avail;
if (need > 0)
newBlock(p, need);
}
}

View File

@@ -295,25 +295,36 @@ static naRef f_die(naContext c, naRef me, int argc, naRef* args)
return naNil(); // never executes
}
// Wrapper around vsnprintf, iteratively increasing the buffer size
// until it fits. Returned buffer should be freed by the caller.
// Wrapper around vsnprintf that will allocate the required size
// by calling vsnprintf with NULL and 0 - and vsnsprintf will measure the
// required amount of characters which we then allocate and return
// Returned buffer should be freed by the caller.
static char* dosprintf(char* f, ...)
{
char* buf;
va_list va;
int olen, len = 16;
while(1) {
buf = naAlloc(len);
va_start(va, f);
olen = vsnprintf(buf, len, f, va);
if(olen >= 0 && olen < len) {
va_end(va);
return buf;
}
va_end(va);
naFree(buf);
len *= 2;
va_start(va, f);
int len = vsnprintf(0, 0, f, va);
va_end(va);
if (len <= 0) {
buf = (char *) naAlloc(2);
*buf = 0;
}
else {
len++;// allow for terminating null
buf = (char *) naAlloc(len);
va_list va;
va_start(va, f);
len = vsnprintf(buf, len, f, va);
va_end(va);
}
return buf;
}
// Inspects a printf format string f, and finds the next "%..." format

View File

@@ -29,74 +29,71 @@
namespace simgear
{
class PropertyBasedMgr:
public SGSubsystem,
public SGPropertyChangeListener
{
public:
void init() override;
void shutdown() override;
class PropertyBasedMgr : public SGSubsystem,
public SGPropertyChangeListener
{
public:
// Subsystem API.
void init() override;
void shutdown() override;
void update(double delta_time_sec) override;
void update (double delta_time_sec) override;
/**
* Create a new PropertyBasedElement
*
* @param name Name of the new element
*/
PropertyBasedElementPtr createElement(const std::string& name = "");
/**
* Create a new PropertyBasedElement
*
* @param name Name of the new element
*/
PropertyBasedElementPtr createElement(const std::string& name = "");
/**
* Get an existing PropertyBasedElement by its index
*
* @param index Index of element node in property tree
*/
PropertyBasedElementPtr getElement(size_t index) const;
/**
* Get an existing PropertyBasedElement by its index
*
* @param index Index of element node in property tree
*/
PropertyBasedElementPtr getElement(size_t index) const;
/**
* Get an existing PropertyBasedElement by its name
*
* @param name Name (value of child node "name" will be matched)
*/
PropertyBasedElementPtr getElement(const std::string& name) const;
/**
* Get an existing PropertyBasedElement by its name
*
* @param name Name (value of child node "name" will be matched)
*/
PropertyBasedElementPtr getElement(const std::string& name) const;
virtual const SGPropertyNode* getPropertyRoot() const;
virtual const SGPropertyNode* getPropertyRoot() const;
protected:
typedef boost::function<PropertyBasedElementPtr(SGPropertyNode*)>
ElementFactory;
protected:
/** Branch in the property tree for this property managed subsystem */
SGPropertyNode_ptr _props;
typedef boost::function<PropertyBasedElementPtr(SGPropertyNode*)>
ElementFactory;
/** Property name of managed elements */
const std::string _name_elements;
/** Branch in the property tree for this property managed subsystem */
SGPropertyNode_ptr _props;
/** The actually managed elements */
std::vector<PropertyBasedElementPtr> _elements;
/** Property name of managed elements */
const std::string _name_elements;
/** Function object which creates a new element */
ElementFactory _element_factory;
/** The actually managed elements */
std::vector<PropertyBasedElementPtr> _elements;
/**
* @param props Root node of property branch used for controlling
* this subsystem
* @param name_elements The name of the nodes for the managed elements
*/
PropertyBasedMgr( SGPropertyNode_ptr props,
const std::string& name_elements,
ElementFactory element_factory );
virtual ~PropertyBasedMgr() = 0;
/** Function object which creates a new element */
ElementFactory _element_factory;
/**
* @param props Root node of property branch used for controlling
* this subsystem
* @param name_elements The name of the nodes for the managed elements
*/
PropertyBasedMgr( SGPropertyNode_ptr props,
const std::string& name_elements,
ElementFactory element_factory );
virtual ~PropertyBasedMgr() = 0;
void childAdded( SGPropertyNode * parent,
void childAdded( SGPropertyNode * parent,
SGPropertyNode * child ) override;
void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child ) override;
void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child ) override;
virtual void elementCreated(PropertyBasedElementPtr element) {}
};
virtual void elementCreated(PropertyBasedElementPtr element) {}
};
} // namespace simgear

View File

@@ -28,119 +28,113 @@
#include <list>
namespace simgear
namespace simgear {
/**
* Subsystem that manages interpolation of properties.
*
* By default the numeric values of the properties are interpolated. For
* example, for strings this is probably not the wanted behavior. For this
* adapter classes can be registered to allow providing specific
* interpolations for certain types of properties. Using the type "color",
* provided by ColorInterpolator, strings containing %CSS colors can also be
* interpolated.
*
* Additionally different functions can be used for easing of the animation.
* By default "linear" (constant animation speed) and "swing" (smooth
* acceleration and deceleration) are available.
*/
class PropertyInterpolationMgr : public SGSubsystem
{
public:
typedef PropertyInterpolator* (*InterpolatorFactory)();
/**
* Subsystem that manages interpolation of properties.
*
* By default the numeric values of the properties are interpolated. For
* example, for strings this is probably not the wanted behavior. For this
* adapter classes can be registered to allow providing specific
* interpolations for certain types of properties. Using the type "color",
* provided by ColorInterpolator, strings containing %CSS colors can also be
* interpolated.
*
* Additionally different functions can be used for easing of the animation.
* By default "linear" (constant animation speed) and "swing" (smooth
* acceleration and deceleration) are available.
*/
class PropertyInterpolationMgr:
public SGSubsystem
{
public:
typedef PropertyInterpolator* (*InterpolatorFactory)();
PropertyInterpolationMgr();
PropertyInterpolationMgr();
// Subsystem API.
void update(double dt) override;
/**
* Update all active interpolators.
*/
void update(double dt);
/**
* Create a new property interpolator.
*
* @note To actually use it the interpolator needs to be attached to a
* property using PropertyInterpolationMgr::interpolate.
*
* @param type Type of animation ("numeric", "color", etc.)
* @param target Property containing target value
* @param duration Duration if the animation (in seconds)
* @param easing Type of easing ("linear", "swing", etc.)
*/
PropertyInterpolator*
createInterpolator(const std::string& type,
const SGPropertyNode& target,
double duration,
const std::string& easing);
/**
* Create a new property interpolator.
*
* @note To actually use it the interpolator needs to be attached to a
* property using PropertyInterpolationMgr::interpolate.
*
* @param type Type of animation ("numeric", "color", etc.)
* @param target Property containing target value
* @param duration Duration if the animation (in seconds)
* @param easing Type of easing ("linear", "swing", etc.)
*/
PropertyInterpolator*
createInterpolator( const std::string& type,
const SGPropertyNode& target,
double duration,
const std::string& easing );
/**
* Add animation of the given property from its current value to the
* target value of the interpolator. If no interpolator is given any
* existing animation of the given property is aborted.
*
* @param prop Property to be interpolated
* @param interp Interpolator used for interpolation
*/
bool interpolate(SGPropertyNode* prop,
PropertyInterpolatorRef interp = 0);
/**
* Add animation of the given property from its current value to the
* target value of the interpolator. If no interpolator is given any
* existing animation of the given property is aborted.
*
* @param prop Property to be interpolated
* @param interp Interpolator used for interpolation
*/
bool interpolate( SGPropertyNode* prop,
PropertyInterpolatorRef interp = 0 );
bool interpolate(SGPropertyNode* prop,
const std::string& type,
const SGPropertyNode& target,
double duration,
const std::string& easing);
bool interpolate( SGPropertyNode* prop,
const std::string& type,
const SGPropertyNode& target,
double duration,
const std::string& easing );
bool interpolate(SGPropertyNode* prop,
const std::string& type,
const PropertyList& values,
const double_list& deltas,
const std::string& easing);
bool interpolate( SGPropertyNode* prop,
const std::string& type,
const PropertyList& values,
const double_list& deltas,
const std::string& easing );
/**
* Register factory for interpolation type.
*/
void addInterpolatorFactory( const std::string& type,
InterpolatorFactory factory );
template<class T>
void addInterpolatorFactory(const std::string& type)
{
addInterpolatorFactory
(
type,
&simgear::make_new_derived<PropertyInterpolator, T>
/**
* Register factory for interpolation type.
*/
void addInterpolatorFactory(const std::string& type,
InterpolatorFactory factory);
template <class T>
void addInterpolatorFactory(const std::string& type)
{
addInterpolatorFactory(
type,
&simgear::make_new_derived<PropertyInterpolator, T>
);
}
}
/**
* Register easing function.
*/
void addEasingFunction(const std::string& type, easing_func_t func);
/**
* Register easing function.
*/
void addEasingFunction(const std::string& type, easing_func_t func);
/**
* Set property containing real time delta (not sim time)
*
* TODO better pass both deltas to all update methods...
*/
void setRealtimeProperty(SGPropertyNode* node);
/**
* Set property containing real time delta (not sim time)
*
* TODO better pass both deltas to all update methods...
*/
void setRealtimeProperty(SGPropertyNode* node);
protected:
protected:
typedef std::map<std::string, InterpolatorFactory> InterpolatorFactoryMap;
typedef std::map<std::string, easing_func_t> EasingFunctionMap;
typedef std::pair<SGPropertyNode*,
PropertyInterpolatorRef> PropertyInterpolatorPair;
typedef std::list<PropertyInterpolatorPair> InterpolatorList;
typedef std::map<std::string, InterpolatorFactory> InterpolatorFactoryMap;
typedef std::map<std::string, easing_func_t> EasingFunctionMap;
typedef std::pair< SGPropertyNode*,
PropertyInterpolatorRef > PropertyInterpolatorPair;
typedef std::list<PropertyInterpolatorPair> InterpolatorList;
struct PredicateIsSameProp;
struct PredicateIsSameProp;
InterpolatorFactoryMap _interpolator_factories;
EasingFunctionMap _easing_functions;
InterpolatorList _interpolators;
InterpolatorFactoryMap _interpolator_factories;
EasingFunctionMap _easing_functions;
InterpolatorList _interpolators;
SGPropertyNode_ptr _rt_prop;
};
SGPropertyNode_ptr _rt_prop;
};
} // namespace simgear

View File

@@ -1,79 +1,86 @@
#include <simgear/scene/dem/SGDem.hxx>
#include <simgear/scene/dem/SGDemSession.hxx>
SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root )
SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root)
{
setOffsets( SGDem::longitudeDegToOffset((double)mnLon),
SGDem::latitudeDegToOffset((double)mnLat),
SGDem::longitudeDegToOffset((double)mxLon),
SGDem::latitudeDegToOffset((double)mxLat) );
setOffsets(SGDem::longitudeDegToOffset((double)mnLon),
SGDem::latitudeDegToOffset((double)mnLat),
SGDem::longitudeDegToOffset((double)mxLon),
SGDem::latitudeDegToOffset((double)mxLat));
pDemRoot = root;
lvlIndex = idx;
lvlWidth = lvlW;
pDemRoot = root;
lvlIndex = idx;
lvlWidth = lvlW;
lvlHeight = lvlH;
}
SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root ) {
setOffsets( SGDem::longitudeDegToOffset((double)mnLon),
SGDem::latitudeDegToOffset((double)mnLat),
SGDem::longitudeDegToOffset((double)mxLon),
SGDem::latitudeDegToOffset((double)mxLat) );
SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root)
{
setOffsets(SGDem::longitudeDegToOffset((double)mnLon),
SGDem::latitudeDegToOffset((double)mnLat),
SGDem::longitudeDegToOffset((double)mxLon),
SGDem::latitudeDegToOffset((double)mxLat));
pDemRoot = root;
lvlIndex = -1; // no level - session is raw input dir
pDemRoot = root;
lvlIndex = -1; // no level - session is raw input dir
}
void SGDemSession::close( void )
void SGDemSession::close(void)
{
if ( tileRefs.size() ) {
if (tileRefs.size()) {
tileRefs.clear();
if ( lvlIndex >= 0 ) {
pDemRoot->flushCaches( lvlIndex );
if (lvlIndex >= 0) {
pDemRoot->flushCaches(lvlIndex);
}
}
}
void SGDemSession::getGeods( unsigned wo, unsigned so, unsigned eo, unsigned no, int resx, int resy, int incx, int incy, ::std::vector<SGGeod>& geods, bool Debug1, bool Debug2 )
void SGDemSession::getGeods(unsigned wo, unsigned so, unsigned eo, unsigned no, int resx, int resy, int incx, int incy, ::std::vector<SGGeod>& geods, bool Debug1, bool Debug2)
{
// todo - store this info in deminfo
unsigned span; // smallest tile width/height in level ( in offsets )
switch( lvlIndex ) {
case 0: span = 1; break; // 1/8 deg
case 1: span = 16; break; // 2 degrees
case 2: span = 480; break; // 60 degrees
default:
fprintf( stderr, "invalid lvlIndex %d\n", lvlIndex );
exit(0);
switch (lvlIndex) {
case 0:
span = 1;
break; // 1/8 deg
case 1:
span = 16;
break; // 2 degrees
case 2:
span = 480;
break; // 60 degrees
default:
fprintf(stderr, "invalid lvlIndex %d\n", lvlIndex);
exit(0);
}
if ( lvlIndex >= 0 ) {
if (lvlIndex >= 0) {
unsigned tileLon, tileLat;
unsigned meshLon, meshLat;
int subx, suby;
int subx, suby;
meshLon = wo;
tileLon = SGDem::roundDown( meshLon, lvlWidth);
tileLon = SGDem::roundDown(meshLon, lvlWidth);
subx = (meshLon - tileLon) / span;
// fprintf(stderr, "getGeods: lon is %lf : meshLon is %u, tileLon is %u, subx is %d\n", SGDem::offsetToLongitudeDeg(wo), meshLon, tileLon, subx );
meshLat = so;
tileLat = SGDem::roundDown( meshLat, lvlHeight );
tileLat = SGDem::roundDown(meshLat, lvlHeight);
suby = (meshLat - tileLat) / span;
// fprintf(stderr, "getGeods: lat is %lf : meshLat is %u, tileLat is %u, suby is %d\n", SGDem::offsetToLatitudeDeg(so), meshLat, tileLat, suby );
// get the tle from the tile cache
unsigned long key = tileLon << 16 | tileLat;
SGDemTileRef tile = pDemRoot->getTile( lvlIndex, key );
if ( tile ) {
SGDemTileRef tile = pDemRoot->getTile(lvlIndex, key);
if (tile) {
tile->getGeods(wo, so, eo, no, resx, resy, subx, suby, incx, incy, geods, Debug1, Debug2);
} else {
fprintf(stderr, " *** ERROR: tile %d,%d not in session @ (%lf,%lf) - (%lf,%lf)\n",
fprintf(stderr, " *** ERROR: tile %u,%u not in session @ (%lf,%lf) - (%lf,%lf)\n",
tileLon, tileLat,
SGDem::offsetToLongitudeDeg( west_off ),
SGDem::offsetToLatitudeDeg( south_off ),
SGDem::offsetToLongitudeDeg( east_off ),
SGDem::offsetToLatitudeDeg( north_off ) );
SGDem::offsetToLongitudeDeg(west_off),
SGDem::offsetToLatitudeDeg(south_off),
SGDem::offsetToLongitudeDeg(east_off),
SGDem::offsetToLatitudeDeg(north_off));
}
}
}

View File

@@ -3,58 +3,83 @@
#include <simgear/scene/dem/SGDemRoot.hxx>
class SGDemSession
{
class SGDemSession {
public:
SGDemSession() {
lvlIndex = -1;
SGDemSession()
: west_off(0)
, south_off(0)
, east_off(0)
, north_off(0)
, maxLon(0)
, maxLat(0)
, pDemRoot(NULL)
, lvlIndex(-1)
, lvlWidth(0)
, lvlHeight(0)
{
}
SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root );
SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root );
SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root);
SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root);
SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root ) {
setOffsets( wo, so, eo, no );
pDemRoot = root;
lvlIndex = idx;
lvlWidth = lvlW;
lvlHeight = lvlH;
SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root)
: west_off(wo)
, south_off(so)
, east_off(eo)
, north_off(no)
, maxLon(0)
, maxLat(0)
, pDemRoot(root)
, lvlIndex(idx)
, lvlWidth(lvlW)
, lvlHeight(lvlH)
{
}
SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root ) {
setOffsets( wo, so, eo, no );
pDemRoot = root;
lvlIndex = -1; // no level - session is raw input dir
SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root)
: west_off(wo)
, south_off(so)
, east_off(eo)
, north_off(no)
, maxLon(0)
, maxLat(0)
, pDemRoot(root)
, lvlIndex(-1) // no level - session is raw input dir
, lvlWidth(0)
, lvlHeight(0)
{
}
~SGDemSession() {
~SGDemSession()
{
close();
}
void addTile(SGDemTileRef pTile) {
tileRefs.push_back( pTile );
void addTile(SGDemTileRef pTile)
{
tileRefs.push_back(pTile);
}
const std::vector<SGDemTileRef>& getTiles( void ) const {
const std::vector<SGDemTileRef>& getTiles(void) const
{
return tileRefs;
}
unsigned int size( void ) const {
unsigned int size(void) const
{
return tileRefs.size();
}
void getGeods( unsigned wp, unsigned so, unsigned eo, unsigned no,
int resx, int resy, int incx, int incy,
::std::vector<SGGeod>& geods,
bool Debug1, bool Debug2
);
void getGeods(unsigned wp, unsigned so, unsigned eo, unsigned no,
int resx, int resy, int incx, int incy,
::std::vector<SGGeod>& geods,
bool Debug1, bool Debug2);
void close( void );
void close(void);
int getLvlIndex( void ) const {
return lvlIndex;
int getLvlIndex(void) const
{
return lvlIndex;
};
private:
@@ -64,13 +89,13 @@ private:
east_off = eo;
north_off = no;
}
unsigned west_off, south_off;
unsigned east_off, north_off;
int maxLon, maxLat;
SGDemRoot* pDemRoot;
int lvlIndex;
unsigned lvlWidth, lvlHeight;
unsigned west_off, south_off;
unsigned east_off, north_off;
int maxLon, maxLat;
SGDemRoot* pDemRoot;
int lvlIndex;
unsigned lvlWidth, lvlHeight;
std::vector<SGDemTileRef> tileRefs;
};

View File

@@ -77,6 +77,7 @@
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx>
#include <simgear/structure/SGExpression.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
@@ -245,11 +246,12 @@ int Effect::getGenerator(Effect::Generator what) const
// There should always be a valid technique in an effect.
Technique* Effect::chooseTechnique(RenderInfo* info)
Technique* Effect::chooseTechnique(RenderInfo* info, const std::string &scheme)
{
BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
{
if (technique->valid(info) == Technique::VALID)
if (technique->valid(info) == Technique::VALID &&
technique->getScheme() == scheme)
return technique.get();
}
return 0;
@@ -976,6 +978,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
type);
program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, type);
}
PropertyList pUniformBlockBindings
= prop->getChildren("uniform-block-binding");
for (const auto &pUniformBlockBinding : pUniformBlockBindings) {
program->addBindUniformBlock(
pUniformBlockBinding->getStringValue("name"),
pUniformBlockBinding->getIntValue("index"));
}
programMap.insert(ProgramMap::value_type(prgKey, program));
resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program));
pass->setAttributeAndModes(program);
@@ -1308,6 +1317,7 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop,
{
Technique* tniq = new Technique;
effect->techniques.push_back(tniq);
tniq->setScheme(prop->getStringValue("scheme"));
const SGPropertyNode* predProp = prop->getChild("predicate");
if (!predProp) {
tniq->setAlwaysValid(true);
@@ -1411,12 +1421,60 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
return true;
}
SGPropertyNode_ptr schemeList;
void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options)
{
if (!schemeList) {
schemeList = new SGPropertyNode;
const string schemes_file("Effects/schemes.xml");
string absFileName
= SGModelLib::findDataFile(schemes_file, options);
if (absFileName.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "Could not find '" << schemes_file << "'");
return;
}
try {
readProperties(absFileName, schemeList, 0, true);
} catch (sg_io_exception& e) {
SG_LOG(SG_INPUT, SG_ALERT, "Error reading '" << schemes_file <<
"': " << e.getFormattedMessage());
return;
}
}
PropertyList p_schemes = schemeList->getChildren("scheme");
for (const auto &p_scheme : p_schemes) {
string scheme_name = p_scheme->getStringValue("name");
string fallback_name = p_scheme->getStringValue("fallback");
if (scheme_name.empty() || fallback_name.empty())
continue;
vector<SGPropertyNode_ptr> techniques = effect->root->getChildren("technique");
auto it = std::find_if(techniques.begin(), techniques.end(),
[&scheme_name](const SGPropertyNode_ptr &tniq) {
return tniq->getStringValue("scheme") == scheme_name;
});
// Only merge the fallback effect if we haven't found a technique
// implementing the scheme
if (it == techniques.end()) {
ref_ptr<Effect> fallback = makeEffect(fallback_name, false, options);
if (fallback) {
SGPropertyNode *new_root = new SGPropertyNode;
mergePropertyTrees(new_root, effect->root, fallback->root);
effect->root = new_root;
effect->parametersProp = effect->root->getChild("parameters");
}
}
}
}
// Walk the techniques property tree, building techniques and
// passes.
static SGMutex realizeTechniques_lock;
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{
SGGuard<SGMutex> g(realizeTechniques_lock);
mergeSchemesFallbacks(this, options);
if (_isRealized)
return true;

View File

@@ -92,7 +92,7 @@ public:
SGPropertyNode_ptr root;
// Pointer to the parameters node, if it exists
SGPropertyNode_ptr parametersProp;
Technique* chooseTechnique(osg::RenderInfo* renderInfo);
Technique* chooseTechnique(osg::RenderInfo* renderInfo, const std::string &scheme);
virtual void resizeGLObjectBuffers(unsigned int maxSize);
virtual void releaseGLObjects(osg::State* state = 0) const;
/**

View File

@@ -21,6 +21,8 @@
#include <osg/StateSet>
#include <osg/Texture2D>
#include <osg/io_utils>
#include "EffectCullVisitor.hxx"
#include "EffectGeode.hxx"
@@ -34,9 +36,9 @@ namespace simgear
using osgUtil::CullVisitor;
EffectCullVisitor::EffectCullVisitor(bool collectLights, Effect *effectOverride) :
EffectCullVisitor::EffectCullVisitor(bool collectLights, const std::string &effScheme) :
_collectLights(collectLights),
_effectOverride(effectOverride)
_effScheme(effScheme)
{
}
@@ -50,6 +52,19 @@ CullVisitor* EffectCullVisitor::clone() const
return new EffectCullVisitor(*this);
}
void EffectCullVisitor::apply(osg::Node &node)
{
// TODO: Properly cull lights outside the viewport (override computeBounds())
// if (isCulled(node))
// return;
SGLight *light = dynamic_cast<SGLight *>(&node);
if (!light) {
CullVisitor::apply(node);
return;
}
_lightList.push_back(light);
}
void EffectCullVisitor::apply(osg::Geode& node)
{
if (isCulled(node))
@@ -59,21 +74,12 @@ void EffectCullVisitor::apply(osg::Geode& node)
CullVisitor::apply(node);
return;
}
if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) {
_lightList.push_back( eg );
}
Effect *effect;
if (_effectOverride) {
effect = _effectOverride;
} else {
effect = eg->getEffect();
if (!effect) {
CullVisitor::apply(node);
return;
}
}
Effect* effect = eg->getEffect();
Technique* technique = 0;
if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
if (!effect) {
CullVisitor::apply(node);
return;
} else if (!(technique = effect->chooseTechnique(&getRenderInfo(), _effScheme))) {
return;
}
// push the node's state.

View File

@@ -21,6 +21,8 @@
#include <map>
#include <simgear/scene/model/SGLight.hxx>
namespace osg
{
class Geode;
@@ -34,10 +36,11 @@ class EffectGeode;
class EffectCullVisitor : public osgUtil::CullVisitor
{
public:
EffectCullVisitor(bool collectLights = false, Effect *effectOverride = 0);
EffectCullVisitor(bool collectLights = false, const std::string &effScheme = "");
EffectCullVisitor(const EffectCullVisitor&);
virtual osgUtil::CullVisitor* clone() const;
using osgUtil::CullVisitor::apply;
virtual void apply(osg::Node& node);
virtual void apply(osg::Geode& node);
virtual void reset();
@@ -45,11 +48,13 @@ public:
void addBuffer(std::string b, osg::Texture2D* tex);
osg::Texture2D* getBuffer(std::string b);
SGLightList getLightList() const { return _lightList; }
private:
std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList;
std::vector<osg::ref_ptr<EffectGeode> > _lightList;
SGLightList _lightList;
bool _collectLights;
osg::ref_ptr<Effect> _effectOverride;
std::string _effScheme;
};
}
#endif

View File

@@ -98,6 +98,8 @@ public:
void setGLExtensionsPred(float glVersion,
const std::vector<std::string>& extensions);
void refreshValidity();
const std::string &getScheme() const { return _scheme; }
void setScheme(const std::string &scheme) { _scheme = scheme; }
protected:
// Validity of technique in a graphics context.
struct ContextInfo : public osg::Referenced
@@ -117,6 +119,7 @@ protected:
osg::ref_ptr<osg::StateSet> _shadowingStateSet;
SGSharedPtr<SGExpressionb> _validExpression;
int _contextIdLocation;
std::string _scheme;
};
class TechniquePredParser : public expression::ExpressionParser

View File

@@ -33,6 +33,7 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/structure/SGExpression.hxx>
@@ -126,7 +127,12 @@ Effect* makeEffect(const string& name,
itr->second.valid())
return itr->second.get();
}
string effectFileName(name);
string effectFileName;
// Use getPropertyRoot() because the SGReaderWriterOptions might not have a
// valid property tree
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
effectFileName += "Compositor/";
effectFileName += name;
effectFileName += ".eff";
string absFileName
= SGModelLib::findDataFile(effectFileName, options);

View File

@@ -10,6 +10,7 @@ set(HEADERS
PrimitiveCollector.hxx
SGClipGroup.hxx
SGInteractionAnimation.hxx
SGLight.hxx
SGMaterialAnimation.hxx
SGPickAnimation.hxx
SGOffsetTransform.hxx
@@ -35,6 +36,7 @@ set(SOURCES
PrimitiveCollector.cxx
SGClipGroup.cxx
SGInteractionAnimation.cxx
SGLight.cxx
SGLightAnimation.cxx
SGPickAnimation.cxx
SGMaterialAnimation.cxx
@@ -62,4 +64,4 @@ if(ENABLE_TESTS)
target_link_libraries(test_animations ${TEST_LIBS} ${OPENSCENEGRAPH_LIBRARIES})
add_test(animations ${EXECUTABLE_OUTPUT_PATH}/test_animations)
endif(ENABLE_TESTS)
endif(ENABLE_TESTS)

View File

@@ -62,6 +62,7 @@
#include <simgear/props/condition.hxx>
#include <simgear/io/sg_file.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <simgear/misc/lru_cache.hxx>
#include "BoundingVolumeBuildVisitor.hxx"
#include "model.hxx"
@@ -182,7 +183,7 @@ public:
} // namespace
static int nearestPowerOfTwo(unsigned int _v)
static int nearestPowerOfTwo(int _v)
{
// uint v; // compute the next highest power of 2 of 32-bit v
unsigned int v = (unsigned int)_v;
@@ -207,10 +208,12 @@ static int nearestPowerOfTwo(unsigned int _v)
_v = (int)v;
return v;
}
static bool isPowerOfTwo(int v)
static bool isPowerOfTwo(int v)
{
return ((v & (v - 1)) == 0);
}
osg::Node* DefaultProcessPolicy::process(osg::Node* node, const std::string& filename,
const Options* opt)
{
@@ -232,144 +235,28 @@ osg::Image* getImageByName(const std::string& filename)
return nullptr;
}
#endif
// a cache which evicts the least recently used item when it is full
#include <map>
#include <list>
#include <utility>
#include <boost/optional.hpp>
template<class Key, class Value>
class lru_cache
{
public:
SGMutex _mutex;
// least recently used cache to speed up the process of finding a file
// after the first time - otherwise each time we'll have to generate a hash
// of the contents.
static lru_cache < std::string, std::string> filename_hash_cache(100000);
typedef Key key_type;
typedef Value value_type;
typedef std::list<key_type> list_type;
typedef std::map<
key_type,
std::pair<value_type, typename list_type::iterator>
> map_type;
lru_cache(size_t capacity)
: m_capacity(capacity)
{
}
~lru_cache()
{
}
size_t size() const
{
return m_map.size();
}
size_t capacity() const
{
return m_capacity;
}
bool empty() const
{
return m_map.empty();
}
bool contains(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
return m_map.find(key) != m_map.end();
}
void insert(const key_type &key, const value_type &value)
{
SGGuard<SGMutex> scopeLock(_mutex);
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// insert item into the cache, but first check if it is full
if (size() >= m_capacity) {
// cache is full, evict the least recently used item
evict();
}
// insert the new item
m_list.push_front(key);
m_map[key] = std::make_pair(value, m_list.begin());
}
}
boost::optional<key_type> findValue(const std::string &requiredValue)
{
SGGuard<SGMutex> scopeLock(_mutex);
for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it)
if (it->second.first == requiredValue)
return it->first;
return boost::none;
}
boost::optional<value_type> get(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
// lookup value in the cache
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// value not in cache
return boost::none;
}
// return the value, but first update its place in the most
// recently used list
typename list_type::iterator j = i->second.second;
if (j != m_list.begin()) {
// move item to the front of the most recently used list
m_list.erase(j);
m_list.push_front(key);
// update iterator in map
j = m_list.begin();
const value_type &value = i->second.first;
m_map[key] = std::make_pair(value, j);
// return the value
return value;
}
else {
// the item is already at the front of the most recently
// used list so just return it
return i->second.first;
}
}
void clear()
{
SGGuard<SGMutex> scopeLock(_mutex);
m_map.clear();
m_list.clear();
}
private:
void evict()
{
SGGuard<SGMutex> scopeLock(_mutex);
// evict item from the end of most recently used list
typename list_type::iterator i = --m_list.end();
m_map.erase(*i);
m_list.erase(i);
}
private:
map_type m_map;
list_type m_list;
size_t m_capacity;
};
lru_cache < std::string, std::string> filename_hash_cache(100000);
lru_cache < std::string, bool> filesCleaned(100000);
static bool refreshCache = false;
// experimental (incomplete) features to allow maintenance of the filecache.
//lru_cache < std::string, bool> filesCleaned(100000);
//static bool refreshCache = false;
ReaderWriter::ReadResult
ModelRegistry::readImage(const string& fileName,
const Options* opt)
{
// experimental feature to see if we can reload textures during model load
// as otherwise texture creation/editting requires a restart or a change to
// a different filenaem
//if (SGSceneFeatures::instance()->getReloadCache()) {
// SG_LOG(SG_IO, SG_INFO, "Clearing DDS-TC LRU Cache");
// filename_hash_cache.clear();
// SGSceneFeatures::instance()->setReloadCache(false);
//}
/*
* processor is the interface to the osg_nvtt plugin
*/
@@ -423,13 +310,21 @@ ModelRegistry::readImage(const string& fileName,
if (fileExists(absFileName)) {
SGFile f(absFileName);
std::string hash;
// std::string attr = "";
// if (sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED) {
// attr += " effnorm";
// }
// else if (sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
// attr += " eff";
// // can_compress = false;
// }
// SG_LOG(SG_IO, SG_INFO, absFileName << attr);
boost::optional<std::string> cachehash = filename_hash_cache.get(absFileName);
if (cachehash) {
hash = *cachehash;
// SG_LOG(SG_IO, SG_ALERT, "Hash for " + absFileName + " in cache " + hash);
}
else {
// SG_LOG(SG_IO, SG_ALERT, "Creating hash for " + absFileName);
try {
hash = f.computeHash();
}
@@ -444,9 +339,8 @@ ModelRegistry::readImage(const string& fileName,
// possibly a shared texture - but warn the user to allow investigation.
if (cacheFilename && *cacheFilename != absFileName) {
SG_LOG(SG_IO, SG_ALERT, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName);
SG_LOG(SG_IO, SG_INFO, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName);
}
// SG_LOG(SG_IO, SG_ALERT, " >>>> " + hash + " :: " + newName);
}
newName = cache_root + "/" + hash.substr(0, 2) + "/" + hash + ".cache.dds";
}
@@ -456,21 +350,15 @@ ModelRegistry::readImage(const string& fileName,
newName += "." + tstream.str();
newName += ".cache.dds";
}
bool doRefresh = refreshCache;
//if (fileExists(newName) && sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
// doRefresh = true;
//
//bool doRefresh = refreshCache;
//if (newName != std::string() && fileExists(newName) && doRefresh) {
// if (!filesCleaned.contains(newName)) {
// SG_LOG(SG_IO, SG_INFO, "Removing previously cached effects image " + newName);
// SGPath(newName).remove();
// filesCleaned.insert(newName, true);
// }
//}
if (newName != std::string() && fileExists(newName) && doRefresh) {
if (!filesCleaned.contains(newName)) {
SG_LOG(SG_IO, SG_ALERT, "Removing previously cached effects image " + newName);
SGPath(newName).remove();
filesCleaned.insert(newName, true);
}
}
if (newName != std::string() && !fileExists(newName)) {
res = registry->readImageImplementation(absFileName, opt);
if (res.validImage()) {
@@ -494,14 +382,15 @@ ModelRegistry::readImage(const string& fileName,
isNormalMap = true;
}
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_ALERT, "From effects transparent " + absFileName);
SG_LOG(SG_IO, SG_INFO, "From effects transparent " + absFileName + " will generate mipmap only");
isEffect = true;
can_compress = false;
}
else if (sgoptC && !transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_INFO, "From effects " + absFileName + " will generate mipmap only");
isEffect = true;
// can_compress = false;
}
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_ALERT, "From effects " + absFileName);
isEffect = true;
}
if (can_compress)
{
std::string pot_message;
@@ -516,8 +405,9 @@ ModelRegistry::readImage(const string& fileName,
resize = true;
pot_message += std::string(" not POT: resized height to ") + std::to_string(height);
}
if (pot_message.size())
SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
if (pot_message.size())
SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
// unlikely that after resizing in height the width will still be outside of the max texture size.
if (height > max_texture_size)
@@ -536,6 +426,7 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
width /= factor;
resize = true;
}
if (resize) {
osg::ref_ptr<osg::Image> resizedImage;
@@ -601,21 +492,22 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
// normal maps:
// nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm
processor->compress(*srcImage, targetFormat, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION);
SG_LOG(SG_IO, SG_ALERT, "-- finished creating DDS: " + newName);
SG_LOG(SG_IO, SG_INFO, "-- finished creating DDS: " + newName);
//processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
}
else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
SG_LOG(SG_IO, SG_WARN, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName);
SG_LOG(SG_IO, SG_INFO, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
}
}
else {
SG_LOG(SG_IO, SG_ALERT, "Creating uncompressed DDS for " + absFileName);
if (processor) {
processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
}
else {
else {
SG_LOG(SG_IO, SG_INFO, "Creating uncompressed DDS for " + absFileName);
//if (processor) {
// processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
//}
//else
{
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
}
@@ -660,25 +552,28 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
osg::ref_ptr<osg::Image> srcImage1 = res.getImage();
//printf(" --> finished loading %s [%s] (%s) %d\n", absFileName.c_str(), srcImage1->getFileName().c_str(), res.loadedFromCache() ? "from cache" : "from disk", res.getImage()->getOrigin());
/*
* Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works
*/
if (srcImage1->getFileName().empty()) {
srcImage1->setFileName(absFileName);
}
* Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works
*/
//if (srcImage1->getFileName().empty()) {
// srcImage1->setFileName(absFileName);
//}
srcImage1->setFileName(originalFileName);
if(cache_active && getFileExtension(absFileName) != "dds")
if(cache_active && getFileExtension(absFileName) != "dds"&& getFileExtension(absFileName) != "gz")
{
if (processor) {
processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU);
SG_LOG(SG_IO, SG_ALERT, "Created nvtt mipmaps DDS for " + absFileName);
}
else {
// In testing the internal mipmap generation works better than the external nvtt one
// (less artefacts); it might be that there are flags we can use to make this better
// but for now we'll just using the built in one
//if (processor) {
// processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU);
// SG_LOG(SG_IO, SG_INFO, "Created nvtt mipmaps DDS for " + absFileName);
// }
// else
{
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage1 = simgear::effect::computeMipmap(srcImage1, mipmapFunctions);
SG_LOG(SG_IO, SG_ALERT, "Created sg mipmaps DDS for " + absFileName);
SG_LOG(SG_IO, SG_DEBUG, "Created sg mipmaps DDS for " + absFileName);
}
}
@@ -689,75 +584,6 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
SG_LOG(SG_IO, SG_BULK, "Reading image \""
<< res.getImage()->getFileName() << "\"");
// as of March 2018 all patents have expired, https://en.wikipedia.org/wiki/S3_Texture_Compression#Patent
// there is support for S3TC DXT1..5 in MESA https://www.phoronix.com/scan.php?page=news_item&px=S3TC-Lands-In-Mesa
// so it seems that there isn't a valid reason to warn any longer; and beside this is one of those cases where it should
// really only be a developer message
#ifdef WARN_DDS_TEXTURES
// Check for precompressed textures that depend on an extension
switch (res.getImage()->getPixelFormat()) {
// GL_EXT_texture_compression_s3tc
// patented, no way to decompress these
#ifndef GL_EXT_texture_compression_s3tc
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
// GL_EXT_texture_sRGB
// patented, no way to decompress these
#ifndef GL_EXT_texture_sRGB
#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
#endif
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
// GL_TDFX_texture_compression_FXT1
// can decompress these in software but
// no code present in simgear.
#ifndef GL_3DFX_texture_compression_FXT1
#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1
#endif
case GL_COMPRESSED_RGB_FXT1_3DFX:
case GL_COMPRESSED_RGBA_FXT1_3DFX:
// GL_EXT_texture_compression_rgtc
// can decompress these in software but
// no code present in simgear.
#ifndef GL_EXT_texture_compression_rgtc
#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#endif
case GL_COMPRESSED_RED_RGTC1_EXT:
case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
SG_LOG(SG_IO, SG_WARN, "Image \"" << fileName << "\"\n"
"uses compressed textures which cannot be supported on "
"some systems.\n"
"Please decompress this texture for improved portability.");
break;
default:
break;
}
#endif
return res;
}

View File

@@ -0,0 +1,157 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "SGLight.hxx"
#include <osg/Geode>
#include <osg/MatrixTransform>
#include <osg/PolygonMode>
#include <osg/ShapeDrawable>
#include <osg/Switch>
#include <simgear/math/SGMath.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
class SGLightDebugListener : public SGPropertyChangeListener {
public:
SGLightDebugListener(osg::Switch *sw) : _sw(sw) {}
virtual void valueChanged(SGPropertyNode *node) {
_sw->setValue(0, node->getBoolValue());
}
private:
osg::ref_ptr<osg::Switch> _sw;
};
osg::Node *
SGLight::appendLight(const SGPropertyNode *configNode,
SGPropertyNode *modelRoot,
const osgDB::Options *options)
{
SGConstPropertyNode_ptr p;
SGLight *light = new SGLight;
if((p = configNode->getNode("type")) != NULL) {
std::string type = p->getStringValue();
if (type == "point")
light->setType(SGLight::Type::POINT);
else if (type == "spot")
light->setType(SGLight::Type::SPOT);
else
SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown light type '" << type << "'");
}
light->setRange(configNode->getFloatValue("range-m"));
#define SGLIGHT_GET_COLOR_VALUE(n) \
osg::Vec4(configNode->getFloatValue(n "/r"), \
configNode->getFloatValue(n "/g"), \
configNode->getFloatValue(n "/b"), \
configNode->getFloatValue(n "/a"))
light->setAmbient(SGLIGHT_GET_COLOR_VALUE("ambient"));
light->setDiffuse(SGLIGHT_GET_COLOR_VALUE("diffuse"));
light->setSpecular(SGLIGHT_GET_COLOR_VALUE("specular"));
#undef SGLIGHT_GET_COLOR_VALUE
light->setConstantAttenuation(configNode->getFloatValue("attenuation/c"));
light->setLinearAttenuation(configNode->getFloatValue("attenuation/l"));
light->setQuadraticAttenuation(configNode->getFloatValue("attenuation/q"));
light->setSpotExponent(configNode->getFloatValue("spot-exponent"));
light->setSpotCutoff(configNode->getFloatValue("spot-cutoff"));
osg::Group *group = 0;
if ((p = configNode->getNode("offsets")) == NULL) {
group = new osg::Group;
} else {
// Set up the alignment node ("stolen" from animation.cxx)
// XXX Order of rotations is probably not correct.
osg::MatrixTransform *align = new osg::MatrixTransform;
osg::Matrix res_matrix;
res_matrix.makeRotate(
p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
osg::Vec3(0, 1, 0),
p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
osg::Vec3(1, 0, 0),
p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
osg::Vec3(0, 0, 1));
osg::Matrix tmat;
tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
configNode->getFloatValue("offsets/y-m", 0.0),
configNode->getFloatValue("offsets/z-m", 0.0));
align->setMatrix(res_matrix * tmat);
group = align;
}
group->addChild(light);
osg::Shape *debug_shape;
if (light->getType() == SGLight::Type::POINT) {
debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange());
} else if (light->getType() == SGLight::Type::SPOT) {
debug_shape = new osg::Cone(
// Origin of the cone is at its center of mass
osg::Vec3(0, 0, -0.75 * light->getRange()),
tan(light->getSpotCutoff() * SG_DEGREES_TO_RADIANS) * light->getRange(),
light->getRange());
}
osg::ShapeDrawable *debug_drawable = new osg::ShapeDrawable(debug_shape);
debug_drawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0));
osg::Geode *debug_geode = new osg::Geode;
debug_geode->addDrawable(debug_drawable);
osg::StateSet *debug_ss = debug_drawable->getOrCreateStateSet();
debug_ss->setAttributeAndModes(
new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE),
osg::StateAttribute::ON);
debug_ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::Switch *debug_switch = new osg::Switch;
debug_switch->addChild(debug_geode);
simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)->
addChangeListener(new SGLightDebugListener(debug_switch), true);
group->addChild(debug_switch);
if ((p = configNode->getNode("name")) != NULL)
group->setName(p->getStringValue());
else
group->setName("light");
return group;
}
SGLight::SGLight() :
_type(Type::POINT),
_range(0.0f)
{
// Default values taken from osg::Light
// They don't matter anyway as they are overwritten by the XML config values
_ambient.set(0.05f, 0.05f, 0.05f, 1.0f);
_diffuse.set(0.8f, 0.8f, 0.8f, 1.0f);
_specular.set(0.05f, 0.05f, 0.05f, 1.0f);
_constant_attenuation = 1.0f;
_linear_attenuation = 0.0f;
_quadratic_attenuation = 0.0f;
_spot_exponent = 0.0f;
_spot_cutoff = 180.0f;
}
SGLight::~SGLight()
{
}

View File

@@ -0,0 +1,105 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_LIGHT_HXX
#define SG_LIGHT_HXX
#include <osgDB/ReaderWriter>
#include <osg/Group>
#include <simgear/props/props.hxx>
class SGLight : public osg::Node {
public:
enum Type {
POINT,
SPOT
};
SGLight();
SGLight(const SGLight& l,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) :
osg::Node(l, copyop),
_type(l._type),
_range(l._range),
_ambient(l._ambient),
_diffuse(l._diffuse),
_specular(l._specular),
_constant_attenuation(l._constant_attenuation),
_linear_attenuation(l._linear_attenuation),
_quadratic_attenuation(l._quadratic_attenuation),
_spot_exponent(l._spot_exponent),
_spot_cutoff(l._spot_cutoff)
{}
META_Node(simgear, SGLight);
static osg::Node *appendLight(const SGPropertyNode *configNode,
SGPropertyNode *modelRoot,
const osgDB::Options *options);
void setType(Type type) { _type = type; }
Type getType() const { return _type; }
void setRange(float range) { _range = range; }
float getRange() const { return _range; }
void setAmbient(const osg::Vec4 &ambient) { _ambient = ambient; }
const osg::Vec4 &getAmbient() const { return _ambient; }
void setDiffuse(const osg::Vec4 &diffuse) { _diffuse = diffuse; }
const osg::Vec4 &getDiffuse() const { return _diffuse; }
void setSpecular(const osg::Vec4 &specular) { _specular = specular; }
const osg::Vec4 &getSpecular() const { return _specular; }
void setConstantAttenuation(float constant_attenuation) { _constant_attenuation = constant_attenuation; }
float getConstantAttenuation() const { return _constant_attenuation; }
void setLinearAttenuation(float linear_attenuation) { _linear_attenuation = linear_attenuation; }
float getLinearAttenuation() const { return _linear_attenuation; }
void setQuadraticAttenuation(float quadratic_attenuation) { _quadratic_attenuation = quadratic_attenuation; }
float getQuadraticAttenuation() const { return _quadratic_attenuation; }
void setSpotExponent(float spot_exponent) { _spot_exponent = spot_exponent; }
float getSpotExponent() const { return _spot_exponent; }
void setSpotCutoff(float spot_cutoff) { _spot_cutoff = spot_cutoff; }
float getSpotCutoff() const { return _spot_cutoff; }
protected:
virtual ~SGLight();
Type _type;
float _range;
osg::Vec4 _ambient;
osg::Vec4 _diffuse;
osg::Vec4 _specular;
float _constant_attenuation;
float _linear_attenuation;
float _quadratic_attenuation;
float _spot_exponent;
float _spot_cutoff;
};
typedef std::vector<osg::ref_ptr<SGLight>> SGLightList;
#endif /* SG_LIGHT_HXX */

View File

@@ -50,6 +50,7 @@
#include "animation.hxx"
#include "particles.hxx"
#include "model.hxx"
#include "SGLight.hxx"
#include "SGText.hxx"
#include "SGMaterialAnimation.hxx"
@@ -518,6 +519,14 @@ sgLoad3DModel_internal(const SGPath& path,
options.get()));
}
std::vector<SGPropertyNode_ptr> light_nodes;
light_nodes = props->getChildren("light");
for (unsigned i = 0; i < light_nodes.size(); ++i) {
group->addChild(SGLight::appendLight(light_nodes[i],
prop_root,
options.get()));
}
PropertyList effect_nodes = props->getChildren("effect");
PropertyList animation_nodes = props->getChildren("animation");

View File

@@ -1115,3 +1115,9 @@ void SGTerraSync::reposition()
{
// stub, remove
}
// Register the subsystem.
SGSubsystemMgr::Registrant<SGTerraSync> registrantSGTerraSync(
SGSubsystemMgr::GENERAL,
{{"FGRenderer", SGSubsystemMgr::Dependency::NONSUBSYSTEM_HARD}});

View File

@@ -39,16 +39,19 @@ class BufferedLogCallback;
class SGTerraSync : public SGSubsystem
{
public:
SGTerraSync();
virtual ~SGTerraSync();
virtual void init();
virtual void shutdown();
virtual void reinit();
virtual void bind();
virtual void unbind();
virtual void update(double);
// Subsystem API.
void bind() override;
void init() override;
void reinit() override;
void shutdown() override;
void unbind() override;
void update(double) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "terrasync"; }
/// notify terrasync that the sim was repositioned, as opposed to
/// us travelling in a direction. Avoid last_lat / last_lon blocking
@@ -80,6 +83,7 @@ public:
void scheduleDataDir(const std::string& dataDir);
bool isDataDirPending(const std::string& dataDir) const;
protected:
void syncAirportsModels();
string_list getSceneryPathSuffixes() const;

View File

@@ -1812,7 +1812,7 @@ namespace
{
static void write(const ImageUtils::PixelWriter* iw, const osg::Vec4f& c, int s, int t, int r, int m)
{
GLubyte* ptr = (GLubyte*)iw->data(s, t, r, m);
iw->data(s, t, r, m);
//OE_WARN << LC << "Target GL_UNSIGNED_BYTE_3_3_2 not yet implemented" << std::endl;
}
};

View File

@@ -1,5 +1,5 @@
set(HEADERS
ClusteredForward.hxx
ClusteredShading.hxx
Compositor.hxx
CompositorBuffer.hxx
CompositorPass.hxx
@@ -7,7 +7,7 @@ set(HEADERS
)
set(SOURCES
ClusteredForward.cxx
ClusteredShading.cxx
Compositor.cxx
CompositorBuffer.cxx
CompositorPass.cxx

View File

@@ -1,216 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "ClusteredForward.hxx"
#include <osg/BufferIndexBinding>
#include <osg/BufferObject>
#include <osg/RenderInfo>
#include <osg/Texture3D>
#include <osg/TextureBuffer>
#include <osg/Version>
namespace simgear {
namespace compositor {
///// BEGIN DEBUG
#define DATA_SIZE 24
const GLfloat LIGHT_DATA[DATA_SIZE] = {
0.0, 0.0, -10.0, 1.0, 1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 10.0, 1.0, 0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0
};
#define MAX_LIGHT_INDICES 4096
#define MAX_POINT_LIGHTS 256
struct Light {
osg::Vec3 position;
float range;
};
#define NUM_LIGHTS 2
Light LIGHT_LIST[NUM_LIGHTS] = {
{osg::Vec3(0.0f, 0.0f, -10.0f), 10.0f},
{osg::Vec3(0.0f, 0.0f, 5.0f), 1000.0f}
};
///// END DEBUG
ClusteredForwardDrawCallback::ClusteredForwardDrawCallback() :
_initialized(false),
_tile_size(64),
_light_grid(new osg::Image),
_light_indices(new osg::Image),
_light_data(new osg::FloatArray(MAX_POINT_LIGHTS))
{
}
void
ClusteredForwardDrawCallback::operator()(osg::RenderInfo &renderInfo) const
{
osg::Camera *camera = renderInfo.getCurrentCamera();
const osg::Viewport *vp = camera->getViewport();
const int width = vp->width();
const int height = vp->height();
// Round up
int n_htiles = (width + _tile_size - 1) / _tile_size;
int n_vtiles = (height + _tile_size - 1) / _tile_size;
if (!_initialized) {
// Create and associate the light grid 3D texture
_light_grid->allocateImage(n_htiles, n_vtiles, 1,
GL_RGB_INTEGER_EXT, GL_UNSIGNED_SHORT);
_light_grid->setInternalTextureFormat(GL_RGB16UI_EXT);
osg::ref_ptr<osg::Texture3D> light_grid_tex = new osg::Texture3D;
light_grid_tex->setResizeNonPowerOfTwoHint(false);
light_grid_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST);
light_grid_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST);
light_grid_tex->setImage(0, _light_grid.get());
camera->getOrCreateStateSet()->setTextureAttributeAndModes(
10, light_grid_tex.get(), osg::StateAttribute::ON);
// Create and associate the light indices TBO
_light_indices->allocateImage(4096, 1, 1, GL_RED_INTEGER_EXT, GL_UNSIGNED_SHORT);
osg::ref_ptr<osg::TextureBuffer> light_indices_tbo =
new osg::TextureBuffer;
light_indices_tbo->setInternalFormat(GL_R16UI);
light_indices_tbo->setImage(_light_indices.get());
camera->getOrCreateStateSet()->setTextureAttribute(
11, light_indices_tbo.get());
// Create and associate the light data UBO
osg::ref_ptr<osg::UniformBufferObject> light_data_ubo =
new osg::UniformBufferObject;
_light_data->setBufferObject(light_data_ubo.get());
#if OSG_VERSION_LESS_THAN(3,6,0)
osg::ref_ptr<osg::UniformBufferBinding> light_data_ubb =
new osg::UniformBufferBinding(0, light_data_ubo.get(),
0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat));
#else
osg::ref_ptr<osg::UniformBufferBinding> light_data_ubb =
new osg::UniformBufferBinding(0, _light_data.get(),
0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat));
#endif
light_data_ubb->setDataVariance(osg::Object::DYNAMIC);
camera->getOrCreateStateSet()->setAttribute(
light_data_ubb.get(), osg::StateAttribute::ON);
_initialized = true;
}
std::vector<osg::Polytope> subfrustums;
const osg::Matrix &view_matrix = camera->getViewMatrix();
const osg::Matrix &proj_matrix = camera->getProjectionMatrix();
osg::Matrix view_proj_inverse = osg::Matrix::inverse(view_matrix * proj_matrix);
double x_step = (_tile_size / width) * 2.0;
double y_step = (_tile_size / height) * 2.0;
for (int y = 0; y < n_vtiles; ++y) {
for (int x = 0; x < n_htiles; ++x) {
// Create the subfrustum in clip space
double x_min = -1.0 + x_step * x; double x_max = x_min + x_step;
double y_min = -1.0 + y_step * y; double y_max = y_min + y_step;
double z_min = 1.0; double z_max = -1.0;
osg::BoundingBox subfrustum_bb(
x_min, y_min, z_min, x_max, y_max, z_max);
osg::Polytope subfrustum;
subfrustum.setToBoundingBox(subfrustum_bb);
// Transform it to world space
subfrustum.transformProvidingInverse(view_proj_inverse);
subfrustums.push_back(subfrustum);
}
}
GLushort *grid_data = reinterpret_cast<GLushort *>
(_light_grid->data());
GLushort *index_data = reinterpret_cast<GLushort *>
(_light_indices->data());
GLushort global_light_count = 0;
for (size_t i = 0; i < subfrustums.size(); ++i) {
GLushort start_offset = global_light_count;
GLushort local_light_count = 0;
for (GLushort light_list_index = 0;
light_list_index < NUM_LIGHTS;
++light_list_index) {
const Light &light = LIGHT_LIST[light_list_index];
osg::BoundingSphere bs(light.position, light.range);
if (subfrustums[i].contains(bs)) {
index_data[global_light_count] = light_list_index;
++local_light_count;
++global_light_count;
}
}
grid_data[i * 3 + 0] = start_offset;
grid_data[i * 3 + 1] = local_light_count;
grid_data[i * 3 + 2] = 0;
}
_light_grid->dirty();
_light_indices->dirty();
// Upload light data
for (int i = 0; i < DATA_SIZE; ++i) {
(*_light_data)[i] = LIGHT_DATA[i];
}
// DEBUG
/*
if (!_debug) {
for (int y = 0; y < num_vtiles; ++y) {
for (int x = 0; x < num_htiles; ++x) {
std::cout << grid_data[(y * num_htiles + x) * 3 + 0] << ","
<< grid_data[(y * num_htiles + x) * 3 + 1] << " ";
}
std::cout << std::endl;
}
std::cout << "\n\n";
for (int i = 0; i < num_vtiles * num_htiles; ++i) {
std::cout << index_data[i] << " ";
}
std::cout << "\n";
_debug = true;
}
*/
/*
for (int y = 0; y < num_vtiles; ++y) {
for (int x = 0; x < num_htiles; ++x) {
data[(y * num_htiles + x) * 3 + 0] = (unsigned short)x;
data[(y * num_htiles + x) * 3 + 1] = (unsigned short)y;
data[(y * num_htiles + x) * 3 + 2] = 0;
}
}
_light_grid->dirty();
*/
}
} // namespace compositor
} // namespace simgear

View File

@@ -1,40 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CLUSTERED_FORWARD_HXX
#define SG_CLUSTERED_FORWARD_HXX
#include <osg/Camera>
namespace simgear {
namespace compositor {
class ClusteredForwardDrawCallback : public osg::Camera::DrawCallback {
public:
ClusteredForwardDrawCallback();
virtual void operator()(osg::RenderInfo &renderInfo) const;
protected:
mutable bool _initialized;
int _tile_size;
osg::ref_ptr<osg::Image> _light_grid;
osg::ref_ptr<osg::Image> _light_indices;
osg::ref_ptr<osg::FloatArray> _light_data;
};
} // namespace compositor
} // namespace simgear
#endif /* SG_CLUSTERED_FORWARD_HXX */

View File

@@ -0,0 +1,395 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "ClusteredShading.hxx"
#include <thread>
#include <osg/BufferIndexBinding>
#include <osg/BufferObject>
#include <osg/RenderInfo>
#include <osg/Texture3D>
#include <osg/TextureBuffer>
#include <osg/Version>
#include <osg/io_utils>
#include <simgear/structure/exception.hxx>
namespace simgear {
namespace compositor {
const int MAX_LIGHT_INDICES = 524288; // 1 MB (2 bytes per index)
const int MAX_POINTLIGHTS = 256;
const int MAX_SPOTLIGHTS = 256;
// Size in floats (4 bytes) of the light struct to be passed to the GLSL shader.
// It must be a multiple of the size of a vec4 as per the std140 layout rules.
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt
const int POINTLIGHT_BLOCK_SIZE = 20;
const int SPOTLIGHT_BLOCK_SIZE = 8;
ClusteredShading::ClusteredShading(osg::Camera *camera,
const SGPropertyNode *config) :
_camera(camera)
{
_tile_size = config->getIntValue("tile-size", 128);
_depth_slices = config->getIntValue("depth-slices", 1);
_num_threads = config->getIntValue("num-threads", 1);
_slices_per_thread = _depth_slices / _num_threads;
if (_slices_per_thread == 0) {
SG_LOG(SG_INPUT, SG_INFO, "ClusteredShading::ClusteredShading(): "
"More threads than depth slices");
_num_threads = _depth_slices;
}
_slices_remainder = _depth_slices % _num_threads;
osg::StateSet *ss = _camera->getOrCreateStateSet();
osg::Uniform *tile_size_uniform =
new osg::Uniform("fg_ClusteredTileSize", _tile_size);
ss->addUniform(tile_size_uniform);
_slice_scale = new osg::Uniform("fg_ClusteredSliceScale", 0.0f);
ss->addUniform(_slice_scale.get());
_slice_bias = new osg::Uniform("fg_ClusteredSliceBias", 0.0f);
ss->addUniform(_slice_bias.get());
// Create and associate the light grid 3D texture
////////////////////////////////////////////////////////////////////////////
_light_grid = new osg::Image;
_light_grid->setInternalTextureFormat(GL_RGB32UI_EXT);
// Image allocation happens in setupSubfrusta() because the light grid size
// can change at runtime (viewport resize)
osg::ref_ptr<osg::Texture3D> light_grid_tex = new osg::Texture3D;
light_grid_tex->setResizeNonPowerOfTwoHint(false);
light_grid_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST);
light_grid_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST);
light_grid_tex->setImage(_light_grid.get());
int light_grid_bind_unit = config->getIntValue("grid-bind-unit", 11);
ss->setTextureAttributeAndModes(
light_grid_bind_unit, light_grid_tex.get(), osg::StateAttribute::ON);
osg::ref_ptr<osg::Uniform> light_grid_uniform =
new osg::Uniform("fg_ClusteredLightGrid", light_grid_bind_unit);
ss->addUniform(light_grid_uniform.get());
// Create and associate the light indices TBO
////////////////////////////////////////////////////////////////////////////
_light_indices = new osg::Image;
_light_indices->allocateImage(
MAX_LIGHT_INDICES, 1, 1, GL_RED_INTEGER_EXT, GL_UNSIGNED_SHORT);
osg::ref_ptr<osg::TextureBuffer> light_indices_tbo =
new osg::TextureBuffer;
light_indices_tbo->setInternalFormat(GL_R16UI);
light_indices_tbo->setImage(_light_indices.get());
int light_indices_bind_unit = config->getIntValue("indices-bind-unit", 12);
ss->setTextureAttribute(light_indices_bind_unit, light_indices_tbo.get());
osg::ref_ptr<osg::Uniform> light_indices_uniform =
new osg::Uniform("fg_ClusteredLightIndices", light_indices_bind_unit);
ss->addUniform(light_indices_uniform.get());
// Create and associate the pointlight data UBO
////////////////////////////////////////////////////////////////////////////
_pointlight_data = new osg::FloatArray(MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE);
osg::ref_ptr<osg::UniformBufferObject> pointlight_data_ubo =
new osg::UniformBufferObject;
_pointlight_data->setBufferObject(pointlight_data_ubo.get());
int pointlight_ubo_index = config->getIntValue("pointlight-ubo-index", 5);
#if OSG_VERSION_LESS_THAN(3,6,0)
osg::ref_ptr<osg::UniformBufferBinding> pointlight_data_ubb =
new osg::UniformBufferBinding(
pointlight_ubo_index,
pointlight_data_ubo.get(),
0,
MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE * sizeof(GLfloat));
#else
osg::ref_ptr<osg::UniformBufferBinding> pointlight_data_ubb =
new osg::UniformBufferBinding(
pointlight_ubo_index,
_pointlight_data.get(),
0,
MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE * sizeof(GLfloat));
#endif
pointlight_data_ubb->setDataVariance(osg::Object::DYNAMIC);
ss->setAttribute(pointlight_data_ubb.get(), osg::StateAttribute::ON);
// Create and associate the spotlight data UBO
////////////////////////////////////////////////////////////////////////////
_spotlight_data = new osg::FloatArray(MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE);
osg::ref_ptr<osg::UniformBufferObject> spotlight_data_ubo =
new osg::UniformBufferObject;
_spotlight_data->setBufferObject(spotlight_data_ubo.get());
int spotlight_ubo_index = config->getIntValue("spotlight-ubo-index", 6);
#if OSG_VERSION_LESS_THAN(3,6,0)
osg::ref_ptr<osg::UniformBufferBinding> spotlight_data_ubb =
new osg::UniformBufferBinding(
spotlight_ubo_index,
spotlight_data_ubo.get(),
0,
MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE * sizeof(GLfloat));
#else
osg::ref_ptr<osg::UniformBufferBinding> spotlight_data_ubb =
new osg::UniformBufferBinding(
spotlight_ubo_index,
_spotlight_data.get(),
0,
MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE * sizeof(GLfloat));
#endif
spotlight_data_ubb->setDataVariance(osg::Object::DYNAMIC);
ss->setAttribute(spotlight_data_ubb.get(), osg::StateAttribute::ON);
}
ClusteredShading::~ClusteredShading()
{
}
void
ClusteredShading::update(const SGLightList &light_list)
{
// Transform every light to a more comfortable data structure for collision
// testing, separating point and spot lights in the process
_point_bounds.clear();
_spot_bounds.clear();
for (const auto &light : light_list) {
if (light->getType() == SGLight::Type::POINT) {
PointlightBound point;
point.light = light;
point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) *
osg::computeLocalToWorld(light->getParentalNodePaths()[0]) *
_camera->getViewMatrix();
point.range = light->getRange();
_point_bounds.push_back(point);
} else if (light->getType() == SGLight::Type::SPOT) {
}
}
if (_point_bounds.size() > MAX_POINTLIGHTS ||
_spot_bounds.size() > MAX_SPOTLIGHTS) {
throw sg_range_exception("Maximum amount of visible lights surpassed");
}
float l, r, b, t;
_camera->getProjectionMatrix().getFrustum(l, r, b, t, _zNear, _zFar);
_slice_scale->set(_depth_slices / log2(_zFar / _zNear));
_slice_bias->set(-_depth_slices * log2(_zNear) / log2(_zFar / _zNear));
const osg::Viewport *vp = _camera->getViewport();
static int old_width = 0, old_height = 0;
int width = vp->width(); int height = vp->height();
if (width != old_width || height != old_height) {
old_width = width; old_height = height;
_n_htiles = (width + _tile_size - 1) / _tile_size;
_n_vtiles = (height + _tile_size - 1) / _tile_size;
_x_step = (_tile_size / float(width)) * 2.0;
_y_step = (_tile_size / float(height)) * 2.0;
_light_grid->allocateImage(_n_htiles, _n_vtiles, _depth_slices,
GL_RGB_INTEGER_EXT, GL_UNSIGNED_INT);
_subfrusta.reset(new Subfrustum[_n_htiles * _n_vtiles]);
}
for (int y = 0; y < _n_vtiles; ++y) {
float ymin = -1.0 + _y_step * float(y);
float ymax = ymin + _y_step;
for (int x = 0; x < _n_htiles; ++x) {
float xmin = -1.0 + _x_step * float(x);
float xmax = xmin + _x_step;
// Create the subfrustum in clip space
// The near and far planes will be filled later as they change from
// slice to slice
Subfrustum &subfrustum = _subfrusta[y*_n_htiles + x];
subfrustum.plane[0].set(1.0f,0.0f,0.0f,-xmin); // left plane.
subfrustum.plane[1].set(-1.0f,0.0f,0.0f,xmax); // right plane.
subfrustum.plane[2].set(0.0f,1.0f,0.0f,-ymin); // bottom plane.
subfrustum.plane[3].set(0.0f,-1.0f,0.0f,ymax); // top plane.
// Transform it to view space
for (int i = 0; i < 4; ++i) {
osg::Vec4f &p = subfrustum.plane[i];
p = _camera->getProjectionMatrix() * p;
float inv_length = 1.0 / sqrt(p._v[0]*p._v[0] +
p._v[1]*p._v[1] +
p._v[2]*p._v[2]);
p *= inv_length;
}
}
}
_global_light_count = 0;
if (_depth_slices == 1) {
// Just run the light assignment on the main thread to avoid the
// unnecessary threading overhead
assignLightsToSlice(0);
} else if (_num_threads == 1) {
// Again, avoid the unnecessary threading overhead
threadFunc(0);
} else {
std::vector<std::thread> threads;
threads.reserve(_num_threads);
for (int i = 0; i < _num_threads; ++i)
threads.emplace_back(&ClusteredShading::threadFunc, this, i);
for (auto &t : threads) t.join();
}
// Force upload of the image data
_light_grid->dirty();
_light_indices->dirty();
// Upload pointlight data
writePointlightData();
}
void
ClusteredShading::threadFunc(int thread_id)
{
for (int i = 0; i < _slices_per_thread; ++i)
assignLightsToSlice(thread_id * _slices_per_thread + i);
if (_slices_remainder > thread_id)
assignLightsToSlice(_slices_per_thread * _num_threads + thread_id);
}
void
ClusteredShading::assignLightsToSlice(int slice)
{
size_t z_offset = slice * _n_htiles * _n_vtiles;
float near = getDepthForSlice(slice);
float far = getDepthForSlice(slice + 1);
osg::Vec4f near_plane(0.0f, 0.0f, -1.0f, -near);
osg::Vec4f far_plane (0.0f, 0.0f, 1.0f, far);
GLuint *grid = reinterpret_cast<GLuint *>(_light_grid->data());
GLushort *indices = reinterpret_cast<GLushort *>(_light_indices->data());
for (int i = 0; i < (_n_htiles * _n_vtiles); ++i) {
Subfrustum subfrustum = _subfrusta[i];
subfrustum.plane[4] = near_plane;
subfrustum.plane[5] = far_plane;
GLuint start_offset = _global_light_count;
GLuint local_point_count = 0;
GLuint local_spot_count = 0;
for (GLushort point_iterator = 0;
point_iterator < _point_bounds.size();
++point_iterator) {
PointlightBound point = _point_bounds[point_iterator];
// Perform frustum-sphere collision tests
float distance = 0.0f;
for (int j = 0; j < 6; j++) {
distance = subfrustum.plane[j] * point.position + point.range;
if (distance <= 0.0f)
break;
}
if (distance > 0.0f) {
// Update light index list
indices[_global_light_count] = point_iterator;
++local_point_count;
++_global_light_count; // Atomic increment
}
if (_global_light_count >= MAX_LIGHT_INDICES) {
throw sg_range_exception(
"Clustered shading light index count is over the hardcoded limit ("
+ std::to_string(MAX_LIGHT_INDICES) + ")");
}
}
// Update light grid
grid[(z_offset + i) * 3 + 0] = start_offset;
grid[(z_offset + i) * 3 + 1] = local_point_count;
grid[(z_offset + i) * 3 + 2] = local_spot_count;
}
// for (int y = 0; y < _n_vtiles; ++y) {
// for (int x = 0; x < _n_htiles; ++x) {
// std::cout << grid[(y * _n_htiles + x) * 3 + 0] << ","
// << grid[(y * _n_htiles + x) * 3 + 1] << " ";
// }
// std::cout << std::endl;
// }
// std::cout << "\n\n";
// for (int i = 0; i < n_vtiles * n_htiles; ++i) {
// std::cout << indices[i] << " ";
// }
// std::cout << "\n";
}
void
ClusteredShading::writePointlightData()
{
GLfloat *data = reinterpret_cast<GLfloat *>(&(*_pointlight_data)[0]);
for (const auto &point : _point_bounds) {
// vec4 position
*data++ = point.position.x();
*data++ = point.position.y();
*data++ = point.position.z();
*data++ = 1.0f;
// vec4 ambient
*data++ = point.light->getAmbient().x();
*data++ = point.light->getAmbient().y();
*data++ = point.light->getAmbient().z();
*data++ = point.light->getAmbient().w();
// vec4 diffuse
*data++ = point.light->getDiffuse().x();
*data++ = point.light->getDiffuse().y();
*data++ = point.light->getDiffuse().z();
*data++ = point.light->getDiffuse().w();
// vec4 specular
*data++ = point.light->getSpecular().x();
*data++ = point.light->getSpecular().y();
*data++ = point.light->getSpecular().z();
*data++ = point.light->getSpecular().w();
// vec4 attenuation (x = constant, y = linear, z = quadratic, w = range)
*data++ = point.light->getConstantAttenuation();
*data++ = point.light->getLinearAttenuation();
*data++ = point.light->getQuadraticAttenuation();
*data++ = point.light->getRange();
// No padding needed as the resulting size is a multiple of vec4
}
_pointlight_data->dirty();
}
float
ClusteredShading::getDepthForSlice(int slice) const
{
return _zNear * pow(_zFar / _zNear, float(slice) / _depth_slices);
}
} // namespace compositor
} // namespace simgear

View File

@@ -0,0 +1,96 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CLUSTERED_SHADING_HXX
#define SG_CLUSTERED_SHADING_HXX
#include <atomic>
#include <osg/Camera>
#include <osg/Uniform>
#include <simgear/scene/model/SGLight.hxx>
namespace simgear {
namespace compositor {
class ClusteredShading : public osg::Referenced {
public:
ClusteredShading(osg::Camera *camera, const SGPropertyNode *config);
~ClusteredShading();
void update(const SGLightList &light_list);
protected:
// We could make use of osg::Polytope, but it does a lot of std::vector
// push_back() calls, so we make our own frustum structure for huge
// performance gains.
struct Subfrustum {
osg::Vec4f plane[6];
};
struct PointlightBound {
SGLight *light;
osg::Vec4f position;
float range;
};
struct SpotlightBound {
SGLight *light;
osg::Vec4f position;
float range;
};
void threadFunc(int thread_id);
void assignLightsToSlice(int slice);
void writePointlightData();
float getDepthForSlice(int slice) const;
osg::observer_ptr<osg::Camera> _camera;
osg::ref_ptr<osg::Uniform> _slice_scale;
osg::ref_ptr<osg::Uniform> _slice_bias;
int _tile_size;
int _depth_slices;
int _num_threads;
int _slices_per_thread;
int _slices_remainder;
float _zNear;
float _zFar;
int _n_htiles;
int _n_vtiles;
float _x_step;
float _y_step;
osg::ref_ptr<osg::Image> _light_grid;
osg::ref_ptr<osg::Image> _light_indices;
osg::ref_ptr<osg::FloatArray> _pointlight_data;
osg::ref_ptr<osg::FloatArray> _spotlight_data;
std::unique_ptr<Subfrustum[]> _subfrusta;
std::vector<PointlightBound> _point_bounds;
std::vector<SpotlightBound> _spot_bounds;
std::atomic<GLuint> _global_light_count;
};
} // namespace compositor
} // namespace simgear
#endif /* SG_CLUSTERED_SHADING_HXX */

View File

@@ -39,7 +39,8 @@ Compositor *
Compositor::create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const SGPropertyNode *property_list)
const SGPropertyNode *property_list,
const SGReaderWriterOptions *options)
{
osg::ref_ptr<Compositor> compositor = new Compositor(view, gc, viewport);
compositor->_name = property_list->getStringValue("name");
@@ -55,7 +56,7 @@ Compositor::create(osg::View *view,
"a name to be available to passes. Skipping...");
continue;
}
Buffer *buffer = buildBuffer(compositor.get(), p_buffer);
Buffer *buffer = buildBuffer(compositor.get(), p_buffer, options);
if (buffer)
compositor->addBuffer(buffer_name, buffer);
}
@@ -64,7 +65,7 @@ Compositor::create(osg::View *view,
for (auto const &p_pass : p_passes) {
if (!checkConditional(p_pass))
continue;
Pass *pass = buildPass(compositor.get(), p_pass);
Pass *pass = buildPass(compositor.get(), p_pass, options);
if (pass)
compositor->addPass(pass);
}
@@ -76,7 +77,8 @@ Compositor *
Compositor::create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const std::string &name)
const std::string &name,
const SGReaderWriterOptions *options)
{
std::string filename(name);
filename += ".xml";
@@ -96,7 +98,7 @@ Compositor::create(osg::View *view,
return 0;
}
return create(view, gc, viewport, property_list);
return create(view, gc, viewport, property_list, options);
}
Compositor::Compositor(osg::View *view,
@@ -200,51 +202,6 @@ Compositor::resized()
}
}
bool
Compositor::computeIntersection(
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections)
{
using osgUtil::Intersector;
using osgUtil::LineSegmentIntersector;
osg::Camera *camera = getPass(0)->camera;
const osg::Viewport* viewport = camera->getViewport();
SGRect<double> viewportRect(viewport->x(), viewport->y(),
viewport->x() + viewport->width() - 1.0,
viewport->y() + viewport->height()- 1.0);
double epsilon = 0.5;
if (!viewportRect.contains(windowPos.x(), windowPos.y(), epsilon))
return false;
osg::Vec4d start(windowPos.x(), windowPos.y(), 0.0, 1.0);
osg::Vec4d end(windowPos.x(), windowPos.y(), 1.0, 1.0);
osg::Matrix windowMat = viewport->computeWindowMatrix();
osg::Matrix startPtMat = osg::Matrix::inverse(camera->getProjectionMatrix()
* windowMat);
osg::Matrix endPtMat = startPtMat; // no far camera
start = start * startPtMat;
start /= start.w();
end = end * endPtMat;
end /= end.w();
osg::ref_ptr<LineSegmentIntersector> picker
= new LineSegmentIntersector(Intersector::VIEW,
osg::Vec3d(start.x(), start.y(), start.z()),
osg::Vec3d(end.x(), end.y(), end.z()));
osgUtil::IntersectionVisitor iv(picker.get());
iv.setTraversalMask( simgear::PICK_BIT );
const_cast<osg::Camera*>(camera)->accept(iv);
if (picker->containsIntersections()) {
intersections = picker->getIntersections();
return true;
}
return false;
}
void
Compositor::addBuffer(const std::string &name, Buffer *buffer)
{
@@ -272,7 +229,7 @@ Compositor::addPass(Pass *pass)
identifier = sceneView->getCullVisitor()->getIdentifier();
sceneView->setCullVisitor(
new EffectCullVisitor(false, pass->effect_override));
new EffectCullVisitor(false, pass->effect_scheme));
sceneView->getCullVisitor()->setIdentifier(identifier.get());
identifier = sceneView->getCullVisitorLeft()->getIdentifier();

View File

@@ -73,7 +73,8 @@ public:
static Compositor *create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const SGPropertyNode *property_list);
const SGPropertyNode *property_list,
const SGReaderWriterOptions *options);
/**
* \overload
* \brief Create a Compositor from a file.
@@ -84,20 +85,19 @@ public:
static Compositor *create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const std::string &name);
const std::string &name,
const SGReaderWriterOptions *options);
void update(const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix);
void resized();
bool computeIntersection(
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections);
osg::View *getView() const { return _view; }
const osg::GraphicsContext *getGraphicsContext() const { return _gc; }
osg::GraphicsContext *getGraphicsContext() const { return _gc; }
const osg::Viewport *getViewport() const { return _viewport; }
osg::Viewport *getViewport() const { return _viewport; }
typedef std::array<
osg::ref_ptr<osg::Uniform>,
@@ -121,8 +121,6 @@ public:
Pass * getPass(const std::string &name) const;
protected:
friend class PassBuilder;
osg::View *_view;
osg::GraphicsContext *_gc;
osg::ref_ptr<osg::Viewport> _viewport;

View File

@@ -28,6 +28,7 @@
#include <simgear/props/props.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include "Compositor.hxx"
#include "CompositorUtil.hxx"
@@ -49,6 +50,7 @@ PropStringMap<BufferFormat> buffer_format_map {
{"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}},
{"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}},
{"r32f", {GL_R32F, GL_RED, GL_FLOAT}},
{"rg16f", {GL_RG16F, GL_RG, GL_FLOAT}},
{"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}},
{"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}},
{"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}},
@@ -92,7 +94,8 @@ PropStringMap<osg::Texture::ShadowCompareFunc> shadow_compare_func_map = {
};
Buffer *
buildBuffer(Compositor *compositor, const SGPropertyNode *node)
buildBuffer(Compositor *compositor, const SGPropertyNode *node,
const SGReaderWriterOptions *options)
{
std::string type = node->getStringValue("type");
if (type.empty()) {

View File

@@ -22,6 +22,9 @@
class SGPropertyNode;
namespace simgear {
class SGReaderWriterOptions;
namespace compositor {
class Compositor;
@@ -38,7 +41,8 @@ struct Buffer : public osg::Referenced {
float width_scale, height_scale;
};
Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node);
Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node,
const SGReaderWriterOptions *options);
} // namespace compositor
} // namespace simgear

View File

@@ -22,13 +22,17 @@
#include <osg/PolygonMode>
#include <osg/io_utils>
#include <osgUtil/CullVisitor>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/material/EffectCullVisitor.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGUpdateVisitor.hxx>
#include <simgear/structure/exception.hxx>
#include "ClusteredForward.hxx"
#include "ClusteredShading.hxx"
#include "Compositor.hxx"
#include "CompositorUtil.hxx"
@@ -50,34 +54,37 @@ PropStringMap<osg::Camera::BufferComponent> buffer_component_map = {
{"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER}
};
Pass *
PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
{
// The pass index matches its render order
int render_order = root->getIndex();
Pass *
PassBuilder::build(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options)
{
osg::ref_ptr<Pass> pass = new Pass;
// The pass index matches its render order
pass->render_order = root->getIndex();
pass->name = root->getStringValue("name");
if (pass->name.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << render_order
SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << pass->render_order
<< " has no name. It won't be addressable by name!");
}
pass->type = root->getStringValue("type");
std::string eff_override_file = root->getStringValue("effect-override");
if (!eff_override_file.empty())
pass->effect_override = makeEffect(eff_override_file, true, 0);
pass->effect_scheme = root->getStringValue("effect-scheme");
osg::Camera *camera = new Camera;
pass->camera = camera;
camera->setName(pass->name);
camera->setGraphicsContext(compositor->_gc);
camera->setGraphicsContext(compositor->getGraphicsContext());
// Even though this camera will be added as a slave to the view, it will
// always be updated manually in Compositor::update()
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
// Same with the projection matrix
camera->setProjectionResizePolicy(osg::Camera::FIXED);
// We only use POST_RENDER. Leave PRE_RENDER for Canvas and other RTT stuff
// that doesn't involve the rendering pipeline itself. NESTED_RENDER is also
// not a possibility since we don't want to share RenderStage with the View
// master camera.
camera->setRenderOrder(osg::Camera::POST_RENDER, pass->render_order * 10);
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
// XXX: Should we make this configurable?
@@ -144,7 +151,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
} catch (sg_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping binding "
<< p_binding->getIndex() << " in pass " << render_order
<< p_binding->getIndex() << " in pass " << pass->render_order
<< ": " << e.what());
}
}
@@ -153,22 +160,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
if (p_attachments.empty()) {
// If there are no attachments, assume the pass is rendering
// directly to the screen
camera->setRenderOrder(osg::Camera::NESTED_RENDER, render_order * 10);
// OSG cameras use the framebuffer by default, but it is stated
// explicitly anyway
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
camera->setDrawBuffer(GL_BACK);
camera->setReadBuffer(GL_BACK);
// Use the physical viewport. We can't let the user choose the viewport
// size because some parts of the window might not be ours.
camera->setViewport(compositor->_viewport);
camera->setViewport(compositor->getViewport());
} else {
// This is a RTT camera
camera->setRenderOrder(osg::Camera::PRE_RENDER, render_order * 10);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
bool viewport_absolute = false;
@@ -252,13 +252,13 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
camera->setViewport(
0,
0,
buffer->width_scale * compositor->_viewport->width(),
buffer->height_scale * compositor->_viewport->height());
buffer->width_scale * compositor->getViewport()->width(),
buffer->height_scale * compositor->getViewport()->height());
}
}
} catch (sg_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping attachment "
<< p_attachment->getIndex() << " in pass " << render_order
<< p_attachment->getIndex() << " in pass " << pass->render_order
<< ": " << e.what());
}
}
@@ -271,8 +271,10 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
struct QuadPassBuilder : public PassBuilder {
public:
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
pass->useMastersSceneData = false;
osg::Camera *camera = pass->camera;
camera->setAllowEventFocus(false);
@@ -289,11 +291,29 @@ public:
scale = p_geometry->getFloatValue("scale", scale);
}
const std::string eff_file = root->getStringValue("effect");
osg::ref_ptr<osg::Geode> quad = createFullscreenQuad(
left, bottom, width, height, scale, eff_file);
osg::ref_ptr<EffectGeode> quad = new EffectGeode;
camera->addChild(quad);
quad->setCullingActive(false);
const std::string eff_file = root->getStringValue("effect");
if (!eff_file.empty()) {
Effect *eff = makeEffect(eff_file, true, options);
if (eff)
quad->setEffect(eff);
}
osg::ref_ptr<osg::Geometry> geom = createFullscreenQuadGeom(
left, bottom, width, height, scale);
quad->addDrawable(geom);
osg::ref_ptr<osg::StateSet> quad_state = quad->getOrCreateStateSet();
int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
quad_state->setAttribute(new osg::PolygonMode(
osg::PolygonMode::FRONT_AND_BACK,
osg::PolygonMode::FILL),
values);
quad_state->setMode(GL_LIGHTING, values);
quad_state->setMode(GL_DEPTH_TEST, values);
osg::StateSet *ss = camera->getOrCreateStateSet();
for (const auto &uniform : compositor->getUniforms())
@@ -302,13 +322,12 @@ public:
return pass.release();
}
protected:
osg::Geode *createFullscreenQuad(float left,
float bottom,
float width,
float height,
float scale,
const std::string &eff_file) {
osg::Geometry *geom;
osg::Geometry *createFullscreenQuadGeom(float left,
float bottom,
float width,
float height,
float scale) {
osg::ref_ptr<osg::Geometry> geom;
// When the quad is fullscreen, it can be optimized by using a
// a fullscreen triangle instead of a quad to avoid discarding pixels
@@ -349,31 +368,52 @@ protected:
osg::PrimitiveSet::TRIANGLES, 0, 3));
}
osg::ref_ptr<EffectGeode> quad = new EffectGeode;
if (!eff_file.empty()) {
Effect *eff = makeEffect(eff_file, true, 0);
if (eff)
quad->setEffect(eff);
}
quad->addDrawable(geom);
quad->setCullingActive(false);
osg::ref_ptr<osg::StateSet> quad_state = quad->getOrCreateStateSet();
int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
quad_state->setAttribute(new osg::PolygonMode(
osg::PolygonMode::FRONT_AND_BACK,
osg::PolygonMode::FILL),
values);
quad_state->setMode(GL_LIGHTING, values);
quad_state->setMode(GL_DEPTH_TEST, values);
return quad.release();
return geom.release();
}
};
RegisterPassBuilder<QuadPassBuilder> registerQuadPass("quad");
//------------------------------------------------------------------------------
class ShadowMapCullCallback : public osg::NodeCallback {
public:
ShadowMapCullCallback(const std::string &suffix) {
_light_matrix_uniform = new osg::Uniform(
osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix);
}
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
osg::Camera *camera = static_cast<osg::Camera *>(node);
traverse(node, nv);
// The light matrix uniform is updated after the traverse in case the
// OSG near/far plane calculations were enabled
osg::Matrixf light_matrix =
// Include the real camera inverse view matrix because if the shader
// used world coordinates, there would be precision issues.
_real_inverse_view *
camera->getViewMatrix() *
camera->getProjectionMatrix() *
// Bias matrices
osg::Matrix::translate(1.0, 1.0, 1.0) *
osg::Matrix::scale(0.5, 0.5, 0.5);
_light_matrix_uniform->set(light_matrix);
}
void setRealInverseViewMatrix(const osg::Matrix &matrix) {
_real_inverse_view = matrix;
}
osg::Uniform *getLightMatrixUniform() const {
return _light_matrix_uniform.get();
}
protected:
osg::Matrix _real_inverse_view;
osg::ref_ptr<osg::Uniform> _light_matrix_uniform;
};
class LightFinder : public osg::NodeVisitor {
public:
LightFinder(const std::string &name) :
@@ -406,15 +446,14 @@ protected:
struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback {
public:
ShadowMapUpdateCallback(const std::string &light_name,
ShadowMapUpdateCallback(ShadowMapCullCallback *cull_callback,
const std::string &light_name,
float near_m, float far_m,
const std::string &suffix,
int sm_width, int sm_height) :
_cull_callback(cull_callback),
_light_finder(new LightFinder(light_name)),
_near_m(near_m),
_far_m(far_m) {
_light_matrix_uniform = new osg::Uniform(
osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix);
_half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0;
}
virtual void updatePass(Pass &pass,
@@ -443,6 +482,7 @@ public:
// This is not a problem though (for now).
osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix);
_cull_callback->setRealInverseViewMatrix(view_inverse);
// Calculate the light's point of view transformation matrices.
// Taken from Project Rembrandt.
@@ -489,37 +529,30 @@ public:
osg::Matrixd round_matrix = osg::Matrixd::translate(
rounding.x(), rounding.y(), 0.0);
light_proj_matrix *= round_matrix;
osg::Matrixf light_matrix =
// Include the real camera inverse view matrix because if the shader
// used world coordinates, there would be precision issues.
view_inverse *
camera->getViewMatrix() *
camera->getProjectionMatrix() *
// Bias matrices
osg::Matrix::translate(1.0, 1.0, 1.0) *
osg::Matrix::scale(0.5, 0.5, 0.5);
_light_matrix_uniform->set(light_matrix);
}
osg::Uniform *getLightMatrixUniform() const {
return _light_matrix_uniform.get();
}
protected:
osg::observer_ptr<ShadowMapCullCallback> _cull_callback;
osg::ref_ptr<LightFinder> _light_finder;
float _near_m;
float _far_m;
osg::ref_ptr<osg::Uniform> _light_matrix_uniform;
osg::Vec2d _half_sm_size;
};
struct ShadowMapPassBuilder : public PassBuilder {
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
pass->useMastersSceneData = true;
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
osg::Camera *camera = pass->camera;
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
camera->setCullingMode(camera->getCullingMode() &
~osg::CullSettings::SMALL_FEATURE_CULLING);
//camera->setComputeNearFarMode(
// osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
ShadowMapCullCallback *cull_callback = new ShadowMapCullCallback(pass->name);
camera->setCullCallback(cull_callback);
std::string light_name = root->getStringValue("light-name");
float near_m = root->getFloatValue("near-m");
@@ -527,9 +560,9 @@ struct ShadowMapPassBuilder : public PassBuilder {
int sm_width = camera->getViewport()->width();
int sm_height = camera->getViewport()->height();
pass->update_callback = new ShadowMapUpdateCallback(
cull_callback,
light_name,
near_m, far_m,
pass->name,
sm_width, sm_height);
return pass.release();
@@ -624,43 +657,69 @@ protected:
float _zFar;
};
class SceneCullCallback : public osg::NodeCallback {
public:
SceneCullCallback(ClusteredShading *clustered) :
_clustered(clustered) {}
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
osg::Camera *camera = static_cast<osg::Camera *>(node);
EffectCullVisitor *cv = dynamic_cast<EffectCullVisitor *>(nv);
cv->traverse(*camera);
if (_clustered) {
// Retrieve the light list from the cull visitor
SGLightList light_list = cv->getLightList();
_clustered->update(light_list);
}
}
protected:
osg::ref_ptr<ClusteredShading> _clustered;
};
struct ScenePassBuilder : public PassBuilder {
public:
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
pass->useMastersSceneData = true;
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
pass->inherit_cull_mask = true;
osg::Camera *camera = pass->camera;
camera->setAllowEventFocus(true);
const SGPropertyNode *clustered = root->getChild("clustered-forward");
if (clustered) {
camera->setInitialDrawCallback(new ClusteredForwardDrawCallback);
}
const SGPropertyNode *p_clustered = root->getNode("clustered-shading");
ClusteredShading *clustered = 0;
if (p_clustered)
clustered = new ClusteredShading(camera, p_clustered);
camera->setCullCallback(new SceneCullCallback(clustered));
int cubemap_face = root->getIntValue("cubemap-face", -1);
float zNear = root->getFloatValue("z-near", 0.0f);
float zFar = root->getFloatValue("z-far", 0.0f);
pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar);
std::string shadow_pass_name = root->getStringValue("use-shadow-pass");
if (!shadow_pass_name.empty()) {
Pass *shadow_pass = compositor->getPass(shadow_pass_name);
if (shadow_pass) {
ShadowMapUpdateCallback *updatecb =
dynamic_cast<ShadowMapUpdateCallback *>(
shadow_pass->update_callback.get());
if (updatecb) {
camera->getOrCreateStateSet()->addUniform(
updatecb->getLightMatrixUniform());
PropertyList p_shadow_passes = root->getChildren("use-shadow-pass");
for (const auto &p_shadow_pass : p_shadow_passes) {
std::string shadow_pass_name = p_shadow_pass->getStringValue();
if (!shadow_pass_name.empty()) {
Pass *shadow_pass = compositor->getPass(shadow_pass_name);
if (shadow_pass) {
ShadowMapCullCallback *cullcb =
dynamic_cast<ShadowMapCullCallback *>(
shadow_pass->camera->getCullCallback());
if (cullcb) {
camera->getOrCreateStateSet()->addUniform(
cullcb->getLightMatrixUniform());
} else {
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '"
<< shadow_pass_name << "is not a shadow pass");
}
} else {
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '"
<< shadow_pass_name << "is not a shadow pass");
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not "
"find shadow pass named '" << shadow_pass_name << "'");
}
} else {
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not "
"find shadow pass named '" << shadow_pass_name << "'");
}
}
@@ -673,7 +732,8 @@ RegisterPassBuilder<ScenePassBuilder> registerScenePass("scene");
//------------------------------------------------------------------------------
Pass *
buildPass(Compositor *compositor, const SGPropertyNode *root)
buildPass(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options)
{
std::string type = root->getStringValue("type");
if (type.empty()) {
@@ -687,7 +747,7 @@ buildPass(Compositor *compositor, const SGPropertyNode *root)
return 0;
}
return builder->build(compositor, root);
return builder->build(compositor, root, options);
}
} // namespace compositor

View File

@@ -27,6 +27,9 @@
#include <simgear/props/props.hxx>
namespace simgear {
class SGReaderWriterOptions;
namespace compositor {
class Compositor;
@@ -45,17 +48,17 @@ class Compositor;
*/
struct Pass : public osg::Referenced {
Pass() :
useMastersSceneData(false),
useMastersSceneData(true),
cull_mask(0xffffff),
inherit_cull_mask(false),
viewport_width_scale(0.0f),
viewport_height_scale(0.0f) {}
int render_order;
std::string name;
std::string type;
std::string effect_scheme;
osg::ref_ptr<osg::Camera> camera;
/** If null, there is no effect override for this pass. */
osg::ref_ptr<Effect> effect_override;
bool useMastersSceneData;
osg::Node::NodeMask cull_mask;
/** Whether the cull mask is ANDed with the view master camera cull mask. */
@@ -85,10 +88,11 @@ public:
* and overrided for more special passes.
*
* @param compositor The Compositor instance that owns the pass.
* @param The root node of the pass property tree.
* @param root The root node of the pass property tree.
* @return A Pass or a null pointer if an error occurred.
*/
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root);
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options);
static PassBuilder *find(const std::string &type) {
auto itr = PassBuilderMapSingleton::instance()->_map.find(type);
@@ -125,7 +129,8 @@ struct RegisterPassBuilder {
* @param node The root node of the pass property tree.
* @return A Pass or a null pointer if an error occurred.
*/
Pass *buildPass(Compositor *compositor, const SGPropertyNode *root);
Pass *buildPass(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options);
} // namespace compositor
} // namespace simgear

View File

@@ -6,6 +6,7 @@ set(HEADERS
xmlsound.hxx
soundmgr.hxx
filters.hxx
readwav.hxx
)
set(SOURCES
@@ -13,6 +14,7 @@ set(SOURCES
sample_group.cxx
xmlsound.cxx
filters.cxx
readwav.cxx
)
if (USE_AEONWAVE)
@@ -20,13 +22,9 @@ if (USE_AEONWAVE)
soundmgr_aeonwave.cxx
)
else()
set(HEADERS ${HEADERS}
readwav.hxx
)
set(SOURCES ${SOURCES}
soundmgr_openal.cxx
soundmgr_openal_private.hxx
readwav.cxx
)
endif()

View File

@@ -49,19 +49,21 @@ class SGSoundSample;
class SGSoundMgr : public SGSubsystem
{
public:
SGSoundMgr();
~SGSoundMgr();
void init();
void update(double dt);
// Subsystem API.
void init() override;
void reinit() override;
void resume() override;
void suspend() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "sound"; }
void suspend();
void resume();
void stop();
void reinit();
/**
* Select a specific sound device.
* Requires a init/reinit call before sound is actually switched.
@@ -310,7 +312,7 @@ public:
size_t *size,
int *freq,
int *block );
/**
* Get a list of available playback devices.
*/
@@ -324,7 +326,6 @@ public:
bool testForError(std::string s, std::string name = "sound manager");
static const char* subsystemName() { return "sound"; };
private:
class SoundManagerPrivate;
/// private implementation object

View File

@@ -340,7 +340,7 @@ void SGSoundMgr::suspend()
for ( auto current = d->_sample_groups.begin();
current != d->_sample_groups.end(); ++current ) {
SGSampleGroup *sgrp = current->second;
sgrp->stop();
sgrp->suspend();
}
_active = false;
}

View File

@@ -83,6 +83,9 @@ SGXmlSound::~SGXmlSound()
if (_sample)
_sample->stop();
if (_sgrp && (_name != ""))
_sgrp->remove(_name);
_volume.clear();
_pitch.clear();
}

View File

@@ -33,6 +33,7 @@
#include <sstream>
#include <boost/bind.hpp>
#include <cstring> // for strcmp
#include <cassert>
#include <simgear/props/props.hxx>

View File

@@ -138,3 +138,7 @@ SGPerformanceMonitor::reportTiming(const string& name, SampleStatistic* timeStat
timeStat->reset();
}
// Register the subsystem.
//SGSubsystemMgr::Registrant<SGPerformanceMonitor> registrantSGPerformanceMonitor;

View File

@@ -34,14 +34,17 @@ class SampleStatistic;
class SGPerformanceMonitor : public SGSubsystem
{
public:
SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root);
virtual void bind (void);
virtual void unbind (void);
virtual void init (void);
virtual void update (double dt);
// Subsystem API.
void bind() override;
void init() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "performance-mon"; }
private:
static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat);

View File

@@ -47,7 +47,6 @@ SGEventMgr::SGEventMgr() :
_inited(false),
_shutdown(false)
{
_name = "EventMgr";
}
SGEventMgr::~SGEventMgr()
@@ -127,6 +126,11 @@ void SGEventMgr::dump()
_rtQueue.dump();
}
// Register the subsystem.
SGSubsystemMgr::Registrant<SGEventMgr> registrantSGEventMgr(
SGSubsystemMgr::DISPLAY);
////////////////////////////////////////////////////////////////////////
// SGTimerQueue
// This is the priority queue implementation:

View File

@@ -8,11 +8,12 @@
class SGEventMgr;
class SGTimer {
class SGTimer
{
public:
~SGTimer();
void run();
std::string name;
double interval;
SGCallback* callback;
@@ -20,7 +21,8 @@ public:
bool running;
};
class SGTimerQueue {
class SGTimerQueue
{
public:
SGTimerQueue(int preSize=1);
~SGTimerQueue();
@@ -37,8 +39,9 @@ public:
double nextTime() { return -_table[0].pri; }
SGTimer* findByName(const std::string& name) const;
void dump();
private:
// The "priority" is stored as a negative time. This allows the
// implementation to treat the "top" of the heap as the largest
@@ -50,9 +53,9 @@ private:
int rchild(int n) { return ((n+1)*2 + 1) - 1; }
double pri(int n) { return _table[n].pri; }
void swap(int a, int b) {
HeapEntry tmp = _table[a];
_table[a] = _table[b];
_table[b] = tmp;
HeapEntry tmp = _table[a];
_table[a] = _table[b];
_table[b] = tmp;
}
void siftDown(int n);
void siftUp(int n);
@@ -73,10 +76,15 @@ public:
SGEventMgr();
~SGEventMgr();
virtual void init();
virtual void update(double delta_time_sec);
virtual void unbind();
virtual void shutdown();
// Subsystem API.
void init() override;
void shutdown() override;
void unbind() override;
void update(double delta_time_sec) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "events"; }
void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; }
/**
@@ -119,8 +127,9 @@ public:
void removeTask(const std::string& name);
void dump();
private:
friend class SGTimer;
@@ -130,7 +139,7 @@ private:
SGPropertyNode_ptr _freezeProp;
SGPropertyNode_ptr _rtProp;
SGTimerQueue _rtQueue;
SGTimerQueue _rtQueue;
SGTimerQueue _simQueue;
bool _inited, _shutdown;
};

View File

@@ -21,6 +21,7 @@
#include <simgear_config.h>
#include <algorithm>
#include <cassert>
#include <simgear/debug/logstream.hxx>
#include <simgear/timing/timestamp.hxx>
@@ -137,28 +138,28 @@ void SGSubsystem::stamp(const string& name)
void SGSubsystem::set_name(const std::string &n)
{
assert(_name.empty());
_name = n;
assert(_subsystemId.empty());
_subsystemId = n;
}
std::string SGSubsystem::typeName() const
std::string SGSubsystem::subsystemClassId() const
{
auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR);
auto pos = _subsystemId.find(SUBSYSTEM_NAME_SEPARATOR);
if (pos == std::string::npos) {
return _name;
return _subsystemId;
}
return _name.substr(0, pos);
return _subsystemId.substr(0, pos);
}
std::string SGSubsystem::instanceName() const
std::string SGSubsystem::subsystemInstanceId() const
{
auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR);
auto pos = _subsystemId.find(SUBSYSTEM_NAME_SEPARATOR);
if (pos == std::string::npos) {
return {};
}
return _name.substr(pos+1);
return _subsystemId.substr(pos+1);
}
void SGSubsystem::set_group(SGSubsystemGroup* group)
@@ -174,7 +175,7 @@ SGSubsystemGroup* SGSubsystem::get_group() const
SGSubsystemMgr* SGSubsystem::get_manager() const
{
if (!get_group())
throw sg_exception("SGSubsystem::get_manager: subsystem " + name() + " has no group");
throw sg_exception("SGSubsystem::get_manager: subsystem " + subsystemId() + " has no group");
return get_group()->get_manager();
}
@@ -232,12 +233,11 @@ public:
SGSubsystemGroup::SGSubsystemGroup(const char *name) :
SGSubsystemGroup::SGSubsystemGroup() :
_fixedUpdateTime(-1.0),
_updateTimeRemainder(0.0),
_initPosition(-1)
{
_name = name;
}
SGSubsystemGroup::~SGSubsystemGroup ()
@@ -473,7 +473,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
if (reportDeltas) {
auto deltaT = _executionTime - _lastExecutionTime;
if (deltaT != 0) {
t << name() << "(+" << std::setprecision(2) << std::right << deltaT << "ms).";
t << subsystemInstanceId() << "(+" << std::setprecision(2) << std::right << deltaT << "ms).";
_name = t.str();
}
}
@@ -505,7 +505,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
SGSubsystem::reportTimingStats(_lastValues);
std::string _name = name();
std::string _name = subsystemInstanceId();
if (!_name.size())
_name = typeid(this).name();
if (_lastValues) {
@@ -513,11 +513,11 @@ void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
if (deltaT != 0) {
SG_LOG(SG_EVENT, SG_ALERT,
" +" << std::setw(6) << std::setprecision(4) << std::right << deltaT << "ms "
<< name() );
<< subsystemInstanceId() );
}
}
else
SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << name() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s");
SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << subsystemInstanceId() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s");
for (auto member : _members) {
member->reportTimingStats(_lastValues);
}
@@ -574,11 +574,11 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
if (name.empty()) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group without a name");
// TODO, make this an exception in the future
} else if (subsystem->name().empty()) {
} else if (subsystem->subsystemId().empty()) {
subsystem->set_name(name);
} else if (name != subsystem->name()) {
} else if (name != subsystem->subsystemId()) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group with name '" << name
<< "', but name() returns '" << subsystem->name() << "'");
<< "', but subsystemId() returns '" << subsystem->subsystemId() << "'");
}
notifyWillChange(subsystem, State::ADD);
@@ -614,7 +614,7 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
void
SGSubsystemGroup::set_subsystem (SGSubsystem * subsystem, double min_step_sec)
{
set_subsystem(subsystem->name(), subsystem, min_step_sec);
set_subsystem(subsystem->subsystemId(), subsystem, min_step_sec);
}
SGSubsystem *
@@ -837,7 +837,7 @@ namespace {
} // end of anonymous namespace
SGSubsystemMgr::SGSubsystemMgr (const char *name) :
SGSubsystemMgr::SGSubsystemMgr () :
_groups(MAX_GROUPS)
{
if (global_defaultSubsystemManager == nullptr) {
@@ -854,7 +854,7 @@ SGSubsystemMgr::SGSubsystemMgr (const char *name) :
#endif
for (int i = 0; i < MAX_GROUPS; i++) {
auto g = new SGSubsystemGroup(name);
auto g = new SGSubsystemGroup();
g->set_manager(this);
_groups[i].reset(g);
}
@@ -1038,9 +1038,9 @@ SGSubsystemMgr::get_subsystem (const string &name) const
}
SGSubsystem*
SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& instanceName) const
SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const
{
return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + instanceName);
return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId);
}
@@ -1173,7 +1173,7 @@ SGSubsystemMgr::create(const std::string& name)
}
SGSubsystemRef
SGSubsystemMgr::createInstance(const std::string& name, const std::string& instanceName)
SGSubsystemMgr::createInstance(const std::string& name, const std::string& subsystemInstanceId)
{
auto it = findRegistration(name);
auto &global_registrations = getGlobalRegistrations();
@@ -1190,7 +1190,7 @@ SGSubsystemMgr::createInstance(const std::string& name, const std::string& insta
throw sg_exception("SGSubsystemMgr::create: functor failed to create an instsance of " + name);
}
const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + instanceName;
const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId;
ref->set_name(combinedName);
return ref;
}
@@ -1275,7 +1275,7 @@ namespace {
// allow override of the name but defaultt o the subsystem name
std::string name = arg->getStringValue("name");
std::string instanceName = arg->getStringValue("instance");
std::string subsystemInstanceId = arg->getStringValue("instance");
if (name.empty()) {
// default name to subsystem name, but before we parse any instance name
@@ -1284,19 +1284,19 @@ namespace {
auto separatorPos = subsystem.find(SUBSYSTEM_NAME_SEPARATOR);
if (separatorPos != std::string::npos) {
if (!instanceName.empty()) {
if (!subsystemInstanceId.empty()) {
SG_LOG(SG_GENERAL, SG_WARN, "Specified a composite subsystem name and an instance name, please do one or the other: "
<< instanceName << " and " << subsystem);
<< subsystemInstanceId << " and " << subsystem);
return false;
}
instanceName = subsystem.substr(separatorPos + 1);
subsystemInstanceId = subsystem.substr(separatorPos + 1);
subsystem = subsystem.substr(0, separatorPos);
}
SGSubsystem* sub = nullptr;
if (!instanceName.empty()) {
sub = manager->createInstance(subsystem, instanceName);
if (!subsystemInstanceId.empty()) {
sub = manager->createInstance(subsystem, subsystemInstanceId);
} else {
sub = manager->create(subsystem);
}
@@ -1312,7 +1312,7 @@ namespace {
double minTime = arg->getDoubleValue("min-time-sec", 0.0);
const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + instanceName;
const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId;
manager->add(combinedName.c_str(),
sub,
group,

View File

@@ -127,6 +127,7 @@ typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, Sam
* subsystems may also override the suspend() and resume() methods to
* take different actions.</p>
*/
class SGSubsystem : public SGReferenced
{
public:
@@ -281,19 +282,19 @@ public:
/**
* composite name for this subsystem (type name & optional instance name)
*/
std::string name() const
{ return _name; }
std::string subsystemId() const
{ return _subsystemId; }
/**
* @brief the type (class)-specific part of the subsystem name.
*/
std::string typeName() const;
std::string subsystemClassId() const;
/**
* @brief the instance part of the subsystem name. Empty if this
* subsystem is not instanced
*/
std::string instanceName() const;
std::string subsystemInstanceId() const;
virtual bool is_group() const
{ return false; }
@@ -342,26 +343,31 @@ public:
protected:
friend class SGSubsystemMgr;
friend class SGSubsystemGroup;
void set_name(const std::string& n);
void set_group(SGSubsystemGroup* group);
/// composite name for the subsystem (type name and instance name if this
/// is an instanced subsystem. (Since this member was originally defined as
/// protected, not private, we can't rename it easily)
std::string _name;
bool _suspended = false;
bool _suspended = false;
eventTimeVec timingInfo;
eventTimeVec timingInfo;
static SGSubsystemTimingCb reportTimingCb;
static void* reportTimingUserData;
static bool reportTimingStatsRequest;
static int maxTimePerFrame_ms;
static SGSubsystemTimingCb reportTimingCb;
static void* reportTimingUserData;
static bool reportTimingStatsRequest;
static int maxTimePerFrame_ms;
private:
/// composite name for the subsystem (type name and instance name if this
/// is an instanced subsystem. (Since this member was originally defined as
/// protected, not private, we can't rename it easily)
std::string _subsystemId;
SGSubsystemGroup* _group = nullptr;
protected:
TimerStats _timerStats, _lastTimerStats;
@@ -378,20 +384,21 @@ typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
class SGSubsystemGroup : public SGSubsystem
{
public:
SGSubsystemGroup (const char *name);
SGSubsystemGroup ();
virtual ~SGSubsystemGroup ();
// Subsystem API.
void bind() override;
InitStatus incrementalInit() override;
void init() override;
InitStatus incrementalInit () override;
void postinit () override;
void reinit () override;
void shutdown () override;
void bind () override;
void unbind () override;
void update (double delta_time_sec) override;
void suspend () override;
void resume () override;
bool is_suspended () const override;
void postinit() override;
void reinit() override;
void resume() override;
void shutdown() override;
void suspend() override;
void unbind() override;
void update(double delta_time_sec) override;
bool is_suspended() const override;
virtual void set_subsystem (const std::string &name,
SGSubsystem * subsystem,
@@ -416,21 +423,22 @@ public:
*/
void set_fixed_update_time(double fixed_dt);
/**
* retrive list of member subsystem names
*/
/**
* retrive list of member subsystem names
*/
string_list member_names() const;
template<class T>
T* get_subsystem()
{
return dynamic_cast<T*>(get_subsystem(T::subsystemName()));
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
}
bool is_group() const override
{ return true; }
SGSubsystemMgr* get_manager() const override;
private:
void forEach(std::function<void(SGSubsystem*)> f);
void reverseForEach(std::function<void(SGSubsystem*)> f);
@@ -501,20 +509,24 @@ public:
MAX_GROUPS
};
SGSubsystemMgr (const char *name);
SGSubsystemMgr ();
virtual ~SGSubsystemMgr ();
void init () override;
InitStatus incrementalInit () override;
void postinit () override;
void reinit () override;
void shutdown () override;
void bind () override;
void unbind () override;
void update (double delta_time_sec) override;
void suspend () override;
void resume () override;
bool is_suspended () const override;
// Subsystem API.
void bind() override;
void init() override;
InitStatus incrementalInit() override;
void postinit() override;
void reinit() override;
void resume() override;
void shutdown() override;
void suspend() override;
void unbind() override;
void update(double delta_time_sec) override;
bool is_suspended() const override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "subsystem-mgr"; }
virtual void add (const char * name,
SGSubsystem * subsystem,
@@ -531,7 +543,7 @@ public:
SGSubsystem* get_subsystem(const std::string &name) const;
SGSubsystem* get_subsystem(const std::string &name, const std::string& instanceName) const;
SGSubsystem* get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const;
void reportTiming();
void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; }
@@ -552,34 +564,40 @@ public:
template<class T>
T* get_subsystem() const
{
return dynamic_cast<T*>(get_subsystem(T::subsystemName()));
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
}
// instanced overloads, for both raw char* and std::string
// these concatenate the subsystem type name with the instance name
template<class T>
T* get_subsystem(const char* instanceName) const
T* get_subsystem(const char* subsystemInstanceId) const
{
return dynamic_cast<T*>(get_subsystem(T::subsystemName(), instanceName));
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId));
}
template<class T>
T* get_subsystem(const std::string& instanceName) const
T* get_subsystem(const std::string& subsystemInstanceId) const
{
return dynamic_cast<T*>(get_subsystem(T::subsystemName(), instanceName));
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId));
}
/**
* @brief Subsystem dependency structure.
*/
struct Dependency {
enum Type {
HARD,
SOFT,
PROPERTY
HARD, ///< The subsystem cannot run without this subsystem dependency.
SOFT, ///< The subsystem uses this subsystem dependency, but can run without it.
SEQUENCE, ///< Used for ordering subsystem initialisation.
NONSUBSYSTEM_HARD, ///< The subsystem cannot run without this non-subsystem dependency.
NONSUBSYSTEM_SOFT, ///< The subsystem uses this non-subsystem dependency, but can run without it.
PROPERTY ///< The subsystem requires this property to exist to run.
};
std::string name;
Type type;
};
using DependencyVec = std::vector<SGSubsystemMgr::Dependency>;
using SubsystemFactoryFunctor = std::function<SGSubsystemRef()>;
@@ -598,11 +616,12 @@ public:
class Registrant
{
public:
Registrant(GroupType group = GENERAL, double updateInterval = 0.0,
std::initializer_list<Dependency> deps = {})
Registrant(GroupType group = GENERAL,
std::initializer_list<Dependency> deps = {},
double updateInterval = 0.0)
{
SubsystemFactoryFunctor factory = [](){ return new T; };
SGSubsystemMgr::registerSubsystem(T::subsystemName(),
SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
factory, group,
false, updateInterval,
deps);
@@ -616,11 +635,11 @@ public:
{
public:
InstancedRegistrant(GroupType group = GENERAL,
double updateInterval = 0.0,
std::initializer_list<Dependency> deps = {})
std::initializer_list<Dependency> deps = {},
double updateInterval = 0.0)
{
SubsystemFactoryFunctor factory = [](){ return new T; };
SGSubsystemMgr::registerSubsystem(T::subsystemName(),
SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
factory, group,
true, updateInterval,
deps);
@@ -636,14 +655,14 @@ public:
template <class T>
SGSharedPtr<T> add(GroupType customGroup = INVALID, double customInterval = 0.0)
{
auto ref = create(T::subsystemName());
auto ref = create(T::staticSubsystemClassId());
const GroupType group = (customGroup == INVALID) ?
defaultGroupFor(T::subsystemName()) : customGroup;
defaultGroupFor(T::staticSubsystemClassId()) : customGroup;
const double interval = (customInterval == 0.0) ?
defaultUpdateIntervalFor(T::subsystemName()) : customInterval;
add(ref->name().c_str(), ref.ptr(), group, interval);
defaultUpdateIntervalFor(T::staticSubsystemClassId()) : customInterval;
add(ref->subsystemId().c_str(), ref.ptr(), group, interval);
return dynamic_cast<T*>(ref.ptr());
}
@@ -654,20 +673,20 @@ public:
template <class T>
SGSharedPtr<T> create()
{
auto ref = create(T::subsystemName());
auto ref = create(T::staticSubsystemClassId());
return dynamic_cast<T*>(ref.ptr());
}
SGSubsystemRef create(const std::string& name);
template <class T>
SGSharedPtr<T> createInstance(const std::string& instanceName)
SGSharedPtr<T> createInstance(const std::string& subsystemInstanceId)
{
auto ref = createInstance(T::subsystemName(), instanceName);
auto ref = createInstance(T::staticSubsystemClassId(), subsystemInstanceId);
return dynamic_cast<T*>(ref.ptr());
}
SGSubsystemRef createInstance(const std::string& name, const std::string& instanceName);
SGSubsystemRef createInstance(const std::string& name, const std::string& subsystemInstanceId);
static GroupType defaultGroupFor(const char* name);
static double defaultUpdateIntervalFor(const char* name);

View File

@@ -20,7 +20,7 @@ using std::endl;
class MySub1 : public SGSubsystem
{
public:
static const char* subsystemName() { return "mysub"; }
static const char* staticSubsystemClassId() { return "mysub"; }
void init() override
{
@@ -45,7 +45,7 @@ public:
class AnotherSub : public SGSubsystem
{
public:
static const char* subsystemName() { return "anothersub"; }
static const char* staticSubsystemClassId() { return "anothersub"; }
void init() override
{
@@ -70,7 +70,7 @@ public:
class FakeRadioSub : public SGSubsystem
{
public:
static const char* subsystemName() { return "fake-radio"; }
static const char* staticSubsystemClassId() { return "fake-radio"; }
void init() override
{
@@ -89,8 +89,8 @@ public:
class InstrumentGroup : public SGSubsystemGroup
{
public:
static const char* subsystemName() { return "instruments"; }
InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::subsystemName()) {}
static const char* staticSubsystemClassId() { return "instruments"; }
virtual ~InstrumentGroup()
{
}
@@ -119,11 +119,11 @@ class RecorderDelegate : public SGSubsystemMgr::Delegate
public:
void willChange(SGSubsystem* sub, SGSubsystem::State newState) override
{
events.push_back({sub->name(), false, newState});
events.push_back({sub->subsystemId(), false, newState});
}
void didChange(SGSubsystem* sub, SGSubsystem::State newState) override
{
events.push_back({sub->name(), true, newState});
events.push_back({sub->subsystemId(), true, newState});
}
struct Event {
@@ -164,14 +164,14 @@ SGSubsystemMgr::InstancedRegistrant<FakeRadioSub> registrant3(SGSubsystemMgr::PO
void testRegistrationAndCreation()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto anotherSub = manager->create<AnotherSub>();
SG_VERIFY(anotherSub);
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->typeName(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->instanceName(), std::string());
SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId());
SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->subsystemClassId(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->subsystemInstanceId(), std::string());
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
@@ -181,14 +181,14 @@ void testRegistrationAndCreation()
void testAddGetRemove()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate;
manager->addDelegate(d);
auto anotherSub = manager->add<AnotherSub>();
SG_VERIFY(anotherSub);
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId());
SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub"));
SG_VERIFY(d->hasEvent("anothersub-will-add"));
SG_VERIFY(d->hasEvent("anothersub-did-add"));
@@ -200,14 +200,14 @@ void testAddGetRemove()
// manual create & add
auto mySub = manager->create<MySub1>();
manager->add(MySub1::subsystemName(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234);
manager->add(MySub1::staticSubsystemClassId(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234);
SG_VERIFY(d->hasEvent("mysub-will-add"));
SG_VERIFY(d->hasEvent("mysub-did-add"));
SG_CHECK_EQUAL(manager->get_subsystem<MySub1>(), mySub);
bool ok = manager->remove(AnotherSub::subsystemName());
bool ok = manager->remove(AnotherSub::staticSubsystemClassId());
SG_VERIFY(ok);
SG_VERIFY(d->hasEvent("anothersub-will-remove"));
SG_VERIFY(d->hasEvent("anothersub-did-remove"));
@@ -219,7 +219,7 @@ void testAddGetRemove()
// re-add of removed, and let's test overriding
auto another2 = manager->add<AnotherSub>(SGSubsystemMgr::SOUND);
SG_CHECK_EQUAL(another2->name(), AnotherSub::subsystemName());
SG_CHECK_EQUAL(another2->subsystemId(), AnotherSub::staticSubsystemClassId());
auto soundGroup = manager->get_group(SGSubsystemMgr::SOUND);
SG_CHECK_EQUAL(soundGroup->get_subsystem("anothersub"), another2);
@@ -228,7 +228,7 @@ void testAddGetRemove()
void testSubGrouping()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -238,10 +238,10 @@ void testSubGrouping()
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio.nav1"));
SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio.nav2"));
SG_CHECK_EQUAL(radio1->typeName(), std::string("fake-radio"));
SG_CHECK_EQUAL(radio2->instanceName(), std::string("nav2"));
SG_CHECK_EQUAL(radio1->subsystemId(), std::string("fake-radio.nav1"));
SG_CHECK_EQUAL(radio2->subsystemId(), std::string("fake-radio.nav2"));
SG_CHECK_EQUAL(radio1->subsystemClassId(), std::string("fake-radio"));
SG_CHECK_EQUAL(radio2->subsystemInstanceId(), std::string("nav2"));
instruments->set_subsystem(radio1);
instruments->set_subsystem(radio2);
@@ -299,7 +299,7 @@ void testSubGrouping()
void testIncrementalInit()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -347,7 +347,7 @@ void testEmptyGroup()
// https://sourceforge.net/p/flightgear/codetickets/2043/
// when an empty group is inited, we skipped setting the state
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -371,7 +371,7 @@ void testEmptyGroup()
void testSuspendResume()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -441,7 +441,7 @@ void testSuspendResume()
void testPropertyRoot()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
SGPropertyNode_ptr props(new SGPropertyNode);
manager->set_root_node(props);
@@ -467,7 +467,7 @@ void testPropertyRoot()
void testAddRemoveAfterInit()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate;
manager->addDelegate(d);

View File

@@ -25,6 +25,7 @@
#endif
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include "SGThread.hxx"
@@ -416,3 +417,98 @@ SGWaitCondition::broadcast()
{
_privateData->broadcast();
}
SGExclusiveThread::SGExclusiveThread() :
_started(false), _terminated(false), last_await_time(0),
dataReady(false), complete(true), process_ran(false), process_running(false)
{
}
SGExclusiveThread::~SGExclusiveThread()
{
}
void SGExclusiveThread::release() {
std::unique_lock<std::mutex> lck(mutex_);
if (!complete) {
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] not finished - skipping");
return;
}
if (!complete.exchange(false))
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (2)");
if (dataReady.exchange(true))
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (1)");
condVar.notify_one();
}
void SGExclusiveThread::wait() {
std::unique_lock<std::mutex> lck(mutex_);
if (!dataReady)
{
do
{
condVar.wait(lck);
} while (!dataReady);
}
}
void SGExclusiveThread::clearAwaitCompletionTime() {
last_await_time = 0;
}
void SGExclusiveThread::awaitCompletion() {
timestamp.stamp();
std::unique_lock<std::mutex> lck(Cmutex_);
if (!complete)
{
do {
CcondVar.wait(lck);
} while (!complete.load());
}
if (process_ran) {
last_await_time = timestamp.elapsedUSec();
process_ran = 0;
}
}
void SGExclusiveThread::setCompletion() {
std::unique_lock<std::mutex> lck(Cmutex_);
if (!dataReady.exchange(false))
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on dataReady failed (5)\n");
if (complete.exchange(true))
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on complete failed (5)\n");
CcondVar.notify_one();
}
void SGExclusiveThread::run()
{
process_running = true;
while (!_terminated) {
wait();
process_ran = process();
setCompletion();
}
process_running = false;
_terminated = false;
_started = false;
}
void SGExclusiveThread::terminate() {
_terminated = true;
}
bool SGExclusiveThread::stop()
{
return true;
}
void SGExclusiveThread::ensure_running()
{
if (!_started)
{
_started = true;
start();
}
}
bool SGExclusiveThread::is_running()
{
return process_running;
}

View File

@@ -23,7 +23,11 @@
#ifndef SGTHREAD_HXX_INCLUDED
#define SGTHREAD_HXX_INCLUDED 1
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <simgear/compiler.h>
#include <simgear/timing/timestamp.hxx>
/**
* Encapsulate generic threading methods.
@@ -184,4 +188,41 @@ private:
PrivateData* _privateData;
};
///
/// an exclusive thread is one that is designed for frame processing;
/// it has the ability to synchronise such that the caller can await
/// the processing to finish.
class SGExclusiveThread : public SGThread{
private:
std::mutex mutex_;
std::condition_variable condVar;
SGTimeStamp timestamp;
std::mutex Cmutex_;
std::condition_variable CcondVar;
bool _started;
bool _terminated;
int last_await_time;
std::atomic<bool> dataReady;
std::atomic<bool> complete;
std::atomic<bool> process_ran;
std::atomic<bool> process_running;
public:
SGExclusiveThread();
virtual ~SGExclusiveThread();
void release();
void wait();
void clearAwaitCompletionTime();
virtual void awaitCompletion();
void setCompletion();
virtual int process() = 0;
virtual void run();
void terminate();
bool stop();
void ensure_running();
bool is_running();
};
#endif /* SGTHREAD_HXX_INCLUDED */

View File

@@ -337,3 +337,11 @@ int SGTimeStamp::elapsedMSec() const
return static_cast<int>((now - *this).toMSecs());
}
int SGTimeStamp::elapsedUSec() const
{
SGTimeStamp now;
now.stamp();
return static_cast<int>((now - *this).toUSecs());
}

View File

@@ -221,9 +221,13 @@ public:
{ return sleepFor(fromMSec(msec)); }
/**
* elapsed time since the stamp was taken, in msec
*/
* elapsed time since the stamp was taken, in msec
*/
int elapsedMSec() const;
/**
* elapsed time since the stamp was taken, in usec
*/
int elapsedUSec() const;
private:
SGTimeStamp(sec_type sec, nsec_type nsec)
{ setTime(sec, nsec); }