utils: add precond/postcond macro guarded by feature flags

We link the behavior of certain precondition/postcondition checks to feature flag states.

- If provided *flag* is true, then this macro will assert when the *condition* is false.
- If provided *flag* is fase, then this macro will output a warning when the condition is false.
- If the condition is true, then neither of the above will happen

These two macros will enable us to provide less restrictive behavior for certain correctness assertions, allowing clients to modify their before enabling these assertions by default.
This commit is contained in:
Powei Feng
2025-08-05 16:42:55 -07:00
parent 4909826d21
commit ac29bb5094
5 changed files with 68 additions and 39 deletions
+7 -14
View File
@@ -1235,20 +1235,13 @@ bool FEngine::destroy(const FMaterialInstance* p) {
size_t const primitiveCount = rcm.getPrimitiveCount(ri, 0);
for (size_t j = 0; j < primitiveCount; j++) {
auto const* const mi = rcm.getMaterialInstanceAt(ri, 0, j);
if (features.engine.debug.assert_material_instance_in_use) {
FILAMENT_CHECK_PRECONDITION(mi != p)
<< "destroying MaterialInstance \""
<< mi->getName() << "\" which is still in use by Renderable (entity="
<< entity.getId() << ", instance="
<< ri.asValue() << ", index=" << j << ")";
} else {
if (UTILS_UNLIKELY(mi == p)) {
LOG(ERROR) << "destroying MaterialInstance \"" << mi->getName()
<< "\" which is still in use by Renderable (entity="
<< entity.getId() << ", instance=" << ri.asValue()
<< ", index=" << j << ")";
}
}
auto const& featureFlags = features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(
mi != p,
featureFlags.assert_material_instance_in_use)
<< "destroying MaterialInstance \"" << mi->getName()
<< "\" which is still in use by Renderable (entity=" << entity.getId()
<< ", instance=" << ri.asValue() << ", index=" << j << ")";
}
}
}
+5 -10
View File
@@ -391,16 +391,11 @@ void FMaterial::terminate(FEngine& engine) {
auto const& materialInstanceResourceList = engine.getMaterialInstanceResourceList();
auto pos = materialInstanceResourceList.find(this);
if (UTILS_LIKELY(pos != materialInstanceResourceList.cend())) {
if (engine.features.engine.debug.assert_destroy_material_before_material_instance) {
FILAMENT_CHECK_PRECONDITION(pos->second.empty())
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
} else {
if (UTILS_UNLIKELY(!pos->second.empty())) {
LOG(ERROR) << "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
}
}
auto const& featureFlags = engine.features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(pos->second.empty(),
featureFlags.assert_destroy_material_before_material_instance)
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
}
#if FILAMENT_ENABLE_MATDBG
+9 -15
View File
@@ -158,12 +158,11 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
static_assert(DOCUMENTED_MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_BUFFER_COUNT);
if (downcast(engine).features.engine.debug.assert_vertex_buffer_count_exceeds_8) {
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount <= DOCUMENTED_MAX_VERTEX_BUFFER_COUNT)
<< "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
} else if (mImpl->mBufferCount > DOCUMENTED_MAX_VERTEX_BUFFER_COUNT) {
LOG(WARNING) << "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
}
auto const& featureFlags = static_cast<FEngine*>(&engine)->features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(
mImpl->mBufferCount <= DOCUMENTED_MAX_VERTEX_BUFFER_COUNT,
featureFlags.assert_vertex_buffer_count_exceeds_8)
<< "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
// Next we check if any unused buffer slots have been allocated. This helps prevent errors
// because uploading to an unused slot can trigger undefined behavior in the backend.
@@ -177,15 +176,10 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
<< "attribute " << j << " offset=" << attributes[j].offset
<< " is not multiple of 4";
FEngine* fengine = static_cast<FEngine*>(&engine);
if (fengine->features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4) {
FILAMENT_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0)
<< "attribute " << j << " stride=" << attributes[j].stride
<< " is not multiple of 4";
} else if ((attributes[j].stride & 0x3u) != 0) {
LOG(WARNING) << "attribute " << j << " stride=" << attributes[j].stride
<< " is not multiple of 4";
}
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0,
featureFlags.assert_vertex_buffer_attribute_stride_mult_of_4)
<< "attribute " << j << " stride=" << +attributes[j].stride
<< " is not multiple of 4";
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
FILAMENT_CHECK_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET))
+35
View File
@@ -544,6 +544,27 @@ public:
}
};
template<typename PanicType>
class FlagGuardedStream : public PanicStream {
public:
FlagGuardedStream(bool enable, char const* function, char const* file, int line,
char const* condition)
: PanicStream(function, file, line, condition),
mEnablePanic(enable) {}
~FlagGuardedStream() noexcept(false) {
if (mEnablePanic) {
PanicType::panic(mFunction, mFile, mLine, mLiteral, mStream.c_str());
} else {
logWarning();
}
}
private:
void logWarning() noexcept;
bool mEnablePanic;
};
} // namespace details
// -----------------------------------------------------------------------------------------------
@@ -582,6 +603,20 @@ public:
FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, ArithmeticPanic)
#endif
#define FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, TYPE) \
::utils::details::FlagGuardedStream<::utils::TYPE>(flag, PANIC_FUNCTION, PANIC_FILE(__FILE__), \
__LINE__, #condition)
#ifndef FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION
#define FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(condition, flag) \
FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PreconditionPanic)
#endif
#ifndef FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION
#define FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION(condition, flag) \
FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PostconditionPanic)
#endif
#define PANIC_PRECONDITION_IMPL(cond, format, ...) \
::utils::PreconditionPanic::panic(PANIC_FUNCTION, \
PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__)
+12
View File
@@ -21,6 +21,7 @@
#include <utils/CallStack.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Logger.h>
#include <utils/ostream.h>
#include <stdarg.h>
@@ -352,6 +353,17 @@ PanicStream& PanicStream::operator<<(std::string_view const& value) noexcept {
return *this;
}
template<typename PanicType>
void FlagGuardedStream<PanicType>::logWarning() noexcept {
LOG(WARNING) << "Failed [" << mLiteral << "]" << "\n file=" << mFile
<< "\n function=" << mFunction << "\n line=" << mLine << "\n info=\""
<< mStream.c_str() << "\"";
}
template void FlagGuardedStream<PreconditionPanic>::logWarning() noexcept;
template void FlagGuardedStream<PostconditionPanic>::logWarning() noexcept;
} // namespace details
// -----------------------------------------------------------------------------------------------