Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next
This commit is contained in:
6
3rdparty/udns/udns_init.c
vendored
6
3rdparty/udns/udns_init.c
vendored
@@ -24,7 +24,7 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
# include <winsock2.h> /* includes <windows.h> */
|
||||
# include <iphlpapi.h> /* for dns server addresses etc */
|
||||
#else
|
||||
@@ -53,7 +53,7 @@ static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
|
||||
dns_add_srch(ctx, srch);
|
||||
}
|
||||
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
|
||||
#ifndef NO_IPHLPAPI
|
||||
/* Apparently, some systems does not have proper headers for IPHLPAIP to work.
|
||||
@@ -217,7 +217,7 @@ int dns_init(struct dns_ctx *ctx, int do_open) {
|
||||
ctx = &dns_defctx;
|
||||
dns_reset(ctx);
|
||||
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
if (dns_initns_iphlpapi(ctx) != 0)
|
||||
dns_initns_registry(ctx);
|
||||
/*XXX WINDOWS: probably good to get default domain and search list too...
|
||||
|
||||
8
3rdparty/udns/udns_resolver.c
vendored
8
3rdparty/udns/udns_resolver.c
vendored
@@ -24,7 +24,7 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
# include <winsock2.h> /* includes <windows.h> */
|
||||
# include <ws2tcpip.h> /* needed for struct in6_addr */
|
||||
#else
|
||||
@@ -392,7 +392,7 @@ dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) {
|
||||
}
|
||||
|
||||
static unsigned dns_nonrandom_32(void) {
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
return ft.dwLowDateTime;
|
||||
@@ -551,7 +551,7 @@ int dns_open(struct dns_ctx *ctx) {
|
||||
ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
{ unsigned long on = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
|
||||
closesocket(sock);
|
||||
@@ -991,7 +991,7 @@ again: /* receive the reply */
|
||||
* or remote. On local errors, we should stop, while
|
||||
* remote errors should be ignored (for now anyway).
|
||||
*/
|
||||
#ifdef WINDOWS
|
||||
#if defined(_WINDOWS) || defined(WINDOWS)
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
#else
|
||||
if (errno == EAGAIN)
|
||||
|
||||
2
3rdparty/udns/udns_rr_a.c
vendored
2
3rdparty/udns/udns_rr_a.c
vendored
@@ -27,7 +27,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#ifndef WINDOWS
|
||||
#if !defined(_WINDOWS) && !defined(WINDOWS)
|
||||
# include <sys/types.h>
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
@@ -150,7 +150,7 @@ endif()
|
||||
|
||||
|
||||
if (MSVC)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} DIRECTORY)
|
||||
if (CMAKE_CL_64)
|
||||
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
|
||||
else (CMAKE_CL_64)
|
||||
@@ -198,7 +198,7 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
# if this variable was not set by the user, set it to 3rdparty root's
|
||||
# parent dir, which is the normal location for people using our
|
||||
# windows-3rd-party repo
|
||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||
get_filename_component(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} DIRECTORY)
|
||||
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
|
||||
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
@@ -80,7 +80,7 @@ if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||
set(_boostConfig "BoostTestTargetsDynamic.h")
|
||||
endif()
|
||||
endif()
|
||||
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
|
||||
configure_file("${_moddir}/${_boostConfig}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
|
||||
COPYONLY)
|
||||
|
||||
@@ -30,12 +30,12 @@ function(copy_resources_to_build_tree _target)
|
||||
endif()
|
||||
|
||||
get_target_property(_path ${_target} LOCATION)
|
||||
get_filename_component(_path "${_path}" PATH)
|
||||
get_filename_component(_path "${_path}" DIRECTORY)
|
||||
|
||||
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
get_target_property(_path${_config} ${_target} LOCATION_${_config})
|
||||
get_filename_component(_path${_config} "${_path${_config}}" PATH)
|
||||
get_filename_component(_path${_config} "${_path${_config}}" DIRECTORY)
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
|
||||
@@ -6,6 +6,7 @@ foreach( mylibfolder
|
||||
bvh
|
||||
debug
|
||||
embedded_resources
|
||||
emesary
|
||||
ephemeris
|
||||
io
|
||||
magvar
|
||||
|
||||
@@ -28,42 +28,39 @@ namespace simgear
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class CanvasMgr:
|
||||
public PropertyBasedMgr
|
||||
{
|
||||
public:
|
||||
class CanvasMgr : public PropertyBasedMgr
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param node Root node of branch used to control canvasses
|
||||
*/
|
||||
CanvasMgr(SGPropertyNode_ptr node);
|
||||
|
||||
/**
|
||||
* @param node Root node of branch used to control canvasses
|
||||
*/
|
||||
CanvasMgr(SGPropertyNode_ptr node);
|
||||
/**
|
||||
* Create a new canvas
|
||||
*
|
||||
* @param name Name of the new canvas
|
||||
*/
|
||||
CanvasPtr createCanvas(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* Create a new canvas
|
||||
*
|
||||
* @param name Name of the new canvas
|
||||
*/
|
||||
CanvasPtr createCanvas(const std::string& name = "");
|
||||
/**
|
||||
* Get ::Canvas by index
|
||||
*
|
||||
* @param index Index of texture node in /canvas/by-index/
|
||||
*/
|
||||
CanvasPtr getCanvas(size_t index) const;
|
||||
|
||||
/**
|
||||
* Get ::Canvas by index
|
||||
*
|
||||
* @param index Index of texture node in /canvas/by-index/
|
||||
*/
|
||||
CanvasPtr getCanvas(size_t index) const;
|
||||
/**
|
||||
* Get ::Canvas by name
|
||||
*
|
||||
* @param name Value of child node "name" in
|
||||
* /canvas/by-index/texture[i]/name
|
||||
*/
|
||||
CanvasPtr getCanvas(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* Get ::Canvas by name
|
||||
*
|
||||
* @param name Value of child node "name" in
|
||||
* /canvas/by-index/texture[i]/name
|
||||
*/
|
||||
CanvasPtr getCanvas(const std::string& name) const;
|
||||
|
||||
protected:
|
||||
|
||||
void elementCreated(PropertyBasedElementPtr element) override;
|
||||
};
|
||||
protected:
|
||||
void elementCreated(PropertyBasedElementPtr element) override;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -425,8 +425,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
m_startupLogging = on;
|
||||
m_startupEntries.clear();
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
m_startupLogging = on;
|
||||
m_startupEntries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
@@ -438,13 +441,14 @@ public:
|
||||
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_startupLogging) {
|
||||
// save to the startup list for not-yet-added callbacks to
|
||||
// pull down on startup
|
||||
m_startupEntries.push_back(entry);
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
if (m_startupLogging) {
|
||||
// save to the startup list for not-yet-added callbacks to
|
||||
// pull down on startup
|
||||
m_startupEntries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// submit to each installed callback in turn
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
@@ -455,14 +459,16 @@ public:
|
||||
|
||||
bool stop()
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
if (!m_isRunning) {
|
||||
return false;
|
||||
}
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
if (!m_isRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// log a special marker value, which will cause the thread to wakeup,
|
||||
// and then exit
|
||||
log(SG_NONE, SG_ALERT, "done", -1, "");
|
||||
// log a special marker value, which will cause the thread to wakeup,
|
||||
// and then exit
|
||||
log(SG_NONE, SG_ALERT, "done", -1, "");
|
||||
}
|
||||
join();
|
||||
|
||||
m_isRunning = false;
|
||||
|
||||
33
simgear/emesary/CMakeLists.txt
Normal file
33
simgear/emesary/CMakeLists.txt
Normal 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
simgear/emesary/Emesary.cxx
Normal file
31
simgear/emesary/Emesary.cxx
Normal 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
simgear/emesary/Emesary.hxx
Normal file
48
simgear/emesary/Emesary.hxx
Normal 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
simgear/emesary/INotification.hxx
Normal file
54
simgear/emesary/INotification.hxx
Normal 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
simgear/emesary/IReceiver.hxx
Normal file
47
simgear/emesary/IReceiver.hxx
Normal 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
simgear/emesary/ITransmitter.hxx
Normal file
52
simgear/emesary/ITransmitter.hxx
Normal 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
simgear/emesary/ReceiptStatus.hxx
Normal file
54
simgear/emesary/ReceiptStatus.hxx
Normal 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
simgear/emesary/Transmitter.hxx
Normal file
203
simgear/emesary/Transmitter.hxx
Normal 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
simgear/emesary/notifications.hxx
Normal file
68
simgear/emesary/notifications.hxx
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef NOTIFICATIONS_hxx
|
||||
#define NOTIFICATIONS_hxx
|
||||
/*---------------------------------------------------------------------------
|
||||
*
|
||||
* Title : Emesary - class based inter-object communication
|
||||
*
|
||||
* File Type : Implementation File
|
||||
*
|
||||
* Description : simgear notifications
|
||||
*
|
||||
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
|
||||
*
|
||||
* Author : Richard Harrison (richard@zaretto.com)
|
||||
*
|
||||
* Creation Date : 18 March 2002, rewrite 2017
|
||||
*
|
||||
* Version : $Header: $
|
||||
*
|
||||
* Copyright <20> 2002 - 2017 Richard Harrison All Rights Reserved.
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "INotification.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace Notifications
|
||||
{
|
||||
class MainLoopNotification : public simgear::Emesary::INotification
|
||||
{
|
||||
public:
|
||||
enum Type { Started, Stopped, Begin, End };
|
||||
MainLoopNotification(Type v) : _type(v) {}
|
||||
|
||||
virtual Type GetValue() { return _type; }
|
||||
virtual const char *GetType() { return "MainLoop"; }
|
||||
|
||||
protected:
|
||||
Type _type;
|
||||
};
|
||||
|
||||
class NasalGarbageCollectionConfigurationNotification : public simgear::Emesary::INotification
|
||||
{
|
||||
public:
|
||||
NasalGarbageCollectionConfigurationNotification(bool canWait, bool active) : CanWait(canWait), Active(active) {}
|
||||
|
||||
virtual bool GetCanWait() { return CanWait; }
|
||||
virtual bool GetActive() { return Active; }
|
||||
virtual const char *GetType() { return "NasalGarbageCollectionConfiguration"; }
|
||||
virtual bool SetWait(bool wait) {
|
||||
if (wait == CanWait)
|
||||
return false;
|
||||
CanWait = wait;
|
||||
return true;
|
||||
}
|
||||
virtual bool SetActive(bool active) {
|
||||
if (active == Active)
|
||||
return false;
|
||||
Active = active;
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
bool CanWait;
|
||||
bool Active;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
126
simgear/emesary/test_emesary.cxx
Normal file
126
simgear/emesary/test_emesary.cxx
Normal 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;
|
||||
}
|
||||
@@ -19,6 +19,7 @@ set(HEADERS
|
||||
tabbed_values.hxx
|
||||
texcoord.hxx
|
||||
test_macros.hxx
|
||||
lru_cache.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
|
||||
@@ -30,11 +30,16 @@
|
||||
// code can register another one immediately without worrying about
|
||||
// timer aliasing.
|
||||
|
||||
class SGInterpolator : public SGSubsystem {
|
||||
class SGInterpolator : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
SGInterpolator() { _list = 0; }
|
||||
virtual void init() {}
|
||||
virtual void update(double delta_time_sec);
|
||||
|
||||
// Subsystem API.
|
||||
void update(double delta_time_sec) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "interpolator"; }
|
||||
|
||||
// Simple method that interpolates a double property value from
|
||||
// its current value (default of zero) to the specified target
|
||||
|
||||
173
simgear/misc/lru_cache.hxx
Normal file
173
simgear/misc/lru_cache.hxx
Normal 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_ */
|
||||
@@ -157,7 +157,7 @@ static void initContext(naContext c)
|
||||
c->error[0] = 0;
|
||||
c->userData = 0;
|
||||
}
|
||||
|
||||
#define BASE_SIZE 256000
|
||||
static void initGlobals()
|
||||
{
|
||||
int i;
|
||||
@@ -168,10 +168,10 @@ static void initGlobals()
|
||||
globals->sem = naNewSem();
|
||||
globals->lock = naNewLock();
|
||||
|
||||
globals->allocCount = 256; // reasonable starting value
|
||||
globals->allocCount = BASE_SIZE; // reasonable starting value
|
||||
for(i=0; i<NUM_NASAL_TYPES; i++)
|
||||
naGC_init(&(globals->pools[i]), i);
|
||||
globals->deadsz = 256;
|
||||
globals->deadsz = BASE_SIZE;
|
||||
globals->ndead = 0;
|
||||
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
|
||||
|
||||
@@ -833,9 +833,13 @@ naRef naGetSourceFile(naContext ctx, int frame)
|
||||
{
|
||||
naRef f;
|
||||
frame = findFrame(ctx, &ctx, frame);
|
||||
f = ctx->fStack[frame].func;
|
||||
f = PTR(f).func->code;
|
||||
return PTR(f).code->srcFile;
|
||||
if (frame >= 0) {
|
||||
f = ctx->fStack[frame].func;
|
||||
f = PTR(f).func->code;
|
||||
if (!IS_NIL(f) && PTR(f).code)
|
||||
return PTR(f).code->srcFile;
|
||||
}
|
||||
return naNil();
|
||||
}
|
||||
|
||||
char* naGetError(naContext ctx)
|
||||
|
||||
@@ -5,6 +5,7 @@ set(HEADERS
|
||||
Ghost.hxx
|
||||
NasalCallContext.hxx
|
||||
NasalContext.hxx
|
||||
NasalEmesaryInterface.hxx
|
||||
NasalHash.hxx
|
||||
NasalMe.hxx
|
||||
NasalMethodHolder.hxx
|
||||
|
||||
123
simgear/nasal/cppbind/NasalEmesaryInterface.hxx
Normal file
123
simgear/nasal/cppbind/NasalEmesaryInterface.hxx
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef NASALEMESARYINTERFACE_INCLUDED
|
||||
#define NASALEMESARYINTERFACE_INCLUDED 1
|
||||
// Nasal Emesary receipient interface.
|
||||
//
|
||||
// Copyright (C) 2019 Richard Harrison rjh@zaretto.com
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include <simgear/emesary/Emesary.hxx>
|
||||
#include <simgear/emesary/notifications.hxx>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <simgear/threads/SGThread.hxx>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
extern"C" {
|
||||
extern int GCglobalAlloc();
|
||||
extern int naGarbageCollect();
|
||||
// these are used by the detailed debug in the Nasal GC.
|
||||
SGTimeStamp global_timestamp;
|
||||
void global_stamp() {
|
||||
global_timestamp.stamp();
|
||||
}
|
||||
extern int global_elapsedUSec()
|
||||
{
|
||||
return global_timestamp.elapsedUSec();
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadedGarbageCollector : public SGExclusiveThread {
|
||||
public:
|
||||
ThreadedGarbageCollector() : SGExclusiveThread() {}
|
||||
virtual ~ThreadedGarbageCollector() {}
|
||||
|
||||
virtual int process(){
|
||||
return naGarbageCollect();
|
||||
}
|
||||
};
|
||||
|
||||
class NasalMainLoopRecipient : public simgear::Emesary::IReceiver {
|
||||
public:
|
||||
NasalMainLoopRecipient() : receiveCount(0) {
|
||||
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
|
||||
SG_LOG(SG_NASAL, SG_INFO, "NasalMainLoopRecipient created");
|
||||
}
|
||||
virtual ~NasalMainLoopRecipient() {
|
||||
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this);
|
||||
}
|
||||
|
||||
std::atomic<int> receiveCount;
|
||||
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
|
||||
{
|
||||
|
||||
simgear::Notifications::MainLoopNotification *mln = dynamic_cast<simgear::Notifications::MainLoopNotification *>(&n);
|
||||
|
||||
if (mln) {
|
||||
switch (mln->GetValue()) {
|
||||
case simgear::Notifications::MainLoopNotification::Type::Begin:
|
||||
if (gct.is_running()) {
|
||||
if (Active && CanWait)
|
||||
gct.awaitCompletion();
|
||||
else
|
||||
gct.clearAwaitCompletionTime();
|
||||
}
|
||||
break;
|
||||
case simgear::Notifications::MainLoopNotification::Type::End:
|
||||
if (Active) {
|
||||
if (gct.is_running())
|
||||
gct.release();
|
||||
}
|
||||
break;
|
||||
case simgear::Notifications::MainLoopNotification::Type::Started:
|
||||
gct.ensure_running();
|
||||
break;
|
||||
case simgear::Notifications::MainLoopNotification::Type::Stopped:
|
||||
gct.terminate();
|
||||
break;
|
||||
}
|
||||
return simgear::Emesary::ReceiptStatusOK;
|
||||
}
|
||||
|
||||
auto *gccn = dynamic_cast<simgear::Notifications::NasalGarbageCollectionConfigurationNotification *>(&n);
|
||||
if (gccn) {
|
||||
CanWait = gccn->GetCanWait();
|
||||
Active = gccn->GetActive();
|
||||
return simgear::Emesary::ReceiptStatusOK;
|
||||
}
|
||||
return simgear::Emesary::ReceiptStatusNotProcessed;
|
||||
}
|
||||
protected:
|
||||
bool CanWait;
|
||||
bool Active;
|
||||
ThreadedGarbageCollector gct;
|
||||
};
|
||||
|
||||
} // namespace nasal
|
||||
#endif
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "to_nasal_helper.hxx"
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalEmesaryInterface.hxx>
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
@@ -27,6 +28,19 @@
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
// create single instance of the main loop recipient for Nasal - this will self register at the
|
||||
// global transmitter - and that's all that is needed to link up the background GC to the main
|
||||
// loop in FG that will send out the MainLoop notifications.
|
||||
//class NasalMainLoopRecipientSingleton : public simgear::Singleton<NasalMainLoopRecipient>
|
||||
//{
|
||||
//public:
|
||||
// NasalMainLoopRecipientSingleton()
|
||||
// {
|
||||
// }
|
||||
// virtual ~NasalMainLoopRecipientSingleton() {}
|
||||
//};
|
||||
NasalMainLoopRecipient mrl;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
naRef to_nasal_helper(naContext c, const std::string& str)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "nasal.h"
|
||||
#include "data.h"
|
||||
#include "code.h"
|
||||
|
||||
#define MIN_BLOCK_SIZE 32
|
||||
|
||||
static void reap(struct naPool* p);
|
||||
@@ -12,14 +11,17 @@ struct Block {
|
||||
char* block;
|
||||
struct Block* next;
|
||||
};
|
||||
|
||||
// Must be called with the giant exclusive lock!
|
||||
static void freeDead()
|
||||
extern void global_stamp();
|
||||
extern int global_elapsedUSec();
|
||||
|
||||
static int freeDead()
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<globals->ndead; i++)
|
||||
naFree(globals->deadBlocks[i]);
|
||||
globals->ndead = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void marktemps(struct Context* c)
|
||||
@@ -31,50 +33,127 @@ static void marktemps(struct Context* c)
|
||||
mark(r);
|
||||
}
|
||||
}
|
||||
|
||||
//#define GC_DETAIL_DEBUG
|
||||
static int __elements_visited = 0;
|
||||
static int gc_busy=0;
|
||||
// Must be called with the big lock!
|
||||
static void garbageCollect()
|
||||
{
|
||||
if (gc_busy)
|
||||
return;
|
||||
gc_busy = 1;
|
||||
int i;
|
||||
struct Context* c;
|
||||
globals->allocCount = 0;
|
||||
c = globals->allContexts;
|
||||
while(c) {
|
||||
for(i=0; i<NUM_NASAL_TYPES; i++)
|
||||
|
||||
#if GC_DETAIL_DEBUG
|
||||
int ctxc = 0;
|
||||
__elements_visited = 0;
|
||||
int st = global_elapsedUSec();
|
||||
int et = 0;
|
||||
int stel = __elements_visited;
|
||||
int eel = 0;
|
||||
#endif
|
||||
|
||||
c = globals->allContexts;
|
||||
while (c) {
|
||||
#if GC_DETAIL_DEBUG
|
||||
ctxc++;
|
||||
#endif
|
||||
for (i = 0; i < NUM_NASAL_TYPES; i++)
|
||||
c->nfree[i] = 0;
|
||||
for(i=0; i < c->fTop; i++) {
|
||||
for (i = 0; i < c->fTop; i++) {
|
||||
mark(c->fStack[i].func);
|
||||
mark(c->fStack[i].locals);
|
||||
}
|
||||
for(i=0; i < c->opTop; i++)
|
||||
for (i = 0; i < c->opTop; i++)
|
||||
mark(c->opStack[i]);
|
||||
mark(c->dieArg);
|
||||
marktemps(c);
|
||||
c = c->nextAll;
|
||||
}
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
printf("--> garbageCollect(#e%-5d): %-4d ", eel, et);
|
||||
#endif
|
||||
|
||||
mark(globals->save);
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
printf("s(%5d) %-5d ", eel, et);
|
||||
#endif
|
||||
|
||||
mark(globals->save_hash);
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
printf("h(%5d) %-5d ", eel, et);
|
||||
#endif
|
||||
|
||||
|
||||
mark(globals->symbols);
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
//printf("sy(%5d) %-4d ", eel, et);
|
||||
#endif
|
||||
|
||||
mark(globals->meRef);
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
//printf("me(%5d) %-5d ", eel, et);
|
||||
#endif
|
||||
|
||||
mark(globals->argRef);
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
//printf("ar(%5d) %-5d ", eel, et);
|
||||
#endif
|
||||
|
||||
mark(globals->parentsRef);
|
||||
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
eel = __elements_visited - stel; stel = __elements_visited;
|
||||
#endif
|
||||
//printf(" ev[%3d] %-5d", eel, et);
|
||||
// Finally collect all the freed objects
|
||||
for(i=0; i<NUM_NASAL_TYPES; i++)
|
||||
for (i = 0; i < NUM_NASAL_TYPES; i++) {
|
||||
reap(&(globals->pools[i]));
|
||||
|
||||
}
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
printf(" >> reap %-5d", et);
|
||||
#endif
|
||||
// Make enough space for the dead blocks we need to free during
|
||||
// execution. This works out to 1 spot for every 2 live objects,
|
||||
// which should be limit the number of bottleneck operations
|
||||
// without imposing an undue burden of extra "freeable" memory.
|
||||
if(globals->deadsz < globals->allocCount) {
|
||||
globals->deadsz = globals->allocCount;
|
||||
if(globals->deadsz < 256) globals->deadsz = 256;
|
||||
if(globals->deadsz < 256000) globals->deadsz = 256000;
|
||||
naFree(globals->deadBlocks);
|
||||
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
|
||||
}
|
||||
globals->needGC = 0;
|
||||
#if GC_DETAIL_DEBUG
|
||||
et = global_elapsedUSec() - st;
|
||||
st = global_elapsedUSec();
|
||||
printf(">> %-5d ", et);
|
||||
#endif
|
||||
gc_busy = 0;
|
||||
}
|
||||
|
||||
void naModLock()
|
||||
@@ -104,6 +183,7 @@ void naModUnlock()
|
||||
// you think about it).
|
||||
static void bottleneck()
|
||||
{
|
||||
global_stamp();
|
||||
struct Globals* g = globals;
|
||||
g->bottleneck = 1;
|
||||
while(g->bottleneck && g->waitCount < g->nThreads - 1) {
|
||||
@@ -111,12 +191,39 @@ static void bottleneck()
|
||||
UNLOCK(); naSemDown(g->sem); LOCK();
|
||||
g->waitCount--;
|
||||
}
|
||||
#if GC_DETAIL_DEBUG
|
||||
printf("GC: wait %2d ", global_elapsedUSec());
|
||||
#endif
|
||||
if(g->waitCount >= g->nThreads - 1) {
|
||||
freeDead();
|
||||
if(g->needGC) garbageCollect();
|
||||
int fd = freeDead();
|
||||
#if GC_DETAIL_DEBUG
|
||||
printf("--> freedead (%5d) : %5d", fd, global_elapsedUSec());
|
||||
#endif
|
||||
if(g->needGC)
|
||||
garbageCollect();
|
||||
if(g->waitCount) naSemUp(g->sem, g->waitCount);
|
||||
g->bottleneck = 0;
|
||||
}
|
||||
#if GC_DETAIL_DEBUG
|
||||
printf(" :: finished: %5d\n", global_elapsedUSec());
|
||||
#endif
|
||||
}
|
||||
|
||||
static void bottleneckFreeDead()
|
||||
{
|
||||
global_stamp();
|
||||
struct Globals* g = globals;
|
||||
g->bottleneck = 1;
|
||||
while (g->bottleneck && g->waitCount < g->nThreads - 1) {
|
||||
g->waitCount++;
|
||||
UNLOCK(); naSemDown(g->sem); LOCK();
|
||||
g->waitCount--;
|
||||
}
|
||||
if (g->waitCount >= g->nThreads - 1) {
|
||||
freeDead();
|
||||
if (g->waitCount) naSemUp(g->sem, g->waitCount);
|
||||
g->bottleneck = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void naGC()
|
||||
@@ -127,6 +234,29 @@ void naGC()
|
||||
UNLOCK();
|
||||
naCheckBottleneck();
|
||||
}
|
||||
int naGarbageCollect()
|
||||
{
|
||||
int rv = 1;
|
||||
LOCK();
|
||||
//
|
||||
// The number here is again based on observation - if this is too low then the inline GC will be used
|
||||
// which is fine occasionally.
|
||||
// So what we're doing by checking the global alloc is to see if GC is likely required during the next frame and if
|
||||
// so we pre-empt this by doing it now.
|
||||
// GC can typically take between 5ms and 50ms (F-15, FG1000 PFD & MFD, Advanced weather) - but usually it is completed
|
||||
// prior to the start of the next frame.
|
||||
|
||||
globals->needGC = nasal_globals->allocCount < 23000;
|
||||
if (globals->needGC)
|
||||
bottleneck();
|
||||
else {
|
||||
bottleneckFreeDead();
|
||||
rv = 0;
|
||||
}
|
||||
UNLOCK();
|
||||
naCheckBottleneck();
|
||||
return rv;
|
||||
}
|
||||
|
||||
void naCheckBottleneck()
|
||||
{
|
||||
@@ -207,7 +337,9 @@ static int poolsize(struct naPool* p)
|
||||
while(b) { total += b->size; b = b->next; }
|
||||
return total;
|
||||
}
|
||||
|
||||
int GCglobalAlloc() {
|
||||
return globals->allocCount;
|
||||
}
|
||||
struct naObj** naGC_get(struct naPool* p, int n, int* nout)
|
||||
{
|
||||
struct naObj** result;
|
||||
@@ -215,6 +347,9 @@ struct naObj** naGC_get(struct naPool* p, int n, int* nout)
|
||||
LOCK();
|
||||
while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) {
|
||||
globals->needGC = 1;
|
||||
#if GC_DETAIL_DEBUG
|
||||
printf("++");
|
||||
#endif
|
||||
bottleneck();
|
||||
}
|
||||
if(p->nfree == 0)
|
||||
@@ -248,7 +383,7 @@ static void mark(naRef r)
|
||||
|
||||
if(PTR(r).obj->mark == 1)
|
||||
return;
|
||||
|
||||
__elements_visited++;
|
||||
PTR(r).obj->mark = 1;
|
||||
switch(PTR(r).obj->type) {
|
||||
case T_VEC: markvec(r); break;
|
||||
@@ -306,11 +441,14 @@ static void reap(struct naPool* p)
|
||||
|
||||
// Allocate more if necessary (try to keep 25-50% of the objects
|
||||
// available)
|
||||
if(p->nfree < total/4) {
|
||||
// This was changed (2019.2) to allocate in larger blocks
|
||||
// previously it used total/4 and used/2 now we
|
||||
// use total/2 and used / 1
|
||||
if (p->nfree < total / 2) {
|
||||
int used = total - p->nfree;
|
||||
int avail = total - used;
|
||||
int need = used/2 - avail;
|
||||
if(need > 0)
|
||||
int need = used / 1 - avail;
|
||||
if (need > 0)
|
||||
newBlock(p, need);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,25 +295,36 @@ static naRef f_die(naContext c, naRef me, int argc, naRef* args)
|
||||
return naNil(); // never executes
|
||||
}
|
||||
|
||||
// Wrapper around vsnprintf, iteratively increasing the buffer size
|
||||
// until it fits. Returned buffer should be freed by the caller.
|
||||
// Wrapper around vsnprintf that will allocate the required size
|
||||
// by calling vsnprintf with NULL and 0 - and vsnsprintf will measure the
|
||||
// required amount of characters which we then allocate and return
|
||||
// Returned buffer should be freed by the caller.
|
||||
static char* dosprintf(char* f, ...)
|
||||
{
|
||||
char* buf;
|
||||
|
||||
va_list va;
|
||||
int olen, len = 16;
|
||||
while(1) {
|
||||
buf = naAlloc(len);
|
||||
va_start(va, f);
|
||||
olen = vsnprintf(buf, len, f, va);
|
||||
if(olen >= 0 && olen < len) {
|
||||
va_end(va);
|
||||
return buf;
|
||||
}
|
||||
va_end(va);
|
||||
naFree(buf);
|
||||
len *= 2;
|
||||
va_start(va, f);
|
||||
|
||||
int len = vsnprintf(0, 0, f, va);
|
||||
va_end(va);
|
||||
|
||||
if (len <= 0) {
|
||||
buf = (char *) naAlloc(2);
|
||||
*buf = 0;
|
||||
}
|
||||
else {
|
||||
len++;// allow for terminating null
|
||||
buf = (char *) naAlloc(len);
|
||||
|
||||
va_list va;
|
||||
va_start(va, f);
|
||||
|
||||
len = vsnprintf(buf, len, f, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Inspects a printf format string f, and finds the next "%..." format
|
||||
|
||||
@@ -29,74 +29,71 @@
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class PropertyBasedMgr:
|
||||
public SGSubsystem,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
void init() override;
|
||||
void shutdown() override;
|
||||
class PropertyBasedMgr : public SGSubsystem,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void shutdown() override;
|
||||
void update(double delta_time_sec) override;
|
||||
|
||||
void update (double delta_time_sec) override;
|
||||
/**
|
||||
* Create a new PropertyBasedElement
|
||||
*
|
||||
* @param name Name of the new element
|
||||
*/
|
||||
PropertyBasedElementPtr createElement(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* Create a new PropertyBasedElement
|
||||
*
|
||||
* @param name Name of the new element
|
||||
*/
|
||||
PropertyBasedElementPtr createElement(const std::string& name = "");
|
||||
/**
|
||||
* Get an existing PropertyBasedElement by its index
|
||||
*
|
||||
* @param index Index of element node in property tree
|
||||
*/
|
||||
PropertyBasedElementPtr getElement(size_t index) const;
|
||||
|
||||
/**
|
||||
* Get an existing PropertyBasedElement by its index
|
||||
*
|
||||
* @param index Index of element node in property tree
|
||||
*/
|
||||
PropertyBasedElementPtr getElement(size_t index) const;
|
||||
/**
|
||||
* Get an existing PropertyBasedElement by its name
|
||||
*
|
||||
* @param name Name (value of child node "name" will be matched)
|
||||
*/
|
||||
PropertyBasedElementPtr getElement(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* Get an existing PropertyBasedElement by its name
|
||||
*
|
||||
* @param name Name (value of child node "name" will be matched)
|
||||
*/
|
||||
PropertyBasedElementPtr getElement(const std::string& name) const;
|
||||
virtual const SGPropertyNode* getPropertyRoot() const;
|
||||
|
||||
virtual const SGPropertyNode* getPropertyRoot() const;
|
||||
protected:
|
||||
typedef boost::function<PropertyBasedElementPtr(SGPropertyNode*)>
|
||||
ElementFactory;
|
||||
|
||||
protected:
|
||||
/** Branch in the property tree for this property managed subsystem */
|
||||
SGPropertyNode_ptr _props;
|
||||
|
||||
typedef boost::function<PropertyBasedElementPtr(SGPropertyNode*)>
|
||||
ElementFactory;
|
||||
/** Property name of managed elements */
|
||||
const std::string _name_elements;
|
||||
|
||||
/** Branch in the property tree for this property managed subsystem */
|
||||
SGPropertyNode_ptr _props;
|
||||
/** The actually managed elements */
|
||||
std::vector<PropertyBasedElementPtr> _elements;
|
||||
|
||||
/** Property name of managed elements */
|
||||
const std::string _name_elements;
|
||||
/** Function object which creates a new element */
|
||||
ElementFactory _element_factory;
|
||||
|
||||
/** The actually managed elements */
|
||||
std::vector<PropertyBasedElementPtr> _elements;
|
||||
/**
|
||||
* @param props Root node of property branch used for controlling
|
||||
* this subsystem
|
||||
* @param name_elements The name of the nodes for the managed elements
|
||||
*/
|
||||
PropertyBasedMgr( SGPropertyNode_ptr props,
|
||||
const std::string& name_elements,
|
||||
ElementFactory element_factory );
|
||||
virtual ~PropertyBasedMgr() = 0;
|
||||
|
||||
/** Function object which creates a new element */
|
||||
ElementFactory _element_factory;
|
||||
|
||||
/**
|
||||
* @param props Root node of property branch used for controlling
|
||||
* this subsystem
|
||||
* @param name_elements The name of the nodes for the managed elements
|
||||
*/
|
||||
PropertyBasedMgr( SGPropertyNode_ptr props,
|
||||
const std::string& name_elements,
|
||||
ElementFactory element_factory );
|
||||
virtual ~PropertyBasedMgr() = 0;
|
||||
|
||||
void childAdded( SGPropertyNode * parent,
|
||||
void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child ) override;
|
||||
void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child ) override;
|
||||
void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child ) override;
|
||||
|
||||
virtual void elementCreated(PropertyBasedElementPtr element) {}
|
||||
|
||||
};
|
||||
virtual void elementCreated(PropertyBasedElementPtr element) {}
|
||||
};
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
@@ -28,119 +28,113 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace simgear
|
||||
namespace simgear {
|
||||
|
||||
/**
|
||||
* Subsystem that manages interpolation of properties.
|
||||
*
|
||||
* By default the numeric values of the properties are interpolated. For
|
||||
* example, for strings this is probably not the wanted behavior. For this
|
||||
* adapter classes can be registered to allow providing specific
|
||||
* interpolations for certain types of properties. Using the type "color",
|
||||
* provided by ColorInterpolator, strings containing %CSS colors can also be
|
||||
* interpolated.
|
||||
*
|
||||
* Additionally different functions can be used for easing of the animation.
|
||||
* By default "linear" (constant animation speed) and "swing" (smooth
|
||||
* acceleration and deceleration) are available.
|
||||
*/
|
||||
class PropertyInterpolationMgr : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
typedef PropertyInterpolator* (*InterpolatorFactory)();
|
||||
|
||||
/**
|
||||
* Subsystem that manages interpolation of properties.
|
||||
*
|
||||
* By default the numeric values of the properties are interpolated. For
|
||||
* example, for strings this is probably not the wanted behavior. For this
|
||||
* adapter classes can be registered to allow providing specific
|
||||
* interpolations for certain types of properties. Using the type "color",
|
||||
* provided by ColorInterpolator, strings containing %CSS colors can also be
|
||||
* interpolated.
|
||||
*
|
||||
* Additionally different functions can be used for easing of the animation.
|
||||
* By default "linear" (constant animation speed) and "swing" (smooth
|
||||
* acceleration and deceleration) are available.
|
||||
*/
|
||||
class PropertyInterpolationMgr:
|
||||
public SGSubsystem
|
||||
{
|
||||
public:
|
||||
typedef PropertyInterpolator* (*InterpolatorFactory)();
|
||||
PropertyInterpolationMgr();
|
||||
|
||||
PropertyInterpolationMgr();
|
||||
// Subsystem API.
|
||||
void update(double dt) override;
|
||||
|
||||
/**
|
||||
* Update all active interpolators.
|
||||
*/
|
||||
void update(double dt);
|
||||
/**
|
||||
* Create a new property interpolator.
|
||||
*
|
||||
* @note To actually use it the interpolator needs to be attached to a
|
||||
* property using PropertyInterpolationMgr::interpolate.
|
||||
*
|
||||
* @param type Type of animation ("numeric", "color", etc.)
|
||||
* @param target Property containing target value
|
||||
* @param duration Duration if the animation (in seconds)
|
||||
* @param easing Type of easing ("linear", "swing", etc.)
|
||||
*/
|
||||
PropertyInterpolator*
|
||||
createInterpolator(const std::string& type,
|
||||
const SGPropertyNode& target,
|
||||
double duration,
|
||||
const std::string& easing);
|
||||
|
||||
/**
|
||||
* Create a new property interpolator.
|
||||
*
|
||||
* @note To actually use it the interpolator needs to be attached to a
|
||||
* property using PropertyInterpolationMgr::interpolate.
|
||||
*
|
||||
* @param type Type of animation ("numeric", "color", etc.)
|
||||
* @param target Property containing target value
|
||||
* @param duration Duration if the animation (in seconds)
|
||||
* @param easing Type of easing ("linear", "swing", etc.)
|
||||
*/
|
||||
PropertyInterpolator*
|
||||
createInterpolator( const std::string& type,
|
||||
const SGPropertyNode& target,
|
||||
double duration,
|
||||
const std::string& easing );
|
||||
/**
|
||||
* Add animation of the given property from its current value to the
|
||||
* target value of the interpolator. If no interpolator is given any
|
||||
* existing animation of the given property is aborted.
|
||||
*
|
||||
* @param prop Property to be interpolated
|
||||
* @param interp Interpolator used for interpolation
|
||||
*/
|
||||
bool interpolate(SGPropertyNode* prop,
|
||||
PropertyInterpolatorRef interp = 0);
|
||||
|
||||
/**
|
||||
* Add animation of the given property from its current value to the
|
||||
* target value of the interpolator. If no interpolator is given any
|
||||
* existing animation of the given property is aborted.
|
||||
*
|
||||
* @param prop Property to be interpolated
|
||||
* @param interp Interpolator used for interpolation
|
||||
*/
|
||||
bool interpolate( SGPropertyNode* prop,
|
||||
PropertyInterpolatorRef interp = 0 );
|
||||
bool interpolate(SGPropertyNode* prop,
|
||||
const std::string& type,
|
||||
const SGPropertyNode& target,
|
||||
double duration,
|
||||
const std::string& easing);
|
||||
|
||||
bool interpolate( SGPropertyNode* prop,
|
||||
const std::string& type,
|
||||
const SGPropertyNode& target,
|
||||
double duration,
|
||||
const std::string& easing );
|
||||
bool interpolate(SGPropertyNode* prop,
|
||||
const std::string& type,
|
||||
const PropertyList& values,
|
||||
const double_list& deltas,
|
||||
const std::string& easing);
|
||||
|
||||
bool interpolate( SGPropertyNode* prop,
|
||||
const std::string& type,
|
||||
const PropertyList& values,
|
||||
const double_list& deltas,
|
||||
const std::string& easing );
|
||||
|
||||
/**
|
||||
* Register factory for interpolation type.
|
||||
*/
|
||||
void addInterpolatorFactory( const std::string& type,
|
||||
InterpolatorFactory factory );
|
||||
template<class T>
|
||||
void addInterpolatorFactory(const std::string& type)
|
||||
{
|
||||
addInterpolatorFactory
|
||||
(
|
||||
type,
|
||||
&simgear::make_new_derived<PropertyInterpolator, T>
|
||||
/**
|
||||
* Register factory for interpolation type.
|
||||
*/
|
||||
void addInterpolatorFactory(const std::string& type,
|
||||
InterpolatorFactory factory);
|
||||
template <class T>
|
||||
void addInterpolatorFactory(const std::string& type)
|
||||
{
|
||||
addInterpolatorFactory(
|
||||
type,
|
||||
&simgear::make_new_derived<PropertyInterpolator, T>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register easing function.
|
||||
*/
|
||||
void addEasingFunction(const std::string& type, easing_func_t func);
|
||||
/**
|
||||
* Register easing function.
|
||||
*/
|
||||
void addEasingFunction(const std::string& type, easing_func_t func);
|
||||
|
||||
/**
|
||||
* Set property containing real time delta (not sim time)
|
||||
*
|
||||
* TODO better pass both deltas to all update methods...
|
||||
*/
|
||||
void setRealtimeProperty(SGPropertyNode* node);
|
||||
/**
|
||||
* Set property containing real time delta (not sim time)
|
||||
*
|
||||
* TODO better pass both deltas to all update methods...
|
||||
*/
|
||||
void setRealtimeProperty(SGPropertyNode* node);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
typedef std::map<std::string, InterpolatorFactory> InterpolatorFactoryMap;
|
||||
typedef std::map<std::string, easing_func_t> EasingFunctionMap;
|
||||
typedef std::pair<SGPropertyNode*,
|
||||
PropertyInterpolatorRef> PropertyInterpolatorPair;
|
||||
typedef std::list<PropertyInterpolatorPair> InterpolatorList;
|
||||
|
||||
typedef std::map<std::string, InterpolatorFactory> InterpolatorFactoryMap;
|
||||
typedef std::map<std::string, easing_func_t> EasingFunctionMap;
|
||||
typedef std::pair< SGPropertyNode*,
|
||||
PropertyInterpolatorRef > PropertyInterpolatorPair;
|
||||
typedef std::list<PropertyInterpolatorPair> InterpolatorList;
|
||||
struct PredicateIsSameProp;
|
||||
|
||||
struct PredicateIsSameProp;
|
||||
InterpolatorFactoryMap _interpolator_factories;
|
||||
EasingFunctionMap _easing_functions;
|
||||
InterpolatorList _interpolators;
|
||||
|
||||
InterpolatorFactoryMap _interpolator_factories;
|
||||
EasingFunctionMap _easing_functions;
|
||||
InterpolatorList _interpolators;
|
||||
|
||||
SGPropertyNode_ptr _rt_prop;
|
||||
};
|
||||
SGPropertyNode_ptr _rt_prop;
|
||||
};
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
@@ -1,79 +1,86 @@
|
||||
#include <simgear/scene/dem/SGDem.hxx>
|
||||
#include <simgear/scene/dem/SGDemSession.hxx>
|
||||
|
||||
SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root )
|
||||
SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root)
|
||||
{
|
||||
setOffsets( SGDem::longitudeDegToOffset((double)mnLon),
|
||||
SGDem::latitudeDegToOffset((double)mnLat),
|
||||
SGDem::longitudeDegToOffset((double)mxLon),
|
||||
SGDem::latitudeDegToOffset((double)mxLat) );
|
||||
setOffsets(SGDem::longitudeDegToOffset((double)mnLon),
|
||||
SGDem::latitudeDegToOffset((double)mnLat),
|
||||
SGDem::longitudeDegToOffset((double)mxLon),
|
||||
SGDem::latitudeDegToOffset((double)mxLat));
|
||||
|
||||
pDemRoot = root;
|
||||
lvlIndex = idx;
|
||||
lvlWidth = lvlW;
|
||||
pDemRoot = root;
|
||||
lvlIndex = idx;
|
||||
lvlWidth = lvlW;
|
||||
lvlHeight = lvlH;
|
||||
}
|
||||
|
||||
SGDemSession::SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root ) {
|
||||
setOffsets( SGDem::longitudeDegToOffset((double)mnLon),
|
||||
SGDem::latitudeDegToOffset((double)mnLat),
|
||||
SGDem::longitudeDegToOffset((double)mxLon),
|
||||
SGDem::latitudeDegToOffset((double)mxLat) );
|
||||
SGDemSession::SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root)
|
||||
{
|
||||
setOffsets(SGDem::longitudeDegToOffset((double)mnLon),
|
||||
SGDem::latitudeDegToOffset((double)mnLat),
|
||||
SGDem::longitudeDegToOffset((double)mxLon),
|
||||
SGDem::latitudeDegToOffset((double)mxLat));
|
||||
|
||||
pDemRoot = root;
|
||||
lvlIndex = -1; // no level - session is raw input dir
|
||||
pDemRoot = root;
|
||||
lvlIndex = -1; // no level - session is raw input dir
|
||||
}
|
||||
|
||||
void SGDemSession::close( void )
|
||||
void SGDemSession::close(void)
|
||||
{
|
||||
if ( tileRefs.size() ) {
|
||||
if (tileRefs.size()) {
|
||||
tileRefs.clear();
|
||||
if ( lvlIndex >= 0 ) {
|
||||
pDemRoot->flushCaches( lvlIndex );
|
||||
if (lvlIndex >= 0) {
|
||||
pDemRoot->flushCaches(lvlIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SGDemSession::getGeods( unsigned wo, unsigned so, unsigned eo, unsigned no, int resx, int resy, int incx, int incy, ::std::vector<SGGeod>& geods, bool Debug1, bool Debug2 )
|
||||
void SGDemSession::getGeods(unsigned wo, unsigned so, unsigned eo, unsigned no, int resx, int resy, int incx, int incy, ::std::vector<SGGeod>& geods, bool Debug1, bool Debug2)
|
||||
{
|
||||
// todo - store this info in deminfo
|
||||
unsigned span; // smallest tile width/height in level ( in offsets )
|
||||
switch( lvlIndex ) {
|
||||
case 0: span = 1; break; // 1/8 deg
|
||||
case 1: span = 16; break; // 2 degrees
|
||||
case 2: span = 480; break; // 60 degrees
|
||||
default:
|
||||
fprintf( stderr, "invalid lvlIndex %d\n", lvlIndex );
|
||||
exit(0);
|
||||
switch (lvlIndex) {
|
||||
case 0:
|
||||
span = 1;
|
||||
break; // 1/8 deg
|
||||
case 1:
|
||||
span = 16;
|
||||
break; // 2 degrees
|
||||
case 2:
|
||||
span = 480;
|
||||
break; // 60 degrees
|
||||
default:
|
||||
fprintf(stderr, "invalid lvlIndex %d\n", lvlIndex);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ( lvlIndex >= 0 ) {
|
||||
if (lvlIndex >= 0) {
|
||||
unsigned tileLon, tileLat;
|
||||
unsigned meshLon, meshLat;
|
||||
int subx, suby;
|
||||
int subx, suby;
|
||||
|
||||
meshLon = wo;
|
||||
tileLon = SGDem::roundDown( meshLon, lvlWidth);
|
||||
tileLon = SGDem::roundDown(meshLon, lvlWidth);
|
||||
subx = (meshLon - tileLon) / span;
|
||||
// fprintf(stderr, "getGeods: lon is %lf : meshLon is %u, tileLon is %u, subx is %d\n", SGDem::offsetToLongitudeDeg(wo), meshLon, tileLon, subx );
|
||||
|
||||
meshLat = so;
|
||||
tileLat = SGDem::roundDown( meshLat, lvlHeight );
|
||||
tileLat = SGDem::roundDown(meshLat, lvlHeight);
|
||||
suby = (meshLat - tileLat) / span;
|
||||
// fprintf(stderr, "getGeods: lat is %lf : meshLat is %u, tileLat is %u, suby is %d\n", SGDem::offsetToLatitudeDeg(so), meshLat, tileLat, suby );
|
||||
|
||||
// get the tle from the tile cache
|
||||
unsigned long key = tileLon << 16 | tileLat;
|
||||
SGDemTileRef tile = pDemRoot->getTile( lvlIndex, key );
|
||||
if ( tile ) {
|
||||
SGDemTileRef tile = pDemRoot->getTile(lvlIndex, key);
|
||||
if (tile) {
|
||||
tile->getGeods(wo, so, eo, no, resx, resy, subx, suby, incx, incy, geods, Debug1, Debug2);
|
||||
} else {
|
||||
fprintf(stderr, " *** ERROR: tile %d,%d not in session @ (%lf,%lf) - (%lf,%lf)\n",
|
||||
fprintf(stderr, " *** ERROR: tile %u,%u not in session @ (%lf,%lf) - (%lf,%lf)\n",
|
||||
tileLon, tileLat,
|
||||
SGDem::offsetToLongitudeDeg( west_off ),
|
||||
SGDem::offsetToLatitudeDeg( south_off ),
|
||||
SGDem::offsetToLongitudeDeg( east_off ),
|
||||
SGDem::offsetToLatitudeDeg( north_off ) );
|
||||
SGDem::offsetToLongitudeDeg(west_off),
|
||||
SGDem::offsetToLatitudeDeg(south_off),
|
||||
SGDem::offsetToLongitudeDeg(east_off),
|
||||
SGDem::offsetToLatitudeDeg(north_off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,58 +3,83 @@
|
||||
|
||||
#include <simgear/scene/dem/SGDemRoot.hxx>
|
||||
|
||||
class SGDemSession
|
||||
{
|
||||
class SGDemSession {
|
||||
public:
|
||||
SGDemSession() {
|
||||
lvlIndex = -1;
|
||||
SGDemSession()
|
||||
: west_off(0)
|
||||
, south_off(0)
|
||||
, east_off(0)
|
||||
, north_off(0)
|
||||
, maxLon(0)
|
||||
, maxLat(0)
|
||||
, pDemRoot(NULL)
|
||||
, lvlIndex(-1)
|
||||
, lvlWidth(0)
|
||||
, lvlHeight(0)
|
||||
{
|
||||
}
|
||||
|
||||
SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root );
|
||||
SGDemSession( int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root );
|
||||
SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, int idx, int lvlW, int lvlH, SGDemRoot* root);
|
||||
SGDemSession(int mnLon, int mnLat, int mxLon, int mxLat, SGDemRoot* root);
|
||||
|
||||
SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root ) {
|
||||
setOffsets( wo, so, eo, no );
|
||||
|
||||
pDemRoot = root;
|
||||
lvlIndex = idx;
|
||||
lvlWidth = lvlW;
|
||||
lvlHeight = lvlH;
|
||||
SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, int idx, unsigned lvlW, unsigned lvlH, SGDemRoot* root)
|
||||
: west_off(wo)
|
||||
, south_off(so)
|
||||
, east_off(eo)
|
||||
, north_off(no)
|
||||
, maxLon(0)
|
||||
, maxLat(0)
|
||||
, pDemRoot(root)
|
||||
, lvlIndex(idx)
|
||||
, lvlWidth(lvlW)
|
||||
, lvlHeight(lvlH)
|
||||
{
|
||||
}
|
||||
|
||||
SGDemSession( unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root ) {
|
||||
setOffsets( wo, so, eo, no );
|
||||
|
||||
pDemRoot = root;
|
||||
lvlIndex = -1; // no level - session is raw input dir
|
||||
SGDemSession(unsigned wo, unsigned so, unsigned eo, unsigned no, SGDemRoot* root)
|
||||
: west_off(wo)
|
||||
, south_off(so)
|
||||
, east_off(eo)
|
||||
, north_off(no)
|
||||
, maxLon(0)
|
||||
, maxLat(0)
|
||||
, pDemRoot(root)
|
||||
, lvlIndex(-1) // no level - session is raw input dir
|
||||
, lvlWidth(0)
|
||||
, lvlHeight(0)
|
||||
{
|
||||
}
|
||||
|
||||
~SGDemSession() {
|
||||
~SGDemSession()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void addTile(SGDemTileRef pTile) {
|
||||
tileRefs.push_back( pTile );
|
||||
void addTile(SGDemTileRef pTile)
|
||||
{
|
||||
tileRefs.push_back(pTile);
|
||||
}
|
||||
|
||||
const std::vector<SGDemTileRef>& getTiles( void ) const {
|
||||
const std::vector<SGDemTileRef>& getTiles(void) const
|
||||
{
|
||||
return tileRefs;
|
||||
}
|
||||
|
||||
unsigned int size( void ) const {
|
||||
unsigned int size(void) const
|
||||
{
|
||||
return tileRefs.size();
|
||||
}
|
||||
|
||||
void getGeods( unsigned wp, unsigned so, unsigned eo, unsigned no,
|
||||
int resx, int resy, int incx, int incy,
|
||||
::std::vector<SGGeod>& geods,
|
||||
bool Debug1, bool Debug2
|
||||
);
|
||||
void getGeods(unsigned wp, unsigned so, unsigned eo, unsigned no,
|
||||
int resx, int resy, int incx, int incy,
|
||||
::std::vector<SGGeod>& geods,
|
||||
bool Debug1, bool Debug2);
|
||||
|
||||
void close( void );
|
||||
void close(void);
|
||||
|
||||
int getLvlIndex( void ) const {
|
||||
return lvlIndex;
|
||||
int getLvlIndex(void) const
|
||||
{
|
||||
return lvlIndex;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -64,13 +89,13 @@ private:
|
||||
east_off = eo;
|
||||
north_off = no;
|
||||
}
|
||||
|
||||
unsigned west_off, south_off;
|
||||
unsigned east_off, north_off;
|
||||
int maxLon, maxLat;
|
||||
SGDemRoot* pDemRoot;
|
||||
int lvlIndex;
|
||||
unsigned lvlWidth, lvlHeight;
|
||||
|
||||
unsigned west_off, south_off;
|
||||
unsigned east_off, north_off;
|
||||
int maxLon, maxLat;
|
||||
SGDemRoot* pDemRoot;
|
||||
int lvlIndex;
|
||||
unsigned lvlWidth, lvlHeight;
|
||||
|
||||
std::vector<SGDemTileRef> tileRefs;
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
#include <simgear/scene/util/StateAttributeFactory.hxx>
|
||||
#include <simgear/structure/OSGUtils.hxx>
|
||||
#include <simgear/structure/SGExpression.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/props/vectorPropTemplates.hxx>
|
||||
#include <simgear/threads/SGThread.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
@@ -245,11 +246,12 @@ int Effect::getGenerator(Effect::Generator what) const
|
||||
|
||||
// There should always be a valid technique in an effect.
|
||||
|
||||
Technique* Effect::chooseTechnique(RenderInfo* info)
|
||||
Technique* Effect::chooseTechnique(RenderInfo* info, const std::string &scheme)
|
||||
{
|
||||
BOOST_FOREACH(ref_ptr<Technique>& technique, techniques)
|
||||
{
|
||||
if (technique->valid(info) == Technique::VALID)
|
||||
if (technique->valid(info) == Technique::VALID &&
|
||||
technique->getScheme() == scheme)
|
||||
return technique.get();
|
||||
}
|
||||
return 0;
|
||||
@@ -976,6 +978,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
|
||||
type);
|
||||
program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, type);
|
||||
}
|
||||
PropertyList pUniformBlockBindings
|
||||
= prop->getChildren("uniform-block-binding");
|
||||
for (const auto &pUniformBlockBinding : pUniformBlockBindings) {
|
||||
program->addBindUniformBlock(
|
||||
pUniformBlockBinding->getStringValue("name"),
|
||||
pUniformBlockBinding->getIntValue("index"));
|
||||
}
|
||||
programMap.insert(ProgramMap::value_type(prgKey, program));
|
||||
resolvedProgramMap.insert(ProgramMap::value_type(resolvedKey, program));
|
||||
pass->setAttributeAndModes(program);
|
||||
@@ -1308,6 +1317,7 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop,
|
||||
{
|
||||
Technique* tniq = new Technique;
|
||||
effect->techniques.push_back(tniq);
|
||||
tniq->setScheme(prop->getStringValue("scheme"));
|
||||
const SGPropertyNode* predProp = prop->getChild("predicate");
|
||||
if (!predProp) {
|
||||
tniq->setAlwaysValid(true);
|
||||
@@ -1411,12 +1421,60 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
|
||||
return true;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr schemeList;
|
||||
|
||||
void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options)
|
||||
{
|
||||
if (!schemeList) {
|
||||
schemeList = new SGPropertyNode;
|
||||
const string schemes_file("Effects/schemes.xml");
|
||||
string absFileName
|
||||
= SGModelLib::findDataFile(schemes_file, options);
|
||||
if (absFileName.empty()) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "Could not find '" << schemes_file << "'");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
readProperties(absFileName, schemeList, 0, true);
|
||||
} catch (sg_io_exception& e) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "Error reading '" << schemes_file <<
|
||||
"': " << e.getFormattedMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyList p_schemes = schemeList->getChildren("scheme");
|
||||
for (const auto &p_scheme : p_schemes) {
|
||||
string scheme_name = p_scheme->getStringValue("name");
|
||||
string fallback_name = p_scheme->getStringValue("fallback");
|
||||
if (scheme_name.empty() || fallback_name.empty())
|
||||
continue;
|
||||
vector<SGPropertyNode_ptr> techniques = effect->root->getChildren("technique");
|
||||
auto it = std::find_if(techniques.begin(), techniques.end(),
|
||||
[&scheme_name](const SGPropertyNode_ptr &tniq) {
|
||||
return tniq->getStringValue("scheme") == scheme_name;
|
||||
});
|
||||
// Only merge the fallback effect if we haven't found a technique
|
||||
// implementing the scheme
|
||||
if (it == techniques.end()) {
|
||||
ref_ptr<Effect> fallback = makeEffect(fallback_name, false, options);
|
||||
if (fallback) {
|
||||
SGPropertyNode *new_root = new SGPropertyNode;
|
||||
mergePropertyTrees(new_root, effect->root, fallback->root);
|
||||
effect->root = new_root;
|
||||
effect->parametersProp = effect->root->getChild("parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the techniques property tree, building techniques and
|
||||
// passes.
|
||||
static SGMutex realizeTechniques_lock;
|
||||
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
|
||||
{
|
||||
SGGuard<SGMutex> g(realizeTechniques_lock);
|
||||
mergeSchemesFallbacks(this, options);
|
||||
|
||||
if (_isRealized)
|
||||
return true;
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
SGPropertyNode_ptr root;
|
||||
// Pointer to the parameters node, if it exists
|
||||
SGPropertyNode_ptr parametersProp;
|
||||
Technique* chooseTechnique(osg::RenderInfo* renderInfo);
|
||||
Technique* chooseTechnique(osg::RenderInfo* renderInfo, const std::string &scheme);
|
||||
virtual void resizeGLObjectBuffers(unsigned int maxSize);
|
||||
virtual void releaseGLObjects(osg::State* state = 0) const;
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
#include <osg/io_utils>
|
||||
|
||||
#include "EffectCullVisitor.hxx"
|
||||
|
||||
#include "EffectGeode.hxx"
|
||||
@@ -34,9 +36,9 @@ namespace simgear
|
||||
|
||||
using osgUtil::CullVisitor;
|
||||
|
||||
EffectCullVisitor::EffectCullVisitor(bool collectLights, Effect *effectOverride) :
|
||||
EffectCullVisitor::EffectCullVisitor(bool collectLights, const std::string &effScheme) :
|
||||
_collectLights(collectLights),
|
||||
_effectOverride(effectOverride)
|
||||
_effScheme(effScheme)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,6 +52,19 @@ CullVisitor* EffectCullVisitor::clone() const
|
||||
return new EffectCullVisitor(*this);
|
||||
}
|
||||
|
||||
void EffectCullVisitor::apply(osg::Node &node)
|
||||
{
|
||||
// TODO: Properly cull lights outside the viewport (override computeBounds())
|
||||
// if (isCulled(node))
|
||||
// return;
|
||||
SGLight *light = dynamic_cast<SGLight *>(&node);
|
||||
if (!light) {
|
||||
CullVisitor::apply(node);
|
||||
return;
|
||||
}
|
||||
_lightList.push_back(light);
|
||||
}
|
||||
|
||||
void EffectCullVisitor::apply(osg::Geode& node)
|
||||
{
|
||||
if (isCulled(node))
|
||||
@@ -59,21 +74,12 @@ void EffectCullVisitor::apply(osg::Geode& node)
|
||||
CullVisitor::apply(node);
|
||||
return;
|
||||
}
|
||||
if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) {
|
||||
_lightList.push_back( eg );
|
||||
}
|
||||
Effect *effect;
|
||||
if (_effectOverride) {
|
||||
effect = _effectOverride;
|
||||
} else {
|
||||
effect = eg->getEffect();
|
||||
if (!effect) {
|
||||
CullVisitor::apply(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Effect* effect = eg->getEffect();
|
||||
Technique* technique = 0;
|
||||
if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
|
||||
if (!effect) {
|
||||
CullVisitor::apply(node);
|
||||
return;
|
||||
} else if (!(technique = effect->chooseTechnique(&getRenderInfo(), _effScheme))) {
|
||||
return;
|
||||
}
|
||||
// push the node's state.
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <simgear/scene/model/SGLight.hxx>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Geode;
|
||||
@@ -34,10 +36,11 @@ class EffectGeode;
|
||||
class EffectCullVisitor : public osgUtil::CullVisitor
|
||||
{
|
||||
public:
|
||||
EffectCullVisitor(bool collectLights = false, Effect *effectOverride = 0);
|
||||
EffectCullVisitor(bool collectLights = false, const std::string &effScheme = "");
|
||||
EffectCullVisitor(const EffectCullVisitor&);
|
||||
virtual osgUtil::CullVisitor* clone() const;
|
||||
using osgUtil::CullVisitor::apply;
|
||||
virtual void apply(osg::Node& node);
|
||||
virtual void apply(osg::Geode& node);
|
||||
virtual void reset();
|
||||
|
||||
@@ -45,11 +48,13 @@ public:
|
||||
void addBuffer(std::string b, osg::Texture2D* tex);
|
||||
osg::Texture2D* getBuffer(std::string b);
|
||||
|
||||
SGLightList getLightList() const { return _lightList; }
|
||||
|
||||
private:
|
||||
std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList;
|
||||
std::vector<osg::ref_ptr<EffectGeode> > _lightList;
|
||||
SGLightList _lightList;
|
||||
bool _collectLights;
|
||||
osg::ref_ptr<Effect> _effectOverride;
|
||||
std::string _effScheme;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -98,6 +98,8 @@ public:
|
||||
void setGLExtensionsPred(float glVersion,
|
||||
const std::vector<std::string>& extensions);
|
||||
void refreshValidity();
|
||||
const std::string &getScheme() const { return _scheme; }
|
||||
void setScheme(const std::string &scheme) { _scheme = scheme; }
|
||||
protected:
|
||||
// Validity of technique in a graphics context.
|
||||
struct ContextInfo : public osg::Referenced
|
||||
@@ -117,6 +119,7 @@ protected:
|
||||
osg::ref_ptr<osg::StateSet> _shadowingStateSet;
|
||||
SGSharedPtr<SGExpressionb> _validExpression;
|
||||
int _contextIdLocation;
|
||||
std::string _scheme;
|
||||
};
|
||||
|
||||
class TechniquePredParser : public expression::ExpressionParser
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/scene/tgdb/userdata.hxx>
|
||||
#include <simgear/scene/util/SGSceneFeatures.hxx>
|
||||
#include <simgear/scene/util/SplicingVisitor.hxx>
|
||||
#include <simgear/structure/SGExpression.hxx>
|
||||
@@ -126,7 +127,12 @@ Effect* makeEffect(const string& name,
|
||||
itr->second.valid())
|
||||
return itr->second.get();
|
||||
}
|
||||
string effectFileName(name);
|
||||
string effectFileName;
|
||||
// Use getPropertyRoot() because the SGReaderWriterOptions might not have a
|
||||
// valid property tree
|
||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
||||
effectFileName += "Compositor/";
|
||||
effectFileName += name;
|
||||
effectFileName += ".eff";
|
||||
string absFileName
|
||||
= SGModelLib::findDataFile(effectFileName, options);
|
||||
|
||||
@@ -10,6 +10,7 @@ set(HEADERS
|
||||
PrimitiveCollector.hxx
|
||||
SGClipGroup.hxx
|
||||
SGInteractionAnimation.hxx
|
||||
SGLight.hxx
|
||||
SGMaterialAnimation.hxx
|
||||
SGPickAnimation.hxx
|
||||
SGOffsetTransform.hxx
|
||||
@@ -35,6 +36,7 @@ set(SOURCES
|
||||
PrimitiveCollector.cxx
|
||||
SGClipGroup.cxx
|
||||
SGInteractionAnimation.cxx
|
||||
SGLight.cxx
|
||||
SGLightAnimation.cxx
|
||||
SGPickAnimation.cxx
|
||||
SGMaterialAnimation.cxx
|
||||
@@ -62,4 +64,4 @@ if(ENABLE_TESTS)
|
||||
target_link_libraries(test_animations ${TEST_LIBS} ${OPENSCENEGRAPH_LIBRARIES})
|
||||
add_test(animations ${EXECUTABLE_OUTPUT_PATH}/test_animations)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include <simgear/props/condition.hxx>
|
||||
#include <simgear/io/sg_file.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
#include <simgear/misc/lru_cache.hxx>
|
||||
|
||||
#include "BoundingVolumeBuildVisitor.hxx"
|
||||
#include "model.hxx"
|
||||
@@ -182,7 +183,7 @@ public:
|
||||
|
||||
} // namespace
|
||||
|
||||
static int nearestPowerOfTwo(unsigned int _v)
|
||||
static int nearestPowerOfTwo(int _v)
|
||||
{
|
||||
// uint v; // compute the next highest power of 2 of 32-bit v
|
||||
unsigned int v = (unsigned int)_v;
|
||||
@@ -207,10 +208,12 @@ static int nearestPowerOfTwo(unsigned int _v)
|
||||
_v = (int)v;
|
||||
return v;
|
||||
}
|
||||
static bool isPowerOfTwo(int v)
|
||||
|
||||
static bool isPowerOfTwo(int v)
|
||||
{
|
||||
return ((v & (v - 1)) == 0);
|
||||
}
|
||||
|
||||
osg::Node* DefaultProcessPolicy::process(osg::Node* node, const std::string& filename,
|
||||
const Options* opt)
|
||||
{
|
||||
@@ -232,144 +235,28 @@ osg::Image* getImageByName(const std::string& filename)
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
// a cache which evicts the least recently used item when it is full
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
template<class Key, class Value>
|
||||
class lru_cache
|
||||
{
|
||||
public:
|
||||
SGMutex _mutex;
|
||||
// least recently used cache to speed up the process of finding a file
|
||||
// after the first time - otherwise each time we'll have to generate a hash
|
||||
// of the contents.
|
||||
static lru_cache < std::string, std::string> filename_hash_cache(100000);
|
||||
|
||||
typedef Key key_type;
|
||||
typedef Value value_type;
|
||||
typedef std::list<key_type> list_type;
|
||||
typedef std::map<
|
||||
key_type,
|
||||
std::pair<value_type, typename list_type::iterator>
|
||||
> map_type;
|
||||
|
||||
lru_cache(size_t capacity)
|
||||
: m_capacity(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
~lru_cache()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
size_t capacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
bool contains(const key_type &key)
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
return m_map.find(key) != m_map.end();
|
||||
}
|
||||
|
||||
void insert(const key_type &key, const value_type &value)
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
typename map_type::iterator i = m_map.find(key);
|
||||
if (i == m_map.end()) {
|
||||
// insert item into the cache, but first check if it is full
|
||||
if (size() >= m_capacity) {
|
||||
// cache is full, evict the least recently used item
|
||||
evict();
|
||||
}
|
||||
|
||||
// insert the new item
|
||||
m_list.push_front(key);
|
||||
m_map[key] = std::make_pair(value, m_list.begin());
|
||||
}
|
||||
}
|
||||
boost::optional<key_type> findValue(const std::string &requiredValue)
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it)
|
||||
if (it->second.first == requiredValue)
|
||||
return it->first;
|
||||
return boost::none;
|
||||
}
|
||||
boost::optional<value_type> get(const key_type &key)
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
// lookup value in the cache
|
||||
typename map_type::iterator i = m_map.find(key);
|
||||
if (i == m_map.end()) {
|
||||
// value not in cache
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// return the value, but first update its place in the most
|
||||
// recently used list
|
||||
typename list_type::iterator j = i->second.second;
|
||||
if (j != m_list.begin()) {
|
||||
// move item to the front of the most recently used list
|
||||
m_list.erase(j);
|
||||
m_list.push_front(key);
|
||||
|
||||
// update iterator in map
|
||||
j = m_list.begin();
|
||||
const value_type &value = i->second.first;
|
||||
m_map[key] = std::make_pair(value, j);
|
||||
|
||||
// return the value
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
// the item is already at the front of the most recently
|
||||
// used list so just return it
|
||||
return i->second.first;
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
m_map.clear();
|
||||
m_list.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
void evict()
|
||||
{
|
||||
SGGuard<SGMutex> scopeLock(_mutex);
|
||||
// evict item from the end of most recently used list
|
||||
typename list_type::iterator i = --m_list.end();
|
||||
m_map.erase(*i);
|
||||
m_list.erase(i);
|
||||
}
|
||||
|
||||
private:
|
||||
map_type m_map;
|
||||
list_type m_list;
|
||||
size_t m_capacity;
|
||||
};
|
||||
lru_cache < std::string, std::string> filename_hash_cache(100000);
|
||||
lru_cache < std::string, bool> filesCleaned(100000);
|
||||
static bool refreshCache = false;
|
||||
// experimental (incomplete) features to allow maintenance of the filecache.
|
||||
//lru_cache < std::string, bool> filesCleaned(100000);
|
||||
//static bool refreshCache = false;
|
||||
|
||||
ReaderWriter::ReadResult
|
||||
ModelRegistry::readImage(const string& fileName,
|
||||
const Options* opt)
|
||||
{
|
||||
// experimental feature to see if we can reload textures during model load
|
||||
// as otherwise texture creation/editting requires a restart or a change to
|
||||
// a different filenaem
|
||||
//if (SGSceneFeatures::instance()->getReloadCache()) {
|
||||
// SG_LOG(SG_IO, SG_INFO, "Clearing DDS-TC LRU Cache");
|
||||
// filename_hash_cache.clear();
|
||||
// SGSceneFeatures::instance()->setReloadCache(false);
|
||||
//}
|
||||
/*
|
||||
* processor is the interface to the osg_nvtt plugin
|
||||
*/
|
||||
@@ -423,13 +310,21 @@ ModelRegistry::readImage(const string& fileName,
|
||||
if (fileExists(absFileName)) {
|
||||
SGFile f(absFileName);
|
||||
std::string hash;
|
||||
|
||||
// std::string attr = "";
|
||||
// if (sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED) {
|
||||
// attr += " effnorm";
|
||||
// }
|
||||
// else if (sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
|
||||
// attr += " eff";
|
||||
// // can_compress = false;
|
||||
// }
|
||||
// SG_LOG(SG_IO, SG_INFO, absFileName << attr);
|
||||
boost::optional<std::string> cachehash = filename_hash_cache.get(absFileName);
|
||||
if (cachehash) {
|
||||
hash = *cachehash;
|
||||
// SG_LOG(SG_IO, SG_ALERT, "Hash for " + absFileName + " in cache " + hash);
|
||||
}
|
||||
else {
|
||||
// SG_LOG(SG_IO, SG_ALERT, "Creating hash for " + absFileName);
|
||||
try {
|
||||
hash = f.computeHash();
|
||||
}
|
||||
@@ -444,9 +339,8 @@ ModelRegistry::readImage(const string& fileName,
|
||||
|
||||
// possibly a shared texture - but warn the user to allow investigation.
|
||||
if (cacheFilename && *cacheFilename != absFileName) {
|
||||
SG_LOG(SG_IO, SG_ALERT, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName);
|
||||
SG_LOG(SG_IO, SG_INFO, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName);
|
||||
}
|
||||
// SG_LOG(SG_IO, SG_ALERT, " >>>> " + hash + " :: " + newName);
|
||||
}
|
||||
newName = cache_root + "/" + hash.substr(0, 2) + "/" + hash + ".cache.dds";
|
||||
}
|
||||
@@ -456,21 +350,15 @@ ModelRegistry::readImage(const string& fileName,
|
||||
newName += "." + tstream.str();
|
||||
newName += ".cache.dds";
|
||||
}
|
||||
bool doRefresh = refreshCache;
|
||||
//if (fileExists(newName) && sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
|
||||
// doRefresh = true;
|
||||
//
|
||||
//bool doRefresh = refreshCache;
|
||||
//if (newName != std::string() && fileExists(newName) && doRefresh) {
|
||||
// if (!filesCleaned.contains(newName)) {
|
||||
// SG_LOG(SG_IO, SG_INFO, "Removing previously cached effects image " + newName);
|
||||
// SGPath(newName).remove();
|
||||
// filesCleaned.insert(newName, true);
|
||||
// }
|
||||
//}
|
||||
|
||||
if (newName != std::string() && fileExists(newName) && doRefresh) {
|
||||
if (!filesCleaned.contains(newName)) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Removing previously cached effects image " + newName);
|
||||
SGPath(newName).remove();
|
||||
filesCleaned.insert(newName, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (newName != std::string() && !fileExists(newName)) {
|
||||
res = registry->readImageImplementation(absFileName, opt);
|
||||
if (res.validImage()) {
|
||||
@@ -494,14 +382,15 @@ ModelRegistry::readImage(const string& fileName,
|
||||
isNormalMap = true;
|
||||
}
|
||||
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "From effects transparent " + absFileName);
|
||||
SG_LOG(SG_IO, SG_INFO, "From effects transparent " + absFileName + " will generate mipmap only");
|
||||
isEffect = true;
|
||||
can_compress = false;
|
||||
}
|
||||
else if (sgoptC && !transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
|
||||
SG_LOG(SG_IO, SG_INFO, "From effects " + absFileName + " will generate mipmap only");
|
||||
isEffect = true;
|
||||
// can_compress = false;
|
||||
}
|
||||
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "From effects " + absFileName);
|
||||
isEffect = true;
|
||||
}
|
||||
if (can_compress)
|
||||
{
|
||||
std::string pot_message;
|
||||
@@ -516,8 +405,9 @@ ModelRegistry::readImage(const string& fileName,
|
||||
resize = true;
|
||||
pot_message += std::string(" not POT: resized height to ") + std::to_string(height);
|
||||
}
|
||||
if (pot_message.size())
|
||||
SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
|
||||
|
||||
if (pot_message.size())
|
||||
SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
|
||||
|
||||
// unlikely that after resizing in height the width will still be outside of the max texture size.
|
||||
if (height > max_texture_size)
|
||||
@@ -536,6 +426,7 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
|
||||
width /= factor;
|
||||
resize = true;
|
||||
}
|
||||
|
||||
if (resize) {
|
||||
osg::ref_ptr<osg::Image> resizedImage;
|
||||
|
||||
@@ -601,21 +492,22 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
|
||||
// normal maps:
|
||||
// nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm
|
||||
processor->compress(*srcImage, targetFormat, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION);
|
||||
SG_LOG(SG_IO, SG_ALERT, "-- finished creating DDS: " + newName);
|
||||
SG_LOG(SG_IO, SG_INFO, "-- finished creating DDS: " + newName);
|
||||
//processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
|
||||
}
|
||||
else {
|
||||
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
|
||||
SG_LOG(SG_IO, SG_WARN, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName);
|
||||
SG_LOG(SG_IO, SG_INFO, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName);
|
||||
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Creating uncompressed DDS for " + absFileName);
|
||||
if (processor) {
|
||||
processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
|
||||
}
|
||||
else {
|
||||
else {
|
||||
SG_LOG(SG_IO, SG_INFO, "Creating uncompressed DDS for " + absFileName);
|
||||
//if (processor) {
|
||||
// processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
|
||||
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
|
||||
}
|
||||
@@ -660,25 +552,28 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
|
||||
|
||||
osg::ref_ptr<osg::Image> srcImage1 = res.getImage();
|
||||
|
||||
//printf(" --> finished loading %s [%s] (%s) %d\n", absFileName.c_str(), srcImage1->getFileName().c_str(), res.loadedFromCache() ? "from cache" : "from disk", res.getImage()->getOrigin());
|
||||
/*
|
||||
* Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works
|
||||
*/
|
||||
if (srcImage1->getFileName().empty()) {
|
||||
srcImage1->setFileName(absFileName);
|
||||
}
|
||||
* Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works
|
||||
*/
|
||||
//if (srcImage1->getFileName().empty()) {
|
||||
// srcImage1->setFileName(absFileName);
|
||||
//}
|
||||
srcImage1->setFileName(originalFileName);
|
||||
|
||||
if(cache_active && getFileExtension(absFileName) != "dds")
|
||||
if(cache_active && getFileExtension(absFileName) != "dds"&& getFileExtension(absFileName) != "gz")
|
||||
{
|
||||
if (processor) {
|
||||
processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU);
|
||||
SG_LOG(SG_IO, SG_ALERT, "Created nvtt mipmaps DDS for " + absFileName);
|
||||
}
|
||||
else {
|
||||
// In testing the internal mipmap generation works better than the external nvtt one
|
||||
// (less artefacts); it might be that there are flags we can use to make this better
|
||||
// but for now we'll just using the built in one
|
||||
//if (processor) {
|
||||
// processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU);
|
||||
// SG_LOG(SG_IO, SG_INFO, "Created nvtt mipmaps DDS for " + absFileName);
|
||||
// }
|
||||
// else
|
||||
{
|
||||
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
|
||||
srcImage1 = simgear::effect::computeMipmap(srcImage1, mipmapFunctions);
|
||||
SG_LOG(SG_IO, SG_ALERT, "Created sg mipmaps DDS for " + absFileName);
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Created sg mipmaps DDS for " + absFileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,75 +584,6 @@ SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
|
||||
SG_LOG(SG_IO, SG_BULK, "Reading image \""
|
||||
<< res.getImage()->getFileName() << "\"");
|
||||
|
||||
// as of March 2018 all patents have expired, https://en.wikipedia.org/wiki/S3_Texture_Compression#Patent
|
||||
// there is support for S3TC DXT1..5 in MESA https://www.phoronix.com/scan.php?page=news_item&px=S3TC-Lands-In-Mesa
|
||||
// so it seems that there isn't a valid reason to warn any longer; and beside this is one of those cases where it should
|
||||
// really only be a developer message
|
||||
#ifdef WARN_DDS_TEXTURES
|
||||
// Check for precompressed textures that depend on an extension
|
||||
switch (res.getImage()->getPixelFormat()) {
|
||||
|
||||
// GL_EXT_texture_compression_s3tc
|
||||
// patented, no way to decompress these
|
||||
#ifndef GL_EXT_texture_compression_s3tc
|
||||
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
|
||||
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
|
||||
#endif
|
||||
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
|
||||
// GL_EXT_texture_sRGB
|
||||
// patented, no way to decompress these
|
||||
#ifndef GL_EXT_texture_sRGB
|
||||
#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
|
||||
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
|
||||
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
|
||||
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
|
||||
#endif
|
||||
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
||||
|
||||
// GL_TDFX_texture_compression_FXT1
|
||||
// can decompress these in software but
|
||||
// no code present in simgear.
|
||||
#ifndef GL_3DFX_texture_compression_FXT1
|
||||
#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
|
||||
#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1
|
||||
#endif
|
||||
case GL_COMPRESSED_RGB_FXT1_3DFX:
|
||||
case GL_COMPRESSED_RGBA_FXT1_3DFX:
|
||||
|
||||
// GL_EXT_texture_compression_rgtc
|
||||
// can decompress these in software but
|
||||
// no code present in simgear.
|
||||
#ifndef GL_EXT_texture_compression_rgtc
|
||||
#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
|
||||
#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
|
||||
#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
|
||||
#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
|
||||
#endif
|
||||
case GL_COMPRESSED_RED_RGTC1_EXT:
|
||||
case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
|
||||
case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
|
||||
case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
|
||||
|
||||
SG_LOG(SG_IO, SG_WARN, "Image \"" << fileName << "\"\n"
|
||||
"uses compressed textures which cannot be supported on "
|
||||
"some systems.\n"
|
||||
"Please decompress this texture for improved portability.");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
157
simgear/scene/model/SGLight.cxx
Normal file
157
simgear/scene/model/SGLight.cxx
Normal 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
simgear/scene/model/SGLight.hxx
Normal file
105
simgear/scene/model/SGLight.hxx
Normal 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 "particles.hxx"
|
||||
#include "model.hxx"
|
||||
#include "SGLight.hxx"
|
||||
#include "SGText.hxx"
|
||||
#include "SGMaterialAnimation.hxx"
|
||||
|
||||
@@ -518,6 +519,14 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
options.get()));
|
||||
}
|
||||
|
||||
std::vector<SGPropertyNode_ptr> light_nodes;
|
||||
light_nodes = props->getChildren("light");
|
||||
for (unsigned i = 0; i < light_nodes.size(); ++i) {
|
||||
group->addChild(SGLight::appendLight(light_nodes[i],
|
||||
prop_root,
|
||||
options.get()));
|
||||
}
|
||||
|
||||
PropertyList effect_nodes = props->getChildren("effect");
|
||||
PropertyList animation_nodes = props->getChildren("animation");
|
||||
|
||||
|
||||
@@ -1115,3 +1115,9 @@ void SGTerraSync::reposition()
|
||||
{
|
||||
// stub, remove
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<SGTerraSync> registrantSGTerraSync(
|
||||
SGSubsystemMgr::GENERAL,
|
||||
{{"FGRenderer", SGSubsystemMgr::Dependency::NONSUBSYSTEM_HARD}});
|
||||
|
||||
@@ -39,16 +39,19 @@ class BufferedLogCallback;
|
||||
class SGTerraSync : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
|
||||
SGTerraSync();
|
||||
virtual ~SGTerraSync();
|
||||
|
||||
virtual void init();
|
||||
virtual void shutdown();
|
||||
virtual void reinit();
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
virtual void update(double);
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void init() override;
|
||||
void reinit() override;
|
||||
void shutdown() override;
|
||||
void unbind() override;
|
||||
void update(double) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "terrasync"; }
|
||||
|
||||
/// notify terrasync that the sim was repositioned, as opposed to
|
||||
/// us travelling in a direction. Avoid last_lat / last_lon blocking
|
||||
@@ -80,6 +83,7 @@ public:
|
||||
void scheduleDataDir(const std::string& dataDir);
|
||||
|
||||
bool isDataDirPending(const std::string& dataDir) const;
|
||||
|
||||
protected:
|
||||
void syncAirportsModels();
|
||||
string_list getSceneryPathSuffixes() const;
|
||||
|
||||
@@ -1812,7 +1812,7 @@ namespace
|
||||
{
|
||||
static void write(const ImageUtils::PixelWriter* iw, const osg::Vec4f& c, int s, int t, int r, int m)
|
||||
{
|
||||
GLubyte* ptr = (GLubyte*)iw->data(s, t, r, m);
|
||||
iw->data(s, t, r, m);
|
||||
//OE_WARN << LC << "Target GL_UNSIGNED_BYTE_3_3_2 not yet implemented" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
set(HEADERS
|
||||
ClusteredForward.hxx
|
||||
ClusteredShading.hxx
|
||||
Compositor.hxx
|
||||
CompositorBuffer.hxx
|
||||
CompositorPass.hxx
|
||||
@@ -7,7 +7,7 @@ set(HEADERS
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
ClusteredForward.cxx
|
||||
ClusteredShading.cxx
|
||||
Compositor.cxx
|
||||
CompositorBuffer.cxx
|
||||
CompositorPass.cxx
|
||||
|
||||
@@ -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
|
||||
@@ -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
simgear/scene/viewer/ClusteredShading.cxx
Normal file
395
simgear/scene/viewer/ClusteredShading.cxx
Normal 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
simgear/scene/viewer/ClusteredShading.hxx
Normal file
96
simgear/scene/viewer/ClusteredShading.hxx
Normal 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 */
|
||||
@@ -39,7 +39,8 @@ Compositor *
|
||||
Compositor::create(osg::View *view,
|
||||
osg::GraphicsContext *gc,
|
||||
osg::Viewport *viewport,
|
||||
const SGPropertyNode *property_list)
|
||||
const SGPropertyNode *property_list,
|
||||
const SGReaderWriterOptions *options)
|
||||
{
|
||||
osg::ref_ptr<Compositor> compositor = new Compositor(view, gc, viewport);
|
||||
compositor->_name = property_list->getStringValue("name");
|
||||
@@ -55,7 +56,7 @@ Compositor::create(osg::View *view,
|
||||
"a name to be available to passes. Skipping...");
|
||||
continue;
|
||||
}
|
||||
Buffer *buffer = buildBuffer(compositor.get(), p_buffer);
|
||||
Buffer *buffer = buildBuffer(compositor.get(), p_buffer, options);
|
||||
if (buffer)
|
||||
compositor->addBuffer(buffer_name, buffer);
|
||||
}
|
||||
@@ -64,7 +65,7 @@ Compositor::create(osg::View *view,
|
||||
for (auto const &p_pass : p_passes) {
|
||||
if (!checkConditional(p_pass))
|
||||
continue;
|
||||
Pass *pass = buildPass(compositor.get(), p_pass);
|
||||
Pass *pass = buildPass(compositor.get(), p_pass, options);
|
||||
if (pass)
|
||||
compositor->addPass(pass);
|
||||
}
|
||||
@@ -76,7 +77,8 @@ Compositor *
|
||||
Compositor::create(osg::View *view,
|
||||
osg::GraphicsContext *gc,
|
||||
osg::Viewport *viewport,
|
||||
const std::string &name)
|
||||
const std::string &name,
|
||||
const SGReaderWriterOptions *options)
|
||||
{
|
||||
std::string filename(name);
|
||||
filename += ".xml";
|
||||
@@ -96,7 +98,7 @@ Compositor::create(osg::View *view,
|
||||
return 0;
|
||||
}
|
||||
|
||||
return create(view, gc, viewport, property_list);
|
||||
return create(view, gc, viewport, property_list, options);
|
||||
}
|
||||
|
||||
Compositor::Compositor(osg::View *view,
|
||||
@@ -200,51 +202,6 @@ Compositor::resized()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Compositor::computeIntersection(
|
||||
const osg::Vec2d& windowPos,
|
||||
osgUtil::LineSegmentIntersector::Intersections& intersections)
|
||||
{
|
||||
using osgUtil::Intersector;
|
||||
using osgUtil::LineSegmentIntersector;
|
||||
|
||||
osg::Camera *camera = getPass(0)->camera;
|
||||
const osg::Viewport* viewport = camera->getViewport();
|
||||
SGRect<double> viewportRect(viewport->x(), viewport->y(),
|
||||
viewport->x() + viewport->width() - 1.0,
|
||||
viewport->y() + viewport->height()- 1.0);
|
||||
|
||||
double epsilon = 0.5;
|
||||
if (!viewportRect.contains(windowPos.x(), windowPos.y(), epsilon))
|
||||
return false;
|
||||
|
||||
osg::Vec4d start(windowPos.x(), windowPos.y(), 0.0, 1.0);
|
||||
osg::Vec4d end(windowPos.x(), windowPos.y(), 1.0, 1.0);
|
||||
osg::Matrix windowMat = viewport->computeWindowMatrix();
|
||||
osg::Matrix startPtMat = osg::Matrix::inverse(camera->getProjectionMatrix()
|
||||
* windowMat);
|
||||
osg::Matrix endPtMat = startPtMat; // no far camera
|
||||
|
||||
start = start * startPtMat;
|
||||
start /= start.w();
|
||||
end = end * endPtMat;
|
||||
end /= end.w();
|
||||
osg::ref_ptr<LineSegmentIntersector> picker
|
||||
= new LineSegmentIntersector(Intersector::VIEW,
|
||||
osg::Vec3d(start.x(), start.y(), start.z()),
|
||||
osg::Vec3d(end.x(), end.y(), end.z()));
|
||||
osgUtil::IntersectionVisitor iv(picker.get());
|
||||
iv.setTraversalMask( simgear::PICK_BIT );
|
||||
|
||||
const_cast<osg::Camera*>(camera)->accept(iv);
|
||||
if (picker->containsIntersections()) {
|
||||
intersections = picker->getIntersections();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Compositor::addBuffer(const std::string &name, Buffer *buffer)
|
||||
{
|
||||
@@ -272,7 +229,7 @@ Compositor::addPass(Pass *pass)
|
||||
identifier = sceneView->getCullVisitor()->getIdentifier();
|
||||
|
||||
sceneView->setCullVisitor(
|
||||
new EffectCullVisitor(false, pass->effect_override));
|
||||
new EffectCullVisitor(false, pass->effect_scheme));
|
||||
sceneView->getCullVisitor()->setIdentifier(identifier.get());
|
||||
|
||||
identifier = sceneView->getCullVisitorLeft()->getIdentifier();
|
||||
|
||||
@@ -73,7 +73,8 @@ public:
|
||||
static Compositor *create(osg::View *view,
|
||||
osg::GraphicsContext *gc,
|
||||
osg::Viewport *viewport,
|
||||
const SGPropertyNode *property_list);
|
||||
const SGPropertyNode *property_list,
|
||||
const SGReaderWriterOptions *options);
|
||||
/**
|
||||
* \overload
|
||||
* \brief Create a Compositor from a file.
|
||||
@@ -84,20 +85,19 @@ public:
|
||||
static Compositor *create(osg::View *view,
|
||||
osg::GraphicsContext *gc,
|
||||
osg::Viewport *viewport,
|
||||
const std::string &name);
|
||||
const std::string &name,
|
||||
const SGReaderWriterOptions *options);
|
||||
|
||||
void update(const osg::Matrix &view_matrix,
|
||||
const osg::Matrix &proj_matrix);
|
||||
|
||||
void resized();
|
||||
|
||||
bool computeIntersection(
|
||||
const osg::Vec2d& windowPos,
|
||||
osgUtil::LineSegmentIntersector::Intersections& intersections);
|
||||
osg::View *getView() const { return _view; }
|
||||
|
||||
const osg::GraphicsContext *getGraphicsContext() const { return _gc; }
|
||||
osg::GraphicsContext *getGraphicsContext() const { return _gc; }
|
||||
|
||||
const osg::Viewport *getViewport() const { return _viewport; }
|
||||
osg::Viewport *getViewport() const { return _viewport; }
|
||||
|
||||
typedef std::array<
|
||||
osg::ref_ptr<osg::Uniform>,
|
||||
@@ -121,8 +121,6 @@ public:
|
||||
Pass * getPass(const std::string &name) const;
|
||||
|
||||
protected:
|
||||
friend class PassBuilder;
|
||||
|
||||
osg::View *_view;
|
||||
osg::GraphicsContext *_gc;
|
||||
osg::ref_ptr<osg::Viewport> _viewport;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/vectorPropTemplates.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
|
||||
#include "Compositor.hxx"
|
||||
#include "CompositorUtil.hxx"
|
||||
@@ -49,6 +50,7 @@ PropStringMap<BufferFormat> buffer_format_map {
|
||||
{"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}},
|
||||
{"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}},
|
||||
{"r32f", {GL_R32F, GL_RED, GL_FLOAT}},
|
||||
{"rg16f", {GL_RG16F, GL_RG, GL_FLOAT}},
|
||||
{"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}},
|
||||
{"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}},
|
||||
{"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}},
|
||||
@@ -92,7 +94,8 @@ PropStringMap<osg::Texture::ShadowCompareFunc> shadow_compare_func_map = {
|
||||
};
|
||||
|
||||
Buffer *
|
||||
buildBuffer(Compositor *compositor, const SGPropertyNode *node)
|
||||
buildBuffer(Compositor *compositor, const SGPropertyNode *node,
|
||||
const SGReaderWriterOptions *options)
|
||||
{
|
||||
std::string type = node->getStringValue("type");
|
||||
if (type.empty()) {
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
class SGPropertyNode;
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class SGReaderWriterOptions;
|
||||
|
||||
namespace compositor {
|
||||
|
||||
class Compositor;
|
||||
@@ -38,7 +41,8 @@ struct Buffer : public osg::Referenced {
|
||||
float width_scale, height_scale;
|
||||
};
|
||||
|
||||
Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node);
|
||||
Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node,
|
||||
const SGReaderWriterOptions *options);
|
||||
|
||||
} // namespace compositor
|
||||
} // namespace simgear
|
||||
|
||||
@@ -22,13 +22,17 @@
|
||||
#include <osg/PolygonMode>
|
||||
#include <osg/io_utils>
|
||||
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <simgear/props/vectorPropTemplates.hxx>
|
||||
#include <simgear/scene/material/EffectCullVisitor.hxx>
|
||||
#include <simgear/scene/material/EffectGeode.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/scene/util/SGUpdateVisitor.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include "ClusteredForward.hxx"
|
||||
#include "ClusteredShading.hxx"
|
||||
#include "Compositor.hxx"
|
||||
#include "CompositorUtil.hxx"
|
||||
|
||||
@@ -50,34 +54,37 @@ PropStringMap<osg::Camera::BufferComponent> buffer_component_map = {
|
||||
{"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER}
|
||||
};
|
||||
|
||||
Pass *
|
||||
PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
|
||||
{
|
||||
// The pass index matches its render order
|
||||
int render_order = root->getIndex();
|
||||
|
||||
Pass *
|
||||
PassBuilder::build(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options)
|
||||
{
|
||||
osg::ref_ptr<Pass> pass = new Pass;
|
||||
// The pass index matches its render order
|
||||
pass->render_order = root->getIndex();
|
||||
pass->name = root->getStringValue("name");
|
||||
if (pass->name.empty()) {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << render_order
|
||||
SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << pass->render_order
|
||||
<< " has no name. It won't be addressable by name!");
|
||||
}
|
||||
pass->type = root->getStringValue("type");
|
||||
|
||||
std::string eff_override_file = root->getStringValue("effect-override");
|
||||
if (!eff_override_file.empty())
|
||||
pass->effect_override = makeEffect(eff_override_file, true, 0);
|
||||
pass->effect_scheme = root->getStringValue("effect-scheme");
|
||||
|
||||
osg::Camera *camera = new Camera;
|
||||
pass->camera = camera;
|
||||
|
||||
camera->setName(pass->name);
|
||||
camera->setGraphicsContext(compositor->_gc);
|
||||
camera->setGraphicsContext(compositor->getGraphicsContext());
|
||||
// Even though this camera will be added as a slave to the view, it will
|
||||
// always be updated manually in Compositor::update()
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
// Same with the projection matrix
|
||||
camera->setProjectionResizePolicy(osg::Camera::FIXED);
|
||||
// We only use POST_RENDER. Leave PRE_RENDER for Canvas and other RTT stuff
|
||||
// that doesn't involve the rendering pipeline itself. NESTED_RENDER is also
|
||||
// not a possibility since we don't want to share RenderStage with the View
|
||||
// master camera.
|
||||
camera->setRenderOrder(osg::Camera::POST_RENDER, pass->render_order * 10);
|
||||
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
|
||||
// XXX: Should we make this configurable?
|
||||
@@ -144,7 +151,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
|
||||
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
} catch (sg_exception &e) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping binding "
|
||||
<< p_binding->getIndex() << " in pass " << render_order
|
||||
<< p_binding->getIndex() << " in pass " << pass->render_order
|
||||
<< ": " << e.what());
|
||||
}
|
||||
}
|
||||
@@ -153,22 +160,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
|
||||
if (p_attachments.empty()) {
|
||||
// If there are no attachments, assume the pass is rendering
|
||||
// directly to the screen
|
||||
|
||||
camera->setRenderOrder(osg::Camera::NESTED_RENDER, render_order * 10);
|
||||
// OSG cameras use the framebuffer by default, but it is stated
|
||||
// explicitly anyway
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
|
||||
|
||||
camera->setDrawBuffer(GL_BACK);
|
||||
camera->setReadBuffer(GL_BACK);
|
||||
|
||||
// Use the physical viewport. We can't let the user choose the viewport
|
||||
// size because some parts of the window might not be ours.
|
||||
camera->setViewport(compositor->_viewport);
|
||||
camera->setViewport(compositor->getViewport());
|
||||
} else {
|
||||
// This is a RTT camera
|
||||
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, render_order * 10);
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
|
||||
bool viewport_absolute = false;
|
||||
@@ -252,13 +252,13 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
|
||||
camera->setViewport(
|
||||
0,
|
||||
0,
|
||||
buffer->width_scale * compositor->_viewport->width(),
|
||||
buffer->height_scale * compositor->_viewport->height());
|
||||
buffer->width_scale * compositor->getViewport()->width(),
|
||||
buffer->height_scale * compositor->getViewport()->height());
|
||||
}
|
||||
}
|
||||
} catch (sg_exception &e) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping attachment "
|
||||
<< p_attachment->getIndex() << " in pass " << render_order
|
||||
<< p_attachment->getIndex() << " in pass " << pass->render_order
|
||||
<< ": " << e.what());
|
||||
}
|
||||
}
|
||||
@@ -271,8 +271,10 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
|
||||
|
||||
struct QuadPassBuilder : public PassBuilder {
|
||||
public:
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
|
||||
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options) {
|
||||
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
|
||||
pass->useMastersSceneData = false;
|
||||
|
||||
osg::Camera *camera = pass->camera;
|
||||
camera->setAllowEventFocus(false);
|
||||
@@ -289,11 +291,29 @@ public:
|
||||
scale = p_geometry->getFloatValue("scale", scale);
|
||||
}
|
||||
|
||||
const std::string eff_file = root->getStringValue("effect");
|
||||
|
||||
osg::ref_ptr<osg::Geode> quad = createFullscreenQuad(
|
||||
left, bottom, width, height, scale, eff_file);
|
||||
osg::ref_ptr<EffectGeode> quad = new EffectGeode;
|
||||
camera->addChild(quad);
|
||||
quad->setCullingActive(false);
|
||||
|
||||
const std::string eff_file = root->getStringValue("effect");
|
||||
if (!eff_file.empty()) {
|
||||
Effect *eff = makeEffect(eff_file, true, options);
|
||||
if (eff)
|
||||
quad->setEffect(eff);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> geom = createFullscreenQuadGeom(
|
||||
left, bottom, width, height, scale);
|
||||
quad->addDrawable(geom);
|
||||
|
||||
osg::ref_ptr<osg::StateSet> quad_state = quad->getOrCreateStateSet();
|
||||
int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
|
||||
quad_state->setAttribute(new osg::PolygonMode(
|
||||
osg::PolygonMode::FRONT_AND_BACK,
|
||||
osg::PolygonMode::FILL),
|
||||
values);
|
||||
quad_state->setMode(GL_LIGHTING, values);
|
||||
quad_state->setMode(GL_DEPTH_TEST, values);
|
||||
|
||||
osg::StateSet *ss = camera->getOrCreateStateSet();
|
||||
for (const auto &uniform : compositor->getUniforms())
|
||||
@@ -302,13 +322,12 @@ public:
|
||||
return pass.release();
|
||||
}
|
||||
protected:
|
||||
osg::Geode *createFullscreenQuad(float left,
|
||||
float bottom,
|
||||
float width,
|
||||
float height,
|
||||
float scale,
|
||||
const std::string &eff_file) {
|
||||
osg::Geometry *geom;
|
||||
osg::Geometry *createFullscreenQuadGeom(float left,
|
||||
float bottom,
|
||||
float width,
|
||||
float height,
|
||||
float scale) {
|
||||
osg::ref_ptr<osg::Geometry> geom;
|
||||
|
||||
// When the quad is fullscreen, it can be optimized by using a
|
||||
// a fullscreen triangle instead of a quad to avoid discarding pixels
|
||||
@@ -349,31 +368,52 @@ protected:
|
||||
osg::PrimitiveSet::TRIANGLES, 0, 3));
|
||||
}
|
||||
|
||||
osg::ref_ptr<EffectGeode> quad = new EffectGeode;
|
||||
if (!eff_file.empty()) {
|
||||
Effect *eff = makeEffect(eff_file, true, 0);
|
||||
if (eff)
|
||||
quad->setEffect(eff);
|
||||
}
|
||||
quad->addDrawable(geom);
|
||||
quad->setCullingActive(false);
|
||||
|
||||
osg::ref_ptr<osg::StateSet> quad_state = quad->getOrCreateStateSet();
|
||||
int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
|
||||
quad_state->setAttribute(new osg::PolygonMode(
|
||||
osg::PolygonMode::FRONT_AND_BACK,
|
||||
osg::PolygonMode::FILL),
|
||||
values);
|
||||
quad_state->setMode(GL_LIGHTING, values);
|
||||
quad_state->setMode(GL_DEPTH_TEST, values);
|
||||
|
||||
return quad.release();
|
||||
return geom.release();
|
||||
}
|
||||
};
|
||||
RegisterPassBuilder<QuadPassBuilder> registerQuadPass("quad");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ShadowMapCullCallback : public osg::NodeCallback {
|
||||
public:
|
||||
ShadowMapCullCallback(const std::string &suffix) {
|
||||
_light_matrix_uniform = new osg::Uniform(
|
||||
osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix);
|
||||
}
|
||||
|
||||
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
|
||||
osg::Camera *camera = static_cast<osg::Camera *>(node);
|
||||
|
||||
traverse(node, nv);
|
||||
|
||||
// The light matrix uniform is updated after the traverse in case the
|
||||
// OSG near/far plane calculations were enabled
|
||||
osg::Matrixf light_matrix =
|
||||
// Include the real camera inverse view matrix because if the shader
|
||||
// used world coordinates, there would be precision issues.
|
||||
_real_inverse_view *
|
||||
camera->getViewMatrix() *
|
||||
camera->getProjectionMatrix() *
|
||||
// Bias matrices
|
||||
osg::Matrix::translate(1.0, 1.0, 1.0) *
|
||||
osg::Matrix::scale(0.5, 0.5, 0.5);
|
||||
_light_matrix_uniform->set(light_matrix);
|
||||
}
|
||||
|
||||
void setRealInverseViewMatrix(const osg::Matrix &matrix) {
|
||||
_real_inverse_view = matrix;
|
||||
}
|
||||
|
||||
osg::Uniform *getLightMatrixUniform() const {
|
||||
return _light_matrix_uniform.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
osg::Matrix _real_inverse_view;
|
||||
osg::ref_ptr<osg::Uniform> _light_matrix_uniform;
|
||||
};
|
||||
|
||||
class LightFinder : public osg::NodeVisitor {
|
||||
public:
|
||||
LightFinder(const std::string &name) :
|
||||
@@ -406,15 +446,14 @@ protected:
|
||||
|
||||
struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback {
|
||||
public:
|
||||
ShadowMapUpdateCallback(const std::string &light_name,
|
||||
ShadowMapUpdateCallback(ShadowMapCullCallback *cull_callback,
|
||||
const std::string &light_name,
|
||||
float near_m, float far_m,
|
||||
const std::string &suffix,
|
||||
int sm_width, int sm_height) :
|
||||
_cull_callback(cull_callback),
|
||||
_light_finder(new LightFinder(light_name)),
|
||||
_near_m(near_m),
|
||||
_far_m(far_m) {
|
||||
_light_matrix_uniform = new osg::Uniform(
|
||||
osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix);
|
||||
_half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0;
|
||||
}
|
||||
virtual void updatePass(Pass &pass,
|
||||
@@ -443,6 +482,7 @@ public:
|
||||
// This is not a problem though (for now).
|
||||
|
||||
osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix);
|
||||
_cull_callback->setRealInverseViewMatrix(view_inverse);
|
||||
|
||||
// Calculate the light's point of view transformation matrices.
|
||||
// Taken from Project Rembrandt.
|
||||
@@ -489,37 +529,30 @@ public:
|
||||
osg::Matrixd round_matrix = osg::Matrixd::translate(
|
||||
rounding.x(), rounding.y(), 0.0);
|
||||
light_proj_matrix *= round_matrix;
|
||||
|
||||
osg::Matrixf light_matrix =
|
||||
// Include the real camera inverse view matrix because if the shader
|
||||
// used world coordinates, there would be precision issues.
|
||||
view_inverse *
|
||||
camera->getViewMatrix() *
|
||||
camera->getProjectionMatrix() *
|
||||
// Bias matrices
|
||||
osg::Matrix::translate(1.0, 1.0, 1.0) *
|
||||
osg::Matrix::scale(0.5, 0.5, 0.5);
|
||||
_light_matrix_uniform->set(light_matrix);
|
||||
}
|
||||
|
||||
osg::Uniform *getLightMatrixUniform() const {
|
||||
return _light_matrix_uniform.get();
|
||||
}
|
||||
protected:
|
||||
osg::observer_ptr<ShadowMapCullCallback> _cull_callback;
|
||||
osg::ref_ptr<LightFinder> _light_finder;
|
||||
float _near_m;
|
||||
float _far_m;
|
||||
osg::ref_ptr<osg::Uniform> _light_matrix_uniform;
|
||||
osg::Vec2d _half_sm_size;
|
||||
};
|
||||
|
||||
struct ShadowMapPassBuilder : public PassBuilder {
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
|
||||
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
|
||||
pass->useMastersSceneData = true;
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options) {
|
||||
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
|
||||
|
||||
osg::Camera *camera = pass->camera;
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||
camera->setCullingMode(camera->getCullingMode() &
|
||||
~osg::CullSettings::SMALL_FEATURE_CULLING);
|
||||
//camera->setComputeNearFarMode(
|
||||
// osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
||||
|
||||
ShadowMapCullCallback *cull_callback = new ShadowMapCullCallback(pass->name);
|
||||
camera->setCullCallback(cull_callback);
|
||||
|
||||
std::string light_name = root->getStringValue("light-name");
|
||||
float near_m = root->getFloatValue("near-m");
|
||||
@@ -527,9 +560,9 @@ struct ShadowMapPassBuilder : public PassBuilder {
|
||||
int sm_width = camera->getViewport()->width();
|
||||
int sm_height = camera->getViewport()->height();
|
||||
pass->update_callback = new ShadowMapUpdateCallback(
|
||||
cull_callback,
|
||||
light_name,
|
||||
near_m, far_m,
|
||||
pass->name,
|
||||
sm_width, sm_height);
|
||||
|
||||
return pass.release();
|
||||
@@ -624,43 +657,69 @@ protected:
|
||||
float _zFar;
|
||||
};
|
||||
|
||||
class SceneCullCallback : public osg::NodeCallback {
|
||||
public:
|
||||
SceneCullCallback(ClusteredShading *clustered) :
|
||||
_clustered(clustered) {}
|
||||
|
||||
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
|
||||
osg::Camera *camera = static_cast<osg::Camera *>(node);
|
||||
EffectCullVisitor *cv = dynamic_cast<EffectCullVisitor *>(nv);
|
||||
|
||||
cv->traverse(*camera);
|
||||
|
||||
if (_clustered) {
|
||||
// Retrieve the light list from the cull visitor
|
||||
SGLightList light_list = cv->getLightList();
|
||||
_clustered->update(light_list);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
osg::ref_ptr<ClusteredShading> _clustered;
|
||||
};
|
||||
|
||||
struct ScenePassBuilder : public PassBuilder {
|
||||
public:
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
|
||||
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
|
||||
pass->useMastersSceneData = true;
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options) {
|
||||
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root, options);
|
||||
pass->inherit_cull_mask = true;
|
||||
|
||||
osg::Camera *camera = pass->camera;
|
||||
camera->setAllowEventFocus(true);
|
||||
|
||||
const SGPropertyNode *clustered = root->getChild("clustered-forward");
|
||||
if (clustered) {
|
||||
camera->setInitialDrawCallback(new ClusteredForwardDrawCallback);
|
||||
}
|
||||
const SGPropertyNode *p_clustered = root->getNode("clustered-shading");
|
||||
ClusteredShading *clustered = 0;
|
||||
if (p_clustered)
|
||||
clustered = new ClusteredShading(camera, p_clustered);
|
||||
|
||||
camera->setCullCallback(new SceneCullCallback(clustered));
|
||||
|
||||
int cubemap_face = root->getIntValue("cubemap-face", -1);
|
||||
float zNear = root->getFloatValue("z-near", 0.0f);
|
||||
float zFar = root->getFloatValue("z-far", 0.0f);
|
||||
pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar);
|
||||
|
||||
std::string shadow_pass_name = root->getStringValue("use-shadow-pass");
|
||||
if (!shadow_pass_name.empty()) {
|
||||
Pass *shadow_pass = compositor->getPass(shadow_pass_name);
|
||||
if (shadow_pass) {
|
||||
ShadowMapUpdateCallback *updatecb =
|
||||
dynamic_cast<ShadowMapUpdateCallback *>(
|
||||
shadow_pass->update_callback.get());
|
||||
if (updatecb) {
|
||||
camera->getOrCreateStateSet()->addUniform(
|
||||
updatecb->getLightMatrixUniform());
|
||||
PropertyList p_shadow_passes = root->getChildren("use-shadow-pass");
|
||||
for (const auto &p_shadow_pass : p_shadow_passes) {
|
||||
std::string shadow_pass_name = p_shadow_pass->getStringValue();
|
||||
if (!shadow_pass_name.empty()) {
|
||||
Pass *shadow_pass = compositor->getPass(shadow_pass_name);
|
||||
if (shadow_pass) {
|
||||
ShadowMapCullCallback *cullcb =
|
||||
dynamic_cast<ShadowMapCullCallback *>(
|
||||
shadow_pass->camera->getCullCallback());
|
||||
if (cullcb) {
|
||||
camera->getOrCreateStateSet()->addUniform(
|
||||
cullcb->getLightMatrixUniform());
|
||||
} else {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '"
|
||||
<< shadow_pass_name << "is not a shadow pass");
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '"
|
||||
<< shadow_pass_name << "is not a shadow pass");
|
||||
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not "
|
||||
"find shadow pass named '" << shadow_pass_name << "'");
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not "
|
||||
"find shadow pass named '" << shadow_pass_name << "'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,7 +732,8 @@ RegisterPassBuilder<ScenePassBuilder> registerScenePass("scene");
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Pass *
|
||||
buildPass(Compositor *compositor, const SGPropertyNode *root)
|
||||
buildPass(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options)
|
||||
{
|
||||
std::string type = root->getStringValue("type");
|
||||
if (type.empty()) {
|
||||
@@ -687,7 +747,7 @@ buildPass(Compositor *compositor, const SGPropertyNode *root)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return builder->build(compositor, root);
|
||||
return builder->build(compositor, root, options);
|
||||
}
|
||||
|
||||
} // namespace compositor
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class SGReaderWriterOptions;
|
||||
|
||||
namespace compositor {
|
||||
|
||||
class Compositor;
|
||||
@@ -45,17 +48,17 @@ class Compositor;
|
||||
*/
|
||||
struct Pass : public osg::Referenced {
|
||||
Pass() :
|
||||
useMastersSceneData(false),
|
||||
useMastersSceneData(true),
|
||||
cull_mask(0xffffff),
|
||||
inherit_cull_mask(false),
|
||||
viewport_width_scale(0.0f),
|
||||
viewport_height_scale(0.0f) {}
|
||||
|
||||
int render_order;
|
||||
std::string name;
|
||||
std::string type;
|
||||
std::string effect_scheme;
|
||||
osg::ref_ptr<osg::Camera> camera;
|
||||
/** If null, there is no effect override for this pass. */
|
||||
osg::ref_ptr<Effect> effect_override;
|
||||
bool useMastersSceneData;
|
||||
osg::Node::NodeMask cull_mask;
|
||||
/** Whether the cull mask is ANDed with the view master camera cull mask. */
|
||||
@@ -85,10 +88,11 @@ public:
|
||||
* and overrided for more special passes.
|
||||
*
|
||||
* @param compositor The Compositor instance that owns the pass.
|
||||
* @param The root node of the pass property tree.
|
||||
* @param root The root node of the pass property tree.
|
||||
* @return A Pass or a null pointer if an error occurred.
|
||||
*/
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root);
|
||||
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options);
|
||||
|
||||
static PassBuilder *find(const std::string &type) {
|
||||
auto itr = PassBuilderMapSingleton::instance()->_map.find(type);
|
||||
@@ -125,7 +129,8 @@ struct RegisterPassBuilder {
|
||||
* @param node The root node of the pass property tree.
|
||||
* @return A Pass or a null pointer if an error occurred.
|
||||
*/
|
||||
Pass *buildPass(Compositor *compositor, const SGPropertyNode *root);
|
||||
Pass *buildPass(Compositor *compositor, const SGPropertyNode *root,
|
||||
const SGReaderWriterOptions *options);
|
||||
|
||||
} // namespace compositor
|
||||
} // namespace simgear
|
||||
|
||||
@@ -6,6 +6,7 @@ set(HEADERS
|
||||
xmlsound.hxx
|
||||
soundmgr.hxx
|
||||
filters.hxx
|
||||
readwav.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -13,6 +14,7 @@ set(SOURCES
|
||||
sample_group.cxx
|
||||
xmlsound.cxx
|
||||
filters.cxx
|
||||
readwav.cxx
|
||||
)
|
||||
|
||||
if (USE_AEONWAVE)
|
||||
@@ -20,13 +22,9 @@ if (USE_AEONWAVE)
|
||||
soundmgr_aeonwave.cxx
|
||||
)
|
||||
else()
|
||||
set(HEADERS ${HEADERS}
|
||||
readwav.hxx
|
||||
)
|
||||
set(SOURCES ${SOURCES}
|
||||
soundmgr_openal.cxx
|
||||
soundmgr_openal_private.hxx
|
||||
readwav.cxx
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -49,19 +49,21 @@ class SGSoundSample;
|
||||
class SGSoundMgr : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
|
||||
SGSoundMgr();
|
||||
~SGSoundMgr();
|
||||
|
||||
void init();
|
||||
void update(double dt);
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void reinit() override;
|
||||
void resume() override;
|
||||
void suspend() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "sound"; }
|
||||
|
||||
void suspend();
|
||||
void resume();
|
||||
void stop();
|
||||
|
||||
void reinit();
|
||||
|
||||
/**
|
||||
* Select a specific sound device.
|
||||
* Requires a init/reinit call before sound is actually switched.
|
||||
@@ -310,7 +312,7 @@ public:
|
||||
size_t *size,
|
||||
int *freq,
|
||||
int *block );
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of available playback devices.
|
||||
*/
|
||||
@@ -324,7 +326,6 @@ public:
|
||||
|
||||
bool testForError(std::string s, std::string name = "sound manager");
|
||||
|
||||
static const char* subsystemName() { return "sound"; };
|
||||
private:
|
||||
class SoundManagerPrivate;
|
||||
/// private implementation object
|
||||
|
||||
@@ -340,7 +340,7 @@ void SGSoundMgr::suspend()
|
||||
for ( auto current = d->_sample_groups.begin();
|
||||
current != d->_sample_groups.end(); ++current ) {
|
||||
SGSampleGroup *sgrp = current->second;
|
||||
sgrp->stop();
|
||||
sgrp->suspend();
|
||||
}
|
||||
_active = false;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,9 @@ SGXmlSound::~SGXmlSound()
|
||||
if (_sample)
|
||||
_sample->stop();
|
||||
|
||||
if (_sgrp && (_name != ""))
|
||||
_sgrp->remove(_name);
|
||||
|
||||
_volume.clear();
|
||||
_pitch.clear();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <sstream>
|
||||
#include <boost/bind.hpp>
|
||||
#include <cstring> // for strcmp
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
@@ -138,3 +138,7 @@ SGPerformanceMonitor::reportTiming(const string& name, SampleStatistic* timeStat
|
||||
|
||||
timeStat->reset();
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
//SGSubsystemMgr::Registrant<SGPerformanceMonitor> registrantSGPerformanceMonitor;
|
||||
|
||||
@@ -34,14 +34,17 @@ class SampleStatistic;
|
||||
|
||||
class SGPerformanceMonitor : public SGSubsystem
|
||||
{
|
||||
|
||||
public:
|
||||
SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root);
|
||||
|
||||
virtual void bind (void);
|
||||
virtual void unbind (void);
|
||||
virtual void init (void);
|
||||
virtual void update (double dt);
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void init() override;
|
||||
void unbind() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "performance-mon"; }
|
||||
|
||||
private:
|
||||
static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat);
|
||||
|
||||
@@ -47,7 +47,6 @@ SGEventMgr::SGEventMgr() :
|
||||
_inited(false),
|
||||
_shutdown(false)
|
||||
{
|
||||
_name = "EventMgr";
|
||||
}
|
||||
|
||||
SGEventMgr::~SGEventMgr()
|
||||
@@ -127,6 +126,11 @@ void SGEventMgr::dump()
|
||||
_rtQueue.dump();
|
||||
}
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<SGEventMgr> registrantSGEventMgr(
|
||||
SGSubsystemMgr::DISPLAY);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// SGTimerQueue
|
||||
// This is the priority queue implementation:
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
|
||||
class SGEventMgr;
|
||||
|
||||
class SGTimer {
|
||||
class SGTimer
|
||||
{
|
||||
public:
|
||||
~SGTimer();
|
||||
void run();
|
||||
|
||||
|
||||
std::string name;
|
||||
double interval;
|
||||
SGCallback* callback;
|
||||
@@ -20,7 +21,8 @@ public:
|
||||
bool running;
|
||||
};
|
||||
|
||||
class SGTimerQueue {
|
||||
class SGTimerQueue
|
||||
{
|
||||
public:
|
||||
SGTimerQueue(int preSize=1);
|
||||
~SGTimerQueue();
|
||||
@@ -37,8 +39,9 @@ public:
|
||||
double nextTime() { return -_table[0].pri; }
|
||||
|
||||
SGTimer* findByName(const std::string& name) const;
|
||||
|
||||
|
||||
void dump();
|
||||
|
||||
private:
|
||||
// The "priority" is stored as a negative time. This allows the
|
||||
// implementation to treat the "top" of the heap as the largest
|
||||
@@ -50,9 +53,9 @@ private:
|
||||
int rchild(int n) { return ((n+1)*2 + 1) - 1; }
|
||||
double pri(int n) { return _table[n].pri; }
|
||||
void swap(int a, int b) {
|
||||
HeapEntry tmp = _table[a];
|
||||
_table[a] = _table[b];
|
||||
_table[b] = tmp;
|
||||
HeapEntry tmp = _table[a];
|
||||
_table[a] = _table[b];
|
||||
_table[b] = tmp;
|
||||
}
|
||||
void siftDown(int n);
|
||||
void siftUp(int n);
|
||||
@@ -73,10 +76,15 @@ public:
|
||||
SGEventMgr();
|
||||
~SGEventMgr();
|
||||
|
||||
virtual void init();
|
||||
virtual void update(double delta_time_sec);
|
||||
virtual void unbind();
|
||||
virtual void shutdown();
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void shutdown() override;
|
||||
void unbind() override;
|
||||
void update(double delta_time_sec) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "events"; }
|
||||
|
||||
void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; }
|
||||
|
||||
/**
|
||||
@@ -119,8 +127,9 @@ public:
|
||||
|
||||
|
||||
void removeTask(const std::string& name);
|
||||
|
||||
|
||||
void dump();
|
||||
|
||||
private:
|
||||
friend class SGTimer;
|
||||
|
||||
@@ -130,7 +139,7 @@ private:
|
||||
|
||||
SGPropertyNode_ptr _freezeProp;
|
||||
SGPropertyNode_ptr _rtProp;
|
||||
SGTimerQueue _rtQueue;
|
||||
SGTimerQueue _rtQueue;
|
||||
SGTimerQueue _simQueue;
|
||||
bool _inited, _shutdown;
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
@@ -137,28 +138,28 @@ void SGSubsystem::stamp(const string& name)
|
||||
|
||||
void SGSubsystem::set_name(const std::string &n)
|
||||
{
|
||||
assert(_name.empty());
|
||||
_name = n;
|
||||
assert(_subsystemId.empty());
|
||||
_subsystemId = n;
|
||||
}
|
||||
|
||||
std::string SGSubsystem::typeName() const
|
||||
std::string SGSubsystem::subsystemClassId() const
|
||||
{
|
||||
auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR);
|
||||
auto pos = _subsystemId.find(SUBSYSTEM_NAME_SEPARATOR);
|
||||
if (pos == std::string::npos) {
|
||||
return _name;
|
||||
return _subsystemId;
|
||||
}
|
||||
|
||||
return _name.substr(0, pos);
|
||||
return _subsystemId.substr(0, pos);
|
||||
}
|
||||
|
||||
std::string SGSubsystem::instanceName() const
|
||||
std::string SGSubsystem::subsystemInstanceId() const
|
||||
{
|
||||
auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR);
|
||||
auto pos = _subsystemId.find(SUBSYSTEM_NAME_SEPARATOR);
|
||||
if (pos == std::string::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return _name.substr(pos+1);
|
||||
return _subsystemId.substr(pos+1);
|
||||
}
|
||||
|
||||
void SGSubsystem::set_group(SGSubsystemGroup* group)
|
||||
@@ -174,7 +175,7 @@ SGSubsystemGroup* SGSubsystem::get_group() const
|
||||
SGSubsystemMgr* SGSubsystem::get_manager() const
|
||||
{
|
||||
if (!get_group())
|
||||
throw sg_exception("SGSubsystem::get_manager: subsystem " + name() + " has no group");
|
||||
throw sg_exception("SGSubsystem::get_manager: subsystem " + subsystemId() + " has no group");
|
||||
return get_group()->get_manager();
|
||||
}
|
||||
|
||||
@@ -232,12 +233,11 @@ public:
|
||||
|
||||
|
||||
|
||||
SGSubsystemGroup::SGSubsystemGroup(const char *name) :
|
||||
SGSubsystemGroup::SGSubsystemGroup() :
|
||||
_fixedUpdateTime(-1.0),
|
||||
_updateTimeRemainder(0.0),
|
||||
_initPosition(-1)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
SGSubsystemGroup::~SGSubsystemGroup ()
|
||||
@@ -473,7 +473,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
|
||||
if (reportDeltas) {
|
||||
auto deltaT = _executionTime - _lastExecutionTime;
|
||||
if (deltaT != 0) {
|
||||
t << name() << "(+" << std::setprecision(2) << std::right << deltaT << "ms).";
|
||||
t << subsystemInstanceId() << "(+" << std::setprecision(2) << std::right << deltaT << "ms).";
|
||||
_name = t.str();
|
||||
}
|
||||
}
|
||||
@@ -505,7 +505,7 @@ void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
|
||||
void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
|
||||
SGSubsystem::reportTimingStats(_lastValues);
|
||||
|
||||
std::string _name = name();
|
||||
std::string _name = subsystemInstanceId();
|
||||
if (!_name.size())
|
||||
_name = typeid(this).name();
|
||||
if (_lastValues) {
|
||||
@@ -513,11 +513,11 @@ void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
|
||||
if (deltaT != 0) {
|
||||
SG_LOG(SG_EVENT, SG_ALERT,
|
||||
" +" << std::setw(6) << std::setprecision(4) << std::right << deltaT << "ms "
|
||||
<< name() );
|
||||
<< subsystemInstanceId() );
|
||||
}
|
||||
}
|
||||
else
|
||||
SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << name() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s");
|
||||
SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << subsystemInstanceId() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s");
|
||||
for (auto member : _members) {
|
||||
member->reportTimingStats(_lastValues);
|
||||
}
|
||||
@@ -574,11 +574,11 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
|
||||
if (name.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group without a name");
|
||||
// TODO, make this an exception in the future
|
||||
} else if (subsystem->name().empty()) {
|
||||
} else if (subsystem->subsystemId().empty()) {
|
||||
subsystem->set_name(name);
|
||||
} else if (name != subsystem->name()) {
|
||||
} else if (name != subsystem->subsystemId()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group with name '" << name
|
||||
<< "', but name() returns '" << subsystem->name() << "'");
|
||||
<< "', but subsystemId() returns '" << subsystem->subsystemId() << "'");
|
||||
}
|
||||
|
||||
notifyWillChange(subsystem, State::ADD);
|
||||
@@ -614,7 +614,7 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
|
||||
void
|
||||
SGSubsystemGroup::set_subsystem (SGSubsystem * subsystem, double min_step_sec)
|
||||
{
|
||||
set_subsystem(subsystem->name(), subsystem, min_step_sec);
|
||||
set_subsystem(subsystem->subsystemId(), subsystem, min_step_sec);
|
||||
}
|
||||
|
||||
SGSubsystem *
|
||||
@@ -837,7 +837,7 @@ namespace {
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
SGSubsystemMgr::SGSubsystemMgr (const char *name) :
|
||||
SGSubsystemMgr::SGSubsystemMgr () :
|
||||
_groups(MAX_GROUPS)
|
||||
{
|
||||
if (global_defaultSubsystemManager == nullptr) {
|
||||
@@ -854,7 +854,7 @@ SGSubsystemMgr::SGSubsystemMgr (const char *name) :
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MAX_GROUPS; i++) {
|
||||
auto g = new SGSubsystemGroup(name);
|
||||
auto g = new SGSubsystemGroup();
|
||||
g->set_manager(this);
|
||||
_groups[i].reset(g);
|
||||
}
|
||||
@@ -1038,9 +1038,9 @@ SGSubsystemMgr::get_subsystem (const string &name) const
|
||||
}
|
||||
|
||||
SGSubsystem*
|
||||
SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& instanceName) const
|
||||
SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const
|
||||
{
|
||||
return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + instanceName);
|
||||
return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1173,7 +1173,7 @@ SGSubsystemMgr::create(const std::string& name)
|
||||
}
|
||||
|
||||
SGSubsystemRef
|
||||
SGSubsystemMgr::createInstance(const std::string& name, const std::string& instanceName)
|
||||
SGSubsystemMgr::createInstance(const std::string& name, const std::string& subsystemInstanceId)
|
||||
{
|
||||
auto it = findRegistration(name);
|
||||
auto &global_registrations = getGlobalRegistrations();
|
||||
@@ -1190,7 +1190,7 @@ SGSubsystemMgr::createInstance(const std::string& name, const std::string& insta
|
||||
throw sg_exception("SGSubsystemMgr::create: functor failed to create an instsance of " + name);
|
||||
}
|
||||
|
||||
const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + instanceName;
|
||||
const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId;
|
||||
ref->set_name(combinedName);
|
||||
return ref;
|
||||
}
|
||||
@@ -1275,7 +1275,7 @@ namespace {
|
||||
|
||||
// allow override of the name but defaultt o the subsystem name
|
||||
std::string name = arg->getStringValue("name");
|
||||
std::string instanceName = arg->getStringValue("instance");
|
||||
std::string subsystemInstanceId = arg->getStringValue("instance");
|
||||
|
||||
if (name.empty()) {
|
||||
// default name to subsystem name, but before we parse any instance name
|
||||
@@ -1284,19 +1284,19 @@ namespace {
|
||||
|
||||
auto separatorPos = subsystem.find(SUBSYSTEM_NAME_SEPARATOR);
|
||||
if (separatorPos != std::string::npos) {
|
||||
if (!instanceName.empty()) {
|
||||
if (!subsystemInstanceId.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Specified a composite subsystem name and an instance name, please do one or the other: "
|
||||
<< instanceName << " and " << subsystem);
|
||||
<< subsystemInstanceId << " and " << subsystem);
|
||||
return false;
|
||||
}
|
||||
|
||||
instanceName = subsystem.substr(separatorPos + 1);
|
||||
subsystemInstanceId = subsystem.substr(separatorPos + 1);
|
||||
subsystem = subsystem.substr(0, separatorPos);
|
||||
}
|
||||
|
||||
SGSubsystem* sub = nullptr;
|
||||
if (!instanceName.empty()) {
|
||||
sub = manager->createInstance(subsystem, instanceName);
|
||||
if (!subsystemInstanceId.empty()) {
|
||||
sub = manager->createInstance(subsystem, subsystemInstanceId);
|
||||
} else {
|
||||
sub = manager->create(subsystem);
|
||||
}
|
||||
@@ -1312,7 +1312,7 @@ namespace {
|
||||
|
||||
double minTime = arg->getDoubleValue("min-time-sec", 0.0);
|
||||
|
||||
const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + instanceName;
|
||||
const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + subsystemInstanceId;
|
||||
manager->add(combinedName.c_str(),
|
||||
sub,
|
||||
group,
|
||||
|
||||
@@ -127,6 +127,7 @@ typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, Sam
|
||||
* subsystems may also override the suspend() and resume() methods to
|
||||
* take different actions.</p>
|
||||
*/
|
||||
|
||||
class SGSubsystem : public SGReferenced
|
||||
{
|
||||
public:
|
||||
@@ -281,19 +282,19 @@ public:
|
||||
/**
|
||||
* composite name for this subsystem (type name & optional instance name)
|
||||
*/
|
||||
std::string name() const
|
||||
{ return _name; }
|
||||
std::string subsystemId() const
|
||||
{ return _subsystemId; }
|
||||
|
||||
/**
|
||||
* @brief the type (class)-specific part of the subsystem name.
|
||||
*/
|
||||
std::string typeName() const;
|
||||
std::string subsystemClassId() const;
|
||||
|
||||
/**
|
||||
* @brief the instance part of the subsystem name. Empty if this
|
||||
* subsystem is not instanced
|
||||
*/
|
||||
std::string instanceName() const;
|
||||
std::string subsystemInstanceId() const;
|
||||
|
||||
virtual bool is_group() const
|
||||
{ return false; }
|
||||
@@ -342,26 +343,31 @@ public:
|
||||
protected:
|
||||
friend class SGSubsystemMgr;
|
||||
friend class SGSubsystemGroup;
|
||||
|
||||
|
||||
void set_name(const std::string& n);
|
||||
|
||||
void set_group(SGSubsystemGroup* group);
|
||||
|
||||
|
||||
/// composite name for the subsystem (type name and instance name if this
|
||||
/// is an instanced subsystem. (Since this member was originally defined as
|
||||
/// protected, not private, we can't rename it easily)
|
||||
std::string _name;
|
||||
|
||||
bool _suspended = false;
|
||||
bool _suspended = false;
|
||||
|
||||
eventTimeVec timingInfo;
|
||||
eventTimeVec timingInfo;
|
||||
|
||||
static SGSubsystemTimingCb reportTimingCb;
|
||||
static void* reportTimingUserData;
|
||||
static bool reportTimingStatsRequest;
|
||||
static int maxTimePerFrame_ms;
|
||||
static SGSubsystemTimingCb reportTimingCb;
|
||||
static void* reportTimingUserData;
|
||||
static bool reportTimingStatsRequest;
|
||||
static int maxTimePerFrame_ms;
|
||||
|
||||
private:
|
||||
/// composite name for the subsystem (type name and instance name if this
|
||||
/// is an instanced subsystem. (Since this member was originally defined as
|
||||
/// protected, not private, we can't rename it easily)
|
||||
std::string _subsystemId;
|
||||
|
||||
SGSubsystemGroup* _group = nullptr;
|
||||
protected:
|
||||
TimerStats _timerStats, _lastTimerStats;
|
||||
@@ -378,20 +384,21 @@ typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
|
||||
class SGSubsystemGroup : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
SGSubsystemGroup (const char *name);
|
||||
SGSubsystemGroup ();
|
||||
virtual ~SGSubsystemGroup ();
|
||||
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
InitStatus incrementalInit() override;
|
||||
void init() override;
|
||||
InitStatus incrementalInit () override;
|
||||
void postinit () override;
|
||||
void reinit () override;
|
||||
void shutdown () override;
|
||||
void bind () override;
|
||||
void unbind () override;
|
||||
void update (double delta_time_sec) override;
|
||||
void suspend () override;
|
||||
void resume () override;
|
||||
bool is_suspended () const override;
|
||||
void postinit() override;
|
||||
void reinit() override;
|
||||
void resume() override;
|
||||
void shutdown() override;
|
||||
void suspend() override;
|
||||
void unbind() override;
|
||||
void update(double delta_time_sec) override;
|
||||
bool is_suspended() const override;
|
||||
|
||||
virtual void set_subsystem (const std::string &name,
|
||||
SGSubsystem * subsystem,
|
||||
@@ -416,21 +423,22 @@ public:
|
||||
*/
|
||||
void set_fixed_update_time(double fixed_dt);
|
||||
|
||||
/**
|
||||
* retrive list of member subsystem names
|
||||
*/
|
||||
/**
|
||||
* retrive list of member subsystem names
|
||||
*/
|
||||
string_list member_names() const;
|
||||
|
||||
template<class T>
|
||||
T* get_subsystem()
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName()));
|
||||
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
|
||||
}
|
||||
|
||||
bool is_group() const override
|
||||
{ return true; }
|
||||
|
||||
SGSubsystemMgr* get_manager() const override;
|
||||
|
||||
private:
|
||||
void forEach(std::function<void(SGSubsystem*)> f);
|
||||
void reverseForEach(std::function<void(SGSubsystem*)> f);
|
||||
@@ -501,20 +509,24 @@ public:
|
||||
MAX_GROUPS
|
||||
};
|
||||
|
||||
SGSubsystemMgr (const char *name);
|
||||
SGSubsystemMgr ();
|
||||
virtual ~SGSubsystemMgr ();
|
||||
|
||||
void init () override;
|
||||
InitStatus incrementalInit () override;
|
||||
void postinit () override;
|
||||
void reinit () override;
|
||||
void shutdown () override;
|
||||
void bind () override;
|
||||
void unbind () override;
|
||||
void update (double delta_time_sec) override;
|
||||
void suspend () override;
|
||||
void resume () override;
|
||||
bool is_suspended () const override;
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void init() override;
|
||||
InitStatus incrementalInit() override;
|
||||
void postinit() override;
|
||||
void reinit() override;
|
||||
void resume() override;
|
||||
void shutdown() override;
|
||||
void suspend() override;
|
||||
void unbind() override;
|
||||
void update(double delta_time_sec) override;
|
||||
bool is_suspended() const override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "subsystem-mgr"; }
|
||||
|
||||
virtual void add (const char * name,
|
||||
SGSubsystem * subsystem,
|
||||
@@ -531,7 +543,7 @@ public:
|
||||
|
||||
SGSubsystem* get_subsystem(const std::string &name) const;
|
||||
|
||||
SGSubsystem* get_subsystem(const std::string &name, const std::string& instanceName) const;
|
||||
SGSubsystem* get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const;
|
||||
|
||||
void reportTiming();
|
||||
void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; }
|
||||
@@ -552,34 +564,40 @@ public:
|
||||
template<class T>
|
||||
T* get_subsystem() const
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName()));
|
||||
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
|
||||
}
|
||||
|
||||
// instanced overloads, for both raw char* and std::string
|
||||
// these concatenate the subsystem type name with the instance name
|
||||
template<class T>
|
||||
T* get_subsystem(const char* instanceName) const
|
||||
T* get_subsystem(const char* subsystemInstanceId) const
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName(), instanceName));
|
||||
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T* get_subsystem(const std::string& instanceName) const
|
||||
T* get_subsystem(const std::string& subsystemInstanceId) const
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName(), instanceName));
|
||||
return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subsystem dependency structure.
|
||||
*/
|
||||
struct Dependency {
|
||||
enum Type {
|
||||
HARD,
|
||||
SOFT,
|
||||
PROPERTY
|
||||
HARD, ///< The subsystem cannot run without this subsystem dependency.
|
||||
SOFT, ///< The subsystem uses this subsystem dependency, but can run without it.
|
||||
SEQUENCE, ///< Used for ordering subsystem initialisation.
|
||||
NONSUBSYSTEM_HARD, ///< The subsystem cannot run without this non-subsystem dependency.
|
||||
NONSUBSYSTEM_SOFT, ///< The subsystem uses this non-subsystem dependency, but can run without it.
|
||||
PROPERTY ///< The subsystem requires this property to exist to run.
|
||||
};
|
||||
|
||||
|
||||
std::string name;
|
||||
Type type;
|
||||
};
|
||||
|
||||
|
||||
using DependencyVec = std::vector<SGSubsystemMgr::Dependency>;
|
||||
using SubsystemFactoryFunctor = std::function<SGSubsystemRef()>;
|
||||
|
||||
@@ -598,11 +616,12 @@ public:
|
||||
class Registrant
|
||||
{
|
||||
public:
|
||||
Registrant(GroupType group = GENERAL, double updateInterval = 0.0,
|
||||
std::initializer_list<Dependency> deps = {})
|
||||
Registrant(GroupType group = GENERAL,
|
||||
std::initializer_list<Dependency> deps = {},
|
||||
double updateInterval = 0.0)
|
||||
{
|
||||
SubsystemFactoryFunctor factory = [](){ return new T; };
|
||||
SGSubsystemMgr::registerSubsystem(T::subsystemName(),
|
||||
SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
|
||||
factory, group,
|
||||
false, updateInterval,
|
||||
deps);
|
||||
@@ -616,11 +635,11 @@ public:
|
||||
{
|
||||
public:
|
||||
InstancedRegistrant(GroupType group = GENERAL,
|
||||
double updateInterval = 0.0,
|
||||
std::initializer_list<Dependency> deps = {})
|
||||
std::initializer_list<Dependency> deps = {},
|
||||
double updateInterval = 0.0)
|
||||
{
|
||||
SubsystemFactoryFunctor factory = [](){ return new T; };
|
||||
SGSubsystemMgr::registerSubsystem(T::subsystemName(),
|
||||
SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
|
||||
factory, group,
|
||||
true, updateInterval,
|
||||
deps);
|
||||
@@ -636,14 +655,14 @@ public:
|
||||
template <class T>
|
||||
SGSharedPtr<T> add(GroupType customGroup = INVALID, double customInterval = 0.0)
|
||||
{
|
||||
auto ref = create(T::subsystemName());
|
||||
auto ref = create(T::staticSubsystemClassId());
|
||||
|
||||
|
||||
const GroupType group = (customGroup == INVALID) ?
|
||||
defaultGroupFor(T::subsystemName()) : customGroup;
|
||||
defaultGroupFor(T::staticSubsystemClassId()) : customGroup;
|
||||
const double interval = (customInterval == 0.0) ?
|
||||
defaultUpdateIntervalFor(T::subsystemName()) : customInterval;
|
||||
add(ref->name().c_str(), ref.ptr(), group, interval);
|
||||
defaultUpdateIntervalFor(T::staticSubsystemClassId()) : customInterval;
|
||||
add(ref->subsystemId().c_str(), ref.ptr(), group, interval);
|
||||
return dynamic_cast<T*>(ref.ptr());
|
||||
}
|
||||
|
||||
@@ -654,20 +673,20 @@ public:
|
||||
template <class T>
|
||||
SGSharedPtr<T> create()
|
||||
{
|
||||
auto ref = create(T::subsystemName());
|
||||
auto ref = create(T::staticSubsystemClassId());
|
||||
return dynamic_cast<T*>(ref.ptr());
|
||||
}
|
||||
|
||||
SGSubsystemRef create(const std::string& name);
|
||||
|
||||
template <class T>
|
||||
SGSharedPtr<T> createInstance(const std::string& instanceName)
|
||||
SGSharedPtr<T> createInstance(const std::string& subsystemInstanceId)
|
||||
{
|
||||
auto ref = createInstance(T::subsystemName(), instanceName);
|
||||
auto ref = createInstance(T::staticSubsystemClassId(), subsystemInstanceId);
|
||||
return dynamic_cast<T*>(ref.ptr());
|
||||
}
|
||||
|
||||
SGSubsystemRef createInstance(const std::string& name, const std::string& instanceName);
|
||||
SGSubsystemRef createInstance(const std::string& name, const std::string& subsystemInstanceId);
|
||||
|
||||
static GroupType defaultGroupFor(const char* name);
|
||||
static double defaultUpdateIntervalFor(const char* name);
|
||||
|
||||
@@ -20,7 +20,7 @@ using std::endl;
|
||||
class MySub1 : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "mysub"; }
|
||||
static const char* staticSubsystemClassId() { return "mysub"; }
|
||||
|
||||
void init() override
|
||||
{
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
class AnotherSub : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "anothersub"; }
|
||||
static const char* staticSubsystemClassId() { return "anothersub"; }
|
||||
|
||||
void init() override
|
||||
{
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
class FakeRadioSub : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "fake-radio"; }
|
||||
static const char* staticSubsystemClassId() { return "fake-radio"; }
|
||||
|
||||
void init() override
|
||||
{
|
||||
@@ -89,8 +89,8 @@ public:
|
||||
class InstrumentGroup : public SGSubsystemGroup
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "instruments"; }
|
||||
InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::subsystemName()) {}
|
||||
static const char* staticSubsystemClassId() { return "instruments"; }
|
||||
|
||||
virtual ~InstrumentGroup()
|
||||
{
|
||||
}
|
||||
@@ -119,11 +119,11 @@ class RecorderDelegate : public SGSubsystemMgr::Delegate
|
||||
public:
|
||||
void willChange(SGSubsystem* sub, SGSubsystem::State newState) override
|
||||
{
|
||||
events.push_back({sub->name(), false, newState});
|
||||
events.push_back({sub->subsystemId(), false, newState});
|
||||
}
|
||||
void didChange(SGSubsystem* sub, SGSubsystem::State newState) override
|
||||
{
|
||||
events.push_back({sub->name(), true, newState});
|
||||
events.push_back({sub->subsystemId(), true, newState});
|
||||
}
|
||||
|
||||
struct Event {
|
||||
@@ -164,14 +164,14 @@ SGSubsystemMgr::InstancedRegistrant<FakeRadioSub> registrant3(SGSubsystemMgr::PO
|
||||
|
||||
void testRegistrationAndCreation()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
|
||||
auto anotherSub = manager->create<AnotherSub>();
|
||||
SG_VERIFY(anotherSub);
|
||||
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
|
||||
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
|
||||
SG_CHECK_EQUAL(anotherSub->typeName(), std::string("anothersub"));
|
||||
SG_CHECK_EQUAL(anotherSub->instanceName(), std::string());
|
||||
SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId());
|
||||
SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub"));
|
||||
SG_CHECK_EQUAL(anotherSub->subsystemClassId(), std::string("anothersub"));
|
||||
SG_CHECK_EQUAL(anotherSub->subsystemInstanceId(), std::string());
|
||||
|
||||
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
|
||||
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
|
||||
@@ -181,14 +181,14 @@ void testRegistrationAndCreation()
|
||||
|
||||
void testAddGetRemove()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
auto anotherSub = manager->add<AnotherSub>();
|
||||
SG_VERIFY(anotherSub);
|
||||
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
|
||||
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
|
||||
SG_CHECK_EQUAL(anotherSub->subsystemId(), AnotherSub::staticSubsystemClassId());
|
||||
SG_CHECK_EQUAL(anotherSub->subsystemId(), std::string("anothersub"));
|
||||
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-add"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-add"));
|
||||
@@ -200,14 +200,14 @@ void testAddGetRemove()
|
||||
|
||||
// manual create & add
|
||||
auto mySub = manager->create<MySub1>();
|
||||
manager->add(MySub1::subsystemName(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234);
|
||||
manager->add(MySub1::staticSubsystemClassId(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234);
|
||||
|
||||
SG_VERIFY(d->hasEvent("mysub-will-add"));
|
||||
SG_VERIFY(d->hasEvent("mysub-did-add"));
|
||||
|
||||
SG_CHECK_EQUAL(manager->get_subsystem<MySub1>(), mySub);
|
||||
|
||||
bool ok = manager->remove(AnotherSub::subsystemName());
|
||||
bool ok = manager->remove(AnotherSub::staticSubsystemClassId());
|
||||
SG_VERIFY(ok);
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-remove"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-remove"));
|
||||
@@ -219,7 +219,7 @@ void testAddGetRemove()
|
||||
|
||||
// re-add of removed, and let's test overriding
|
||||
auto another2 = manager->add<AnotherSub>(SGSubsystemMgr::SOUND);
|
||||
SG_CHECK_EQUAL(another2->name(), AnotherSub::subsystemName());
|
||||
SG_CHECK_EQUAL(another2->subsystemId(), AnotherSub::staticSubsystemClassId());
|
||||
|
||||
auto soundGroup = manager->get_group(SGSubsystemMgr::SOUND);
|
||||
SG_CHECK_EQUAL(soundGroup->get_subsystem("anothersub"), another2);
|
||||
@@ -228,7 +228,7 @@ void testAddGetRemove()
|
||||
|
||||
void testSubGrouping()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
@@ -238,10 +238,10 @@ void testSubGrouping()
|
||||
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
|
||||
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
|
||||
|
||||
SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio.nav1"));
|
||||
SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio.nav2"));
|
||||
SG_CHECK_EQUAL(radio1->typeName(), std::string("fake-radio"));
|
||||
SG_CHECK_EQUAL(radio2->instanceName(), std::string("nav2"));
|
||||
SG_CHECK_EQUAL(radio1->subsystemId(), std::string("fake-radio.nav1"));
|
||||
SG_CHECK_EQUAL(radio2->subsystemId(), std::string("fake-radio.nav2"));
|
||||
SG_CHECK_EQUAL(radio1->subsystemClassId(), std::string("fake-radio"));
|
||||
SG_CHECK_EQUAL(radio2->subsystemInstanceId(), std::string("nav2"));
|
||||
|
||||
instruments->set_subsystem(radio1);
|
||||
instruments->set_subsystem(radio2);
|
||||
@@ -299,7 +299,7 @@ void testSubGrouping()
|
||||
|
||||
void testIncrementalInit()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
@@ -347,7 +347,7 @@ void testEmptyGroup()
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2043/
|
||||
// when an empty group is inited, we skipped setting the state
|
||||
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
@@ -371,7 +371,7 @@ void testEmptyGroup()
|
||||
|
||||
void testSuspendResume()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
@@ -441,7 +441,7 @@ void testSuspendResume()
|
||||
|
||||
void testPropertyRoot()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
SGPropertyNode_ptr props(new SGPropertyNode);
|
||||
manager->set_root_node(props);
|
||||
|
||||
@@ -467,7 +467,7 @@ void testPropertyRoot()
|
||||
|
||||
void testAddRemoveAfterInit()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr();
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include "SGThread.hxx"
|
||||
|
||||
@@ -416,3 +417,98 @@ SGWaitCondition::broadcast()
|
||||
{
|
||||
_privateData->broadcast();
|
||||
}
|
||||
|
||||
|
||||
SGExclusiveThread::SGExclusiveThread() :
|
||||
_started(false), _terminated(false), last_await_time(0),
|
||||
dataReady(false), complete(true), process_ran(false), process_running(false)
|
||||
{
|
||||
}
|
||||
|
||||
SGExclusiveThread::~SGExclusiveThread()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SGExclusiveThread::release() {
|
||||
std::unique_lock<std::mutex> lck(mutex_);
|
||||
if (!complete) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] not finished - skipping");
|
||||
return;
|
||||
}
|
||||
if (!complete.exchange(false))
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (2)");
|
||||
if (dataReady.exchange(true))
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] concurrent failure (1)");
|
||||
condVar.notify_one();
|
||||
}
|
||||
void SGExclusiveThread::wait() {
|
||||
std::unique_lock<std::mutex> lck(mutex_);
|
||||
if (!dataReady)
|
||||
{
|
||||
do
|
||||
{
|
||||
condVar.wait(lck);
|
||||
} while (!dataReady);
|
||||
}
|
||||
}
|
||||
void SGExclusiveThread::clearAwaitCompletionTime() {
|
||||
last_await_time = 0;
|
||||
}
|
||||
void SGExclusiveThread::awaitCompletion() {
|
||||
timestamp.stamp();
|
||||
std::unique_lock<std::mutex> lck(Cmutex_);
|
||||
if (!complete)
|
||||
{
|
||||
do {
|
||||
CcondVar.wait(lck);
|
||||
} while (!complete.load());
|
||||
}
|
||||
|
||||
if (process_ran) {
|
||||
last_await_time = timestamp.elapsedUSec();
|
||||
process_ran = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SGExclusiveThread::setCompletion() {
|
||||
std::unique_lock<std::mutex> lck(Cmutex_);
|
||||
if (!dataReady.exchange(false))
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on dataReady failed (5)\n");
|
||||
|
||||
if (complete.exchange(true))
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "[SGExclusiveThread] atomic operation on complete failed (5)\n");
|
||||
CcondVar.notify_one();
|
||||
}
|
||||
void SGExclusiveThread::run()
|
||||
{
|
||||
process_running = true;
|
||||
while (!_terminated) {
|
||||
wait();
|
||||
process_ran = process();
|
||||
setCompletion();
|
||||
}
|
||||
process_running = false;
|
||||
_terminated = false;
|
||||
_started = false;
|
||||
}
|
||||
|
||||
void SGExclusiveThread::terminate() {
|
||||
_terminated = true;
|
||||
}
|
||||
bool SGExclusiveThread::stop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void SGExclusiveThread::ensure_running()
|
||||
{
|
||||
if (!_started)
|
||||
{
|
||||
_started = true;
|
||||
start();
|
||||
}
|
||||
}
|
||||
bool SGExclusiveThread::is_running()
|
||||
{
|
||||
return process_running;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
#ifndef SGTHREAD_HXX_INCLUDED
|
||||
#define SGTHREAD_HXX_INCLUDED 1
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
/**
|
||||
* Encapsulate generic threading methods.
|
||||
@@ -184,4 +188,41 @@ private:
|
||||
PrivateData* _privateData;
|
||||
};
|
||||
|
||||
///
|
||||
/// an exclusive thread is one that is designed for frame processing;
|
||||
/// it has the ability to synchronise such that the caller can await
|
||||
/// the processing to finish.
|
||||
class SGExclusiveThread : public SGThread{
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condVar;
|
||||
SGTimeStamp timestamp;
|
||||
std::mutex Cmutex_;
|
||||
std::condition_variable CcondVar;
|
||||
|
||||
bool _started;
|
||||
bool _terminated;
|
||||
int last_await_time;
|
||||
|
||||
std::atomic<bool> dataReady;
|
||||
std::atomic<bool> complete;
|
||||
std::atomic<bool> process_ran;
|
||||
std::atomic<bool> process_running;
|
||||
|
||||
public:
|
||||
SGExclusiveThread();
|
||||
virtual ~SGExclusiveThread();
|
||||
void release();
|
||||
void wait();
|
||||
void clearAwaitCompletionTime();
|
||||
virtual void awaitCompletion();
|
||||
void setCompletion();
|
||||
virtual int process() = 0;
|
||||
virtual void run();
|
||||
void terminate();
|
||||
bool stop();
|
||||
void ensure_running();
|
||||
bool is_running();
|
||||
};
|
||||
|
||||
#endif /* SGTHREAD_HXX_INCLUDED */
|
||||
|
||||
@@ -337,3 +337,11 @@ int SGTimeStamp::elapsedMSec() const
|
||||
|
||||
return static_cast<int>((now - *this).toMSecs());
|
||||
}
|
||||
|
||||
int SGTimeStamp::elapsedUSec() const
|
||||
{
|
||||
SGTimeStamp now;
|
||||
now.stamp();
|
||||
|
||||
return static_cast<int>((now - *this).toUSecs());
|
||||
}
|
||||
|
||||
@@ -221,9 +221,13 @@ public:
|
||||
{ return sleepFor(fromMSec(msec)); }
|
||||
|
||||
/**
|
||||
* elapsed time since the stamp was taken, in msec
|
||||
*/
|
||||
* elapsed time since the stamp was taken, in msec
|
||||
*/
|
||||
int elapsedMSec() const;
|
||||
/**
|
||||
* elapsed time since the stamp was taken, in usec
|
||||
*/
|
||||
int elapsedUSec() const;
|
||||
private:
|
||||
SGTimeStamp(sec_type sec, nsec_type nsec)
|
||||
{ setTime(sec, nsec); }
|
||||
|
||||
Reference in New Issue
Block a user