This commit is contained in:
gallaert
2019-07-13 18:12:22 +01:00
73 changed files with 3052 additions and 1199 deletions
+3 -3
View File
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif #endif
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
# include <winsock2.h> /* includes <windows.h> */ # include <winsock2.h> /* includes <windows.h> */
# include <iphlpapi.h> /* for dns server addresses etc */ # include <iphlpapi.h> /* for dns server addresses etc */
#else #else
@@ -53,7 +53,7 @@ static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
dns_add_srch(ctx, srch); dns_add_srch(ctx, srch);
} }
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
#ifndef NO_IPHLPAPI #ifndef NO_IPHLPAPI
/* Apparently, some systems does not have proper headers for IPHLPAIP to work. /* 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; ctx = &dns_defctx;
dns_reset(ctx); dns_reset(ctx);
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
if (dns_initns_iphlpapi(ctx) != 0) if (dns_initns_iphlpapi(ctx) != 0)
dns_initns_registry(ctx); dns_initns_registry(ctx);
/*XXX WINDOWS: probably good to get default domain and search list too... /*XXX WINDOWS: probably good to get default domain and search list too...
+4 -4
View File
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif #endif
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
# include <winsock2.h> /* includes <windows.h> */ # include <winsock2.h> /* includes <windows.h> */
# include <ws2tcpip.h> /* needed for struct in6_addr */ # include <ws2tcpip.h> /* needed for struct in6_addr */
#else #else
@@ -392,7 +392,7 @@ dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) {
} }
static unsigned dns_nonrandom_32(void) { static unsigned dns_nonrandom_32(void) {
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
FILETIME ft; FILETIME ft;
GetSystemTimeAsFileTime(&ft); GetSystemTimeAsFileTime(&ft);
return ft.dwLowDateTime; return ft.dwLowDateTime;
@@ -551,7 +551,7 @@ int dns_open(struct dns_ctx *ctx) {
ctx->dnsc_qstatus = DNS_E_TEMPFAIL; ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
return -1; return -1;
} }
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
{ unsigned long on = 1; { unsigned long on = 1;
if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) { if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
closesocket(sock); closesocket(sock);
@@ -991,7 +991,7 @@ again: /* receive the reply */
* or remote. On local errors, we should stop, while * or remote. On local errors, we should stop, while
* remote errors should be ignored (for now anyway). * remote errors should be ignored (for now anyway).
*/ */
#ifdef WINDOWS #if defined(_WINDOWS) || defined(WINDOWS)
if (WSAGetLastError() == WSAEWOULDBLOCK) if (WSAGetLastError() == WSAEWOULDBLOCK)
#else #else
if (errno == EAGAIN) if (errno == EAGAIN)
+1 -1
View File
@@ -27,7 +27,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#ifndef WINDOWS #if !defined(_WINDOWS) && !defined(WINDOWS)
# include <sys/types.h> # include <sys/types.h>
# include <netinet/in.h> # include <netinet/in.h>
#endif #endif
+2 -2
View File
@@ -150,7 +150,7 @@ endif()
if (MSVC) if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH) GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} DIRECTORY)
if (CMAKE_CL_64) if (CMAKE_CL_64)
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64") SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
else (CMAKE_CL_64) 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 # 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 # parent dir, which is the normal location for people using our
# windows-3rd-party repo # 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}) set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}") message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
endif() endif()
+1 -1
View File
@@ -80,7 +80,7 @@ if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
set(_boostConfig "BoostTestTargetsDynamic.h") set(_boostConfig "BoostTestTargetsDynamic.h")
endif() endif()
endif() endif()
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH) get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
configure_file("${_moddir}/${_boostConfig}" configure_file("${_moddir}/${_boostConfig}"
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h" "${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
COPYONLY) COPYONLY)
+2 -2
View File
@@ -30,12 +30,12 @@ function(copy_resources_to_build_tree _target)
endif() endif()
get_target_property(_path ${_target} LOCATION) 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") if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
foreach(_config ${CMAKE_CONFIGURATION_TYPES}) foreach(_config ${CMAKE_CONFIGURATION_TYPES})
get_target_property(_path${_config} ${_target} LOCATION_${_config}) 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} add_custom_command(TARGET ${_target}
POST_BUILD POST_BUILD
COMMAND COMMAND
+1
View File
@@ -6,6 +6,7 @@ foreach( mylibfolder
bvh bvh
debug debug
embedded_resources embedded_resources
emesary
ephemeris ephemeris
io io
magvar magvar
+29 -32
View File
@@ -28,42 +28,39 @@ namespace simgear
namespace canvas namespace canvas
{ {
class CanvasMgr: class CanvasMgr : public PropertyBasedMgr
public PropertyBasedMgr {
{ public:
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 * Create a new canvas
*/ *
CanvasMgr(SGPropertyNode_ptr node); * @param name Name of the new canvas
*/
CanvasPtr createCanvas(const std::string& name = "");
/** /**
* Create a new canvas * Get ::Canvas by index
* *
* @param name Name of the new canvas * @param index Index of texture node in /canvas/by-index/
*/ */
CanvasPtr createCanvas(const std::string& name = ""); CanvasPtr getCanvas(size_t index) const;
/** /**
* Get ::Canvas by index * Get ::Canvas by name
* *
* @param index Index of texture node in /canvas/by-index/ * @param name Value of child node "name" in
*/ * /canvas/by-index/texture[i]/name
CanvasPtr getCanvas(size_t index) const; */
CanvasPtr getCanvas(const std::string& name) const;
/** protected:
* Get ::Canvas by name void elementCreated(PropertyBasedElementPtr element) override;
* };
* @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;
};
} // namespace canvas } // namespace canvas
} // namespace simgear } // namespace simgear
+21 -15
View File
@@ -425,8 +425,11 @@ public:
return; return;
} }
m_startupLogging = on; {
m_startupEntries.clear(); SGGuard<SGMutex> g(m_lock);
m_startupLogging = on;
m_startupEntries.clear();
}
} }
virtual void run() virtual void run()
@@ -438,13 +441,14 @@ public:
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) { if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
return; return;
} }
{
if (m_startupLogging) { SGGuard<SGMutex> g(m_lock);
// save to the startup list for not-yet-added callbacks to if (m_startupLogging) {
// pull down on startup // save to the startup list for not-yet-added callbacks to
m_startupEntries.push_back(entry); // pull down on startup
m_startupEntries.push_back(entry);
}
} }
// submit to each installed callback in turn // submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) { for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority, (*cb)(entry.debugClass, entry.debugPriority,
@@ -455,14 +459,16 @@ public:
bool stop() bool stop()
{ {
SGGuard<SGMutex> g(m_lock); {
if (!m_isRunning) { SGGuard<SGMutex> g(m_lock);
return false; if (!m_isRunning) {
} return false;
}
// log a special marker value, which will cause the thread to wakeup, // log a special marker value, which will cause the thread to wakeup,
// and then exit // and then exit
log(SG_NONE, SG_ALERT, "done", -1, ""); log(SG_NONE, SG_ALERT, "done", -1, "");
}
join(); join();
m_isRunning = false; m_isRunning = false;
+33
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)
+31
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
{
}
}
+48
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
+54
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
+47
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
+52
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
+54
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
+203
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
+68
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 � 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
+126
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;
}
+1
View File
@@ -19,6 +19,7 @@ set(HEADERS
tabbed_values.hxx tabbed_values.hxx
texcoord.hxx texcoord.hxx
test_macros.hxx test_macros.hxx
lru_cache.hxx
) )
set(SOURCES set(SOURCES
+8 -3
View File
@@ -30,11 +30,16 @@
// code can register another one immediately without worrying about // code can register another one immediately without worrying about
// timer aliasing. // timer aliasing.
class SGInterpolator : public SGSubsystem { class SGInterpolator : public SGSubsystem
{
public: public:
SGInterpolator() { _list = 0; } 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 // Simple method that interpolates a double property value from
// its current value (default of zero) to the specified target // its current value (default of zero) to the specified target
+173
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_ */
+10 -6
View File
@@ -157,7 +157,7 @@ static void initContext(naContext c)
c->error[0] = 0; c->error[0] = 0;
c->userData = 0; c->userData = 0;
} }
#define BASE_SIZE 256000
static void initGlobals() static void initGlobals()
{ {
int i; int i;
@@ -168,10 +168,10 @@ static void initGlobals()
globals->sem = naNewSem(); globals->sem = naNewSem();
globals->lock = naNewLock(); globals->lock = naNewLock();
globals->allocCount = 256; // reasonable starting value globals->allocCount = BASE_SIZE; // reasonable starting value
for(i=0; i<NUM_NASAL_TYPES; i++) for(i=0; i<NUM_NASAL_TYPES; i++)
naGC_init(&(globals->pools[i]), i); naGC_init(&(globals->pools[i]), i);
globals->deadsz = 256; globals->deadsz = BASE_SIZE;
globals->ndead = 0; globals->ndead = 0;
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
@@ -833,9 +833,13 @@ naRef naGetSourceFile(naContext ctx, int frame)
{ {
naRef f; naRef f;
frame = findFrame(ctx, &ctx, frame); frame = findFrame(ctx, &ctx, frame);
f = ctx->fStack[frame].func; if (frame >= 0) {
f = PTR(f).func->code; f = ctx->fStack[frame].func;
return PTR(f).code->srcFile; f = PTR(f).func->code;
if (!IS_NIL(f) && PTR(f).code)
return PTR(f).code->srcFile;
}
return naNil();
} }
char* naGetError(naContext ctx) char* naGetError(naContext ctx)
+1
View File
@@ -5,6 +5,7 @@ set(HEADERS
Ghost.hxx Ghost.hxx
NasalCallContext.hxx NasalCallContext.hxx
NasalContext.hxx NasalContext.hxx
NasalEmesaryInterface.hxx
NasalHash.hxx NasalHash.hxx
NasalMe.hxx NasalMe.hxx
NasalMethodHolder.hxx NasalMethodHolder.hxx
@@ -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
@@ -19,6 +19,7 @@
#include "to_nasal_helper.hxx" #include "to_nasal_helper.hxx"
#include <simgear/nasal/cppbind/NasalHash.hxx> #include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx> #include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalEmesaryInterface.hxx>
#include <simgear/math/SGMath.hxx> #include <simgear/math/SGMath.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
@@ -27,6 +28,19 @@
namespace nasal 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) naRef to_nasal_helper(naContext c, const std::string& str)
{ {
+157 -19
View File
@@ -1,7 +1,6 @@
#include "nasal.h" #include "nasal.h"
#include "data.h" #include "data.h"
#include "code.h" #include "code.h"
#define MIN_BLOCK_SIZE 32 #define MIN_BLOCK_SIZE 32
static void reap(struct naPool* p); static void reap(struct naPool* p);
@@ -12,14 +11,17 @@ struct Block {
char* block; char* block;
struct Block* next; struct Block* next;
}; };
// Must be called with the giant exclusive lock! // Must be called with the giant exclusive lock!
static void freeDead() extern void global_stamp();
extern int global_elapsedUSec();
static int freeDead()
{ {
int i; int i;
for(i=0; i<globals->ndead; i++) for(i=0; i<globals->ndead; i++)
naFree(globals->deadBlocks[i]); naFree(globals->deadBlocks[i]);
globals->ndead = 0; globals->ndead = 0;
return i;
} }
static void marktemps(struct Context* c) static void marktemps(struct Context* c)
@@ -31,50 +33,127 @@ static void marktemps(struct Context* c)
mark(r); mark(r);
} }
} }
//#define GC_DETAIL_DEBUG
static int __elements_visited = 0;
static int gc_busy=0;
// Must be called with the big lock! // Must be called with the big lock!
static void garbageCollect() static void garbageCollect()
{ {
if (gc_busy)
return;
gc_busy = 1;
int i; int i;
struct Context* c; struct Context* c;
globals->allocCount = 0; globals->allocCount = 0;
c = globals->allContexts; 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; 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].func);
mark(c->fStack[i].locals); 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->opStack[i]);
mark(c->dieArg); mark(c->dieArg);
marktemps(c); marktemps(c);
c = c->nextAll; 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); 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); 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); 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); 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); 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); 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 // 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])); 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 // Make enough space for the dead blocks we need to free during
// execution. This works out to 1 spot for every 2 live objects, // execution. This works out to 1 spot for every 2 live objects,
// which should be limit the number of bottleneck operations // which should be limit the number of bottleneck operations
// without imposing an undue burden of extra "freeable" memory. // without imposing an undue burden of extra "freeable" memory.
if(globals->deadsz < globals->allocCount) { if(globals->deadsz < globals->allocCount) {
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); naFree(globals->deadBlocks);
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz); globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
} }
globals->needGC = 0; globals->needGC = 0;
#if GC_DETAIL_DEBUG
et = global_elapsedUSec() - st;
st = global_elapsedUSec();
printf(">> %-5d ", et);
#endif
gc_busy = 0;
} }
void naModLock() void naModLock()
@@ -104,6 +183,7 @@ void naModUnlock()
// you think about it). // you think about it).
static void bottleneck() static void bottleneck()
{ {
global_stamp();
struct Globals* g = globals; struct Globals* g = globals;
g->bottleneck = 1; g->bottleneck = 1;
while(g->bottleneck && g->waitCount < g->nThreads - 1) { while(g->bottleneck && g->waitCount < g->nThreads - 1) {
@@ -111,12 +191,39 @@ static void bottleneck()
UNLOCK(); naSemDown(g->sem); LOCK(); UNLOCK(); naSemDown(g->sem); LOCK();
g->waitCount--; g->waitCount--;
} }
#if GC_DETAIL_DEBUG
printf("GC: wait %2d ", global_elapsedUSec());
#endif
if(g->waitCount >= g->nThreads - 1) { if(g->waitCount >= g->nThreads - 1) {
freeDead(); int fd = freeDead();
if(g->needGC) garbageCollect(); #if GC_DETAIL_DEBUG
printf("--> freedead (%5d) : %5d", fd, global_elapsedUSec());
#endif
if(g->needGC)
garbageCollect();
if(g->waitCount) naSemUp(g->sem, g->waitCount); if(g->waitCount) naSemUp(g->sem, g->waitCount);
g->bottleneck = 0; 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() void naGC()
@@ -127,6 +234,29 @@ void naGC()
UNLOCK(); UNLOCK();
naCheckBottleneck(); 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() void naCheckBottleneck()
{ {
@@ -207,7 +337,9 @@ static int poolsize(struct naPool* p)
while(b) { total += b->size; b = b->next; } while(b) { total += b->size; b = b->next; }
return total; return total;
} }
int GCglobalAlloc() {
return globals->allocCount;
}
struct naObj** naGC_get(struct naPool* p, int n, int* nout) struct naObj** naGC_get(struct naPool* p, int n, int* nout)
{ {
struct naObj** result; struct naObj** result;
@@ -215,6 +347,9 @@ struct naObj** naGC_get(struct naPool* p, int n, int* nout)
LOCK(); LOCK();
while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) { while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) {
globals->needGC = 1; globals->needGC = 1;
#if GC_DETAIL_DEBUG
printf("++");
#endif
bottleneck(); bottleneck();
} }
if(p->nfree == 0) if(p->nfree == 0)
@@ -248,7 +383,7 @@ static void mark(naRef r)
if(PTR(r).obj->mark == 1) if(PTR(r).obj->mark == 1)
return; return;
__elements_visited++;
PTR(r).obj->mark = 1; PTR(r).obj->mark = 1;
switch(PTR(r).obj->type) { switch(PTR(r).obj->type) {
case T_VEC: markvec(r); break; 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 // Allocate more if necessary (try to keep 25-50% of the objects
// available) // 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 used = total - p->nfree;
int avail = total - used; int avail = total - used;
int need = used/2 - avail; int need = used / 1 - avail;
if(need > 0) if (need > 0)
newBlock(p, need); newBlock(p, need);
} }
} }
+25 -14
View File
@@ -295,25 +295,36 @@ static naRef f_die(naContext c, naRef me, int argc, naRef* args)
return naNil(); // never executes return naNil(); // never executes
} }
// Wrapper around vsnprintf, iteratively increasing the buffer size // Wrapper around vsnprintf that will allocate the required size
// until it fits. Returned buffer should be freed by the caller. // 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, ...) static char* dosprintf(char* f, ...)
{ {
char* buf; char* buf;
va_list va; va_list va;
int olen, len = 16; va_start(va, f);
while(1) {
buf = naAlloc(len); int len = vsnprintf(0, 0, f, va);
va_start(va, f); va_end(va);
olen = vsnprintf(buf, len, f, va);
if(olen >= 0 && olen < len) { if (len <= 0) {
va_end(va); buf = (char *) naAlloc(2);
return buf; *buf = 0;
}
va_end(va);
naFree(buf);
len *= 2;
} }
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 // Inspects a printf format string f, and finds the next "%..." format
+52 -55
View File
@@ -29,74 +29,71 @@
namespace simgear namespace simgear
{ {
class PropertyBasedMgr: class PropertyBasedMgr : public SGSubsystem,
public SGSubsystem, public SGPropertyChangeListener
public SGPropertyChangeListener {
{ public:
public: // Subsystem API.
void init() override; void init() override;
void shutdown() 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 * Get an existing PropertyBasedElement by its index
* *
* @param name Name of the new element * @param index Index of element node in property tree
*/ */
PropertyBasedElementPtr createElement(const std::string& name = ""); PropertyBasedElementPtr getElement(size_t index) const;
/** /**
* Get an existing PropertyBasedElement by its index * Get an existing PropertyBasedElement by its name
* *
* @param index Index of element node in property tree * @param name Name (value of child node "name" will be matched)
*/ */
PropertyBasedElementPtr getElement(size_t index) const; PropertyBasedElementPtr getElement(const std::string& name) const;
/** virtual const SGPropertyNode* getPropertyRoot() 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; 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*)> /** Property name of managed elements */
ElementFactory; const std::string _name_elements;
/** Branch in the property tree for this property managed subsystem */ /** The actually managed elements */
SGPropertyNode_ptr _props; std::vector<PropertyBasedElementPtr> _elements;
/** Property name of managed elements */ /** Function object which creates a new element */
const std::string _name_elements; 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 */ void childAdded( SGPropertyNode * parent,
ElementFactory _element_factory; SGPropertyNode * child ) override;
void childRemoved( SGPropertyNode * parent,
/**
* @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,
SGPropertyNode * child ) override; SGPropertyNode * child ) override;
void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child ) override;
virtual void elementCreated(PropertyBasedElementPtr element) {} virtual void elementCreated(PropertyBasedElementPtr element) {}
};
};
} // namespace simgear } // namespace simgear
+92 -98
View File
@@ -28,119 +28,113 @@
#include <list> #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)();
/** PropertyInterpolationMgr();
* 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(); // Subsystem API.
void update(double dt) override;
/** /**
* Update all active interpolators. * Create a new property interpolator.
*/ *
void update(double dt); * @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. * Add animation of the given property from its current value to the
* * target value of the interpolator. If no interpolator is given any
* @note To actually use it the interpolator needs to be attached to a * existing animation of the given property is aborted.
* property using PropertyInterpolationMgr::interpolate. *
* * @param prop Property to be interpolated
* @param type Type of animation ("numeric", "color", etc.) * @param interp Interpolator used for interpolation
* @param target Property containing target value */
* @param duration Duration if the animation (in seconds) bool interpolate(SGPropertyNode* prop,
* @param easing Type of easing ("linear", "swing", etc.) PropertyInterpolatorRef interp = 0);
*/
PropertyInterpolator*
createInterpolator( const std::string& type,
const SGPropertyNode& target,
double duration,
const std::string& easing );
/** bool interpolate(SGPropertyNode* prop,
* Add animation of the given property from its current value to the const std::string& type,
* target value of the interpolator. If no interpolator is given any const SGPropertyNode& target,
* existing animation of the given property is aborted. double duration,
* const std::string& easing);
* @param prop Property to be interpolated
* @param interp Interpolator used for interpolation
*/
bool interpolate( SGPropertyNode* prop,
PropertyInterpolatorRef interp = 0 );
bool interpolate( SGPropertyNode* prop, bool interpolate(SGPropertyNode* prop,
const std::string& type, const std::string& type,
const SGPropertyNode& target, const PropertyList& values,
double duration, const double_list& deltas,
const std::string& easing ); const std::string& easing);
bool interpolate( SGPropertyNode* prop, /**
const std::string& type, * Register factory for interpolation type.
const PropertyList& values, */
const double_list& deltas, void addInterpolatorFactory(const std::string& type,
const std::string& easing ); InterpolatorFactory factory);
template <class T>
/** void addInterpolatorFactory(const std::string& type)
* Register factory for interpolation type. {
*/ addInterpolatorFactory(
void addInterpolatorFactory( const std::string& type, type,
InterpolatorFactory factory ); &simgear::make_new_derived<PropertyInterpolator, T>
template<class T>
void addInterpolatorFactory(const std::string& type)
{
addInterpolatorFactory
(
type,
&simgear::make_new_derived<PropertyInterpolator, T>
); );
} }
/** /**
* Register easing function. * Register easing function.
*/ */
void addEasingFunction(const std::string& type, easing_func_t func); void addEasingFunction(const std::string& type, easing_func_t func);
/** /**
* Set property containing real time delta (not sim time) * Set property containing real time delta (not sim time)
* *
* TODO better pass both deltas to all update methods... * TODO better pass both deltas to all update methods...
*/ */
void setRealtimeProperty(SGPropertyNode* node); 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; struct PredicateIsSameProp;
typedef std::map<std::string, easing_func_t> EasingFunctionMap;
typedef std::pair< SGPropertyNode*,
PropertyInterpolatorRef > PropertyInterpolatorPair;
typedef std::list<PropertyInterpolatorPair> InterpolatorList;
struct PredicateIsSameProp; InterpolatorFactoryMap _interpolator_factories;
EasingFunctionMap _easing_functions;
InterpolatorList _interpolators;
InterpolatorFactoryMap _interpolator_factories; SGPropertyNode_ptr _rt_prop;
EasingFunctionMap _easing_functions; };
InterpolatorList _interpolators;
SGPropertyNode_ptr _rt_prop;
};
} // namespace simgear } // namespace simgear
+45 -38
View File
@@ -1,79 +1,86 @@
#include <simgear/scene/dem/SGDem.hxx> #include <simgear/scene/dem/SGDem.hxx>
#include <simgear/scene/dem/SGDemSession.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), setOffsets(SGDem::longitudeDegToOffset((double)mnLon),
SGDem::latitudeDegToOffset((double)mnLat), SGDem::latitudeDegToOffset((double)mnLat),
SGDem::longitudeDegToOffset((double)mxLon), SGDem::longitudeDegToOffset((double)mxLon),
SGDem::latitudeDegToOffset((double)mxLat) ); SGDem::latitudeDegToOffset((double)mxLat));
pDemRoot = root; pDemRoot = root;
lvlIndex = idx; lvlIndex = idx;
lvlWidth = lvlW; lvlWidth = lvlW;
lvlHeight = lvlH; lvlHeight = lvlH;
} }
SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root ) { SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root)
setOffsets( SGDem::longitudeDegToOffset((double)mnLon), {
SGDem::latitudeDegToOffset((double)mnLat), setOffsets(SGDem::longitudeDegToOffset((double)mnLon),
SGDem::longitudeDegToOffset((double)mxLon), SGDem::latitudeDegToOffset((double)mnLat),
SGDem::latitudeDegToOffset((double)mxLat) ); SGDem::longitudeDegToOffset((double)mxLon),
SGDem::latitudeDegToOffset((double)mxLat));
pDemRoot = root; pDemRoot = root;
lvlIndex = -1; // no level - session is raw input dir 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(); tileRefs.clear();
if ( lvlIndex >= 0 ) { if (lvlIndex >= 0) {
pDemRoot->flushCaches( lvlIndex ); 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 // todo - store this info in deminfo
unsigned span; // smallest tile width/height in level ( in offsets ) unsigned span; // smallest tile width/height in level ( in offsets )
switch( lvlIndex ) { switch (lvlIndex) {
case 0: span = 1; break; // 1/8 deg case 0:
case 1: span = 16; break; // 2 degrees span = 1;
case 2: span = 480; break; // 60 degrees break; // 1/8 deg
default: case 1:
fprintf( stderr, "invalid lvlIndex %d\n", lvlIndex ); span = 16;
exit(0); 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 tileLon, tileLat;
unsigned meshLon, meshLat; unsigned meshLon, meshLat;
int subx, suby; int subx, suby;
meshLon = wo; meshLon = wo;
tileLon = SGDem::roundDown( meshLon, lvlWidth); tileLon = SGDem::roundDown(meshLon, lvlWidth);
subx = (meshLon - tileLon) / span; 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 ); // fprintf(stderr, "getGeods: lon is %lf : meshLon is %u, tileLon is %u, subx is %d\n", SGDem::offsetToLongitudeDeg(wo), meshLon, tileLon, subx );
meshLat = so; meshLat = so;
tileLat = SGDem::roundDown( meshLat, lvlHeight ); tileLat = SGDem::roundDown(meshLat, lvlHeight);
suby = (meshLat - tileLat) / span; 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 ); // 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 // get the tle from the tile cache
unsigned long key = tileLon << 16 | tileLat; unsigned long key = tileLon << 16 | tileLat;
SGDemTileRef tile = pDemRoot->getTile( lvlIndex, key ); SGDemTileRef tile = pDemRoot->getTile(lvlIndex, key);
if ( tile ) { if (tile) {
tile->getGeods(wo, so, eo, no, resx, resy, subx, suby, incx, incy, geods, Debug1, Debug2); tile->getGeods(wo, so, eo, no, resx, resy, subx, suby, incx, incy, geods, Debug1, Debug2);
} else { } 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, tileLon, tileLat,
SGDem::offsetToLongitudeDeg( west_off ), SGDem::offsetToLongitudeDeg(west_off),
SGDem::offsetToLatitudeDeg( south_off ), SGDem::offsetToLatitudeDeg(south_off),
SGDem::offsetToLongitudeDeg( east_off ), SGDem::offsetToLongitudeDeg(east_off),
SGDem::offsetToLatitudeDeg( north_off ) ); SGDem::offsetToLatitudeDeg(north_off));
} }
} }
} }
+61 -36
View File
@@ -3,57 +3,82 @@
#include <simgear/scene/dem/SGDemRoot.hxx> #include <simgear/scene/dem/SGDemRoot.hxx>
class SGDemSession class SGDemSession {
{
public: public:
SGDemSession() { SGDemSession()
lvlIndex = -1; : 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, 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, SGDemRoot* root);
SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root ) { SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root)
setOffsets( wo, so, eo, no ); : west_off(wo)
, south_off(so)
pDemRoot = root; , east_off(eo)
lvlIndex = idx; , north_off(no)
lvlWidth = lvlW; , maxLon(0)
lvlHeight = lvlH; , maxLat(0)
, pDemRoot(root)
, lvlIndex(idx)
, lvlWidth(lvlW)
, lvlHeight(lvlH)
{
} }
SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root ) { SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root)
setOffsets( wo, so, eo, no ); : west_off(wo)
, south_off(so)
pDemRoot = root; , east_off(eo)
lvlIndex = -1; // no level - session is raw input dir , 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(); close();
} }
void addTile(SGDemTileRef pTile) { void addTile(SGDemTileRef pTile)
tileRefs.push_back( pTile ); {
tileRefs.push_back(pTile);
} }
const std::vector<SGDemTileRef>& getTiles( void ) const { const std::vector<SGDemTileRef>& getTiles(void) const
{
return tileRefs; return tileRefs;
} }
unsigned int size( void ) const { unsigned int size(void) const
{
return tileRefs.size(); return tileRefs.size();
} }
void getGeods( unsigned wp, unsigned so, unsigned eo, unsigned no, void getGeods(unsigned wp, unsigned so, unsigned eo, unsigned no,
int resx, int resy, int incx, int incy, int resx, int resy, int incx, int incy,
::std::vector<SGGeod>& geods, ::std::vector<SGGeod>& geods,
bool Debug1, bool Debug2 bool Debug1, bool Debug2);
);
void close( void ); void close(void);
int getLvlIndex( void ) const { int getLvlIndex(void) const
{
return lvlIndex; return lvlIndex;
}; };
@@ -65,12 +90,12 @@ private:
north_off = no; north_off = no;
} }
unsigned west_off, south_off; unsigned west_off, south_off;
unsigned east_off, north_off; unsigned east_off, north_off;
int maxLon, maxLat; int maxLon, maxLat;
SGDemRoot* pDemRoot; SGDemRoot* pDemRoot;
int lvlIndex; int lvlIndex;
unsigned lvlWidth, lvlHeight; unsigned lvlWidth, lvlHeight;
std::vector<SGDemTileRef> tileRefs; std::vector<SGDemTileRef> tileRefs;
}; };
+60 -2
View File
@@ -77,6 +77,7 @@
#include <simgear/scene/util/StateAttributeFactory.hxx> #include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx> #include <simgear/structure/OSGUtils.hxx>
#include <simgear/structure/SGExpression.hxx> #include <simgear/structure/SGExpression.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/vectorPropTemplates.hxx> #include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/threads/SGThread.hxx> #include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.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. // 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) 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 technique.get();
} }
return 0; return 0;
@@ -976,6 +978,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
type); type);
program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, 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)); programMap.insert(ProgramMap::value_type(prgKey, program));
resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program)); resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program));
pass->setAttributeAndModes(program); pass->setAttributeAndModes(program);
@@ -1308,6 +1317,7 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop,
{ {
Technique* tniq = new Technique; Technique* tniq = new Technique;
effect->techniques.push_back(tniq); effect->techniques.push_back(tniq);
tniq->setScheme(prop->getStringValue("scheme"));
const SGPropertyNode* predProp = prop->getChild("predicate"); const SGPropertyNode* predProp = prop->getChild("predicate");
if (!predProp) { if (!predProp) {
tniq->setAlwaysValid(true); tniq->setAlwaysValid(true);
@@ -1411,12 +1421,60 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
return true; 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 // Walk the techniques property tree, building techniques and
// passes. // passes.
static SGMutex realizeTechniques_lock; static SGMutex realizeTechniques_lock;
bool Effect::realizeTechniques(const SGReaderWriterOptions* options) bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{ {
SGGuard<SGMutex> g(realizeTechniques_lock); SGGuard<SGMutex> g(realizeTechniques_lock);
mergeSchemesFallbacks(this, options);
if (_isRealized) if (_isRealized)
return true; return true;
+1 -1
View File
@@ -92,7 +92,7 @@ public:
SGPropertyNode_ptr root; SGPropertyNode_ptr root;
// Pointer to the parameters node, if it exists // Pointer to the parameters node, if it exists
SGPropertyNode_ptr parametersProp; 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 resizeGLObjectBuffers(unsigned int maxSize);
virtual void releaseGLObjects(osg::State* state = 0) const; virtual void releaseGLObjects(osg::State* state = 0) const;
/** /**
+22 -16
View File
@@ -21,6 +21,8 @@
#include <osg/StateSet> #include <osg/StateSet>
#include <osg/Texture2D> #include <osg/Texture2D>
#include <osg/io_utils>
#include "EffectCullVisitor.hxx" #include "EffectCullVisitor.hxx"
#include "EffectGeode.hxx" #include "EffectGeode.hxx"
@@ -34,9 +36,9 @@ namespace simgear
using osgUtil::CullVisitor; using osgUtil::CullVisitor;
EffectCullVisitor::EffectCullVisitor(bool collectLights, Effect *effectOverride) : EffectCullVisitor::EffectCullVisitor(bool collectLights, const std::string &effScheme) :
_collectLights(collectLights), _collectLights(collectLights),
_effectOverride(effectOverride) _effScheme(effScheme)
{ {
} }
@@ -50,6 +52,19 @@ CullVisitor* EffectCullVisitor::clone() const
return new EffectCullVisitor(*this); 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) void EffectCullVisitor::apply(osg::Geode& node)
{ {
if (isCulled(node)) if (isCulled(node))
@@ -59,21 +74,12 @@ void EffectCullVisitor::apply(osg::Geode& node)
CullVisitor::apply(node); CullVisitor::apply(node);
return; return;
} }
if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) { Effect* effect = eg->getEffect();
_lightList.push_back( eg );
}
Effect *effect;
if (_effectOverride) {
effect = _effectOverride;
} else {
effect = eg->getEffect();
if (!effect) {
CullVisitor::apply(node);
return;
}
}
Technique* technique = 0; Technique* technique = 0;
if (!(technique = effect->chooseTechnique(&getRenderInfo()))) { if (!effect) {
CullVisitor::apply(node);
return;
} else if (!(technique = effect->chooseTechnique(&getRenderInfo(), _effScheme))) {
return; return;
} }
// push the node's state. // push the node's state.
+8 -3
View File
@@ -21,6 +21,8 @@
#include <map> #include <map>
#include <simgear/scene/model/SGLight.hxx>
namespace osg namespace osg
{ {
class Geode; class Geode;
@@ -34,10 +36,11 @@ class EffectGeode;
class EffectCullVisitor : public osgUtil::CullVisitor class EffectCullVisitor : public osgUtil::CullVisitor
{ {
public: public:
EffectCullVisitor(bool collectLights = false, Effect *effectOverride = 0); EffectCullVisitor(bool collectLights = false, const std::string &effScheme = "");
EffectCullVisitor(const EffectCullVisitor&); EffectCullVisitor(const EffectCullVisitor&);
virtual osgUtil::CullVisitor* clone() const; virtual osgUtil::CullVisitor* clone() const;
using osgUtil::CullVisitor::apply; using osgUtil::CullVisitor::apply;
virtual void apply(osg::Node& node);
virtual void apply(osg::Geode& node); virtual void apply(osg::Geode& node);
virtual void reset(); virtual void reset();
@@ -45,11 +48,13 @@ public:
void addBuffer(std::string b, osg::Texture2D* tex); void addBuffer(std::string b, osg::Texture2D* tex);
osg::Texture2D* getBuffer(std::string b); osg::Texture2D* getBuffer(std::string b);
SGLightList getLightList() const { return _lightList; }
private: private:
std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList; std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList;
std::vector<osg::ref_ptr<EffectGeode> > _lightList; SGLightList _lightList;
bool _collectLights; bool _collectLights;
osg::ref_ptr<Effect> _effectOverride; std::string _effScheme;
}; };
} }
#endif #endif
+3
View File
@@ -98,6 +98,8 @@ public:
void setGLExtensionsPred(float glVersion, void setGLExtensionsPred(float glVersion,
const std::vector<std::string>& extensions); const std::vector<std::string>& extensions);
void refreshValidity(); void refreshValidity();
const std::string &getScheme() const { return _scheme; }
void setScheme(const std::string &scheme) { _scheme = scheme; }
protected: protected:
// Validity of technique in a graphics context. // Validity of technique in a graphics context.
struct ContextInfo : public osg::Referenced struct ContextInfo : public osg::Referenced
@@ -117,6 +119,7 @@ protected:
osg::ref_ptr<osg::StateSet> _shadowingStateSet; osg::ref_ptr<osg::StateSet> _shadowingStateSet;
SGSharedPtr<SGExpressionb> _validExpression; SGSharedPtr<SGExpressionb> _validExpression;
int _contextIdLocation; int _contextIdLocation;
std::string _scheme;
}; };
class TechniquePredParser : public expression::ExpressionParser class TechniquePredParser : public expression::ExpressionParser
+7 -1
View File
@@ -33,6 +33,7 @@
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/props/props_io.hxx> #include <simgear/props/props_io.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx> #include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SplicingVisitor.hxx> #include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/structure/SGExpression.hxx> #include <simgear/structure/SGExpression.hxx>
@@ -126,7 +127,12 @@ Effect* makeEffect(const string& name,
itr->second.valid()) itr->second.valid())
return itr->second.get(); 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"; effectFileName += ".eff";
string absFileName string absFileName
= SGModelLib::findDataFile(effectFileName, options); = SGModelLib::findDataFile(effectFileName, options);
+2
View File
@@ -10,6 +10,7 @@ set(HEADERS
PrimitiveCollector.hxx PrimitiveCollector.hxx
SGClipGroup.hxx SGClipGroup.hxx
SGInteractionAnimation.hxx SGInteractionAnimation.hxx
SGLight.hxx
SGMaterialAnimation.hxx SGMaterialAnimation.hxx
SGPickAnimation.hxx SGPickAnimation.hxx
SGOffsetTransform.hxx SGOffsetTransform.hxx
@@ -35,6 +36,7 @@ set(SOURCES
PrimitiveCollector.cxx PrimitiveCollector.cxx
SGClipGroup.cxx SGClipGroup.cxx
SGInteractionAnimation.cxx SGInteractionAnimation.cxx
SGLight.cxx
SGLightAnimation.cxx SGLightAnimation.cxx
SGPickAnimation.cxx SGPickAnimation.cxx
SGMaterialAnimation.cxx SGMaterialAnimation.cxx
+73 -247
View File
@@ -62,6 +62,7 @@
#include <simgear/props/condition.hxx> #include <simgear/props/condition.hxx>
#include <simgear/io/sg_file.hxx> #include <simgear/io/sg_file.hxx>
#include <simgear/threads/SGGuard.hxx> #include <simgear/threads/SGGuard.hxx>
#include <simgear/misc/lru_cache.hxx>
#include "BoundingVolumeBuildVisitor.hxx" #include "BoundingVolumeBuildVisitor.hxx"
#include "model.hxx" #include "model.hxx"
@@ -182,7 +183,7 @@ public:
} // namespace } // 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 // uint v; // compute the next highest power of 2 of 32-bit v
unsigned int v = (unsigned int)_v; unsigned int v = (unsigned int)_v;
@@ -207,10 +208,12 @@ static int nearestPowerOfTwo(unsigned int _v)
_v = (int)v; _v = (int)v;
return v; return v;
} }
static bool isPowerOfTwo(int v)
static bool isPowerOfTwo(int v)
{ {
return ((v & (v - 1)) == 0); return ((v & (v - 1)) == 0);
} }
osg::Node* DefaultProcessPolicy::process(osg::Node* node, const std::string& filename, osg::Node* DefaultProcessPolicy::process(osg::Node* node, const std::string& filename,
const Options* opt) const Options* opt)
{ {
@@ -232,144 +235,28 @@ osg::Image* getImageByName(const std::string& filename)
return nullptr; return nullptr;
} }
#endif #endif
// a cache which evicts the least recently used item when it is full
#include <map>
#include <list>
#include <utility>
#include <boost/optional.hpp> // least recently used cache to speed up the process of finding a file
template<class Key, class Value> // after the first time - otherwise each time we'll have to generate a hash
class lru_cache // of the contents.
{ static lru_cache < std::string, std::string> filename_hash_cache(100000);
public:
SGMutex _mutex;
typedef Key key_type; // experimental (incomplete) features to allow maintenance of the filecache.
typedef Value value_type; //lru_cache < std::string, bool> filesCleaned(100000);
typedef std::list<key_type> list_type; //static bool refreshCache = false;
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;
ReaderWriter::ReadResult ReaderWriter::ReadResult
ModelRegistry::readImage(const string& fileName, ModelRegistry::readImage(const string& fileName,
const Options* opt) 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 * processor is the interface to the osg_nvtt plugin
*/ */
@@ -423,13 +310,21 @@ ModelRegistry::readImage(const string& fileName,
if (fileExists(absFileName)) { if (fileExists(absFileName)) {
SGFile f(absFileName); SGFile f(absFileName);
std::string hash; 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); boost::optional<std::string> cachehash = filename_hash_cache.get(absFileName);
if (cachehash) { if (cachehash) {
hash = *cachehash; hash = *cachehash;
// SG_LOG(SG_IO, SG_ALERT, "Hash for " + absFileName + " in cache " + hash);
} }
else { else {
// SG_LOG(SG_IO, SG_ALERT, "Creating hash for " + absFileName);
try { try {
hash = f.computeHash(); hash = f.computeHash();
} }
@@ -444,9 +339,8 @@ ModelRegistry::readImage(const string& fileName,
// possibly a shared texture - but warn the user to allow investigation. // possibly a shared texture - but warn the user to allow investigation.
if (cacheFilename && *cacheFilename != absFileName) { 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"; newName = cache_root + "/" + hash.substr(0, 2) + "/" + hash + ".cache.dds";
} }
@@ -456,21 +350,15 @@ ModelRegistry::readImage(const string& fileName,
newName += "." + tstream.str(); newName += "." + tstream.str();
newName += ".cache.dds"; newName += ".cache.dds";
} }
bool doRefresh = refreshCache; //bool doRefresh = refreshCache;
//if (fileExists(newName) && sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { //if (newName != std::string() && fileExists(newName) && doRefresh) {
// doRefresh = true; // 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)) { if (newName != std::string() && !fileExists(newName)) {
res = registry->readImageImplementation(absFileName, opt); res = registry->readImageImplementation(absFileName, opt);
if (res.validImage()) { if (res.validImage()) {
@@ -494,14 +382,15 @@ ModelRegistry::readImage(const string& fileName,
isNormalMap = true; isNormalMap = true;
} }
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) { 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; isEffect = true;
// can_compress = false; // 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) if (can_compress)
{ {
std::string pot_message; std::string pot_message;
@@ -516,8 +405,9 @@ ModelRegistry::readImage(const string& fileName,
resize = true; resize = true;
pot_message += std::string(" not POT: resized height to ") + std::to_string(height); 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. // unlikely that after resizing in height the width will still be outside of the max texture size.
if (height > max_texture_size) if (height > max_texture_size)
@@ -536,6 +426,7 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
width /= factor; width /= factor;
resize = true; resize = true;
} }
if (resize) { if (resize) {
osg::ref_ptr<osg::Image> resizedImage; osg::ref_ptr<osg::Image> resizedImage;
@@ -601,21 +492,22 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
// normal maps: // normal maps:
// nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm // nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm
processor->compress(*srcImage, targetFormat, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION); 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); //processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
} }
else { else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE); 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); 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); simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions); 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(); 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 * 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()) { //if (srcImage1->getFileName().empty()) {
srcImage1->setFileName(absFileName); // srcImage1->setFileName(absFileName);
} //}
srcImage1->setFileName(originalFileName); srcImage1->setFileName(originalFileName);
if(cache_active && getFileExtension(absFileName) != "dds") if(cache_active && getFileExtension(absFileName) != "dds"&& getFileExtension(absFileName) != "gz")
{ {
if (processor) { // In testing the internal mipmap generation works better than the external nvtt one
processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU); // (less artefacts); it might be that there are flags we can use to make this better
SG_LOG(SG_IO, SG_ALERT, "Created nvtt mipmaps DDS for " + absFileName); // but for now we'll just using the built in one
} //if (processor) {
else { // 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); simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage1 = simgear::effect::computeMipmap(srcImage1, mipmapFunctions); 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 \"" SG_LOG(SG_IO, SG_BULK, "Reading image \""
<< res.getImage()->getFileName() << "\""); << 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; return res;
} }
+157
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()
{
}
+105
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 */
@@ -50,6 +50,7 @@
#include "animation.hxx" #include "animation.hxx"
#include "particles.hxx" #include "particles.hxx"
#include "model.hxx" #include "model.hxx"
#include "SGLight.hxx"
#include "SGText.hxx" #include "SGText.hxx"
#include "SGMaterialAnimation.hxx" #include "SGMaterialAnimation.hxx"
@@ -518,6 +519,14 @@ sgLoad3DModel_internal(const SGPath& path,
options.get())); 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 effect_nodes = props->getChildren("effect");
PropertyList animation_nodes = props->getChildren("animation"); PropertyList animation_nodes = props->getChildren("animation");
+6
View File
@@ -1115,3 +1115,9 @@ void SGTerraSync::reposition()
{ {
// stub, remove // stub, remove
} }
// Register the subsystem.
SGSubsystemMgr::Registrant<SGTerraSync> registrantSGTerraSync(
SGSubsystemMgr::GENERAL,
{{"FGRenderer", SGSubsystemMgr::Dependency::NONSUBSYSTEM_HARD}});
+11 -7
View File
@@ -39,16 +39,19 @@ class BufferedLogCallback;
class SGTerraSync : public SGSubsystem class SGTerraSync : public SGSubsystem
{ {
public: public:
SGTerraSync(); SGTerraSync();
virtual ~SGTerraSync(); virtual ~SGTerraSync();
virtual void init(); // Subsystem API.
virtual void shutdown(); void bind() override;
virtual void reinit(); void init() override;
virtual void bind(); void reinit() override;
virtual void unbind(); void shutdown() override;
virtual void update(double); 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 /// notify terrasync that the sim was repositioned, as opposed to
/// us travelling in a direction. Avoid last_lat / last_lon blocking /// us travelling in a direction. Avoid last_lat / last_lon blocking
@@ -80,6 +83,7 @@ public:
void scheduleDataDir(const std::string& dataDir); void scheduleDataDir(const std::string& dataDir);
bool isDataDirPending(const std::string& dataDir) const; bool isDataDirPending(const std::string& dataDir) const;
protected: protected:
void syncAirportsModels(); void syncAirportsModels();
string_list getSceneryPathSuffixes() const; string_list getSceneryPathSuffixes() const;
+1 -1
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) 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; //OE_WARN << LC << "Target GL_UNSIGNED_BYTE_3_3_2 not yet implemented" << std::endl;
} }
}; };
+2 -2
View File
@@ -1,5 +1,5 @@
set(HEADERS set(HEADERS
ClusteredForward.hxx ClusteredShading.hxx
Compositor.hxx Compositor.hxx
CompositorBuffer.hxx CompositorBuffer.hxx
CompositorPass.hxx CompositorPass.hxx
@@ -7,7 +7,7 @@ set(HEADERS
) )
set(SOURCES set(SOURCES
ClusteredForward.cxx ClusteredShading.cxx
Compositor.cxx Compositor.cxx
CompositorBuffer.cxx CompositorBuffer.cxx
CompositorPass.cxx CompositorPass.cxx
-216
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
-40
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 */
+395
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
+96
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 */
+8 -51
View File
@@ -39,7 +39,8 @@ Compositor *
Compositor::create(osg::View *view, Compositor::create(osg::View *view,
osg::GraphicsContext *gc, osg::GraphicsContext *gc,
osg::Viewport *viewport, osg::Viewport *viewport,
const SGPropertyNode *property_list) const SGPropertyNode *property_list,
const SGReaderWriterOptions *options)
{ {
osg::ref_ptr<Compositor> compositor = new Compositor(view, gc, viewport); osg::ref_ptr<Compositor> compositor = new Compositor(view, gc, viewport);
compositor->_name = property_list->getStringValue("name"); compositor->_name = property_list->getStringValue("name");
@@ -55,7 +56,7 @@ Compositor::create(osg::View *view,
"a name to be available to passes. Skipping..."); "a name to be available to passes. Skipping...");
continue; continue;
} }
Buffer *buffer = buildBuffer(compositor.get(), p_buffer); Buffer *buffer = buildBuffer(compositor.get(), p_buffer, options);
if (buffer) if (buffer)
compositor->addBuffer(buffer_name, buffer); compositor->addBuffer(buffer_name, buffer);
} }
@@ -64,7 +65,7 @@ Compositor::create(osg::View *view,
for (auto const &p_pass : p_passes) { for (auto const &p_pass : p_passes) {
if (!checkConditional(p_pass)) if (!checkConditional(p_pass))
continue; continue;
Pass *pass = buildPass(compositor.get(), p_pass); Pass *pass = buildPass(compositor.get(), p_pass, options);
if (pass) if (pass)
compositor->addPass(pass); compositor->addPass(pass);
} }
@@ -76,7 +77,8 @@ Compositor *
Compositor::create(osg::View *view, Compositor::create(osg::View *view,
osg::GraphicsContext *gc, osg::GraphicsContext *gc,
osg::Viewport *viewport, osg::Viewport *viewport,
const std::string &name) const std::string &name,
const SGReaderWriterOptions *options)
{ {
std::string filename(name); std::string filename(name);
filename += ".xml"; filename += ".xml";
@@ -96,7 +98,7 @@ Compositor::create(osg::View *view,
return 0; return 0;
} }
return create(view, gc, viewport, property_list); return create(view, gc, viewport, property_list, options);
} }
Compositor::Compositor(osg::View *view, 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 void
Compositor::addBuffer(const std::string &name, Buffer *buffer) Compositor::addBuffer(const std::string &name, Buffer *buffer)
{ {
@@ -272,7 +229,7 @@ Compositor::addPass(Pass *pass)
identifier = sceneView->getCullVisitor()->getIdentifier(); identifier = sceneView->getCullVisitor()->getIdentifier();
sceneView->setCullVisitor( sceneView->setCullVisitor(
new EffectCullVisitor(false, pass->effect_override)); new EffectCullVisitor(false, pass->effect_scheme));
sceneView->getCullVisitor()->setIdentifier(identifier.get()); sceneView->getCullVisitor()->setIdentifier(identifier.get());
identifier = sceneView->getCullVisitorLeft()->getIdentifier(); identifier = sceneView->getCullVisitorLeft()->getIdentifier();
+7 -9
View File
@@ -73,7 +73,8 @@ public:
static Compositor *create(osg::View *view, static Compositor *create(osg::View *view,
osg::GraphicsContext *gc, osg::GraphicsContext *gc,
osg::Viewport *viewport, osg::Viewport *viewport,
const SGPropertyNode *property_list); const SGPropertyNode *property_list,
const SGReaderWriterOptions *options);
/** /**
* \overload * \overload
* \brief Create a Compositor from a file. * \brief Create a Compositor from a file.
@@ -84,20 +85,19 @@ public:
static Compositor *create(osg::View *view, static Compositor *create(osg::View *view,
osg::GraphicsContext *gc, osg::GraphicsContext *gc,
osg::Viewport *viewport, osg::Viewport *viewport,
const std::string &name); const std::string &name,
const SGReaderWriterOptions *options);
void update(const osg::Matrix &view_matrix, void update(const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix); const osg::Matrix &proj_matrix);
void resized(); void resized();
bool computeIntersection( osg::View *getView() const { return _view; }
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections);
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< typedef std::array<
osg::ref_ptr<osg::Uniform>, osg::ref_ptr<osg::Uniform>,
@@ -121,8 +121,6 @@ public:
Pass * getPass(const std::string &name) const; Pass * getPass(const std::string &name) const;
protected: protected:
friend class PassBuilder;
osg::View *_view; osg::View *_view;
osg::GraphicsContext *_gc; osg::GraphicsContext *_gc;
osg::ref_ptr<osg::Viewport> _viewport; osg::ref_ptr<osg::Viewport> _viewport;
+4 -1
View File
@@ -28,6 +28,7 @@
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/props/vectorPropTemplates.hxx> #include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/util/OsgMath.hxx> #include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include "Compositor.hxx" #include "Compositor.hxx"
#include "CompositorUtil.hxx" #include "CompositorUtil.hxx"
@@ -49,6 +50,7 @@ PropStringMap<BufferFormat> buffer_format_map {
{"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}}, {"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}},
{"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}}, {"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}},
{"r32f", {GL_R32F, GL_RED, GL_FLOAT}}, {"r32f", {GL_R32F, GL_RED, GL_FLOAT}},
{"rg16f", {GL_RG16F, GL_RG, GL_FLOAT}},
{"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}}, {"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}},
{"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}}, {"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}},
{"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}}, {"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}},
@@ -92,7 +94,8 @@ PropStringMap<osg::Texture::ShadowCompareFunc> shadow_compare_func_map = {
}; };
Buffer * Buffer *
buildBuffer(Compositor *compositor, const SGPropertyNode *node) buildBuffer(Compositor *compositor, const SGPropertyNode *node,
const SGReaderWriterOptions *options)
{ {
std::string type = node->getStringValue("type"); std::string type = node->getStringValue("type");
if (type.empty()) { if (type.empty()) {
+5 -1
View File
@@ -22,6 +22,9 @@
class SGPropertyNode; class SGPropertyNode;
namespace simgear { namespace simgear {
class SGReaderWriterOptions;
namespace compositor { namespace compositor {
class Compositor; class Compositor;
@@ -38,7 +41,8 @@ struct Buffer : public osg::Referenced {
float width_scale, height_scale; 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 compositor
} // namespace simgear } // namespace simgear
+163 -103
View File
@@ -22,13 +22,17 @@
#include <osg/PolygonMode> #include <osg/PolygonMode>
#include <osg/io_utils> #include <osg/io_utils>
#include <osgUtil/CullVisitor>
#include <simgear/props/vectorPropTemplates.hxx> #include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/material/EffectCullVisitor.hxx>
#include <simgear/scene/material/EffectGeode.hxx> #include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/OsgMath.hxx> #include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGUpdateVisitor.hxx> #include <simgear/scene/util/SGUpdateVisitor.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include "ClusteredForward.hxx" #include "ClusteredShading.hxx"
#include "Compositor.hxx" #include "Compositor.hxx"
#include "CompositorUtil.hxx" #include "CompositorUtil.hxx"
@@ -50,34 +54,37 @@ PropStringMap<osg::Camera::BufferComponent> buffer_component_map = {
{"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER} {"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; osg::ref_ptr<Pass> pass = new Pass;
// The pass index matches its render order
pass->render_order = root->getIndex();
pass->name = root->getStringValue("name"); pass->name = root->getStringValue("name");
if (pass->name.empty()) { 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!"); << " has no name. It won't be addressable by name!");
} }
pass->type = root->getStringValue("type"); pass->type = root->getStringValue("type");
pass->effect_scheme = root->getStringValue("effect-scheme");
std::string eff_override_file = root->getStringValue("effect-override");
if (!eff_override_file.empty())
pass->effect_override = makeEffect(eff_override_file, true, 0);
osg::Camera *camera = new Camera; osg::Camera *camera = new Camera;
pass->camera = camera; pass->camera = camera;
camera->setName(pass->name); 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 // Even though this camera will be added as a slave to the view, it will
// always be updated manually in Compositor::update() // always be updated manually in Compositor::update()
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
// Same with the projection matrix // Same with the projection matrix
camera->setProjectionResizePolicy(osg::Camera::FIXED); 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); camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
// XXX: Should we make this configurable? // XXX: Should we make this configurable?
@@ -144,7 +151,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
} catch (sg_exception &e) { } catch (sg_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping binding " 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()); << ": " << e.what());
} }
} }
@@ -153,22 +160,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
if (p_attachments.empty()) { if (p_attachments.empty()) {
// If there are no attachments, assume the pass is rendering // If there are no attachments, assume the pass is rendering
// directly to the screen // 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->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
camera->setDrawBuffer(GL_BACK); camera->setDrawBuffer(GL_BACK);
camera->setReadBuffer(GL_BACK); camera->setReadBuffer(GL_BACK);
// Use the physical viewport. We can't let the user choose the viewport // Use the physical viewport. We can't let the user choose the viewport
// size because some parts of the window might not be ours. // size because some parts of the window might not be ours.
camera->setViewport(compositor->_viewport); camera->setViewport(compositor->getViewport());
} else { } else {
// This is a RTT camera // This is a RTT camera
camera->setRenderOrder(osg::Camera::PRE_RENDER, render_order * 10);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
bool viewport_absolute = false; bool viewport_absolute = false;
@@ -252,13 +252,13 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
camera->setViewport( camera->setViewport(
0, 0,
0, 0,
buffer->width_scale * compositor->_viewport->width(), buffer->width_scale * compositor->getViewport()->width(),
buffer->height_scale * compositor->_viewport->height()); buffer->height_scale * compositor->getViewport()->height());
} }
} }
} catch (sg_exception &e) { } catch (sg_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping attachment " 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()); << ": " << e.what());
} }
} }
@@ -271,8 +271,10 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
struct QuadPassBuilder : public PassBuilder { struct QuadPassBuilder : public PassBuilder {
public: public:
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root); const SGReaderWriterOptions *options) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
pass->useMastersSceneData = false;
osg::Camera *camera = pass->camera; osg::Camera *camera = pass->camera;
camera->setAllowEventFocus(false); camera->setAllowEventFocus(false);
@@ -289,11 +291,29 @@ public:
scale = p_geometry->getFloatValue("scale", scale); scale = p_geometry->getFloatValue("scale", scale);
} }
const std::string eff_file = root->getStringValue("effect"); osg::ref_ptr<EffectGeode> quad = new EffectGeode;
osg::ref_ptr<osg::Geode> quad = createFullscreenQuad(
left, bottom, width, height, scale, eff_file);
camera->addChild(quad); 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(); osg::StateSet *ss = camera->getOrCreateStateSet();
for (const auto &uniform : compositor->getUniforms()) for (const auto &uniform : compositor->getUniforms())
@@ -302,13 +322,12 @@ public:
return pass.release(); return pass.release();
} }
protected: protected:
osg::Geode *createFullscreenQuad(float left, osg::Geometry *createFullscreenQuadGeom(float left,
float bottom, float bottom,
float width, float width,
float height, float height,
float scale, float scale) {
const std::string &eff_file) { osg::ref_ptr<osg::Geometry> geom;
osg::Geometry *geom;
// When the quad is fullscreen, it can be optimized by using a // When the quad is fullscreen, it can be optimized by using a
// a fullscreen triangle instead of a quad to avoid discarding pixels // a fullscreen triangle instead of a quad to avoid discarding pixels
@@ -349,31 +368,52 @@ protected:
osg::PrimitiveSet::TRIANGLES, 0, 3)); osg::PrimitiveSet::TRIANGLES, 0, 3));
} }
osg::ref_ptr<EffectGeode> quad = new EffectGeode; return geom.release();
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();
} }
}; };
RegisterPassBuilder<QuadPassBuilder> registerQuadPass("quad"); 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 { class LightFinder : public osg::NodeVisitor {
public: public:
LightFinder(const std::string &name) : LightFinder(const std::string &name) :
@@ -406,15 +446,14 @@ protected:
struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback { struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback {
public: public:
ShadowMapUpdateCallback(const std::string &light_name, ShadowMapUpdateCallback(ShadowMapCullCallback *cull_callback,
const std::string &light_name,
float near_m, float far_m, float near_m, float far_m,
const std::string &suffix,
int sm_width, int sm_height) : int sm_width, int sm_height) :
_cull_callback(cull_callback),
_light_finder(new LightFinder(light_name)), _light_finder(new LightFinder(light_name)),
_near_m(near_m), _near_m(near_m),
_far_m(far_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; _half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0;
} }
virtual void updatePass(Pass &pass, virtual void updatePass(Pass &pass,
@@ -443,6 +482,7 @@ public:
// This is not a problem though (for now). // This is not a problem though (for now).
osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix); osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix);
_cull_callback->setRealInverseViewMatrix(view_inverse);
// Calculate the light's point of view transformation matrices. // Calculate the light's point of view transformation matrices.
// Taken from Project Rembrandt. // Taken from Project Rembrandt.
@@ -489,37 +529,30 @@ public:
osg::Matrixd round_matrix = osg::Matrixd::translate( osg::Matrixd round_matrix = osg::Matrixd::translate(
rounding.x(), rounding.y(), 0.0); rounding.x(), rounding.y(), 0.0);
light_proj_matrix *= round_matrix; 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: protected:
osg::observer_ptr<ShadowMapCullCallback> _cull_callback;
osg::ref_ptr<LightFinder> _light_finder; osg::ref_ptr<LightFinder> _light_finder;
float _near_m; float _near_m;
float _far_m; float _far_m;
osg::ref_ptr<osg::Uniform> _light_matrix_uniform;
osg::Vec2d _half_sm_size; osg::Vec2d _half_sm_size;
}; };
struct ShadowMapPassBuilder : public PassBuilder { struct ShadowMapPassBuilder : public PassBuilder {
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root); const SGReaderWriterOptions *options) {
pass->useMastersSceneData = true; osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
osg::Camera *camera = pass->camera; osg::Camera *camera = pass->camera;
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); 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"); std::string light_name = root->getStringValue("light-name");
float near_m = root->getFloatValue("near-m"); float near_m = root->getFloatValue("near-m");
@@ -527,9 +560,9 @@ struct ShadowMapPassBuilder : public PassBuilder {
int sm_width = camera->getViewport()->width(); int sm_width = camera->getViewport()->width();
int sm_height = camera->getViewport()->height(); int sm_height = camera->getViewport()->height();
pass->update_callback = new ShadowMapUpdateCallback( pass->update_callback = new ShadowMapUpdateCallback(
cull_callback,
light_name, light_name,
near_m, far_m, near_m, far_m,
pass->name,
sm_width, sm_height); sm_width, sm_height);
return pass.release(); return pass.release();
@@ -624,43 +657,69 @@ protected:
float _zFar; 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 { struct ScenePassBuilder : public PassBuilder {
public: public:
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root); const SGReaderWriterOptions *options) {
pass->useMastersSceneData = true; osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
pass->inherit_cull_mask = true; pass->inherit_cull_mask = true;
osg::Camera *camera = pass->camera; osg::Camera *camera = pass->camera;
camera->setAllowEventFocus(true); camera->setAllowEventFocus(true);
const SGPropertyNode *clustered = root->getChild("clustered-forward"); const SGPropertyNode *p_clustered = root->getNode("clustered-shading");
if (clustered) { ClusteredShading *clustered = 0;
camera->setInitialDrawCallback(new ClusteredForwardDrawCallback); if (p_clustered)
} clustered = new ClusteredShading(camera, p_clustered);
camera->setCullCallback(new SceneCullCallback(clustered));
int cubemap_face = root->getIntValue("cubemap-face", -1); int cubemap_face = root->getIntValue("cubemap-face", -1);
float zNear = root->getFloatValue("z-near", 0.0f); float zNear = root->getFloatValue("z-near", 0.0f);
float zFar = root->getFloatValue("z-far", 0.0f); float zFar = root->getFloatValue("z-far", 0.0f);
pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar); pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar);
std::string shadow_pass_name = root->getStringValue("use-shadow-pass"); PropertyList p_shadow_passes = root->getChildren("use-shadow-pass");
if (!shadow_pass_name.empty()) { for (const auto &p_shadow_pass : p_shadow_passes) {
Pass *shadow_pass = compositor->getPass(shadow_pass_name); std::string shadow_pass_name = p_shadow_pass->getStringValue();
if (shadow_pass) { if (!shadow_pass_name.empty()) {
ShadowMapUpdateCallback *updatecb = Pass *shadow_pass = compositor->getPass(shadow_pass_name);
dynamic_cast<ShadowMapUpdateCallback *>( if (shadow_pass) {
shadow_pass->update_callback.get()); ShadowMapCullCallback *cullcb =
if (updatecb) { dynamic_cast<ShadowMapCullCallback *>(
camera->getOrCreateStateSet()->addUniform( shadow_pass->camera->getCullCallback());
updatecb->getLightMatrixUniform()); 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 { } else {
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not "
<< shadow_pass_name << "is not a shadow pass"); "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 * Pass *
buildPass(Compositor *compositor, const SGPropertyNode *root) buildPass(Compositor *compositor, const SGPropertyNode *root,
const SGReaderWriterOptions *options)
{ {
std::string type = root->getStringValue("type"); std::string type = root->getStringValue("type");
if (type.empty()) { if (type.empty()) {
@@ -687,7 +747,7 @@ buildPass(Compositor *compositor, const SGPropertyNode *root)
return 0; return 0;
} }
return builder->build(compositor, root); return builder->build(compositor, root, options);
} }
} // namespace compositor } // namespace compositor
+11 -6
View File
@@ -27,6 +27,9 @@
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
namespace simgear { namespace simgear {
class SGReaderWriterOptions;
namespace compositor { namespace compositor {
class Compositor; class Compositor;
@@ -45,17 +48,17 @@ class Compositor;
*/ */
struct Pass : public osg::Referenced { struct Pass : public osg::Referenced {
Pass() : Pass() :
useMastersSceneData(false), useMastersSceneData(true),
cull_mask(0xffffff), cull_mask(0xffffff),
inherit_cull_mask(false), inherit_cull_mask(false),
viewport_width_scale(0.0f), viewport_width_scale(0.0f),
viewport_height_scale(0.0f) {} viewport_height_scale(0.0f) {}
int render_order;
std::string name; std::string name;
std::string type; std::string type;
std::string effect_scheme;
osg::ref_ptr<osg::Camera> camera; osg::ref_ptr<osg::Camera> camera;
/** If null, there is no effect override for this pass. */
osg::ref_ptr<Effect> effect_override;
bool useMastersSceneData; bool useMastersSceneData;
osg::Node::NodeMask cull_mask; osg::Node::NodeMask cull_mask;
/** Whether the cull mask is ANDed with the view master camera 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. * and overrided for more special passes.
* *
* @param compositor The Compositor instance that owns the pass. * @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. * @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) { static PassBuilder *find(const std::string &type) {
auto itr = PassBuilderMapSingleton::instance()->_map.find(type); auto itr = PassBuilderMapSingleton::instance()->_map.find(type);
@@ -125,7 +129,8 @@ struct RegisterPassBuilder {
* @param node The root node of the pass property tree. * @param node The root node of the pass property tree.
* @return A Pass or a null pointer if an error occurred. * @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 compositor
} // namespace simgear } // namespace simgear
+2 -4
View File
@@ -6,6 +6,7 @@ set(HEADERS
xmlsound.hxx xmlsound.hxx
soundmgr.hxx soundmgr.hxx
filters.hxx filters.hxx
readwav.hxx
) )
set(SOURCES set(SOURCES
@@ -13,6 +14,7 @@ set(SOURCES
sample_group.cxx sample_group.cxx
xmlsound.cxx xmlsound.cxx
filters.cxx filters.cxx
readwav.cxx
) )
if (USE_AEONWAVE) if (USE_AEONWAVE)
@@ -20,13 +22,9 @@ if (USE_AEONWAVE)
soundmgr_aeonwave.cxx soundmgr_aeonwave.cxx
) )
else() else()
set(HEADERS ${HEADERS}
readwav.hxx
)
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
soundmgr_openal.cxx soundmgr_openal.cxx
soundmgr_openal_private.hxx soundmgr_openal_private.hxx
readwav.cxx
) )
endif() endif()
+9 -8
View File
@@ -49,19 +49,21 @@ class SGSoundSample;
class SGSoundMgr : public SGSubsystem class SGSoundMgr : public SGSubsystem
{ {
public: public:
SGSoundMgr(); SGSoundMgr();
~SGSoundMgr(); ~SGSoundMgr();
void init(); // Subsystem API.
void update(double dt); 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 stop();
void reinit();
/** /**
* Select a specific sound device. * Select a specific sound device.
* Requires a init/reinit call before sound is actually switched. * Requires a init/reinit call before sound is actually switched.
@@ -324,7 +326,6 @@ public:
bool testForError(std::string s, std::string name = "sound manager"); bool testForError(std::string s, std::string name = "sound manager");
static const char* subsystemName() { return "sound"; };
private: private:
class SoundManagerPrivate; class SoundManagerPrivate;
/// private implementation object /// private implementation object
+1 -1
View File
@@ -340,7 +340,7 @@ void SGSoundMgr::suspend()
for ( auto current = d->_sample_groups.begin(); for ( auto current = d->_sample_groups.begin();
current != d->_sample_groups.end(); ++current ) { current != d->_sample_groups.end(); ++current ) {
SGSampleGroup *sgrp = current->second; SGSampleGroup *sgrp = current->second;
sgrp->stop(); sgrp->suspend();
} }
_active = false; _active = false;
} }
+3
View File
@@ -83,6 +83,9 @@ SGXmlSound::~SGXmlSound()
if (_sample) if (_sample)
_sample->stop(); _sample->stop();
if (_sgrp && (_name != ""))
_sgrp->remove(_name);
_volume.clear(); _volume.clear();
_pitch.clear(); _pitch.clear();
} }
+1
View File
@@ -33,6 +33,7 @@
#include <sstream> #include <sstream>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <cstring> // for strcmp #include <cstring> // for strcmp
#include <cassert>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
+4
View File
@@ -138,3 +138,7 @@ SGPerformanceMonitor::reportTiming(const string& name, SampleStatistic* timeStat
timeStat->reset(); timeStat->reset();
} }
// Register the subsystem.
//SGSubsystemMgr::Registrant<SGPerformanceMonitor> registrantSGPerformanceMonitor;
+8 -5
View File
@@ -34,14 +34,17 @@ class SampleStatistic;
class SGPerformanceMonitor : public SGSubsystem class SGPerformanceMonitor : public SGSubsystem
{ {
public: public:
SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root); SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root);
virtual void bind (void); // Subsystem API.
virtual void unbind (void); void bind() override;
virtual void init (void); void init() override;
virtual void update (double dt); void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "performance-mon"; }
private: private:
static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat); static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat);
+5 -1
View File
@@ -47,7 +47,6 @@ SGEventMgr::SGEventMgr() :
_inited(false), _inited(false),
_shutdown(false) _shutdown(false)
{ {
_name = "EventMgr";
} }
SGEventMgr::~SGEventMgr() SGEventMgr::~SGEventMgr()
@@ -127,6 +126,11 @@ void SGEventMgr::dump()
_rtQueue.dump(); _rtQueue.dump();
} }
// Register the subsystem.
SGSubsystemMgr::Registrant<SGEventMgr> registrantSGEventMgr(
SGSubsystemMgr::DISPLAY);
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// SGTimerQueue // SGTimerQueue
// This is the priority queue implementation: // This is the priority queue implementation:
+18 -9
View File
@@ -8,7 +8,8 @@
class SGEventMgr; class SGEventMgr;
class SGTimer { class SGTimer
{
public: public:
~SGTimer(); ~SGTimer();
void run(); void run();
@@ -20,7 +21,8 @@ public:
bool running; bool running;
}; };
class SGTimerQueue { class SGTimerQueue
{
public: public:
SGTimerQueue(int preSize=1); SGTimerQueue(int preSize=1);
~SGTimerQueue(); ~SGTimerQueue();
@@ -39,6 +41,7 @@ public:
SGTimer* findByName(const std::string& name) const; SGTimer* findByName(const std::string& name) const;
void dump(); void dump();
private: private:
// The "priority" is stored as a negative time. This allows the // The "priority" is stored as a negative time. This allows the
// implementation to treat the "top" of the heap as the largest // 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; } int rchild(int n) { return ((n+1)*2 + 1) - 1; }
double pri(int n) { return _table[n].pri; } double pri(int n) { return _table[n].pri; }
void swap(int a, int b) { void swap(int a, int b) {
HeapEntry tmp = _table[a]; HeapEntry tmp = _table[a];
_table[a] = _table[b]; _table[a] = _table[b];
_table[b] = tmp; _table[b] = tmp;
} }
void siftDown(int n); void siftDown(int n);
void siftUp(int n); void siftUp(int n);
@@ -73,10 +76,15 @@ public:
SGEventMgr(); SGEventMgr();
~SGEventMgr(); ~SGEventMgr();
virtual void init(); // Subsystem API.
virtual void update(double delta_time_sec); void init() override;
virtual void unbind(); void shutdown() override;
virtual void shutdown(); void unbind() override;
void update(double delta_time_sec) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "events"; }
void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; } void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; }
/** /**
@@ -121,6 +129,7 @@ public:
void removeTask(const std::string& name); void removeTask(const std::string& name);
void dump(); void dump();
private: private:
friend class SGTimer; friend class SGTimer;
+33 -33
View File
@@ -21,6 +21,7 @@
#include <simgear_config.h> #include <simgear_config.h>
#include <algorithm> #include <algorithm>
#include <cassert>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/timing/timestamp.hxx> #include <simgear/timing/timestamp.hxx>
@@ -137,28 +138,28 @@ void SGSubsystem::stamp(const string& name)
void SGSubsystem::set_name(const std::string &n) void SGSubsystem::set_name(const std::string &n)
{ {
assert(_name.empty()); assert(_subsystemId.empty());
_name = n; _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) { 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) { if (pos == std::string::npos) {
return {}; return {};
} }
return _name.substr(pos+1); return _subsystemId.substr(pos+1);
} }
void SGSubsystem::set_group(SGSubsystemGroup* group) void SGSubsystem::set_group(SGSubsystemGroup* group)
@@ -174,7 +175,7 @@ SGSubsystemGroup* SGSubsystem::get_group() const
SGSubsystemMgr* SGSubsystem::get_manager() const SGSubsystemMgr* SGSubsystem::get_manager() const
{ {
if (!get_group()) 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(); return get_group()->get_manager();
} }
@@ -232,12 +233,11 @@ public:
SGSubsystemGroup::SGSubsystemGroup(const char *name) : SGSubsystemGroup::SGSubsystemGroup() :
_fixedUpdateTime(-1.0), _fixedUpdateTime(-1.0),
_updateTimeRemainder(0.0), _updateTimeRemainder(0.0),
_initPosition(-1) _initPosition(-1)
{ {
_name = name;
} }
SGSubsystemGroup::~SGSubsystemGroup () SGSubsystemGroup::~SGSubsystemGroup ()
@@ -473,7 +473,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
if (reportDeltas) { if (reportDeltas) {
auto deltaT = _executionTime - _lastExecutionTime; auto deltaT = _executionTime - _lastExecutionTime;
if (deltaT != 0) { if (deltaT != 0) {
t << name() << "(+" << std::setprecision(2) << std::right << deltaT << "ms)."; t << subsystemInstanceId() << "(+" << std::setprecision(2) << std::right << deltaT << "ms).";
_name = t.str(); _name = t.str();
} }
} }
@@ -505,7 +505,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) { void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
SGSubsystem::reportTimingStats(_lastValues); SGSubsystem::reportTimingStats(_lastValues);
std::string _name = name(); std::string _name = subsystemInstanceId();
if (!_name.size()) if (!_name.size())
_name = typeid(this).name(); _name = typeid(this).name();
if (_lastValues) { if (_lastValues) {
@@ -513,11 +513,11 @@ void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
if (deltaT != 0) { if (deltaT != 0) {
SG_LOG(SG_EVENT, SG_ALERT, SG_LOG(SG_EVENT, SG_ALERT,
" +" << std::setw(6) << std::setprecision(4) << std::right << deltaT << "ms " " +" << std::setw(6) << std::setprecision(4) << std::right << deltaT << "ms "
<< name() ); << subsystemInstanceId() );
} }
} }
else 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) { for (auto member : _members) {
member->reportTimingStats(_lastValues); member->reportTimingStats(_lastValues);
} }
@@ -574,11 +574,11 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
if (name.empty()) { if (name.empty()) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group without a name"); SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group without a name");
// TODO, make this an exception in the future // TODO, make this an exception in the future
} else if (subsystem->name().empty()) { } else if (subsystem->subsystemId().empty()) {
subsystem->set_name(name); 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 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); notifyWillChange(subsystem, State::ADD);
@@ -614,7 +614,7 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
void void
SGSubsystemGroup::set_subsystem (SGSubsystem * subsystem, double min_step_sec) 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 * SGSubsystem *
@@ -837,7 +837,7 @@ namespace {
} // end of anonymous namespace } // end of anonymous namespace
SGSubsystemMgr::SGSubsystemMgr (const char *name) : SGSubsystemMgr::SGSubsystemMgr () :
_groups(MAX_GROUPS) _groups(MAX_GROUPS)
{ {
if (global_defaultSubsystemManager == nullptr) { if (global_defaultSubsystemManager == nullptr) {
@@ -854,7 +854,7 @@ SGSubsystemMgr::SGSubsystemMgr (const char *name) :
#endif #endif
for (int i = 0; i < MAX_GROUPS; i++) { for (int i = 0; i < MAX_GROUPS; i++) {
auto g = new SGSubsystemGroup(name); auto g = new SGSubsystemGroup();
g->set_manager(this); g->set_manager(this);
_groups[i].reset(g); _groups[i].reset(g);
} }
@@ -1038,9 +1038,9 @@ SGSubsystemMgr::get_subsystem (const string &name) const
} }
SGSubsystem* 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 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 it = findRegistration(name);
auto &global_registrations = getGlobalRegistrations(); 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); 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); ref->set_name(combinedName);
return ref; return ref;
} }
@@ -1275,7 +1275,7 @@ namespace {
// allow override of the name but defaultt o the subsystem name // allow override of the name but defaultt o the subsystem name
std::string name = arg->getStringValue("name"); std::string name = arg->getStringValue("name");
std::string instanceName = arg->getStringValue("instance"); std::string subsystemInstanceId = arg->getStringValue("instance");
if (name.empty()) { if (name.empty()) {
// default name to subsystem name, but before we parse any instance name // default name to subsystem name, but before we parse any instance name
@@ -1284,19 +1284,19 @@ namespace {
auto separatorPos = subsystem.find(SUBSYSTEM_NAME_SEPARATOR); auto separatorPos = subsystem.find(SUBSYSTEM_NAME_SEPARATOR);
if (separatorPos != std::string::npos) { 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: " 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; return false;
} }
instanceName = subsystem.substr(separatorPos + 1); subsystemInstanceId = subsystem.substr(separatorPos + 1);
subsystem = subsystem.substr(0, separatorPos); subsystem = subsystem.substr(0, separatorPos);
} }
SGSubsystem* sub = nullptr; SGSubsystem* sub = nullptr;
if (!instanceName.empty()) { if (!subsystemInstanceId.empty()) {
sub = manager->createInstance(subsystem, instanceName); sub = manager->createInstance(subsystem, subsystemInstanceId);
} else { } else {
sub = manager->create(subsystem); sub = manager->create(subsystem);
} }
@@ -1312,7 +1312,7 @@ namespace {
double minTime = arg->getDoubleValue("min-time-sec", 0.0); 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(), manager->add(combinedName.c_str(),
sub, sub,
group, group,
+79 -60
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 * subsystems may also override the suspend() and resume() methods to
* take different actions.</p> * take different actions.</p>
*/ */
class SGSubsystem : public SGReferenced class SGSubsystem : public SGReferenced
{ {
public: public:
@@ -281,19 +282,19 @@ public:
/** /**
* composite name for this subsystem (type name & optional instance name) * composite name for this subsystem (type name & optional instance name)
*/ */
std::string name() const std::string subsystemId() const
{ return _name; } { return _subsystemId; }
/** /**
* @brief the type (class)-specific part of the subsystem name. * @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 * @brief the instance part of the subsystem name. Empty if this
* subsystem is not instanced * subsystem is not instanced
*/ */
std::string instanceName() const; std::string subsystemInstanceId() const;
virtual bool is_group() const virtual bool is_group() const
{ return false; } { return false; }
@@ -352,16 +353,21 @@ protected:
/// protected, not private, we can't rename it easily) /// protected, not private, we can't rename it easily)
std::string _name; std::string _name;
bool _suspended = false; bool _suspended = false;
eventTimeVec timingInfo; eventTimeVec timingInfo;
static SGSubsystemTimingCb reportTimingCb; static SGSubsystemTimingCb reportTimingCb;
static void* reportTimingUserData; static void* reportTimingUserData;
static bool reportTimingStatsRequest; static bool reportTimingStatsRequest;
static int maxTimePerFrame_ms; static int maxTimePerFrame_ms;
private: 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; SGSubsystemGroup* _group = nullptr;
protected: protected:
TimerStats _timerStats, _lastTimerStats; TimerStats _timerStats, _lastTimerStats;
@@ -378,20 +384,21 @@ typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
class SGSubsystemGroup : public SGSubsystem class SGSubsystemGroup : public SGSubsystem
{ {
public: public:
SGSubsystemGroup (const char *name); SGSubsystemGroup ();
virtual ~SGSubsystemGroup (); virtual ~SGSubsystemGroup ();
// Subsystem API.
void bind() override;
InitStatus incrementalInit() override;
void init() override; void init() override;
InitStatus incrementalInit () override; void postinit() override;
void postinit () override; void reinit() override;
void reinit () override; void resume() override;
void shutdown () override; void shutdown() override;
void bind () override; void suspend() override;
void unbind () override; void unbind() override;
void update (double delta_time_sec) override; void update(double delta_time_sec) override;
void suspend () override; bool is_suspended() const override;
void resume () override;
bool is_suspended () const override;
virtual void set_subsystem (const std::string &name, virtual void set_subsystem (const std::string &name,
SGSubsystem * subsystem, SGSubsystem * subsystem,
@@ -416,21 +423,22 @@ public:
*/ */
void set_fixed_update_time(double fixed_dt); 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; string_list member_names() const;
template<class T> template<class T>
T* get_subsystem() T* get_subsystem()
{ {
return dynamic_cast<T*>(get_subsystem(T::subsystemName())); return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
} }
bool is_group() const override bool is_group() const override
{ return true; } { return true; }
SGSubsystemMgr* get_manager() const override; SGSubsystemMgr* get_manager() const override;
private: private:
void forEach(std::function<void(SGSubsystem*)> f); void forEach(std::function<void(SGSubsystem*)> f);
void reverseForEach(std::function<void(SGSubsystem*)> f); void reverseForEach(std::function<void(SGSubsystem*)> f);
@@ -501,20 +509,24 @@ public:
MAX_GROUPS MAX_GROUPS
}; };
SGSubsystemMgr (const char *name); SGSubsystemMgr ();
virtual ~SGSubsystemMgr (); virtual ~SGSubsystemMgr ();
void init () override; // Subsystem API.
InitStatus incrementalInit () override; void bind() override;
void postinit () override; void init() override;
void reinit () override; InitStatus incrementalInit() override;
void shutdown () override; void postinit() override;
void bind () override; void reinit() override;
void unbind () override; void resume() override;
void update (double delta_time_sec) override; void shutdown() override;
void suspend () override; void suspend() override;
void resume () override; void unbind() override;
bool is_suspended () const 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, virtual void add (const char * name,
SGSubsystem * subsystem, SGSubsystem * subsystem,
@@ -531,7 +543,7 @@ public:
SGSubsystem* get_subsystem(const std::string &name) const; 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 reportTiming();
void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; } void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; }
@@ -552,28 +564,34 @@ public:
template<class T> template<class T>
T* get_subsystem() const 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 // instanced overloads, for both raw char* and std::string
// these concatenate the subsystem type name with the instance name // these concatenate the subsystem type name with the instance name
template<class T> 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> 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 { struct Dependency {
enum Type { enum Type {
HARD, HARD, ///< The subsystem cannot run without this subsystem dependency.
SOFT, SOFT, ///< The subsystem uses this subsystem dependency, but can run without it.
PROPERTY 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; std::string name;
@@ -598,11 +616,12 @@ public:
class Registrant class Registrant
{ {
public: public:
Registrant(GroupType group = GENERAL, double updateInterval = 0.0, Registrant(GroupType group = GENERAL,
std::initializer_list<Dependency> deps = {}) std::initializer_list<Dependency> deps = {},
double updateInterval = 0.0)
{ {
SubsystemFactoryFunctor factory = [](){ return new T; }; SubsystemFactoryFunctor factory = [](){ return new T; };
SGSubsystemMgr::registerSubsystem(T::subsystemName(), SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
factory, group, factory, group,
false, updateInterval, false, updateInterval,
deps); deps);
@@ -616,11 +635,11 @@ public:
{ {
public: public:
InstancedRegistrant(GroupType group = GENERAL, 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; }; SubsystemFactoryFunctor factory = [](){ return new T; };
SGSubsystemMgr::registerSubsystem(T::subsystemName(), SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
factory, group, factory, group,
true, updateInterval, true, updateInterval,
deps); deps);
@@ -636,14 +655,14 @@ public:
template <class T> template <class T>
SGSharedPtr<T> add(GroupType customGroup = INVALID, double customInterval = 0.0) 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) ? const GroupType group = (customGroup == INVALID) ?
defaultGroupFor(T::subsystemName()) : customGroup; defaultGroupFor(T::staticSubsystemClassId()) : customGroup;
const double interval = (customInterval == 0.0) ? const double interval = (customInterval == 0.0) ?
defaultUpdateIntervalFor(T::subsystemName()) : customInterval; defaultUpdateIntervalFor(T::staticSubsystemClassId()) : customInterval;
add(ref->name().c_str(), ref.ptr(), group, interval); add(ref->subsystemId().c_str(), ref.ptr(), group, interval);
return dynamic_cast<T*>(ref.ptr()); return dynamic_cast<T*>(ref.ptr());
} }
@@ -654,20 +673,20 @@ public:
template <class T> template <class T>
SGSharedPtr<T> create() SGSharedPtr<T> create()
{ {
auto ref = create(T::subsystemName()); auto ref = create(T::staticSubsystemClassId());
return dynamic_cast<T*>(ref.ptr()); return dynamic_cast<T*>(ref.ptr());
} }
SGSubsystemRef create(const std::string& name); SGSubsystemRef create(const std::string& name);
template <class T> 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()); 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 GroupType defaultGroupFor(const char* name);
static double defaultUpdateIntervalFor(const char* name); static double defaultUpdateIntervalFor(const char* name);
+28 -28
View File
@@ -20,7 +20,7 @@ using std::endl;
class MySub1 : public SGSubsystem class MySub1 : public SGSubsystem
{ {
public: public:
static const char* subsystemName() { return "mysub"; } static const char* staticSubsystemClassId() { return "mysub"; }
void init() override void init() override
{ {
@@ -45,7 +45,7 @@ public:
class AnotherSub : public SGSubsystem class AnotherSub : public SGSubsystem
{ {
public: public:
static const char* subsystemName() { return "anothersub"; } static const char* staticSubsystemClassId() { return "anothersub"; }
void init() override void init() override
{ {
@@ -70,7 +70,7 @@ public:
class FakeRadioSub : public SGSubsystem class FakeRadioSub : public SGSubsystem
{ {
public: public:
static const char* subsystemName() { return "fake-radio"; } static const char* staticSubsystemClassId() { return "fake-radio"; }
void init() override void init() override
{ {
@@ -89,8 +89,8 @@ public:
class InstrumentGroup : public SGSubsystemGroup class InstrumentGroup : public SGSubsystemGroup
{ {
public: public:
static const char* subsystemName() { return "instruments"; } static const char* staticSubsystemClassId() { return "instruments"; }
InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::subsystemName()) {}
virtual ~InstrumentGroup() virtual ~InstrumentGroup()
{ {
} }
@@ -119,11 +119,11 @@ class RecorderDelegate : public SGSubsystemMgr::Delegate
public: public:
void willChange(SGSubsystem* sub, SGSubsystem::State newState) override 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 void didChange(SGSubsystem* sub, SGSubsystem::State newState) override
{ {
events.push_back({sub->name(), true, newState}); events.push_back({sub->subsystemId(), true, newState});
} }
struct Event { struct Event {
@@ -164,14 +164,14 @@ SGSubsystemMgr::InstancedRegistrant<FakeRadioSub> registrant3(SGSubsystemMgr::PO
void testRegistrationAndCreation() void testRegistrationAndCreation()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto anotherSub = manager->create<AnotherSub>(); auto anotherSub = manager->create<AnotherSub>();
SG_VERIFY(anotherSub); SG_VERIFY(anotherSub);
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName()); SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId());
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub")); SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->typeName(), std::string("anothersub")); SG_CHECK_EQUAL(anotherSub->subsystemClassId(), std::string("anothersub"));
SG_CHECK_EQUAL(anotherSub->instanceName(), std::string()); SG_CHECK_EQUAL(anotherSub->subsystemInstanceId(), std::string());
auto radio1 = manager->createInstance<FakeRadioSub>("nav1"); auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
auto radio2 = manager->createInstance<FakeRadioSub>("nav2"); auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
@@ -181,14 +181,14 @@ void testRegistrationAndCreation()
void testAddGetRemove() void testAddGetRemove()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate; auto d = new RecorderDelegate;
manager->addDelegate(d); manager->addDelegate(d);
auto anotherSub = manager->add<AnotherSub>(); auto anotherSub = manager->add<AnotherSub>();
SG_VERIFY(anotherSub); SG_VERIFY(anotherSub);
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName()); SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId());
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub")); SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub"));
SG_VERIFY(d->hasEvent("anothersub-will-add")); SG_VERIFY(d->hasEvent("anothersub-will-add"));
SG_VERIFY(d->hasEvent("anothersub-did-add")); SG_VERIFY(d->hasEvent("anothersub-did-add"));
@@ -200,14 +200,14 @@ void testAddGetRemove()
// manual create & add // manual create & add
auto mySub = manager->create<MySub1>(); 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-will-add"));
SG_VERIFY(d->hasEvent("mysub-did-add")); SG_VERIFY(d->hasEvent("mysub-did-add"));
SG_CHECK_EQUAL(manager->get_subsystem<MySub1>(), mySub); 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(ok);
SG_VERIFY(d->hasEvent("anothersub-will-remove")); SG_VERIFY(d->hasEvent("anothersub-will-remove"));
SG_VERIFY(d->hasEvent("anothersub-did-remove")); SG_VERIFY(d->hasEvent("anothersub-did-remove"));
@@ -219,7 +219,7 @@ void testAddGetRemove()
// re-add of removed, and let's test overriding // re-add of removed, and let's test overriding
auto another2 = manager->add<AnotherSub>(SGSubsystemMgr::SOUND); 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); auto soundGroup = manager->get_group(SGSubsystemMgr::SOUND);
SG_CHECK_EQUAL(soundGroup->get_subsystem("anothersub"), another2); SG_CHECK_EQUAL(soundGroup->get_subsystem("anothersub"), another2);
@@ -228,7 +228,7 @@ void testAddGetRemove()
void testSubGrouping() void testSubGrouping()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate; auto d = new RecorderDelegate;
manager->addDelegate(d); manager->addDelegate(d);
@@ -238,10 +238,10 @@ void testSubGrouping()
auto radio1 = manager->createInstance<FakeRadioSub>("nav1"); auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
auto radio2 = manager->createInstance<FakeRadioSub>("nav2"); auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio.nav1")); SG_CHECK_EQUAL(radio1->subsystemId(), std::string("fake-radio.nav1"));
SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio.nav2")); SG_CHECK_EQUAL(radio2->subsystemId(), std::string("fake-radio.nav2"));
SG_CHECK_EQUAL(radio1->typeName(), std::string("fake-radio")); SG_CHECK_EQUAL(radio1->subsystemClassId(), std::string("fake-radio"));
SG_CHECK_EQUAL(radio2->instanceName(), std::string("nav2")); SG_CHECK_EQUAL(radio2->subsystemInstanceId(), std::string("nav2"));
instruments->set_subsystem(radio1); instruments->set_subsystem(radio1);
instruments->set_subsystem(radio2); instruments->set_subsystem(radio2);
@@ -299,7 +299,7 @@ void testSubGrouping()
void testIncrementalInit() void testIncrementalInit()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate; auto d = new RecorderDelegate;
manager->addDelegate(d); manager->addDelegate(d);
@@ -347,7 +347,7 @@ void testEmptyGroup()
// https://sourceforge.net/p/flightgear/codetickets/2043/ // https://sourceforge.net/p/flightgear/codetickets/2043/
// when an empty group is inited, we skipped setting the state // 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; auto d = new RecorderDelegate;
manager->addDelegate(d); manager->addDelegate(d);
@@ -371,7 +371,7 @@ void testEmptyGroup()
void testSuspendResume() void testSuspendResume()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate; auto d = new RecorderDelegate;
manager->addDelegate(d); manager->addDelegate(d);
@@ -441,7 +441,7 @@ void testSuspendResume()
void testPropertyRoot() void testPropertyRoot()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
SGPropertyNode_ptr props(new SGPropertyNode); SGPropertyNode_ptr props(new SGPropertyNode);
manager->set_root_node(props); manager->set_root_node(props);
@@ -467,7 +467,7 @@ void testPropertyRoot()
void testAddRemoveAfterInit() void testAddRemoveAfterInit()
{ {
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST"); SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
auto d = new RecorderDelegate; auto d = new RecorderDelegate;
manager->addDelegate(d); manager->addDelegate(d);
+96
View File
@@ -25,6 +25,7 @@
#endif #endif
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include "SGThread.hxx" #include "SGThread.hxx"
@@ -416,3 +417,98 @@ SGWaitCondition::broadcast()
{ {
_privateData->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;
}
+41
View File
@@ -23,7 +23,11 @@
#ifndef SGTHREAD_HXX_INCLUDED #ifndef SGTHREAD_HXX_INCLUDED
#define SGTHREAD_HXX_INCLUDED 1 #define SGTHREAD_HXX_INCLUDED 1
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <simgear/timing/timestamp.hxx>
/** /**
* Encapsulate generic threading methods. * Encapsulate generic threading methods.
@@ -184,4 +188,41 @@ private:
PrivateData* _privateData; 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 */ #endif /* SGTHREAD_HXX_INCLUDED */
+8
View File
@@ -337,3 +337,11 @@ int SGTimeStamp::elapsedMSec() const
return static_cast<int>((now - *this).toMSecs()); return static_cast<int>((now - *this).toMSecs());
} }
int SGTimeStamp::elapsedUSec() const
{
SGTimeStamp now;
now.stamp();
return static_cast<int>((now - *this).toUSecs());
}
+6 -2
View File
@@ -221,9 +221,13 @@ public:
{ return sleepFor(fromMSec(msec)); } { 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; int elapsedMSec() const;
/**
* elapsed time since the stamp was taken, in usec
*/
int elapsedUSec() const;
private: private:
SGTimeStamp(sec_type sec, nsec_type nsec) SGTimeStamp(sec_type sec, nsec_type nsec)
{ setTime(sec, nsec); } { setTime(sec, nsec); }