// 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(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); } 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; }