334 lines
10 KiB
C++
334 lines
10 KiB
C++
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
|
|
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
|
|
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
|
|
|
|
#include "FdFoundation.h"
|
|
#include "foundation/PxString.h"
|
|
#include "foundation/PxPhysicsVersion.h"
|
|
#include "foundation/PxUserAllocated.h"
|
|
#include "foundation/PxBroadcast.h"
|
|
|
|
namespace physx
|
|
{
|
|
#if PX_VC
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4251) // class needs to have dll-interface to be used by clients of class
|
|
#endif
|
|
|
|
class PX_FOUNDATION_API Foundation : public PxFoundation, public PxUserAllocated
|
|
{
|
|
PX_NOCOPY(Foundation)
|
|
|
|
public:
|
|
// PxFoundation
|
|
virtual void release() PX_OVERRIDE;
|
|
virtual PxErrorCallback& getErrorCallback() PX_OVERRIDE { return mErrorCallback; }
|
|
virtual void setErrorLevel(PxErrorCode::Enum mask) PX_OVERRIDE { mErrorMask = mask; }
|
|
virtual PxErrorCode::Enum getErrorLevel() const PX_OVERRIDE { return mErrorMask; }
|
|
virtual PxAllocatorCallback& getAllocatorCallback() PX_OVERRIDE { return mAllocatorCallback; }
|
|
virtual bool getReportAllocationNames() const PX_OVERRIDE { return mReportAllocationNames; }
|
|
virtual void setReportAllocationNames(bool value) PX_OVERRIDE { mReportAllocationNames = value; }
|
|
virtual void registerAllocationListener(physx::PxAllocationListener& listener) PX_OVERRIDE;
|
|
virtual void deregisterAllocationListener(physx::PxAllocationListener& listener) PX_OVERRIDE;
|
|
virtual void registerErrorCallback(PxErrorCallback& listener) PX_OVERRIDE;
|
|
virtual void deregisterErrorCallback(PxErrorCallback& listener) PX_OVERRIDE;
|
|
virtual bool error(PxErrorCode::Enum, const char* file, int line, const char* messageFmt, ...) PX_OVERRIDE;
|
|
virtual bool error(PxErrorCode::Enum, const char* file, int line, const char* messageFmt, va_list) PX_OVERRIDE;
|
|
//~PxFoundation
|
|
|
|
Foundation(PxErrorCallback& errc, PxAllocatorCallback& alloc);
|
|
~Foundation();
|
|
|
|
// init order is tricky here: the mutexes require the allocator, the allocator may require the error stream
|
|
PxAllocatorCallback& mAllocatorCallback;
|
|
PxErrorCallback& mErrorCallback;
|
|
|
|
PxBroadcastingAllocator mBroadcastingAllocator;
|
|
PxBroadcastingErrorCallback mBroadcastingError;
|
|
|
|
bool mReportAllocationNames;
|
|
|
|
PxErrorCode::Enum mErrorMask;
|
|
Mutex mErrorMutex;
|
|
|
|
AllocFreeTable mTempAllocFreeTable;
|
|
Mutex mTempAllocMutex;
|
|
|
|
Mutex mListenerMutex;
|
|
|
|
PxU32 mRefCount;
|
|
static PxU32 mWarnOnceTimestap;
|
|
};
|
|
#if PX_VC
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
} // namespace physx
|
|
|
|
using namespace physx;
|
|
|
|
static PxProfilerCallback* gProfilerCallback = NULL;
|
|
static Foundation* gInstance = NULL;
|
|
|
|
// PT: not in header so that people don't use it, only for temp allocator, will be removed
|
|
AllocFreeTable& getTempAllocFreeTable()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
return gInstance->mTempAllocFreeTable;
|
|
}
|
|
|
|
// PT: not in header so that people don't use it, only for temp allocator, will be removed
|
|
Mutex& getTempAllocMutex()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
return gInstance->mTempAllocMutex;
|
|
}
|
|
|
|
Foundation::Foundation(PxErrorCallback& errc, PxAllocatorCallback& alloc) :
|
|
mAllocatorCallback (alloc),
|
|
mErrorCallback (errc),
|
|
mBroadcastingAllocator (alloc, errc),
|
|
mBroadcastingError (errc),
|
|
#if PX_CHECKED
|
|
mReportAllocationNames (true),
|
|
#else
|
|
mReportAllocationNames (false),
|
|
#endif
|
|
mErrorMask (PxErrorCode::Enum(~0)),
|
|
mErrorMutex ("Foundation::mErrorMutex"),
|
|
mTempAllocMutex ("Foundation::mTempAllocMutex"),
|
|
mRefCount (0)
|
|
{
|
|
}
|
|
|
|
void deallocateTempBufferAllocations(AllocFreeTable& mTempAllocFreeTable);
|
|
|
|
Foundation::~Foundation()
|
|
{
|
|
deallocateTempBufferAllocations(mTempAllocFreeTable);
|
|
}
|
|
|
|
bool Foundation::error(PxErrorCode::Enum c, const char* file, int line, const char* messageFmt, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, messageFmt);
|
|
error(c, file, line, messageFmt, va);
|
|
va_end(va);
|
|
return false;
|
|
}
|
|
|
|
bool Foundation::error(PxErrorCode::Enum e, const char* file, int line, const char* messageFmt, va_list va)
|
|
{
|
|
PX_ASSERT(messageFmt);
|
|
if(e & mErrorMask)
|
|
{
|
|
// this function is reentrant but user's error callback may not be, so...
|
|
Mutex::ScopedLock lock(mErrorMutex);
|
|
|
|
// using a static fixed size buffer here because:
|
|
// 1. vsnprintf return values differ between platforms
|
|
// 2. va_start is only usable in functions with ellipses
|
|
// 3. ellipses (...) cannot be passed to called function
|
|
// which would be necessary to dynamically grow the buffer here
|
|
|
|
static const size_t bufSize = 1024;
|
|
char stringBuffer[bufSize];
|
|
Pxvsnprintf(stringBuffer, bufSize, messageFmt, va);
|
|
|
|
mBroadcastingError.reportError(e, stringBuffer, file, line);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Foundation::release()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
|
|
if(gInstance->mRefCount == 1)
|
|
{
|
|
PxAllocatorCallback& alloc = gInstance->getAllocatorCallback();
|
|
gInstance->~Foundation();
|
|
alloc.deallocate(gInstance);
|
|
gInstance = NULL;
|
|
}
|
|
else
|
|
{
|
|
gInstance->error(PxErrorCode::eINVALID_OPERATION, PX_FL,
|
|
"Foundation destruction failed due to pending module references. Close/release all depending modules first.");
|
|
}
|
|
}
|
|
|
|
PxU32 Foundation::mWarnOnceTimestap = 0;
|
|
|
|
void Foundation::registerAllocationListener(PxAllocationListener& listener)
|
|
{
|
|
Mutex::ScopedLock lock(mListenerMutex);
|
|
mBroadcastingAllocator.registerListener(listener);
|
|
}
|
|
|
|
void Foundation::deregisterAllocationListener(PxAllocationListener& listener)
|
|
{
|
|
Mutex::ScopedLock lock(mListenerMutex);
|
|
mBroadcastingAllocator.deregisterListener(listener);
|
|
}
|
|
|
|
void Foundation::registerErrorCallback(PxErrorCallback& callback)
|
|
{
|
|
Mutex::ScopedLock lock(mListenerMutex);
|
|
mBroadcastingError.registerListener(callback);
|
|
}
|
|
|
|
void Foundation::deregisterErrorCallback(PxErrorCallback& callback)
|
|
{
|
|
Mutex::ScopedLock lock(mListenerMutex);
|
|
mBroadcastingError.deregisterListener(callback);
|
|
}
|
|
|
|
PxFoundation* PxCreateFoundation(PxU32 version, PxAllocatorCallback& allocator, PxErrorCallback& errorCallback)
|
|
{
|
|
if(version != PX_PHYSICS_VERSION)
|
|
{
|
|
char buffer[256];
|
|
Pxsnprintf(buffer, 256, "Wrong version: physics version is 0x%08x, tried to create 0x%08x", PX_PHYSICS_VERSION, version);
|
|
errorCallback.reportError(PxErrorCode::eINVALID_PARAMETER, buffer, PX_FL);
|
|
return 0;
|
|
}
|
|
|
|
if(!gInstance)
|
|
{
|
|
// if we don't assign this here, the Foundation object can't create member
|
|
// subobjects which require the allocator
|
|
|
|
gInstance = reinterpret_cast<Foundation*>(allocator.allocate(sizeof(Foundation), "Foundation", PX_FL));
|
|
|
|
if(gInstance)
|
|
{
|
|
PX_PLACEMENT_NEW(gInstance, Foundation)(errorCallback, allocator);
|
|
|
|
PX_ASSERT(gInstance->mRefCount == 0);
|
|
gInstance->mRefCount = 1;
|
|
|
|
// skip 0 which marks uninitialized timestaps in PX_WARN_ONCE
|
|
gInstance->mWarnOnceTimestap = (gInstance->mWarnOnceTimestap == PX_MAX_U32) ? 1 : gInstance->mWarnOnceTimestap + 1;
|
|
|
|
return gInstance;
|
|
}
|
|
else
|
|
{
|
|
errorCallback.reportError(PxErrorCode::eINTERNAL_ERROR, "Memory allocation for foundation object failed.", PX_FL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCallback.reportError(PxErrorCode::eINVALID_OPERATION, "Foundation object exists already. Only one instance per process can be created.", PX_FL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void PxSetFoundationInstance(PxFoundation& foundation)
|
|
{
|
|
gInstance = &static_cast<Foundation&>(foundation);
|
|
}
|
|
|
|
PxAllocatorCallback* PxGetAllocatorCallback()
|
|
{
|
|
return &gInstance->getAllocatorCallback();
|
|
}
|
|
|
|
PxAllocatorCallback* PxGetBroadcastAllocator(bool* reportAllocationNames)
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
if(reportAllocationNames)
|
|
*reportAllocationNames = gInstance->mReportAllocationNames;
|
|
|
|
return &gInstance->mBroadcastingAllocator;
|
|
}
|
|
|
|
PxErrorCallback* PX_CALL_CONV PxGetErrorCallback()
|
|
{
|
|
return &gInstance->getErrorCallback();
|
|
}
|
|
|
|
PxErrorCallback* PX_CALL_CONV PxGetBroadcastError()
|
|
{
|
|
return &gInstance->mBroadcastingError;
|
|
}
|
|
|
|
PxFoundation& PxGetFoundation()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
return *gInstance;
|
|
}
|
|
|
|
PxFoundation* PxIsFoundationValid()
|
|
{
|
|
return gInstance;
|
|
}
|
|
|
|
PxProfilerCallback* PxGetProfilerCallback()
|
|
{
|
|
return gProfilerCallback;
|
|
}
|
|
|
|
void PxSetProfilerCallback(PxProfilerCallback* profiler)
|
|
{
|
|
gProfilerCallback = profiler;
|
|
}
|
|
|
|
PxU32 PxGetWarnOnceTimeStamp()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
return gInstance->mWarnOnceTimestap;
|
|
}
|
|
|
|
void PxDecFoundationRefCount()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
|
|
if(gInstance->mRefCount > 0)
|
|
gInstance->mRefCount--;
|
|
else
|
|
gInstance->error(PxErrorCode::eINVALID_OPERATION, PX_FL, "Foundation: Invalid deregistration detected.");
|
|
}
|
|
|
|
void PxIncFoundationRefCount()
|
|
{
|
|
PX_ASSERT(gInstance);
|
|
|
|
if(gInstance->mRefCount > 0)
|
|
gInstance->mRefCount++;
|
|
else
|
|
gInstance->error(PxErrorCode::eINVALID_OPERATION, PX_FL, "Foundation: Invalid registration detected.");
|
|
}
|
|
|
|
// Private method to set the global foundation instance to NULL
|
|
PX_C_EXPORT PX_FOUNDATION_API void PX_CALL_CONV PxResetFoundationInstance()
|
|
{
|
|
gInstance = NULL;
|
|
}
|