Compare commits

..
Author SHA1 Message Date
Automatic Release Builder a9dddbcddc new version: 2020.3.10 2021-07-06 10:56:20 +01:00
James Turner fcd72581a1 Make out-of-memory in computeHash fatal 2021-07-06 02:53:28 +01:00
James Turner 0a6091eeeb Error reporting for animations / conditions
Don’t report errors for each condition parse failure, but do 
aggregate them at the animation level in ReaderWriterXML

Sentry-Id: FLIGHTGEAR-DD
2021-07-06 02:50:11 +01:00
James Turner d6400e3737 Add try/catch wrappers in SGSubsystemGroup
Attempt to narrow down the source of some fatal exceptions we see
on Senty, which occur during init/startup. All these blocks re-throw
so user behaviour is unchanged, but we’ll log the name of the subsytem.
2021-07-06 02:49:51 +01:00
James Turner 71e7050349 Attempt to fix realpath() recursion
Sentry-Id: FLIGHTGEAR-D6W
2021-06-15 17:31:33 +01:00
Automatic Release Builder e6f4936536 better STG error reporting 2021-06-15 02:33:31 +01:00
Automatic Release Builder b2ae7e9e63 Fix initial effect errors 2021-06-15 02:33:20 +01:00
Automatic Release Builder ee0b22fd52 Fix HTTP repo erros 2021-06-15 02:33:11 +01:00
James Turner 01233ba537 Error-reporting: improve reports for STGs and effects
When an effect defines no shader sources, special case this error to
avoid confusing result from SGProgram.

Add error-context for readNode STG loading, so failures inside an
STG can be attributed
2021-06-14 20:01:30 +01:00
James Turner ed29d9b75d Effects: better error reporting 2021-06-14 20:00:04 +01:00
James Turner 5d4201cdfa Shaders: report all shader file paths
When reporting a shader log error, include all the shader file
paths and the effect path in the detailed error information.
2021-06-14 19:59:36 +01:00
James Turner 2ef8c0d27b ReadWav: error reporting
Report errors from readWAV, and improve attribution for the
format error (include the file path). As a result, mark the individual
exceptions as dont-report, since we will report higher up.
2021-06-14 19:58:42 +01:00
James Turner 0fbfa3426f Catalogs: check for local IO failures writing to disk
Sentry.io shows some examples where we failed to write valid data
to disk; check for that if possible.
2021-06-14 19:58:18 +01:00
James Turner 57b4060eb3 Error-reporting: show XML path when 3D load fails 2021-06-14 19:57:16 +01:00
James Turner 84a569913d Report out-of-memory in some loading places
BTG can throw bad-alloc in the wild; catch this case and report it.
2021-06-14 19:56:53 +01:00
James Turner b585df04a5 TerraSync: report errors during downloading
Report various out-of-memory and IO failure conditions; especially,
failure to write downloaded to disk, which was previously not 
recorded.
2021-06-14 19:55:07 +01:00
James Turner ddba0c6731 Add osg::Program wrapper for error-reporting 2021-06-14 19:53:32 +01:00
James Turner 43d849232b ErrorReporting: set context for STG loading
Ensure the STG absolute path can be propagated to all files triggered
by STG loading, including the delayed files and proxied files. This
allows us to attribute errors to the correct scenery path.
2021-06-14 19:48:52 +01:00
James Turner 651460bbc8 Allow delayed setup of ErrorContext data
Permit no model data to be provided, when loading a model via the
modelLib API (d’oh)
2021-06-14 19:36:43 +01:00
James Turner 81a489d81d Error reporting: add SGModelData context support
Allow us to pass the current error-context from the main thread
into the osgDB loader thread. This is necessary so we can attribute
AIModel (etc) load problems to the correct source.
2021-06-14 19:36:25 +01:00
James Turner 7e76667af0 Error-reporting: add some errors for Effect shaders 2021-06-14 19:35:49 +01:00
James Turner 32f69df774 Error reporting for submodels
Also add some error codes I forgot for AI and traffic.
2021-06-14 19:34:55 +01:00
James Turner d8d64b2367 Add failure reporting / context to various places.
Not total coverage by far, but working through the list of common
failure points as seen on Sentry
2021-06-14 19:34:28 +01:00
James Turner 747d99450b Suppress reports on some common exception throws 2021-06-14 19:27:26 +01:00
James Turner b023c7c4f4 Add new error reporting function / callback 2021-06-14 19:23:54 +01:00
30 changed files with 755 additions and 190 deletions
+1 -1
View File
@@ -1 +1 @@
2020.3.9 2020.3.10
+73
View File
@@ -17,11 +17,15 @@
#include "ErrorReportingCallback.hxx" #include "ErrorReportingCallback.hxx"
#include <simgear/misc/sg_path.hxx>
using std::string; using std::string;
namespace simgear { namespace simgear {
static ErrorReportCallback static_callback; static ErrorReportCallback static_callback;
static ContextCallback static_contextCallback;
void setErrorReportCallback(ErrorReportCallback cb) void setErrorReportCallback(ErrorReportCallback cb)
{ {
@@ -44,4 +48,73 @@ void reportFatalError(const std::string& msg, const std::string& more)
static_callback(msg, more, true); static_callback(msg, more, true);
} }
static FailureCallback static_failureCallback;
void reportFailure(LoadFailure type, ErrorCode code, const std::string& details, sg_location loc)
{
if (!static_failureCallback) {
return;
}
static_failureCallback(type, code, details, loc);
}
void reportFailure(LoadFailure type, ErrorCode code, const std::string& details, const SGPath& path)
{
if (!static_failureCallback) {
return;
}
static_failureCallback(type, code, details, sg_location{path});
}
void setFailureCallback(FailureCallback cb)
{
static_failureCallback = cb;
}
void setErrorContextCallback(ContextCallback cb)
{
static_contextCallback = cb;
}
ErrorReportContext::ErrorReportContext(const std::string& key, const std::string& value)
{
add(key, value);
}
void ErrorReportContext::add(const std::string& key, const std::string& value)
{
if (static_contextCallback) {
_keys.push_back(key);
static_contextCallback(key, value);
}
}
ErrorReportContext::ErrorReportContext(const ContextMap& context)
{
addFromMap(context);
}
void ErrorReportContext::addFromMap(const ContextMap& context)
{
if (static_contextCallback) {
for (const auto& p : context) {
_keys.push_back(p.first);
static_contextCallback(p.first, p.second);
}
}
}
ErrorReportContext::~ErrorReportContext()
{
if (static_contextCallback) {
// pop all our keys
for (const auto& k : _keys) {
static_contextCallback(k, "POP");
}
}
}
} // namespace simgear } // namespace simgear
+97
View File
@@ -16,7 +16,14 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <map>
#include <string> #include <string>
#include <vector>
#include <simgear/structure/exception.hxx>
//forward decls
class SGPath;
namespace simgear { namespace simgear {
@@ -28,4 +35,94 @@ using ErrorReportCallback = std::function<void(const std::string& msg, const std
void setErrorReportCallback(ErrorReportCallback cb); void setErrorReportCallback(ErrorReportCallback cb);
/** kinds of failures we can report. This is *how* (or why) something failed. Extend
as necessary but update the correponsdings string translations if you do. More detail isn't
necessariyl useful here: better to provide that in the 'details' string
*/
enum class LoadFailure {
Unknown,
NotFound,
OutOfMemory,
BadHeader,
BadData,
Misconfigured,
IOError, // disk full, permissions error, etc
NetworkError
};
/**
@brief enum of the operations which can fail. This should be extended as necessary: it maps to
translated error messages for the user. This describes what failed, the enum above gives why/how. The
combination of what+why should be something at the user level: use details for debug-level information.
*/
enum class ErrorCode {
LoadEffectsShaders,
LoadingTexture,
XMLModelLoad,
ThreeDModelLoad, // AC3D, OBJ, etc
BTGLoad,
ScenarioLoad,
GUIDialog,
AudioFX,
XMLLoadCommand,
AircraftSystems, // autopilot, hydrualics, instruments
InputDeviceConfig,
AITrafficSchedule,
TerraSync
};
/**
@brief Define an error-reporting context value, for the duration of this
object's lifetime. The context value will be active for any errors occuring on the same thread, while
this object exists.
*/
class ErrorReportContext
{
public:
ErrorReportContext(const std::string& key, const std::string& value);
using ContextMap = std::map<std::string, std::string>;
/**
Allow establishing multiple context values in a single operation
*/
ErrorReportContext(const ContextMap& context = {});
void add(const std::string& key, const std::string& value);
/**
@brief allowed delayed add of values
*/
void addFromMap(const ContextMap& context);
~ErrorReportContext();
private:
std::vector<std::string> _keys;
};
/**
* @brief Report failure to load a resource, so they can be collated for reporting
* to the user.
*
* @param type - the reason for the failure, if it can be determined
* @param msg - an informational message about what caused the failure
* @param path - path on disk to the resource. In some cases this may be a relative path;
* especially in the case of a resource not found, we cannot report a file path.
*/
void reportFailure(LoadFailure type, ErrorCode code, const std::string& detailedMessage = {}, sg_location loc = {});
/**
overload taking a path as the location
*/
void reportFailure(LoadFailure type, ErrorCode code, const std::string& detailedMessage, const SGPath& p);
using FailureCallback = std::function<void(LoadFailure type, ErrorCode code, const std::string& details, const sg_location& location)>;
void setFailureCallback(FailureCallback cb);
using ContextCallback = std::function<void(const std::string& key, const std::string& value)>;
void setErrorContextCallback(ContextCallback cb);
} // namespace simgear } // namespace simgear
+57 -17
View File
@@ -35,11 +35,12 @@
#include "simgear/debug/logstream.hxx" #include "simgear/debug/logstream.hxx"
#include "simgear/misc/strutils.hxx" #include "simgear/misc/strutils.hxx"
#include <simgear/misc/sg_dir.hxx> #include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/io/HTTPClient.hxx> #include <simgear/io/HTTPClient.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/io/sg_file.hxx> #include <simgear/io/sg_file.hxx>
#include <simgear/io/untar.hxx> #include <simgear/io/untar.hxx>
#include <simgear/io/iostreams/sgstream.hxx> #include <simgear/misc/sg_dir.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include <simgear/timing/timestamp.hxx> #include <simgear/timing/timestamp.hxx>
@@ -218,8 +219,7 @@ public:
return; return;
} }
char* buf = nullptr; std::vector<char> buf;
size_t bufSize = 0;
for (auto& child : children) { for (auto& child : children) {
if (child.type != HTTPRepository::FileType) if (child.type != HTTPRepository::FileType)
@@ -240,17 +240,34 @@ public:
src.open(SG_IO_IN); src.open(SG_IO_IN);
dst.open(SG_IO_OUT); dst.open(SG_IO_OUT);
if (bufSize < cp.sizeInBytes()) { const auto sizeToCopy = cp.sizeInBytes();
bufSize = cp.sizeInBytes(); if (buf.size() < sizeToCopy) {
free(buf); try {
buf = (char*)malloc(bufSize); buf.resize(sizeToCopy);
if (!buf) { } catch (std::bad_alloc) {
continue; simgear::reportFailure(simgear::LoadFailure::OutOfMemory, simgear::ErrorCode::TerraSync,
"copyInstalledChildren: couldn't allocation copy buffer of size:" + std::to_string(sizeToCopy),
child.path);
return;
} }
} }
src.read(buf, cp.sizeInBytes()); const auto r = src.read(buf.data(), sizeToCopy);
dst.write(buf, cp.sizeInBytes()); if (r != sizeToCopy) {
simgear::reportFailure(simgear::LoadFailure::IOError, simgear::ErrorCode::TerraSync,
"copyInstalledChildren: read underflow, got:" + std::to_string(r),
cp);
return;
}
const auto written = dst.write(buf.data(), sizeToCopy);
if (written != sizeToCopy) {
simgear::reportFailure(simgear::LoadFailure::IOError, simgear::ErrorCode::TerraSync,
"copyInstalledChildren: write underflow, wrote:" + std::to_string(r),
child.path);
return;
}
src.close(); src.close();
dst.close(); dst.close();
@@ -260,9 +277,7 @@ public:
std::string hash = computeHashForPath(child.path); std::string hash = computeHashForPath(child.path);
updatedFileContents(child.path, hash); updatedFileContents(child.path, hash);
} }
free(buf);
} }
/// helper to check and erase 'fooBar' from paths, if passed fooBar.zip, fooBar.tgz, etc. /// helper to check and erase 'fooBar' from paths, if passed fooBar.zip, fooBar.tgz, etc.
@@ -1019,13 +1034,20 @@ HTTPRepository::failure() const
if (!file.get()) { if (!file.get()) {
const bool ok = createOutputFile(); const bool ok = createOutputFile();
if (!ok) { if (!ok) {
ioFailureOccurred = true;
_directory->repository()->http->cancelRequest( _directory->repository()->http->cancelRequest(
this, "Unable to create output file:" + pathInRepo.utf8Str()); this, "Unable to create output file:" + pathInRepo.utf8Str());
} }
} }
sha1_write(&hashContext, s, n); sha1_write(&hashContext, s, n);
file->write(s, n); const auto written = file->write(s, n);
if (written != n) {
SG_LOG(SG_TERRASYNC, SG_WARN, "Underflow writing to " << pathInRepo);
ioFailureOccurred = true;
_directory->repository()->http->cancelRequest(
this, "Unable to write to output file:" + pathInRepo.utf8Str());
}
} }
bool createOutputFile() bool createOutputFile()
@@ -1082,8 +1104,15 @@ HTTPRepository::failure() const
void onFail() override { void onFail() override {
HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET; HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET;
// -1 means request cancelled locally
if (responseCode() == -1) { if (responseCode() == -1) {
code = HTTPRepository::REPO_ERROR_CANCELLED; if (ioFailureOccurred) {
// cancelled by code above due to IO error
code = HTTPRepository::REPO_ERROR_IO;
} else {
code = HTTPRepository::REPO_ERROR_CANCELLED;
}
} }
if (file) { if (file) {
@@ -1120,6 +1149,10 @@ HTTPRepository::failure() const
SGPath pathInRepo; SGPath pathInRepo;
simgear::sha1nfo hashContext; simgear::sha1nfo hashContext;
std::unique_ptr<SGBinaryFile> file; std::unique_ptr<SGBinaryFile> file;
/// becuase we cancel() in the case of an IO failure, we need to a way to distuinguish
/// user initated cancellation and IO-failure cancellation in onFail. This flag lets us do that
bool ioFailureOccurred = false;
}; };
class DirGetRequest : public HTTPRepoGetRequest class DirGetRequest : public HTTPRepoGetRequest
@@ -1373,6 +1406,9 @@ HTTPRepository::failure() const
if (st == HTTPRepository::REPO_ERROR_FILE_NOT_FOUND) { if (st == HTTPRepository::REPO_ERROR_FILE_NOT_FOUND) {
status = HTTPRepository::REPO_ERROR_NOT_FOUND; status = HTTPRepository::REPO_ERROR_NOT_FOUND;
} else { } else {
simgear::reportFailure(simgear::LoadFailure::NetworkError, simgear::ErrorCode::TerraSync,
"failed to get TerraSync repository root:" + innerResultCodeAsString(st),
sg_location{baseUrl});
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st); SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st);
status = st; status = st;
} }
@@ -1389,6 +1425,10 @@ HTTPRepository::failure() const
"failed to update entry:" << relativePath << " status/code: " "failed to update entry:" << relativePath << " status/code: "
<< innerResultCodeAsString(fileStatus) << innerResultCodeAsString(fileStatus)
<< "/" << fileStatus); << "/" << fileStatus);
simgear::reportFailure(simgear::LoadFailure::NetworkError, simgear::ErrorCode::TerraSync,
"failed to update entry:" + innerResultCodeAsString(fileStatus),
sg_location{relativePath});
} }
HTTPRepository::Failure f; HTTPRepository::Failure f;
+4 -1
View File
@@ -41,9 +41,10 @@
#include <bitset> #include <bitset>
#include <simgear/bucket/newbucket.hxx> #include <simgear/bucket/newbucket.hxx>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx> #include <simgear/misc/strutils.hxx>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include "lowlevel.hxx" #include "lowlevel.hxx"
@@ -502,6 +503,8 @@ bool SGBinObject::read_bin( const SGPath& file )
unsigned int nbytes; unsigned int nbytes;
sgSimpleBuffer buf( 32768 ); // 32 Kb sgSimpleBuffer buf( 32768 ); // 32 Kb
simgear::ErrorReportContext ec("btg", file.utf8Str());
// zero out structures // zero out structures
gbs_center = SGVec3d(0, 0, 0); gbs_center = SGVec3d(0, 0, 0);
gbs_radius = 0.0; gbs_radius = 0.0;
+1 -3
View File
@@ -82,9 +82,7 @@ std::string SGFile::computeHash()
[](char* p) { free(p); }}; [](char* p) { free(p); }};
if (!buf) { if (!buf) {
// @TODO report out of memory error throw sg_exception("Malloc of SHA buffer failed", {}, file_name);
SG_LOG(SG_IO, SG_ALERT, "Failed to malloc buffer for SHA1 check:" << file_name);
return {};
} }
size_t readLen; size_t readLen;
+3 -1
View File
@@ -48,6 +48,7 @@
#endif #endif
#include "sg_path.hxx" #include "sg_path.hxx"
#include <simgear/misc/sg_dir.hxx>
using std::string; using std::string;
using simgear::strutils::starts_with; using simgear::strutils::starts_with;
@@ -995,8 +996,9 @@ SGPath SGPath::realpath() const
// (needed for fgValidatePath security) // (needed for fgValidatePath security)
{ {
if (path.empty()) { if (path.empty()) {
return SGPath(".").realpath(); // current directory return simgear::Dir::current().path();
} }
std::string this_dir = dir(); std::string this_dir = dir();
if (isAbsolute() && this_dir.empty()) { // top level if (isAbsolute() && this_dir.empty()) { // top level
this_dir = "/"; this_dir = "/";
+7 -1
View File
@@ -143,9 +143,15 @@ protected:
Dir d(m_owner->installRoot()); Dir d(m_owner->installRoot());
SGPath p = d.file("catalog.xml"); SGPath p = d.file("catalog.xml");
sg_ofstream f(p, std::ios::out | std::ios::trunc); sg_ofstream f(p, std::ios::out | std::ios::trunc);
f.write(m_buffer.data(), m_buffer.size()); const auto sz = m_buffer.size();
f.write(m_buffer.data(), sz);
f.close(); f.close();
if (f.fail()) {
m_owner->refreshComplete(Delegate::FAIL_FILESYSTEM);
return;
}
time(&m_owner->m_retrievedTime); time(&m_owner->m_retrievedTime);
m_owner->writeTimestamp(); m_owner->writeTimestamp();
m_owner->refreshComplete(Delegate::STATUS_REFRESHED); m_owner->refreshComplete(Delegate::STATUS_REFRESHED);
+8 -4
View File
@@ -535,7 +535,8 @@ readComparison( SGPropertyNode *prop_root,
{ {
SGComparisonCondition * condition = new SGComparisonCondition(type, reverse); SGComparisonCondition * condition = new SGComparisonCondition(type, reverse);
if (node->nChildren() < 2 || node->nChildren() > 3 ) { if (node->nChildren() < 2 || node->nChildren() > 3 ) {
throw sg_exception("condition: comparison without two or three children"); throw sg_exception("condition: comparison without two or three children",
{}, {}, false);
} }
const SGPropertyNode* left = node->getChild(0), const SGPropertyNode* left = node->getChild(0),
@@ -551,7 +552,8 @@ readComparison( SGPropertyNode *prop_root,
SGExpressiond* exp = SGReadDoubleExpression(prop_root, left->getChild(0)); SGExpressiond* exp = SGReadDoubleExpression(prop_root, left->getChild(0));
condition->setLeftDExpression(exp); condition->setLeftDExpression(exp);
} else { } else {
throw sg_exception("Unknown condition comparison left child:" + leftName); throw sg_exception("Unknown condition comparison left child:" + leftName,
{}, {}, false);
} }
} }
@@ -565,7 +567,8 @@ readComparison( SGPropertyNode *prop_root,
SGExpressiond* exp = SGReadDoubleExpression(prop_root, right->getChild(0)); SGExpressiond* exp = SGReadDoubleExpression(prop_root, right->getChild(0));
condition->setRightDExpression(exp); condition->setRightDExpression(exp);
} else { } else {
throw sg_exception("Unknown condition comparison right child:" + rightName); throw sg_exception("Unknown condition comparison right child:" + rightName,
{}, {}, false);
} }
} }
@@ -580,7 +583,8 @@ readComparison( SGPropertyNode *prop_root,
SGExpressiond* exp = SGReadDoubleExpression(prop_root, n->getChild(0)); SGExpressiond* exp = SGReadDoubleExpression(prop_root, n->getChild(0));
condition->setPrecisionDExpression(exp); condition->setPrecisionDExpression(exp);
} else { } else {
throw sg_exception("Unknown condition comparison precision child:" + name ); throw sg_exception("Unknown condition comparison precision child:" + name,
{}, {}, false);
} }
} }
+65 -16
View File
@@ -67,9 +67,14 @@
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
#include <osgDB/Registry> #include <osgDB/Registry>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/tgdb/userdata.hxx> #include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/scene/util/OsgMath.hxx> #include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGProgram.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx> #include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx> #include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx> #include <simgear/structure/OSGUtils.hxx>
@@ -103,6 +108,7 @@ bool loadShaderFromUTF8File(osg::Shader* shader, const std::string& fileName)
if (!inStream.is_open()) if (!inStream.is_open())
return false; return false;
shader->setFileName(fileName);
shader->setShaderSource(inStream.read_all()); shader->setShaderSource(inStream.read_all());
return true; return true;
} }
@@ -221,8 +227,9 @@ Effect::Effect()
} }
Effect::Effect(const Effect& rhs, const CopyOp& copyop) Effect::Effect(const Effect& rhs, const CopyOp& copyop)
: osg::Object(rhs,copyop), root(rhs.root), parametersProp(rhs.parametersProp), _cache(0), : osg::Object(rhs, copyop), root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
_isRealized(rhs._isRealized) _isRealized(rhs._isRealized),
_effectFilePath(rhs._effectFilePath)
{ {
typedef vector<ref_ptr<Technique> > TechniqueList; typedef vector<ref_ptr<Technique> > TechniqueList;
for (TechniqueList::const_iterator itr = rhs.techniques.begin(), for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
@@ -287,6 +294,8 @@ Effect::~Effect()
void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop, void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
const SGReaderWriterOptions* options) const SGReaderWriterOptions* options)
{ {
simgear::ErrorReportContext ec("effect-pass", prop->getPath());
Pass* pass = new Pass; Pass* pass = new Pass;
tniq->passes.push_back(pass); tniq->passes.push_back(pass);
for (int i = 0; i < prop->nChildren(); ++i) { for (int i = 0; i < prop->nChildren(); ++i) {
@@ -837,10 +846,12 @@ void reload_shaders()
string fileName = SGModelLib::findDataFile(sitr->first.first); string fileName = SGModelLib::findDataFile(sitr->first.first);
if (!fileName.empty()) { if (!fileName.empty()) {
loadShaderFromUTF8File(shader, fileName); loadShaderFromUTF8File(shader, fileName);
} } else {
else
SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader: " << fileName); SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader: " << fileName);
simgear::reportFailure(simgear::LoadFailure::NotFound,
simgear::ErrorCode::LoadEffectsShaders,
"Reload: couldn't find shader:" + sitr->first.first);
}
} }
} }
@@ -936,10 +947,8 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
string fileName = SGModelLib::findDataFile(shaderName, options); string fileName = SGModelLib::findDataFile(shaderName, options);
if (fileName.empty()) if (fileName.empty())
{ {
SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader" << shaderName); simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadEffectsShaders,
if (!compositorEnabled) { "Couldn't locate shader:" + shaderName, sg_location{shaderName});
reportError("Missing shader", shaderName);
}
throw BuilderException(string("couldn't find shader ") + throw BuilderException(string("couldn't find shader ") +
shaderName); shaderName);
@@ -953,7 +962,11 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
pass->setAttributeAndModes(program); pass->setAttributeAndModes(program);
return; return;
} }
program = new Program;
auto sgprogram = new SGProgram;
program = sgprogram;
sgprogram->setEffectFilePath(effect->filePath());
for (const auto& skey : resolvedKey.shaders) { for (const auto& skey : resolvedKey.shaders) {
const string& fileName = skey.first; const string& fileName = skey.first;
Shader::Type stype = (Shader::Type)skey.second; Shader::Type stype = (Shader::Type)skey.second;
@@ -964,11 +977,30 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
ref_ptr<Shader> shader = new Shader(stype); ref_ptr<Shader> shader = new Shader(stype);
shader->setName(fileName); shader->setName(fileName);
if (loadShaderFromUTF8File(shader, fileName)) { if (loadShaderFromUTF8File(shader, fileName)) {
program->addShader(shader.get()); if (!program->addShader(shader.get())) {
simgear::reportFailure(simgear::LoadFailure::BadData,
simgear::ErrorCode::LoadEffectsShaders,
"Program::addShader failed",
SGPath::fromUtf8(fileName));
}
shaderMap.insert(ShaderMap::value_type(skey, shader)); shaderMap.insert(ShaderMap::value_type(skey, shader));
} else {
simgear::reportFailure(simgear::LoadFailure::BadData,
simgear::ErrorCode::LoadEffectsShaders,
"Failed to read shader source code",
SGPath::fromUtf8(fileName));
} }
} }
} }
if (sgprogram->getNumShaders() == 0) {
simgear::reportFailure(simgear::LoadFailure::BadData,
simgear::ErrorCode::LoadEffectsShaders,
"No shader source code defined for effect",
effect->filePath());
}
for (const auto& key : prgKey.attributes) { for (const auto& key : prgKey.attributes) {
program->addBindAttribLocation(key.first, key.second); program->addBindAttribLocation(key.first, key.second);
} }
@@ -1329,6 +1361,8 @@ InstallAttributeBuilder<DepthBuilder> installDepth("depth");
void buildTechnique(Effect* effect, const SGPropertyNode* prop, void buildTechnique(Effect* effect, const SGPropertyNode* prop,
const SGReaderWriterOptions* options) const SGReaderWriterOptions* options)
{ {
simgear::ErrorReportContext ec("effect-technique", prop->getPath());
Technique* tniq = new Technique; Technique* tniq = new Technique;
effect->techniques.push_back(tniq); effect->techniques.push_back(tniq);
tniq->setScheme(prop->getStringValue("scheme")); tniq->setScheme(prop->getStringValue("scheme"));
@@ -1486,16 +1520,19 @@ void mergeSchemesFallbacks(Effect *effect, const SGReaderWriterOptions *options)
// passes. // passes.
bool Effect::realizeTechniques(const SGReaderWriterOptions* options) bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{ {
simgear::ErrorReportContext ec{"effect", getName()};
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false)) if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
mergeSchemesFallbacks(this, options); mergeSchemesFallbacks(this, options);
if (_isRealized) if (_isRealized)
return true; return true;
PropertyList tniqList = root->getChildren("technique"); PropertyList tniqList = root->getChildren("technique");
for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end(); for (const auto& tniq : tniqList) {
itr != e; buildTechnique(this, tniq, options);
++itr) }
buildTechnique(this, *itr, options);
_isRealized = true; _isRealized = true;
return true; return true;
} }
@@ -1631,3 +1668,15 @@ expression::ExpParserRegistrar propvalueRegistrar("float-property",
propertyExpressionParser<float>); propertyExpressionParser<float>);
} }
using namespace simgear;
void Effect::setFilePath(const SGPath& path)
{
_effectFilePath = path;
}
SGPath Effect::filePath() const
{
return _effectFilePath;
}
+12 -3
View File
@@ -29,10 +29,11 @@
#include <osg/observer_ptr> #include <osg/observer_ptr>
#include <osgDB/ReaderWriter> #include <osgDB/ReaderWriter>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/scene/util/UpdateOnceCallback.hxx> #include <simgear/scene/util/UpdateOnceCallback.hxx>
#include <simgear/threads/SGThread.hxx>
#include <simgear/structure/Singleton.hxx> #include <simgear/structure/Singleton.hxx>
#include <simgear/threads/SGThread.hxx>
namespace osg namespace osg
{ {
@@ -106,6 +107,10 @@ public:
std::string getName(){return _name;} std::string getName(){return _name;}
void setName(std::string name){_name = name;} void setName(std::string name){_name = name;}
void setFilePath(const SGPath& path);
SGPath filePath() const;
protected: protected:
~Effect(); ~Effect();
// Support for a cache of effects that inherit from this one, so // Support for a cache of effects that inherit from this one, so
@@ -142,10 +147,13 @@ protected:
Cache* _cache; Cache* _cache;
friend size_t hash_value(const Key& key); friend size_t hash_value(const Key& key);
friend Effect* makeEffect(SGPropertyNode* prop, bool realizeTechniques, friend Effect* makeEffect(SGPropertyNode* prop, bool realizeTechniques,
const SGReaderWriterOptions* options); const SGReaderWriterOptions* options,
const SGPath& path);
bool _isRealized; bool _isRealized;
std::string _name; std::string _name;
SGPath _effectFilePath;
}; };
// Automatic support for boost hash function // Automatic support for boost hash function
size_t hash_value(const Effect::Key&); size_t hash_value(const Effect::Key&);
@@ -156,7 +164,8 @@ Effect* makeEffect(const std::string& name,
Effect* makeEffect(SGPropertyNode* prop, Effect* makeEffect(SGPropertyNode* prop,
bool realizeTechniques, bool realizeTechniques,
const SGReaderWriterOptions* options); const SGReaderWriterOptions* options,
const SGPath& path = SGPath{});
bool makeParametersFromStateSet(SGPropertyNode* paramRoot, bool makeParametersFromStateSet(SGPropertyNode* paramRoot,
const osg::StateSet* ss); const osg::StateSet* ss);
+2 -2
View File
@@ -75,13 +75,13 @@ BuilderException::BuilderException()
} }
BuilderException::BuilderException(const char* message, const char* origin) BuilderException::BuilderException(const char* message, const char* origin)
: sg_exception(message, origin) : sg_exception(message, origin, {}, false)
{ {
} }
BuilderException::BuilderException(const std::string& message, BuilderException::BuilderException(const std::string& message,
const std::string& origin) const std::string& origin)
: sg_exception(message, origin) : sg_exception(message, origin, {}, false)
{ {
} }
+16 -5
View File
@@ -39,12 +39,13 @@
#include <OpenThreads/Mutex> #include <OpenThreads/Mutex>
#include <OpenThreads/ScopedLock> #include <OpenThreads/ScopedLock>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/util/OsgMath.hxx> #include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx> #include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx> #include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx> #include <simgear/structure/OSGUtils.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
namespace simgear namespace simgear
{ {
@@ -280,9 +281,10 @@ bool setAttrs(const TexTuple& attrs, Texture* tex,
#else #else
result = osgDB::readRefImageFile(imageName, options); result = osgDB::readRefImageFile(imageName, options);
#endif #endif
} catch (std::bad_alloc& ba) { } catch (std::exception& e) {
SG_LOG(SG_GL, SG_ALERT, "Bad allocation loading:" << imageName); simgear::reportFailure(simgear::LoadFailure::OutOfMemory, simgear::ErrorCode::LoadingTexture,
// todo: report low memory warning string{"osgDB::readRefImageFile failed:"} + e.what(),
SGPath::fromUtf8(imageName));
return false; return false;
} }
@@ -304,6 +306,9 @@ bool setAttrs(const TexTuple& attrs, Texture* tex,
tex->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter()); tex->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
} else { } else {
SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file " << imageName); SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file " << imageName);
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::LoadingTexture,
"osgDB::readRefImageFile failed:" + result.message(),
SGPath::fromUtf8(imageName));
return false; return false;
} }
@@ -588,8 +593,8 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode*
const SGPropertyNode* texturesProp = getEffectPropertyChild(effect, props, "images"); const SGPropertyNode* texturesProp = getEffectPropertyChild(effect, props, "images");
const SGPropertyNode* crossProp = getEffectPropertyChild(effect, props, "image"); const SGPropertyNode* crossProp = getEffectPropertyChild(effect, props, "image");
if (!texturesProp && !crossProp) { if (!texturesProp && !crossProp) {
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadingTexture, "No images defined for cube map");
throw BuilderException("no images defined for cube map"); throw BuilderException("no images defined for cube map");
return NULL; // This is redundant
} }
// Using 6 separate images // Using 6 separate images
@@ -770,6 +775,9 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode*
return cubeTexture.release(); return cubeTexture.release();
} else { } else {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::LoadingTexture,
"Could not load cube-map image:" + result.message(),
sg_location{texname});
throw BuilderException("Could not load cube cross"); throw BuilderException("Could not load cube cross");
} }
} }
@@ -850,6 +858,9 @@ Texture* Texture3DBuilder::build(Effect* effect, Pass* pass,
tex->setImage(image3d.get()); tex->setImage(image3d.get());
} else { } else {
SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file " << imageName); SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file " << imageName);
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::LoadingTexture,
"osgDB::readRefImageFile failed:" + result.message(),
SGPath::fromUtf8(imageName));
return NULL; return NULL;
} }
+19 -14
View File
@@ -25,9 +25,11 @@
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
#include <osgDB/Registry> #include <osgDB/Registry>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/props/props_io.hxx> #include <simgear/props/props_io.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx> #include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SplicingVisitor.hxx> #include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/structure/SGExpression.hxx> #include <simgear/structure/SGExpression.hxx>
@@ -126,20 +128,20 @@ Effect* makeEffect(const string& name,
string absFileName string absFileName
= SGModelLib::findDataFile(effectFileName, options); = SGModelLib::findDataFile(effectFileName, options);
if (absFileName.empty()) { if (absFileName.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\""); simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadEffectsShaders, "Couldn't find Effect:" + effectFileName);
return 0; return nullptr;
} }
SGPropertyNode_ptr effectProps = new SGPropertyNode(); SGPropertyNode_ptr effectProps = new SGPropertyNode();
try { try {
readProperties(absFileName, effectProps.ptr(), 0, true); readProperties(absFileName, effectProps.ptr(), 0, true);
} }
catch (sg_io_exception& e) { catch (sg_io_exception& e) {
SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": " simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::LoadEffectsShaders, e.getFormattedMessage(),
<< e.getFormattedMessage()); e.getLocation());
return 0; return nullptr;
} }
ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques, ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
options); options, SGPath::fromUtf8(absFileName));
if (result.valid()) { if (result.valid()) {
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex); OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
pair<EffectMap::iterator, bool> irslt pair<EffectMap::iterator, bool> irslt
@@ -156,7 +158,8 @@ Effect* makeEffect(const string& name,
Effect* makeEffect(SGPropertyNode* prop, Effect* makeEffect(SGPropertyNode* prop,
bool realizeTechniques, bool realizeTechniques,
const SGReaderWriterOptions* options) const SGReaderWriterOptions* options,
const SGPath& filePath)
{ {
// Give default names to techniques and passes // Give default names to techniques and passes
vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique"); vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique");
@@ -217,6 +220,7 @@ Effect* makeEffect(SGPropertyNode* prop,
if (!effect.valid()) { if (!effect.valid()) {
effect = new Effect; effect = new Effect;
effect->setName(nameProp->getStringValue()); effect->setName(nameProp->getStringValue());
effect->setFilePath(filePath.isNull() ? parent->filePath() : filePath);
effect->root = new SGPropertyNode; effect->root = new SGPropertyNode;
mergePropertyTrees(effect->root, prop, parent->root); mergePropertyTrees(effect->root, prop, parent->root);
effect->parametersProp = effect->root->getChild("parameters"); effect->parametersProp = effect->root->getChild("parameters");
@@ -234,12 +238,13 @@ Effect* makeEffect(SGPropertyNode* prop,
effect->generator = parent->generator; // Copy the generators effect->generator = parent->generator; // Copy the generators
} }
} else { } else {
SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " << simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadEffectsShaders,
inheritProp->getStringValue()); string{"couldn't find base effect to inherit from:"} + inheritProp->getStringValue(), filePath);
return 0; return nullptr;
} }
} else { } else {
effect = new Effect; effect = new Effect;
effect->setFilePath(filePath);
effect->setName(nameProp->getStringValue()); effect->setName(nameProp->getStringValue());
effect->root = prop; effect->root = prop;
effect->parametersProp = effect->root->getChild("parameters"); effect->parametersProp = effect->root->getChild("parameters");
@@ -266,9 +271,9 @@ Effect* makeEffect(SGPropertyNode* prop,
effect->realizeTechniques(options); effect->realizeTechniques(options);
} }
catch (BuilderException& e) { catch (BuilderException& e) {
SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: " simgear::reportFailure(simgear::LoadFailure::Misconfigured, simgear::ErrorCode::LoadEffectsShaders,
<< e.getFormattedMessage()); "Failed to build technique:" + e.getFormattedMessage(), filePath);
return 0; return nullptr;
} }
} }
return effect.release(); return effect.release();
+35 -6
View File
@@ -56,12 +56,15 @@
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/NodeAndDrawableVisitor.hxx> #include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/props/props.hxx> #include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/io/sg_file.hxx> #include <simgear/io/sg_file.hxx>
#include <simgear/misc/lru_cache.hxx> #include <simgear/misc/lru_cache.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/structure/exception.hxx>
#include "BoundingVolumeBuildVisitor.hxx" #include "BoundingVolumeBuildVisitor.hxx"
#include "model.hxx" #include "model.hxx"
@@ -289,6 +292,11 @@ ModelRegistry::readImage(const string& fileName,
const SGReaderWriterOptions* sgoptC = dynamic_cast<const SGReaderWriterOptions*>(opt); const SGReaderWriterOptions* sgoptC = dynamic_cast<const SGReaderWriterOptions*>(opt);
simgear::ErrorReportContext ec;
if (sgoptC && sgoptC->getModelData()) {
ec.addFromMap(sgoptC->getModelData()->getErrorContext());
}
if (cache_active && (!sgoptC || sgoptC->getLoadOriginHint() != SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN)) { if (cache_active && (!sgoptC || sgoptC->getLoadOriginHint() != SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN)) {
if (fileExtension != "dds" && fileExtension != "gz") { if (fileExtension != "dds" && fileExtension != "gz") {
@@ -549,7 +557,13 @@ ModelRegistry::readImage(const string& fileName,
} }
} }
res = registry->readImageImplementation(absFileName, opt); try {
res = registry->readImageImplementation(absFileName, opt);
} catch (std::bad_alloc&) {
simgear::reportFailure(simgear::LoadFailure::OutOfMemory, simgear::ErrorCode::ThreeDModelLoad,
"Out of memory loading texture", sg_location{absFileName});
return ReaderWriter::ReadResult::INSUFFICIENT_MEMORY_TO_LOAD;
}
if (!res.success()) { if (!res.success()) {
SG_LOG(SG_IO, SG_DEV_WARN, "Image loading failed:" << res.message()); SG_LOG(SG_IO, SG_DEV_WARN, "Image loading failed:" << res.message());
@@ -714,7 +728,17 @@ ReaderWriter::ReadResult
ModelRegistry::readNode(const string& fileName, ModelRegistry::readNode(const string& fileName,
const Options* opt) const Options* opt)
{ {
ReaderWriter::ReadResult res; // propogate error context from the caller
simgear::ErrorReportContext ec;
auto sgopt = dynamic_cast<const SGReaderWriterOptions*>(opt);
if (sgopt) {
if (sgopt->getModelData()) {
ec.addFromMap(sgopt->getModelData()->getErrorContext());
}
ec.addFromMap(sgopt->getErrorContext());
}
CallbackMap::iterator iter CallbackMap::iterator iter
= nodeCallbackMap.find(getFileExtension(fileName)); = nodeCallbackMap.find(getFileExtension(fileName));
ReaderWriter::ReadResult result; ReaderWriter::ReadResult result;
@@ -723,6 +747,11 @@ ModelRegistry::readNode(const string& fileName,
else else
result = _defaultCallback->readNode(fileName, opt); result = _defaultCallback->readNode(fileName, opt);
if (!result.validNode()) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::ThreeDModelLoad,
"Failed to load 3D model:" + result.message(), sg_location{fileName});
}
return result; return result;
} }
+45 -15
View File
@@ -35,12 +35,13 @@
#include <osgDB/FileNameUtils> #include <osgDB/FileNameUtils>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <simgear/structure/exception.hxx> #include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx> #include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx> #include <simgear/scene/util/SGNodeMasks.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/structure/exception.hxx>
#include "modellib.hxx" #include "modellib.hxx"
#include "SGReaderWriterXML.hxx" #include "SGReaderWriterXML.hxx"
@@ -81,6 +82,7 @@ SGReaderWriterXML::readNode(const std::string& name,
const osgDB::Options* options) const const osgDB::Options* options) const
{ {
std::string fileName = osgDB::findDataFile(name, options); std::string fileName = osgDB::findDataFile(name, options);
simgear::ErrorReportContext ec{"model-xml", fileName};
osg::Node *result=0; osg::Node *result=0;
try { try {
@@ -262,8 +264,10 @@ sgLoad3DModel_internal(const SGPath& path,
SGPropertyNode *overlay) SGPropertyNode *overlay)
{ {
if (!path.exists()) { if (!path.exists()) {
SG_LOG(SG_IO, SG_DEV_ALERT, "Failed to load file: \"" << path << "\""); simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::XMLModelLoad,
return std::make_tuple(0, (osg::Node *) NULL); "Failed to load model XML: not found", path);
SG_LOG(SG_IO, SG_DEV_ALERT, "Failed to load file: \"" << path << "\"");
return std::make_tuple(0, (osg::Node*)NULL);
} }
osg::ref_ptr<SGReaderWriterOptions> options; osg::ref_ptr<SGReaderWriterOptions> options;
@@ -291,6 +295,8 @@ sgLoad3DModel_internal(const SGPath& path,
try { try {
readProperties(modelpath, props); readProperties(modelpath, props);
} catch (const sg_exception &t) { } catch (const sg_exception &t) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::XMLModelLoad,
"Failed to load model XML:" + t.getFormattedMessage(), t.getLocation());
SG_LOG(SG_IO, SG_DEV_ALERT, "Failed to load xml: " SG_LOG(SG_IO, SG_DEV_ALERT, "Failed to load xml: "
<< t.getFormattedMessage()); << t.getFormattedMessage());
throw; throw;
@@ -305,18 +311,24 @@ sgLoad3DModel_internal(const SGPath& path,
if (props->hasValue("/path")) { if (props->hasValue("/path")) {
string modelPathStr = props->getStringValue("/path"); string modelPathStr = props->getStringValue("/path");
modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir); modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir);
if (modelpath.isNull()) if (modelpath.isNull()) {
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::ThreeDModelLoad,
"Model not found:" + modelPathStr, path);
throw sg_io_exception("Model file not found: '" + modelPathStr + "'", throw sg_io_exception("Model file not found: '" + modelPathStr + "'",
path); path, {}, false);
}
if (props->hasValue("/texture-path")) { if (props->hasValue("/texture-path")) {
string texturePathStr = props->getStringValue("/texture-path"); string texturePathStr = props->getStringValue("/texture-path");
if (!texturePathStr.empty()) if (!texturePathStr.empty())
{ {
texturepath = SGModelLib::findDataFile(texturePathStr, NULL, modelDir); texturepath = SGModelLib::findDataFile(texturePathStr, NULL, modelDir);
if (texturepath.isNull()) if (texturepath.isNull()) {
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadingTexture,
"Texture file not found:" + texturePathStr, path);
throw sg_io_exception("Texture file not found: '" + texturePathStr + "'", throw sg_io_exception("Texture file not found: '" + texturePathStr + "'",
path); path);
}
} }
} }
} else { } else {
@@ -343,9 +355,13 @@ sgLoad3DModel_internal(const SGPath& path,
#else #else
modelResult = osgDB::readRefNodeFile(modelpath.utf8Str(), options.get()); modelResult = osgDB::readRefNodeFile(modelpath.utf8Str(), options.get());
#endif #endif
if (!modelResult.validNode()) if (!modelResult.validNode()) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::XMLModelLoad,
"Failed to load 3D model:" + modelResult.message(), modelpath);
throw sg_io_exception("Failed to load 3D model:" + modelResult.message(), throw sg_io_exception("Failed to load 3D model:" + modelResult.message(),
modelpath); modelpath, {}, false);
}
model = copyModel(modelResult.getNode()); model = copyModel(modelResult.getNode());
// Add an extra reference to the model stored in the database. // Add an extra reference to the model stored in the database.
// That is to avoid expiring the object from the cache even if // That is to avoid expiring the object from the cache even if
@@ -413,6 +429,9 @@ sgLoad3DModel_internal(const SGPath& path,
if (submodelPath.isNull()) { if (submodelPath.isNull()) {
SG_LOG(SG_IO, SG_DEV_ALERT, "Failed to load file: \"" << subPathStr << "\""); SG_LOG(SG_IO, SG_DEV_ALERT, "Failed to load file: \"" << subPathStr << "\"");
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::XMLModelLoad,
"Couldn't find file for submodel:" + subPathStr,
SGPath::fromUtf8(subPathStr));
continue; continue;
} }
@@ -425,6 +444,8 @@ sgLoad3DModel_internal(const SGPath& path,
} }
} }
simgear::ErrorReportContext("submodel", submodelPath.utf8Str());
try { try {
int num_anims; int num_anims;
std::tie(num_anims, submodel) = sgLoad3DModel_internal(submodelPath, options.get(), std::tie(num_anims, submodel) = sgLoad3DModel_internal(submodelPath, options.get(),
@@ -553,13 +574,22 @@ sgLoad3DModel_internal(const SGPath& path,
} // of object-names in the animation } // of object-names in the animation
continue; continue;
} }
/*
* Setup the model data for the node currently being animated.
*/
modelData.LoadAnimationValuesForElement(animation_nodes[i], i);
/// OSGFIXME: duh, why not only model????? try {
SGAnimation::animate(modelData); /*
* Setup the model data for the node currently being animated.
*/
modelData.LoadAnimationValuesForElement(animation_nodes[i], i);
/// OSGFIXME: duh, why not only model?????
SGAnimation::animate(modelData);
} catch (sg_exception& e) {
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::XMLModelLoad,
"Couldn't load animation " + animation_nodes[i]->getNameString()
+ ":" + e.getFormattedMessage(),
modelpath);
throw;
}
} }
animationcount += animation_nodes.size(); animationcount += animation_nodes.size();
+11
View File
@@ -39,9 +39,11 @@
#include <simgear/bvh/BVHGroup.hxx> #include <simgear/bvh/BVHGroup.hxx>
#include <simgear/bvh/BVHLineGeometry.hxx> #include <simgear/bvh/BVHLineGeometry.hxx>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/math/interpolater.hxx> #include <simgear/math/interpolater.hxx>
#include <simgear/props/condition.hxx> #include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/scene/material/EffectGeode.hxx> #include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/EffectCullVisitor.hxx> #include <simgear/scene/material/EffectCullVisitor.hxx>
#include <simgear/scene/util/DeletionManager.hxx> #include <simgear/scene/util/DeletionManager.hxx>
@@ -455,6 +457,9 @@ SGAnimation::~SGAnimation()
} }
if (!info.empty()) if (!info.empty())
{ {
reportFailure(LoadFailure::Misconfigured, ErrorCode::XMLModelLoad,
"Could not find at least one of the following object for animation:" + info,
SGPath::fromUtf8(_modelData.getPath()));
SG_LOG(SG_IO, SG_DEV_ALERT, "Could not find at least one of the following" SG_LOG(SG_IO, SG_DEV_ALERT, "Could not find at least one of the following"
" objects for animation: " << info << " in file: " << _modelData.getPath()); " objects for animation: " << info << " in file: " << _modelData.getPath());
} }
@@ -773,10 +778,16 @@ bool SGAnimation::setCenterAndAxisFromObject(osg::Node *rootNode, SGVec3d& cente
object_group->setNodeMask(0); object_group->setNodeMask(0);
} }
else { else {
reportFailure(LoadFailure::Misconfigured, ErrorCode::XMLModelLoad,
"Could not find valid line segment for axis animation:" + axis_object_name,
SGPath::fromUtf8(_modelData.getPath()));
SG_LOG(SG_IO, SG_DEV_ALERT, "Could not find a valid line segment for animation: " << axis_object_name << " in file: " << _modelData.getPath()); SG_LOG(SG_IO, SG_DEV_ALERT, "Could not find a valid line segment for animation: " << axis_object_name << " in file: " << _modelData.getPath());
} }
} }
else if (can_warn) { else if (can_warn) {
reportFailure(LoadFailure::Misconfigured, ErrorCode::XMLModelLoad,
"Could not find object for axis animation:" + axis_object_name,
SGPath::fromUtf8(_modelData.getPath()));
SG_LOG(SG_IO, SG_DEV_ALERT, "Could not find at least one of the following objects for axis animation: " << axis_object_name << " in file: " << _modelData.getPath()); SG_LOG(SG_IO, SG_DEV_ALERT, "Could not find at least one of the following objects for axis animation: " << axis_object_name << " in file: " << _modelData.getPath());
} }
} }
+6 -3
View File
@@ -24,11 +24,12 @@
#include <simgear/scene/util/SplicingVisitor.hxx> #include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/structure/Singleton.hxx> #include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx> #include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx> #include <simgear/structure/Singleton.hxx>
#include <simgear/structure/exception.hxx>
#include "model.hxx" #include "model.hxx"
@@ -380,6 +381,8 @@ ref_ptr<Node> instantiateMaterialEffects(osg::Node* modelGroup,
} else { } else {
effect = DefaultEffect::instance()->getEffect(); effect = DefaultEffect::instance()->getEffect();
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for " << options->getMaterialName()); SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for " << options->getMaterialName());
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadEffectsShaders,
"Unable to get effect for material:" + options->getMaterialName());
} }
} else { } else {
effect = DefaultEffect::instance()->getEffect(); effect = DefaultEffect::instance()->getEffect();
+3 -3
View File
@@ -29,12 +29,13 @@
#include <osgDB/Registry> #include <osgDB/Registry>
#include <simgear/constants.h> #include <simgear/constants.h>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx> #include <simgear/props/props_io.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/model/ModelRegistry.hxx> #include <simgear/scene/model/ModelRegistry.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include "SGReaderWriterXML.hxx" #include "SGReaderWriterXML.hxx"
@@ -159,7 +160,6 @@ SGModelLib::loadDeferredModel(const string &path, SGPropertyNode *prop_root,
proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER); proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
proxyNode->setFileName(0, path); proxyNode->setFileName(0, path);
osg::ref_ptr<SGReaderWriterOptions> opt; osg::ref_ptr<SGReaderWriterOptions> opt;
opt = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); opt = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
opt->getDatabasePathList().push_front( osgDB::getFilePath(path) ); opt->getDatabasePathList().push_front( osgDB::getFilePath(path) );
+5 -4
View File
@@ -18,12 +18,9 @@
#ifndef _SG_MODEL_LIB_HXX #ifndef _SG_MODEL_LIB_HXX
#define _SG_MODEL_LIB_HXX 1 #define _SG_MODEL_LIB_HXX 1
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h> // for SG_USING_STD #include <simgear/compiler.h> // for SG_USING_STD
#include <map>
#include <string> #include <string>
#include <osg/Node> #include <osg/Node>
@@ -112,6 +109,10 @@ public:
virtual void modelLoaded(const std::string& path, SGPropertyNode *prop, virtual void modelLoaded(const std::string& path, SGPropertyNode *prop,
osg::Node* branch) = 0; osg::Node* branch) = 0;
virtual SGModelData* clone() const = 0; virtual SGModelData* clone() const = 0;
using ErrorContext = std::map<std::string, std::string>;
virtual ErrorContext getErrorContext() const = 0;
}; };
/* /*
+32 -18
View File
@@ -38,10 +38,12 @@
#include <osgDB/ReaderWriter> #include <osgDB/ReaderWriter>
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
#include <simgear/math/sg_random.h>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/bucket/newbucket.hxx> #include <simgear/bucket/newbucket.hxx>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/io/iostreams/sgstream.hxx> #include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/scene/util/OptionsReadFileCallback.hxx> #include <simgear/scene/util/OptionsReadFileCallback.hxx>
#include <simgear/scene/util/OsgMath.hxx> #include <simgear/scene/util/OsgMath.hxx>
@@ -154,7 +156,7 @@ struct ReaderWriterSTG::_ModelBin {
proxy->setName("proxyNode"); proxy->setName("proxyNode");
proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER); proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
proxy->setFileName(0, o._name); proxy->setFileName(0, o._name);
proxy->setDatabaseOptions(o._options.get()); proxy->setDatabaseOptions(o._options);
// Give the node some values so the Quadtree builder has // Give the node some values so the Quadtree builder has
// a BoundingBox to work with prior to the model being loaded. // a BoundingBox to work with prior to the model being loaded.
@@ -163,6 +165,7 @@ struct ReaderWriterSTG::_ModelBin {
proxy->setCenterMode(osg::ProxyNode::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED); proxy->setCenterMode(osg::ProxyNode::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED);
node = proxy; node = proxy;
} else { } else {
ErrorReportContext ec("terrain-stg", o._errorLocation.utf8Str());
#if OSG_VERSION_LESS_THAN(3,4,0) #if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(o._name, o._options.get()); node = osgDB::readNodeFile(o._name, o._options.get());
#else #else
@@ -209,6 +212,8 @@ struct ReaderWriterSTG::_ModelBin {
virtual osgDB::ReaderWriter::ReadResult virtual osgDB::ReaderWriter::ReadResult
readNode(const std::string&, const osgDB::Options*) readNode(const std::string&, const osgDB::Options*)
{ {
ErrorReportContext ec("terrain-bucket", _bucket.gen_index_str());
STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD())); STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end()); quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end());
osg::ref_ptr<osg::Group> group = quadtree.getRoot(); osg::ref_ptr<osg::Group> group = quadtree.getRoot();
@@ -465,15 +470,15 @@ struct ReaderWriterSTG::_ModelBin {
else else
opt->setInstantiateEffects(false); opt->setInstantiateEffects(false);
_ObjectStatic obj; _ObjectStatic obj;
opt->addErrorContext("terrain-stg", absoluteFileName.utf8Str());
obj._errorLocation = absoluteFileName; obj._errorLocation = absoluteFileName;
obj._token = token; obj._token = token;
obj._name = name; obj._name = name;
obj._agl = (token == "OBJECT_STATIC_AGL"); obj._agl = (token == "OBJECT_STATIC_AGL");
obj._proxy = true; obj._proxy = true;
in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll; in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
obj._range = range; obj._range = range;
obj._options = opt; obj._options = opt;
checkInsideBucket(absoluteFileName, obj._lon, obj._lat); checkInsideBucket(absoluteFileName, obj._lon, obj._lat);
_objectStaticList.push_back(obj); _objectStaticList.push_back(obj);
} else if (token == "OBJECT_SHARED" || token == "OBJECT_SHARED_AGL") { } else if (token == "OBJECT_SHARED" || token == "OBJECT_SHARED_AGL") {
@@ -509,6 +514,8 @@ struct ReaderWriterSTG::_ModelBin {
_ObjectStatic obj; _ObjectStatic obj;
opt->setInstantiateEffects(false); opt->setInstantiateEffects(false);
opt->addErrorContext("terrain-stg", absoluteFileName.utf8Str());
if (SGPath(name).lower_extension() == "ac") { if (SGPath(name).lower_extension() == "ac") {
// Generate material/Effects lookups for raw models, i.e. // Generate material/Effects lookups for raw models, i.e.
// those not wrapped in an XML while will include Effects // those not wrapped in an XML while will include Effects
@@ -573,7 +580,9 @@ struct ReaderWriterSTG::_ModelBin {
} }
callback(token,name, SGGeod::fromDegM(obj._lon, obj._lat, obj._elev), obj._hdg,restofline); callback(token,name, SGGeod::fromDegM(obj._lon, obj._lat, obj._elev), obj._hdg,restofline);
} else { } else {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName << ": Unknown token '" << token << "'" ); // SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName << ": Unknown token '" << token << "'" );
simgear::reportFailure(simgear::LoadFailure::Misconfigured, simgear::ErrorCode::BTGLoad,
"Unknown STG token:" + token, absoluteFileName);
} }
} }
} }
@@ -594,9 +603,13 @@ struct ReaderWriterSTG::_ModelBin {
std::string terrain_name = string("terrain ").append(bucket.gen_index_str()); std::string terrain_name = string("terrain ").append(bucket.gen_index_str());
terrainGroup->setName(terrain_name); terrainGroup->setName(terrain_name);
simgear::ErrorReportContext ec{"terrain-bucket", bucket.gen_index_str()};
if (_foundBase) { if (_foundBase) {
for (auto stgObject : _objectList) { for (auto stgObject : _objectList) {
osg::ref_ptr<osg::Node> node; osg::ref_ptr<osg::Node> node;
simgear::ErrorReportContext ec("terrain-stg", stgObject._errorLocation.utf8Str());
#if OSG_VERSION_LESS_THAN(3,4,0) #if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(stgObject._name, stgObject._options.get()); node = osgDB::readNodeFile(stgObject._name, stgObject._options.get());
#else #else
@@ -621,7 +634,6 @@ struct ReaderWriterSTG::_ModelBin {
"Warning: failed to generate ocean tile!" ); "Warning: failed to generate ocean tile!" );
} }
} }
for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) { for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) {
if (!i->_agl) if (!i->_agl)
continue; continue;
@@ -701,6 +713,7 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
{ {
_ModelBin modelBin; _ModelBin modelBin;
SGBucket bucket(bucketIndexFromFileName(fileName)); SGBucket bucket(bucketIndexFromFileName(fileName));
simgear::ErrorReportContext ec("terrain-bucket", bucket.gen_index_str());
// We treat 123.stg different than ./123.stg. // We treat 123.stg different than ./123.stg.
// The difference is that ./123.stg as well as any absolute path // The difference is that ./123.stg as well as any absolute path
@@ -708,6 +721,7 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
// In contrast 123.stg uses the search paths to load a set of stg // In contrast 123.stg uses the search paths to load a set of stg
// files spread across the scenery directories. // files spread across the scenery directories.
if (osgDB::getSimpleFileName(fileName) != fileName) { if (osgDB::getSimpleFileName(fileName) != fileName) {
simgear::ErrorReportContext ec("terrain-stg", fileName);
if (!modelBin.read(fileName, options)) if (!modelBin.read(fileName, options))
return ReadResult::FILE_NOT_FOUND; return ReadResult::FILE_NOT_FOUND;
} }
@@ -717,11 +731,10 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
return ReadResult::FILE_NOT_FOUND; return ReadResult::FILE_NOT_FOUND;
} }
osg::ref_ptr<SGReaderWriterOptions> sgOpts(SGReaderWriterOptions::copyOrCreate(options)); const auto sgOpts = dynamic_cast<const SGReaderWriterOptions*>(options);
if (sgOpts->getSceneryPathSuffixes().empty()) { if (!sgOpts || sgOpts->getSceneryPathSuffixes().empty()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Loading tile " << fileName << ", no scenery path suffixes were configured so giving up"); SG_LOG(SG_TERRAIN, SG_ALERT, "Loading tile " << fileName << ", no scenery path suffixes were configured so giving up");
return ReadResult::FILE_NOT_FOUND; return ReadResult::FILE_NOT_FOUND;
} }
SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName); SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
@@ -746,7 +759,8 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
} }
for (auto suffix : sgOpts->getSceneryPathSuffixes()) { for (auto suffix : sgOpts->getSceneryPathSuffixes()) {
SGPath p = base / suffix / basePath / fileName; const auto p = base / suffix / basePath / fileName;
simgear::ErrorReportContext ec("terrain-stg", p.utf8Str());
modelBin.read(p, options); modelBin.read(p, options);
} }
} }
+9 -2
View File
@@ -22,6 +22,7 @@
#include <osgDB/FileNameUtils> #include <osgDB/FileNameUtils>
#include <osgDB/Registry> #include <osgDB/Registry>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/scene/model/ModelRegistry.hxx> #include <simgear/scene/model/ModelRegistry.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx> #include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
@@ -62,13 +63,19 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
const SGReaderWriterOptions* sgOptions; const SGReaderWriterOptions* sgOptions;
sgOptions = dynamic_cast<const SGReaderWriterOptions*>(options); sgOptions = dynamic_cast<const SGReaderWriterOptions*>(options);
osg::Node* result = NULL; osg::Node* result = NULL;
simgear::ErrorReportContext ec{"btg", fileName};
try { try {
result = SGLoadBTG(fileName, sgOptions); result = SGLoadBTG(fileName, sgOptions);
if (!result) if (!result)
return ReadResult::FILE_NOT_HANDLED; return ReadResult::FILE_NOT_HANDLED;
} catch (sg_exception& e) { } catch (sg_exception& e) {
SG_LOG(SG_IO, SG_WARN, "error reading:" << fileName << ":" << simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::BTGLoad,
e.getFormattedMessage()); "Failed to load BTG file:" + e.getFormattedMessage(),
e.getLocation());
return ReadResult::ERROR_IN_READING_FILE;
} catch (std::bad_alloc&) {
simgear::reportFailure(simgear::LoadFailure::OutOfMemory, simgear::ErrorCode::BTGLoad,
"Out of memory loading BTG:" + fileName, sg_location{fileName});
return ReadResult::ERROR_IN_READING_FILE; return ReadResult::ERROR_IN_READING_FILE;
} }
+2
View File
@@ -31,6 +31,7 @@ set(HEADERS
UpdateOnceCallback.hxx UpdateOnceCallback.hxx
VectorArrayAdapter.hxx VectorArrayAdapter.hxx
project.hxx project.hxx
SGProgram.hxx
) )
set(SOURCES set(SOURCES
@@ -55,6 +56,7 @@ set(SOURCES
StateAttributeFactory.cxx StateAttributeFactory.cxx
UpdateOnceCallback.cxx UpdateOnceCallback.cxx
project.cxx project.cxx
SGProgram.cxx
) )
simgear_scene_component(util scene/util "${SOURCES}" "${HEADERS}") simgear_scene_component(util scene/util "${SOURCES}" "${HEADERS}")
+83
View File
@@ -0,0 +1,83 @@
////
// 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_config.h"
#include "SGProgram.hxx"
#include <osg/State>
#include <sstream>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/debug/logstream.hxx>
SGProgram::SGProgram()
{
}
SGProgram::SGProgram(const SGProgram& rhs, const osg::CopyOp& copyop) : osg::Program(rhs, copyop),
_effectFilePath(rhs._effectFilePath)
{
}
void SGProgram::setEffectFilePath(const SGPath& p)
{
_effectFilePath = p;
}
void SGProgram::apply(osg::State& state) const
{
osg::Program::apply(state);
if (_checkState != NotApplied) {
return; // already done
}
auto pcp = state.getLastAppliedProgramObject();
if (!pcp) {
std::string infoLog;
_checkState = FailedToApply;
getPCP(state)->getInfoLog(infoLog);
if (!infoLog.empty()) {
// log all the shader source file names, to help in debugging link errors
std::ostringstream os;
for (int i = 0; i < getNumShaders(); ++i) {
const auto shader = getShader(i);
os << "\t" << shader->getFileName() << "\n";
}
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::LoadEffectsShaders,
"Shader program errors: " + infoLog +
"\n\nShader sources:\n" + os.str(),
_effectFilePath);
}
for (int i = 0; i < getNumShaders(); ++i) {
const auto shader = getShader(i);
auto pcs = shader->getPCS(state);
std::string shaderLog;
pcs->getInfoLog(shaderLog);
if (!shaderLog.empty()) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::LoadEffectsShaders,
"Shader source errors: " + shaderLog, sg_location(shader->getFileName()));
}
}
} else {
_checkState = AppliedOk;
}
}
+50
View File
@@ -0,0 +1,50 @@
////
// 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.
#pragma once
#include <osg/Program>
#include <simgear/misc/sg_path.hxx>
/**
* @brief wrapper around osg::Program to allow detecting shader/link
* errors in the GLSL code at runtime, and reporting of them
*
*/
class SGProgram : public osg::Program
{
public:
SGProgram();
SGProgram(const SGProgram& rhs, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
META_StateAttribute(simgear, SGProgram, PROGRAM);
void apply(osg::State& state) const override;
enum ErrorCheckState {
NotApplied,
AppliedOk,
FailedToApply
};
void setEffectFilePath(const SGPath& p);
private:
SGPath _effectFilePath;
mutable ErrorCheckState _checkState = NotApplied;
};
+7 -3
View File
@@ -17,9 +17,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// //
#ifdef HAVE_CONFIG_H #include <simgear_config.h>
# include <simgear_config.h>
#endif
#include <simgear/scene/util/OsgMath.hxx> #include <simgear/scene/util/OsgMath.hxx>
@@ -54,4 +52,10 @@ SGReaderWriterOptions::fromPath(const SGPath& path)
return options; return options;
} }
void SGReaderWriterOptions::addErrorContext(const std::string& key, const std::string& value)
{
_errorContext[key] = value;
} }
} // namespace simgear
+16 -6
View File
@@ -76,12 +76,11 @@ public:
_LoadOriginHint(ORIGIN_MODEL) _LoadOriginHint(ORIGIN_MODEL)
{ } { }
SGReaderWriterOptions(const SGReaderWriterOptions& options, SGReaderWriterOptions(const SGReaderWriterOptions& options,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) : const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) : osgDB::Options(options, copyop),
osgDB::Options(options, copyop), _propertyNode(options._propertyNode),
_propertyNode(options._propertyNode), _materialLib(options._materialLib),
_materialLib(options._materialLib),
#ifdef ENABLE_GDAL #ifdef ENABLE_GDAL
_dem(options._dem), _dem(options._dem),
#endif #endif
_load_panel(options._load_panel), _load_panel(options._load_panel),
_model_data(options._model_data), _model_data(options._model_data),
@@ -89,7 +88,8 @@ public:
_instantiateMaterialEffects(options._instantiateMaterialEffects), _instantiateMaterialEffects(options._instantiateMaterialEffects),
_materialName(options._materialName), _materialName(options._materialName),
_sceneryPathSuffixes(options._sceneryPathSuffixes), _sceneryPathSuffixes(options._sceneryPathSuffixes),
_LoadOriginHint(ORIGIN_MODEL) _LoadOriginHint(ORIGIN_MODEL),
_errorContext(options._errorContext)
{ } { }
META_Object(simgear, SGReaderWriterOptions); META_Object(simgear, SGReaderWriterOptions);
@@ -160,6 +160,15 @@ public:
void setLoadOriginHint(LoadOriginHint _v) const { _LoadOriginHint = _v; } void setLoadOriginHint(LoadOriginHint _v) const { _LoadOriginHint = _v; }
LoadOriginHint getLoadOriginHint() const { return _LoadOriginHint; } LoadOriginHint getLoadOriginHint() const { return _LoadOriginHint; }
using ErrorContext = std::map<std::string, std::string>;
void addErrorContext(const std::string& key, const std::string& value);
ErrorContext getErrorContext() const
{
return _errorContext;
}
protected: protected:
virtual ~SGReaderWriterOptions(); virtual ~SGReaderWriterOptions();
@@ -179,6 +188,7 @@ private:
string_list _sceneryPathSuffixes; string_list _sceneryPathSuffixes;
SGGeod _geod; SGGeod _geod;
mutable LoadOriginHint _LoadOriginHint; mutable LoadOriginHint _LoadOriginHint;
ErrorContext _errorContext;
}; };
} }
+22 -20
View File
@@ -32,6 +32,7 @@
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/misc/stdint.hxx> #include <simgear/misc/stdint.hxx>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include "sample.hxx" #include "sample.hxx"
@@ -56,7 +57,7 @@ namespace
} }
}; };
unsigned int formatConstruct(ALint numChannels, ALint bitsPerSample, bool compressed) unsigned int formatConstruct(ALint numChannels, ALint bitsPerSample, bool compressed, const SGPath& path)
{ {
unsigned int rv = 0; unsigned int rv = 0;
if (!compressed) { if (!compressed) {
@@ -67,7 +68,7 @@ namespace
else { else {
char msg[81]; char msg[81];
snprintf(msg, 80, "Unsupported audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample); snprintf(msg, 80, "Unsupported audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
throw sg_exception(msg); throw sg_exception(msg, {}, path, false);
} }
} else { } else {
if (numChannels == 1 && bitsPerSample == 4) rv = SG_SAMPLE_ADPCM; if (numChannels == 1 && bitsPerSample == 4) rv = SG_SAMPLE_ADPCM;
@@ -75,7 +76,7 @@ namespace
else { else {
char msg[81]; char msg[81];
snprintf(msg, 80, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample); snprintf(msg, 80, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
throw sg_exception(msg); throw sg_exception(msg, {}, path, false);
} }
} }
return rv; return rv;
@@ -275,29 +276,29 @@ namespace
Codec *codec = codecLinear; Codec *codec = codecLinear;
if (!wavReadBE(fd, magic)) if (!wavReadBE(fd, magic))
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
if (magic != WAV_RIFF_4CC) { if (magic != WAV_RIFF_4CC) {
throw sg_io_exception("not a .wav file", b->path); throw sg_io_exception("not a .wav file", b->path, {}, false);
} }
if (!wavReadLE(fd, chunkLength) || !wavReadBE(fd, magic)) if (!wavReadLE(fd, chunkLength) || !wavReadBE(fd, magic))
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
if (magic != WAV_WAVE_4CC) /* "WAVE" */ if (magic != WAV_WAVE_4CC) /* "WAVE" */
{ {
throw sg_io_exception("unrecognized WAV magic", b->path); throw sg_io_exception("unrecognized WAV magic", b->path, {}, false);
} }
while (1) { while (1) {
if (!wavReadBE(fd, magic) || !wavReadLE(fd, chunkLength)) if (!wavReadBE(fd, magic) || !wavReadLE(fd, chunkLength))
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
if (magic == WAV_FORMAT_4CC) /* "fmt " */ if (magic == WAV_FORMAT_4CC) /* "fmt " */
{ {
found_header = true; found_header = true;
if (chunkLength < 16) { if (chunkLength < 16) {
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
} }
if (!wavReadLE (fd, audioFormat) || if (!wavReadLE (fd, audioFormat) ||
@@ -307,11 +308,11 @@ namespace
!wavReadLE (fd, blockAlign) || !wavReadLE (fd, blockAlign) ||
!wavReadLE (fd, bitsPerSample)) !wavReadLE (fd, bitsPerSample))
{ {
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
} }
if (!gzSkip(fd, chunkLength - 16)) if (!gzSkip(fd, chunkLength - 16))
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
switch (audioFormat) switch (audioFormat)
{ {
@@ -340,33 +341,33 @@ namespace
} }
break; break;
default: default:
throw sg_io_exception("unsupported WAV encoding", b->path); throw sg_io_exception("unsupported WAV encoding:" + std::to_string(audioFormat), b->path, {}, false);
} }
b->block_align = blockAlign; b->block_align = blockAlign;
b->frequency = samplesPerSecond; b->frequency = samplesPerSecond;
b->format = formatConstruct(numChannels, bitsPerSample, compressed); b->format = formatConstruct(numChannels, bitsPerSample, compressed, b->path);
} else if (magic == WAV_DATA_4CC) { } else if (magic == WAV_DATA_4CC) {
if (!found_header) { if (!found_header) {
/* ToDo: A bit wrong to check here, fmt chunk could come later... */ /* ToDo: A bit wrong to check here, fmt chunk could come later... */
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
} }
b->data = malloc(chunkLength); b->data = malloc(chunkLength);
b->length = chunkLength; b->length = chunkLength;
size_t read = gzread(fd, b->data, chunkLength); size_t read = gzread(fd, b->data, chunkLength);
if (read != chunkLength) { if (read != chunkLength) {
throw sg_io_exception("insufficent data reading WAV file", b->path); throw sg_io_exception("insufficent data reading WAV file", b->path, {}, false);
} }
break; break;
} else { } else {
if (!gzSkip(fd, chunkLength)) if (!gzSkip(fd, chunkLength))
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
} }
if ((chunkLength & 1) && !gzeof(fd) && !gzSkip(fd, 1)) if ((chunkLength & 1) && !gzeof(fd) && !gzSkip(fd, 1))
throw sg_io_exception("corrupt or truncated WAV data", b->path); throw sg_io_exception("corrupt or truncated WAV data", b->path, {}, false);
} // of file chunk parser loop } // of file chunk parser loop
codec(b); // might throw if something really bad occurs codec(b); // might throw if something really bad occurs
@@ -380,7 +381,7 @@ namespace simgear
ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf, unsigned int& block_align) ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf, unsigned int& block_align)
{ {
if (!path.exists()) { if (!path.exists()) {
SG_LOG(SG_IO, SG_DEV_ALERT, "loadWAVFromFile: file not found:" << path); simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AudioFX, "loadWAVFromFile: not found", path);
return nullptr; return nullptr;
} }
@@ -396,14 +397,15 @@ ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size,
fd = gzopen(ps.c_str(), "rb"); fd = gzopen(ps.c_str(), "rb");
#endif #endif
if (!fd) { if (!fd) {
SG_LOG(SG_IO, SG_DEV_ALERT, "loadWAVFromFile: unable to open file:" << path); simgear::reportFailure(simgear::LoadFailure::IOError, simgear::ErrorCode::AudioFX, "loadWAVFromFile: unable to open file", path);
return nullptr; return nullptr;
} }
try { try {
loadWavFile(fd, &b); loadWavFile(fd, &b);
} catch (sg_exception& e) { } catch (sg_exception& e) {
SG_LOG(SG_IO, SG_DEV_ALERT, "loadWAVFromFile:" << e.getFormattedMessage() << "\nfor: " << path); simgear::reportFailure(simgear::LoadFailure::IOError, simgear::ErrorCode::AudioFX, "loadWAVFromFile: unable to read file:"
+ e.getFormattedMessage(), e.getLocation());
return nullptr; return nullptr;
} }
+27 -9
View File
@@ -250,10 +250,15 @@ void
SGSubsystemGroup::init () SGSubsystemGroup::init ()
{ {
assert(_state == State::BIND); assert(_state == State::BIND);
forEach([this](SGSubsystem* s){ forEach([this](SGSubsystem* s) {
this->notifyWillChange(s, State::INIT); try {
s->init(); this->notifyWillChange(s, State::INIT);
this->notifyDidChange(s, State::INIT); s->init();
this->notifyDidChange(s, State::INIT);
} catch (std::exception& e) {
simgear::reportError("Caught exception init-ing subsystem " + s->subsystemId() + "\n\t" + e.what());
throw;
}
}); });
_state = State::INIT; _state = State::INIT;
} }
@@ -283,7 +288,15 @@ SGSubsystemGroup::incrementalInit()
const auto m = _members[_initPosition]; const auto m = _members[_initPosition];
SGTimeStamp st; SGTimeStamp st;
st.stamp(); st.stamp();
const InitStatus memberStatus = m->subsystem->incrementalInit(); InitStatus memberStatus;
try {
memberStatus = m->subsystem->incrementalInit();
} catch (std::exception& e) {
simgear::reportError("Caught exception init-ing subsystem " + m->subsystem->subsystemId() + "\n\t" + e.what());
throw;
}
m->initTime += st.elapsedMSec(); m->initTime += st.elapsedMSec();
if (memberStatus == INIT_DONE) { if (memberStatus == INIT_DONE) {
@@ -351,10 +364,15 @@ SGSubsystemGroup::shutdown ()
void void
SGSubsystemGroup::bind () SGSubsystemGroup::bind ()
{ {
forEach([this](SGSubsystem* s){ forEach([this](SGSubsystem* s) {
this->notifyWillChange(s, State::BIND); try {
s->bind(); this->notifyWillChange(s, State::BIND);
this->notifyDidChange(s, State::BIND); s->bind();
this->notifyDidChange(s, State::BIND);
} catch (std::exception& e) {
simgear::reportError("Caught exception binding subsystem " + s->subsystemId() + "\n\t" + e.what());
throw;
}
}); });
_state = State::BIND; _state = State::BIND;
} }
+35 -31
View File
@@ -244,40 +244,43 @@ readXML (istream &input, XMLVisitor &visitor, const string &path)
// FIXME: get proper error string from system // FIXME: get proper error string from system
if (!input.good()) { if (!input.good()) {
sg_io_exception ex ("Problem reading file", sg_io_exception ex("Problem reading file",
sg_location(path, sg_location(path,
XML_GetCurrentLineNumber(parser), XML_GetCurrentLineNumber(parser),
XML_GetCurrentColumnNumber(parser)), XML_GetCurrentColumnNumber(parser)),
"SimGear XML Parser"); "SimGear XML Parser",
visitor.setParser(0); false /* don't report */);
XML_ParserFree(parser); visitor.setParser(0);
throw ex; XML_ParserFree(parser);
throw ex;
} }
input.read(buf,16384); input.read(buf,16384);
if (!XML_Parse(parser, buf, input.gcount(), false)) { if (!XML_Parse(parser, buf, input.gcount(), false)) {
sg_io_exception ex (XML_ErrorString(XML_GetErrorCode(parser)), sg_io_exception ex(XML_ErrorString(XML_GetErrorCode(parser)),
sg_location(path, sg_location(path,
XML_GetCurrentLineNumber(parser), XML_GetCurrentLineNumber(parser),
XML_GetCurrentColumnNumber(parser)), XML_GetCurrentColumnNumber(parser)),
"SimGear XML Parser"); "SimGear XML Parser",
visitor.setParser(0); false /* don't report */);
XML_ParserFree(parser); visitor.setParser(0);
throw ex; XML_ParserFree(parser);
throw ex;
} }
} }
// Verify end of document. // Verify end of document.
if (!XML_Parse(parser, buf, 0, true)) { if (!XML_Parse(parser, buf, 0, true)) {
sg_io_exception ex (XML_ErrorString(XML_GetErrorCode(parser)), sg_io_exception ex(XML_ErrorString(XML_GetErrorCode(parser)),
sg_location(path, sg_location(path,
XML_GetCurrentLineNumber(parser), XML_GetCurrentLineNumber(parser),
XML_GetCurrentColumnNumber(parser)), XML_GetCurrentColumnNumber(parser)),
"SimGear XML Parser"); "SimGear XML Parser",
visitor.setParser(0); false /* don't report */);
XML_ParserFree(parser); visitor.setParser(0);
throw ex; XML_ParserFree(parser);
throw ex;
} }
visitor.setParser(0); visitor.setParser(0);
@@ -300,8 +303,8 @@ readXML (const SGPath &path, XMLVisitor &visitor)
throw; throw;
} }
} else { } else {
throw sg_io_exception("Failed to open file", sg_location(path), throw sg_io_exception("Failed to open file", sg_location(path),
"SimGear XML Parser"); "SimGear XML Parser", false /* don't report */);
} }
input.close(); input.close();
} }
@@ -318,11 +321,12 @@ readXML (const char *buf, const int size, XMLVisitor &visitor)
visitor.startXML(); visitor.startXML();
if (!XML_Parse(parser, buf, size, false)) { if (!XML_Parse(parser, buf, size, false)) {
sg_io_exception ex (XML_ErrorString(XML_GetErrorCode(parser)), sg_io_exception ex(XML_ErrorString(XML_GetErrorCode(parser)),
sg_location("In-memory XML buffer", sg_location("In-memory XML buffer",
XML_GetCurrentLineNumber(parser), XML_GetCurrentLineNumber(parser),
XML_GetCurrentColumnNumber(parser)), XML_GetCurrentColumnNumber(parser)),
"SimGear XML Parser"); "SimGear XML Parser",
false /* don't report */);
XML_ParserFree(parser); XML_ParserFree(parser);
throw ex; throw ex;
} }