diff options
| author | mtamis <[email protected]> | 2017-02-28 18:24:59 +0100 |
|---|---|---|
| committer | mtamis <[email protected]> | 2017-02-28 18:24:59 +0100 |
| commit | 5581909a4d19db97304449f66404ff99a0429d3f (patch) | |
| tree | a90f7eb85c095a8aba45cf5e909c82c1cdbed77d /NvCloth/samples/SampleBase/utils | |
| parent | Fix cmake visual studio project generation (locate_gw_root.bat) (diff) | |
| download | nvcloth-5581909a4d19db97304449f66404ff99a0429d3f.tar.xz nvcloth-5581909a4d19db97304449f66404ff99a0429d3f.zip | |
Add visual samples.
Diffstat (limited to 'NvCloth/samples/SampleBase/utils')
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/CallbackImplementations.cpp | 110 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/CallbackImplementations.h | 230 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp | 193 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h | 54 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/JobManager.cpp | 136 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/JobManager.h | 175 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/SampleProfiler.cpp | 223 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/SampleProfiler.h | 79 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/SampleTime.h | 58 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/UIHelpers.h | 56 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/Utils.cpp | 13 | ||||
| -rw-r--r-- | NvCloth/samples/SampleBase/utils/Utils.h | 89 |
12 files changed, 1416 insertions, 0 deletions
diff --git a/NvCloth/samples/SampleBase/utils/CallbackImplementations.cpp b/NvCloth/samples/SampleBase/utils/CallbackImplementations.cpp new file mode 100644 index 0000000..0a257ba --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/CallbackImplementations.cpp @@ -0,0 +1,110 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "CallbackImplementations.h" +#include "JobManager.h" +#include <iostream> + +//#if USE_DX11 +#include <d3d11.h> +//#endif + +#include <PsThread.h> + +NvClothEnvironment* NvClothEnvironment::sEnv = nullptr; + + +void ErrorCallback::reportError(physx::PxErrorCode::Enum code, const char* message, const char* file, int line) +{ + const char* codeName = "???"; + switch(code) + { +#define CASE(x) case physx::PxErrorCode::Enum::x : codeName = #x; break; + CASE(eNO_ERROR) + CASE(eDEBUG_INFO) + CASE(eDEBUG_WARNING) + CASE(eINVALID_PARAMETER) + CASE(eINVALID_OPERATION) + CASE(eOUT_OF_MEMORY) + CASE(eINTERNAL_ERROR) + CASE(eABORT) + CASE(ePERF_WARNING) + default: + ; +#undef CASE + } + + std::cout << "Log " << codeName << " from file:" << file << ":" << line << "\n MSG:" << message << std::endl; +} + +//#if USE_DX11 +DxContextManagerCallbackImpl::DxContextManagerCallbackImpl(ID3D11Device* device, bool synchronizeResources) + : + mDevice(device), + mSynchronizeResources(synchronizeResources) +{ + mDevice->AddRef(); + mDevice->GetImmediateContext(&mContext); +#ifdef _DEBUG + mLockCountTls = physx::shdfnd::TlsAlloc(); +#endif +} +DxContextManagerCallbackImpl::~DxContextManagerCallbackImpl() +{ + mContext->Release(); + +#if _DEBUG + ID3D11Debug* debugDevice; + mDevice->QueryInterface(&debugDevice); + if(debugDevice) + { + debugDevice->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + debugDevice->Release(); + } +#endif + + mDevice->Release(); + +#if _DEBUG + physx::shdfnd::TlsFree(mLockCountTls); +#endif +} + +void DxContextManagerCallbackImpl::acquireContext() +{ + + mMutex.lock(); +#if _DEBUG + physx::shdfnd::TlsSet(mLockCountTls, reinterpret_cast<void*>(reinterpret_cast<intptr_t>(physx::shdfnd::TlsGet(mLockCountTls)) + 1)); +#endif +} +void DxContextManagerCallbackImpl::releaseContext() +{ +#if _DEBUG + physx::shdfnd::TlsSet(mLockCountTls, reinterpret_cast<void*>(reinterpret_cast<intptr_t>(physx::shdfnd::TlsGet(mLockCountTls)) - 1)); +#endif + mMutex.unlock(); +} +ID3D11Device* DxContextManagerCallbackImpl::getDevice() const +{ + return mDevice; +} +ID3D11DeviceContext* DxContextManagerCallbackImpl::getContext() const +{ +#if _DEBUG + assert(reinterpret_cast<intptr_t>(physx::shdfnd::TlsGet(mLockCountTls)) > 0); +#endif + return mContext; +} +bool DxContextManagerCallbackImpl::synchronizeResources() const +{ + return mSynchronizeResources; +} +//#endif diff --git a/NvCloth/samples/SampleBase/utils/CallbackImplementations.h b/NvCloth/samples/SampleBase/utils/CallbackImplementations.h new file mode 100644 index 0000000..38a4b17 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/CallbackImplementations.h @@ -0,0 +1,230 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#pragma once +#include <NvCloth/Callbacks.h> +#include <foundation/PxAllocatorCallback.h> +#include <foundation/PxErrorCallback.h> +#include <foundation/PxAssert.h> +#include <foundation/PxProfiler.h> + +#include <vector> +#include <map> + +#if USE_CUDA +#include <cuda.h> +#endif + +#include <string> +#include <sstream> +#include <assert.h> +#include <mutex> + +#include <NvCloth/DxContextManagerCallback.h> + +#ifdef _MSC_VER +#include <Windows.h> +#endif + +class Allocator : public physx::PxAllocatorCallback +{ + public: + Allocator() + { + mEnableLeakDetection = false; + } + virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + #ifdef _MSC_VER + void* ptr = _aligned_malloc(size, 16); + #else + void* ptr; + if(posix_memalign(&ptr, 16, size)) ptr = 0; + #endif + if (mEnableLeakDetection) + { + std::lock_guard<std::mutex> lock(mAllocationsMapLock); + mAllocations[ptr] = Allocation(size, typeName, filename, line); + } + return ptr; + } + virtual void deallocate(void* ptr) + { + if (mEnableLeakDetection && ptr) + { + std::lock_guard<std::mutex> lock(mAllocationsMapLock); + auto i = mAllocations.find(ptr); + if (i == mAllocations.end()) + { + printf("Tried to deallocate %p which was not allocated with this allocator callback.",ptr); + } + else + { + mAllocations.erase(i); + } + } + #ifdef _MSC_VER + _aligned_free(ptr); + #else + free(ptr); + #endif + } + + void StartTrackingLeaks() + { + std::lock_guard<std::mutex> lock(mAllocationsMapLock); + mAllocations.clear(); + mEnableLeakDetection = true; + } + + void StopTrackingLeaksAndReport() + { + std::lock_guard<std::mutex> lock(mAllocationsMapLock); + mEnableLeakDetection = false; + + size_t totalBytes = 0; + std::stringstream message; + message << "Memory leaks detected:\n"; + for (auto it = mAllocations.begin(); it != mAllocations.end(); ++it) + { + const Allocation& alloc = it->second; + message << "* Allocated ptr " << it->first << " of " << alloc.mSize << "bytes (type=" << alloc.mTypeName << ") at " << alloc.mFileName << ":" << alloc.mLine<<"\n"; + totalBytes += alloc.mSize; + } + if (mAllocations.size()>0) + { + message << "=====Total of " << totalBytes << " bytes in " << mAllocations.size() << " allocations leaked====="; + const std::string& tmp = message.str(); +#ifdef _MSC_VER +// OutputDebugString(tmp.c_str()); //Write to visual studio output so we can see it after the application closes +#endif + printf("%s\n", tmp.c_str()); + } + + mAllocations.clear(); + } + private: + bool mEnableLeakDetection; + struct Allocation + { + Allocation(){} + Allocation(size_t size, const char* typeName, const char* filename, int line) + : mSize(size), mTypeName(typeName), mFileName(filename), mLine(line) + { + + } + size_t mSize; + std::string mTypeName; + std::string mFileName; + int mLine; + }; + std::map<void*,Allocation> mAllocations; + std::mutex mAllocationsMapLock; +}; + +class ErrorCallback : public physx::PxErrorCallback +{ + public: + ErrorCallback(){} + virtual void reportError(physx::PxErrorCode::Enum code, const char* message, const char* file, int line); +}; + + +class AssertHandler : public physx::PxAssertHandler +{ + public: + virtual void operator()(const char* exp, const char* file, int line, bool& ignore) + { + PX_UNUSED(ignore); + printf("NV_CLOTH_ASSERT(%s) from file:%s:%d Failed\n", exp, file, line); + assert(("Assertion failed, see log/console for more info.",0)); + } +}; + + +class NvClothEnvironment +{ + NvClothEnvironment() + { + SetUp(); + } + virtual ~NvClothEnvironment() + { + TearDown(); + } + + static NvClothEnvironment* sEnv; + + public: + static void AllocateEnv() + { + sEnv = new NvClothEnvironment; + } + static void FreeEnv(){ delete sEnv; sEnv = nullptr; } + static void ReportEnvFreed(){ sEnv = nullptr; } //google test will free it for us, so we just reset the value + static NvClothEnvironment* GetEnv(){ return sEnv; } + + virtual void SetUp() + { + mAllocator = new Allocator; + mAllocator->StartTrackingLeaks(); + mFoundationAllocator = new Allocator; + mFoundationAllocator->StartTrackingLeaks(); + mErrorCallback = new ErrorCallback; + mAssertHandler = new AssertHandler; + nv::cloth::InitializeNvCloth(mAllocator, mErrorCallback, mAssertHandler, nullptr); +#if USE_CUDA + cuInit(0); +#endif + } + virtual void TearDown() + { + mAllocator->StopTrackingLeaksAndReport(); + mFoundationAllocator->StopTrackingLeaksAndReport(); + delete mErrorCallback; + delete mFoundationAllocator; + delete mAllocator; + delete mAssertHandler; + } + + Allocator* GetAllocator(){ return mAllocator; } + Allocator* GetFoundationAllocator(){ return mFoundationAllocator; } + ErrorCallback* GetErrorCallback(){ return mErrorCallback; } + AssertHandler* GetAssertHandler(){ return mAssertHandler; } + + private: + Allocator* mAllocator; + Allocator* mFoundationAllocator; + ErrorCallback* mErrorCallback; + AssertHandler* mAssertHandler; +}; + +//#if USE_DX11 +class DxContextManagerCallbackImpl : public nv::cloth::DxContextManagerCallback +{ +public: + DxContextManagerCallbackImpl(ID3D11Device* device, bool synchronizeResources = false); + ~DxContextManagerCallbackImpl(); + virtual void acquireContext() override; + virtual void releaseContext() override; + virtual ID3D11Device* getDevice() const override; + virtual ID3D11DeviceContext* getContext() const override; + virtual bool synchronizeResources() const override; + +private: + std::recursive_mutex mMutex; + ID3D11Device* mDevice; + ID3D11DeviceContext* mContext; + bool mSynchronizeResources; +#ifdef _DEBUG + uint32_t mLockCountTls; +#endif +}; +//#endif diff --git a/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp new file mode 100644 index 0000000..d75bb25 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp @@ -0,0 +1,193 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "ClothMeshGenerator.h" + +void ClothMeshData::Clear() +{ + mVertices.clear(); + mTriangles.clear(); + mQuads.clear(); +} + +void ClothMeshData::GeneratePlaneCloth(float width, float height, int segmentsX, int segmentsY, bool createQuads, physx::PxMat44 transform, bool alternatingDiagonals) +{ + +/* +GeneratePlaneCloth(x,y,2,2) generates: + + v0______v1_____v2 v0______v1_____v2 + | | | |\ |\ | + | Q0 | Q1 | | \t0 | \t2 | + | | | | t1 \ | t3 \ | + v3------v4-----v5 v3-----\v4----\v5 + | | | | \ | \ | + | Q2 | Q3 | | \t4| \t6| + |______|______| |_t5_\_|_t7__\| + v6 v7 v8 v6 v7 v8 +*/ + + +// Submesh submesh; + + Clear(); + mVertices.resize((segmentsX + 1) * (segmentsY + 1)); + mInvMasses.resize((segmentsX + 1) * (segmentsY + 1)); + mTriangles.resize(segmentsX * segmentsY * 2); + if (createQuads) + mQuads.resize(segmentsX * segmentsY); + + mMesh.vertices.resize(mVertices.size()); + mMesh.indices.resize(3 * mTriangles.size()); + + physx::PxVec3 topLeft(-width * 0.5f, 0.f, -height * 0.5f); +// vec3 topLeftGLM(-width * 0.5f, translateUp, -height * 0.5f); + + //calculate uv scale and offset to keep texture aspect ratio 1:1 + float uvSx = width > height ? 1.0f : width / height; + float uvSy = width > height ? height / width : 1.0f; + float uvOx = 0.5f * (1.0f - uvSx); + float uvOy = 0.5f * (1.0f - uvSy); + + // Vertices + for (int y = 0; y < segmentsY + 1; y++) + { + for(int x = 0; x < segmentsX + 1; x++) + { + mVertices[x + y * (segmentsX + 1)] = transform.transform(topLeft + physx::PxVec3( ((float)x / (float)segmentsX) * width, + 0.f, + ((float)y / (float)segmentsY) * height)); + mInvMasses[x + y * (segmentsX + 1)] = 1.0f; + + mMesh.vertices[x + y * (segmentsX + 1)].position = transform.transform(topLeft + physx::PxVec3(((float)x / (float)segmentsX) * width, + 0.f, + ((float)y / (float)segmentsY) * height)); + mMesh.vertices[x + y * (segmentsX + 1)].normal = transform.transform(physx::PxVec3(0.f, 1.f, 0.f)); + + mMesh.vertices[x + y * (segmentsX + 1)].uv = physx::PxVec2(uvOx + uvSx*(float)x / (float)segmentsX, uvOy + uvSy*(1.0f - (float)y / (float)segmentsY)); + } + } + + if (createQuads) + { + // Quads + for (int y = 0; y < segmentsY; y++) + { + for (int x = 0; x < segmentsX; x++) + { + mQuads[(x + y * segmentsX)] = Quad((uint32_t)(x + 0) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 1) * (segmentsX + 1), + (uint32_t)(x + 0) + (y + 1) * (segmentsX + 1)); + } + } + } + + // Triangles + for (int y = 0; y < segmentsY; y++) + { + for(int x = 0; x < segmentsX; x++) + { + if(alternatingDiagonals && (x^y)&1) + { + //Top right to bottom left + mTriangles[(x + y * segmentsX) * 2 + 0] = Triangle( (uint32_t)(x + 0) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 0) + (y + 1) * (segmentsX + 1)); + + mTriangles[(x + y * segmentsX) * 2 + 1] = Triangle( (uint32_t)(x + 1) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 1) * (segmentsX + 1), + (uint32_t)(x + 0) + (y + 1) * (segmentsX + 1)); + } + else + { + //Top left to bottom right + mTriangles[(x + y * segmentsX) * 2 + 0] = Triangle( (uint32_t)(x + 0) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 1) * (segmentsX + 1)); + + mTriangles[(x + y * segmentsX) * 2 + 1] = Triangle( (uint32_t)(x + 0) + (y + 0) * (segmentsX + 1), + (uint32_t)(x + 1) + (y + 1) * (segmentsX + 1), + (uint32_t)(x + 0) + (y + 1) * (segmentsX + 1)); + } + } + } + + for (int i = 0; i < (int)mTriangles.size(); i++) + { + mMesh.indices[3 * i] = mTriangles[i].a; + mMesh.indices[3 * i + 1] = mTriangles[i].b; + mMesh.indices[3 * i + 2] = mTriangles[i].c; + } +} + +void ClothMeshData::AttachClothPlaneByAngles(int segmentsX, int segmentsY, bool attachByWidth) +{ + for (int y = 0; y < segmentsY + 1; y++) + for (int x = 0; x < segmentsX + 1; x++) + if ((attachByWidth && y == 0) || (!attachByWidth && x == 0)) + if (x == 0 || x == segmentsX) + mInvMasses[x + y * (segmentsX + 1)] = 0.0f; +} + +void ClothMeshData::AttachClothPlaneBySide(int segmentsX, int segmentsY, bool attachByWidth) +{ + for (int y = 0; y < segmentsY + 1; y++) + for (int x = 0; x < segmentsX + 1; x++) + if ((attachByWidth && y == 0) || (!attachByWidth && x == 0)) + mInvMasses[x + y * (segmentsX + 1)] = 0.0f; +} + +void ClothMeshData::SetInvMasses(float invMass) +{ + // Doesn't modify attached vertices + for (int i = 0; i < (int)mInvMasses.size(); ++i) + if (mInvMasses[i] > 1e-6f) + mInvMasses[i] = invMass; +} + +void ClothMeshData::SetInvMassesFromDensity(float density) +{ + // Tempt code, should take into account triangle's areas + // Doesn't modify attached vertices + for (int i = 0; i < (int)mInvMasses.size(); ++i) + if (mInvMasses[i] > 1e-6f) + mInvMasses[i] = 1.f / density; +} + +template <typename T> +nv::cloth::BoundedData ToBoundedData(T& vector) +{ + nv::cloth::BoundedData d; + d.data = &vector[0]; + d.stride = sizeof(vector[0]); + d.count = (physx::PxU32)vector.size(); + + return d; +} + +nv::cloth::ClothMeshDesc ClothMeshData::GetClothMeshDesc() +{ + nv::cloth::ClothMeshDesc d; + d.setToDefault(); + d.points = ToBoundedData(mVertices); + if (mQuads.size() != 0) + d.quads = ToBoundedData(mQuads); + if (mTriangles.size() != 0) + d.triangles = ToBoundedData(mTriangles); + d.invMasses = ToBoundedData(mInvMasses); + + return d; +} + +SimpleMesh ClothMeshData::GetRenderMesh() +{ + return mMesh; +}
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h new file mode 100644 index 0000000..0f57230 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#pragma once +#include <vector> +#include <foundation/PxVec3.h> +#include <foundation/PxVec2.h> +#include "NvClothExt/ClothFabricCooker.h" +#include <foundation/PxMat44.h> +#include "Mesh.h" + +struct ClothMeshData +{ + struct Triangle + { + Triangle(){} + Triangle(uint32_t _a, uint32_t _b, uint32_t _c) : + a(_a), b(_b), c(_c){} + uint32_t a, b, c; + }; + struct Quad + { + Quad(){} + Quad(uint32_t _a, uint32_t _b, uint32_t _c, uint32_t _d) : + a(_a), b(_b), c(_c), d(_d){} + uint32_t a, b, c, d; + }; + std::vector<physx::PxVec3> mVertices; + std::vector<physx::PxVec2> mUvs; + std::vector<Triangle> mTriangles; + std::vector<Quad> mQuads; + std::vector<physx::PxReal> mInvMasses; + + SimpleMesh mMesh; + + void Clear(); + void GeneratePlaneCloth(float width, float height, int segmentsX, int segmentsY, bool createQuads = false, physx::PxMat44 transform = physx::PxIdentity, bool alternatingDiagonals = true); + + void AttachClothPlaneByAngles(int segmentsX, int segmentsY, bool attachByWidth = true); + void AttachClothPlaneBySide(int segmentsX, int segmentsY, bool attachByWidth = true); + + void SetInvMasses(float invMass); + void SetInvMassesFromDensity(float density); // Todo + + nv::cloth::ClothMeshDesc GetClothMeshDesc(); + SimpleMesh GetRenderMesh(); +}; diff --git a/NvCloth/samples/SampleBase/utils/JobManager.cpp b/NvCloth/samples/SampleBase/utils/JobManager.cpp new file mode 100644 index 0000000..093db60 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/JobManager.cpp @@ -0,0 +1,136 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "JobManager.h" +#define _USE_MATH_DEFINES +#include <math.h> +#include <NvCloth/Solver.h> + +void Job::Initialize(JobManager* parent, std::function<void(Job*)> function, int refcount) +{ + mFunction = function; + mParent = parent; + Reset(refcount); +} + +Job::Job(const Job& job) +{ + mFunction = job.mFunction; + mParent = job.mParent; + mRefCount.store(job.mRefCount); + mFinished = job.mFinished; +} + +void Job::Reset(int refcount) +{ + mRefCount = refcount; + mFinished = false; +} + +void Job::Execute() +{ + if (mFunction) + mFunction(this); + else + ExecuteInternal(); + + mFinishedLock.lock(); + mFinished = true; + mFinishedLock.unlock(); + mFinishedEvent.notify_one(); +} + +void Job::AddReference() +{ + mRefCount++; +} +void Job::RemoveReference() +{ + if (0 == --mRefCount) + { + mParent->Submit(this); + } +} + +void Job::Wait() +{ + std::unique_lock<std::mutex> lock(mFinishedLock); + mFinishedEvent.wait(lock, [this](){return mFinished;} ); + return; +} + +void JobManager::WorkerEntryPoint(JobManager* parrent) +{ + while (true) + { + Job* job; + { + std::unique_lock<std::mutex> lock(parrent->mJobQueueLock); + while (parrent->mJobQueue.size() == 0 && !parrent->mQuit) + parrent->mJobQueueEvent.wait(lock); + + if (parrent->mQuit) + return; + + job = parrent->mJobQueue.front(); + parrent->mJobQueue.pop(); + } + job->Execute(); + } +} + +void JobManager::Submit(Job* job) +{ + mJobQueueLock.lock(); + mJobQueue.push(job); + mJobQueueLock.unlock(); + mJobQueueEvent.notify_one(); +} + +void MultithreadedSolverHelper::Initialize(nv::cloth::Solver* solver, JobManager* jobManager) +{ + mSolver = solver; + mJobManager = jobManager; + mEndSimulationJob.Initialize(mJobManager, [this](Job*) { + mSolver->endSimulation(); + }); + + mStartSimulationJob.Initialize(mJobManager, [this](Job*) { + mSolver->beginSimulation(mDt); + for(int j = 0; j < mSolver->getSimulationChunkCount(); j++) + mSimulationChunkJobs[j].RemoveReference(); + }); +} + +void MultithreadedSolverHelper::StartSimulation(float dt) +{ + mDt = dt; + + if (mSolver->getSimulationChunkCount() != mSimulationChunkJobs.size()) + { + mSimulationChunkJobs.resize(mSolver->getSimulationChunkCount(), Job()); + for (int j = 0; j < mSolver->getSimulationChunkCount(); j++) + mSimulationChunkJobs[j].Initialize(mJobManager, [this, j](Job*) {mSolver->simulateChunk(j); mEndSimulationJob.RemoveReference(); }); + } + else + { + for (int j = 0; j < mSolver->getSimulationChunkCount(); j++) + mSimulationChunkJobs[j].Reset(); + } + mStartSimulationJob.Reset(); + mEndSimulationJob.Reset(mSolver->getSimulationChunkCount()); + mStartSimulationJob.RemoveReference(); + +} + +void MultithreadedSolverHelper::WaitForSimulation() +{ + mEndSimulationJob.Wait(); +}
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/JobManager.h b/NvCloth/samples/SampleBase/utils/JobManager.h new file mode 100644 index 0000000..648fa33 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/JobManager.h @@ -0,0 +1,175 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#pragma once + +#include <foundation/PxErrorCallback.h> +#include <regex> +#include "CallbackImplementations.h" +#include <mutex> +#include <thread> +#include <condition_variable> + +#include <foundation/PxVec4.h> +#include <foundation/PxVec3.h> +#include <vector> +#include <queue> +#include <atomic> + +#include <task/PxTaskManager.h> +#include <task/PxTask.h> + +namespace nv +{ +namespace cloth +{ +class Solver; +} +} + +///Dummy task that can be used as end node in a task graph. +class DummyTask : public physx::PxTask +{ +public: + DummyTask() { mFinished = false; mTm = nullptr; } + DummyTask(physx::PxTaskManager* tm) { mFinished = false; mTm = tm; mTm->submitUnnamedTask(*this); } + ~DummyTask() { mTm = nullptr; } //Way to catch race conditions. Will usually crash on nullptr if the task gets deleted before the taskmanager is done with it. + + virtual void run() override { } + virtual void release() override { physx::PxTask::release(); mFinished = true; mWaitEvent.notify_all(); } + virtual const char* getName() const override { return "DummyTask"; } + + void Reset(physx::PxTaskManager* tm) { mFinished = false; mTm = tm; mTm->submitUnnamedTask(*this); } + + ///Use Wait to block the calling thread until this task is finished and save to delete + void Wait() + { + std::mutex eventLock; + std::unique_lock<std::mutex> lock(eventLock); + while (!mFinished) { mWaitEvent.wait(lock); } + } + +private: + std::condition_variable mWaitEvent; + bool mFinished; +}; + +class CpuDispatcher : public physx::PxCpuDispatcher +{ + virtual void submitTask(physx::PxBaseTask& task) + { + task.run(); + task.release(); + } + virtual uint32_t getWorkerCount() const { return 1; } +}; + + +class JobManager; +class Job +{ +public: + Job() = default; + Job(const Job&); + void Initialize(JobManager* parent, std::function<void(Job*)> function = std::function<void(Job*)>(), int refcount = 1); + void Reset(int refcount = 1); //Call this before reusing a job that doesn't need to be reinitialized + void Execute(); + void AddReference(); + void RemoveReference(); + void Wait(); //Block until job is finished +private: + virtual void ExecuteInternal() {} + + std::function<void(Job*)> mFunction; + JobManager* mParent; + std::atomic_int mRefCount; + + bool mFinished; + std::mutex mFinishedLock; + std::condition_variable mFinishedEvent; +}; + +class JobManager +{ +public: + JobManager() + { + mWorkerCount = 8; + mWorkerThreads = new std::thread[mWorkerCount]; + mQuit = false; + + for(int i = 0; i < mWorkerCount; i++) + mWorkerThreads[i] = std::thread(JobManager::WorkerEntryPoint, this); + } + ~JobManager() + { + if(!mQuit) + Quit(); + } + + void Quit() + { + std::unique_lock<std::mutex> lock(mJobQueueLock); + mQuit = true; + lock.unlock(); + mJobQueueEvent.notify_all(); + for(int i = 0; i < mWorkerCount; i++) + { + mWorkerThreads[i].join(); + } + delete[] mWorkerThreads; + } + + template <int count, typename F> + void ParallelLoop(F const& function) + { + /*for(int i = 0; i < count; i++) + function(i);*/ + Job finalJob; + finalJob.Initialize(this, std::function<void(Job*)>(), count); + Job jobs[count]; + for(int j = 0; j < count; j++) + { + jobs[j].Initialize(this, [j, &finalJob, function](Job*) {function(j); finalJob.RemoveReference(); }); + jobs[j].RemoveReference(); + } + finalJob.Wait(); + } + + static void WorkerEntryPoint(JobManager* parrent); +private: + friend class Job; + void Submit(Job* job); + + int mWorkerCount; + std::thread* mWorkerThreads; + + std::mutex mJobQueueLock; + std::queue<Job*> mJobQueue; + std::condition_variable mJobQueueEvent; + bool mQuit; +}; + +class MultithreadedSolverHelper +{ +public: + void Initialize(nv::cloth::Solver* solver, JobManager* jobManager); + void StartSimulation(float dt); + void WaitForSimulation(); +private: + Job mStartSimulationJob; + Job mEndSimulationJob; + std::vector<Job> mSimulationChunkJobs; + + float mDt; + + nv::cloth::Solver* mSolver; + JobManager* mJobManager; +}; diff --git a/NvCloth/samples/SampleBase/utils/SampleProfiler.cpp b/NvCloth/samples/SampleBase/utils/SampleProfiler.cpp new file mode 100644 index 0000000..0438b06 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/SampleProfiler.cpp @@ -0,0 +1,223 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + + +#include "SampleProfiler.h" +#include <map> +#include <iostream> +#include <fstream> +#include <stack> + +using namespace std::chrono; + +struct ProfileData +{ + steady_clock::time_point start; + + microseconds time; + microseconds prevTime; + microseconds maxTime; + uint32_t calls; + uint32_t prevCalls; + + ProfileData() : time(0), prevTime(0), maxTime(0), calls(0), prevCalls(0) + {} +}; + +struct Node +{ + ProfileData data; + std::map<const char*, Node> childs; + Node* parent; +}; + +static std::map<const char*, Node> s_roots; +static Node* s_currentNode; +static bool s_beginEndMismatch; +static microseconds s_overhead; +static microseconds s_prevOverhead; + +void SampleProfilerInit() +{ + s_roots.clear(); + s_currentNode = nullptr; + s_beginEndMismatch = false; + s_overhead = microseconds(); +} + +void SampleProfilerBegin(const char* name) +{ + auto start = steady_clock::now(); + { + Node* parent = s_currentNode; + if (s_currentNode == nullptr) + { + s_currentNode = &s_roots[name]; + } + else + { + s_currentNode = &s_currentNode->childs[name]; + } + s_currentNode->parent = parent; + s_currentNode->data.calls++; + s_currentNode->data.start = steady_clock::now(); + } + s_overhead += duration_cast<microseconds>(steady_clock::now() - start); +} + +void SampleProfilerEnd() +{ + auto start = steady_clock::now(); + { + if (s_currentNode) + { + auto& data = s_currentNode->data; + data.time += duration_cast<microseconds>(steady_clock::now() - data.start); + data.maxTime = data.time > data.maxTime ? data.time : data.maxTime; + s_currentNode = s_currentNode->parent; + } + else + { + s_beginEndMismatch = true; + } + } + s_overhead += duration_cast<microseconds>(steady_clock::now() - start); +} + +struct SampleProfilerTreeIteratorImpl final : public SampleProfilerTreeIterator +{ + struct StackNode + { + Node* node; + const char* name; + }; + + SampleProfilerTreeIteratorImpl(std::map<const char*, Node>& roots) + { + for (auto& root : roots) + { + m_stack.emplace(StackNode { &root.second, root.first }); + } + + next(); + } + + virtual const Data* data() const override + { + return m_valid ? &m_data : nullptr; + } + + Node* node() + { + return m_node; + } + + virtual bool isDone() const + { + return !m_valid; + } + + virtual void next() + { + if (!m_stack.empty()) + { + auto& e = m_stack.top(); + m_stack.pop(); + m_node = e.node; + m_data.depth = 0; + m_data.hash = (uint64_t)m_node; + for (const Node* p = m_node; p != nullptr; p = p->parent) + { + m_data.depth++; + } + m_data.name = e.name; + m_data.calls = m_node->data.prevCalls; + m_data.time = m_node->data.prevTime; + m_data.maxTime = m_node->data.maxTime; + m_data.hasChilds = !m_node->childs.empty(); + + for (auto it = m_node->childs.rbegin(); it != m_node->childs.rend(); ++it) + { + m_stack.emplace(StackNode { &(*it).second, (*it).first }); + } + m_valid = true; + } + else + { + m_valid = false; + } + } + + virtual void release() + { + delete this; + } + + bool m_valid; + Data m_data; + Node* m_node; + std::stack<StackNode > m_stack; +}; + +void SampleProfilerReset() +{ + for (SampleProfilerTreeIteratorImpl it(s_roots); !it.isDone(); it.next()) + { + auto& data = it.node()->data; + data.prevTime = data.time; + data.prevCalls = data.calls; + data.time = microseconds(); + data.calls = 0; + } + s_currentNode = nullptr; + s_beginEndMismatch = false; + s_prevOverhead = s_overhead; + s_overhead = microseconds(); +} + +bool SampleProfilerIsValid() +{ + return !s_beginEndMismatch; +} + +microseconds SampleProfilerGetOverhead() +{ + return s_prevOverhead; +} + +SampleProfilerTreeIterator* SampleProfilerCreateTreeIterator() +{ + return SampleProfilerIsValid() ? new SampleProfilerTreeIteratorImpl(s_roots) : nullptr; +} + +void SampleProfilerDumpToFile(const char* path) +{ + std::ofstream myfile(path, std::ios_base::out); + if (myfile.is_open()) + { + if (s_beginEndMismatch) + { + myfile << "Error: Begin/End Mismatch.\n"; + } + else + { + myfile << "[Root]\n"; + for(SampleProfilerTreeIteratorImpl it(s_roots); !it.isDone(); it.next()) + { + auto data = it.data(); + for (uint32_t i = 0; i < data->depth; ++i) + myfile << "\t"; + myfile << data->name << " --> calls: " << data->calls << ", total: " << data->time.count() * 0.001 << "ms\n"; + } + } + + myfile.close(); + } +} diff --git a/NvCloth/samples/SampleBase/utils/SampleProfiler.h b/NvCloth/samples/SampleBase/utils/SampleProfiler.h new file mode 100644 index 0000000..af0002c --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/SampleProfiler.h @@ -0,0 +1,79 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef SAMPLEPROFILER_H +#define SAMPLEPROFILER_H + +#include <chrono> + +#if NV_PROFILE + +void SampleProfilerInit(); +void SampleProfilerBegin(const char* name); +void SampleProfilerEnd(); +void SampleProfilerReset(); + +struct SampleProfilerScoped +{ + SampleProfilerScoped(const char* name) + { + SampleProfilerBegin(name); + } + + ~SampleProfilerScoped() + { + SampleProfilerEnd(); + } +}; + +#define PROFILER_INIT() SampleProfilerInit() +#define PROFILER_BEGIN(x) SampleProfilerBegin(x) +#define PROFILER_END() SampleProfilerEnd() +#define PROFILER_SCOPED(x) SampleProfilerScoped __scopedProfiler__(x) +#define PROFILER_SCOPED_FUNCTION() SampleProfilerScoped __scopedProfiler__(__FUNCTION__) +#define PROFILER_RESET() SampleProfilerReset() + +#else + +#define PROFILER_INIT() +#define PROFILER_BEGIN(x) +#define PROFILER_END() +#define PROFILER_SCOPED(x) +#define PROFILER_SCOPED_FUNCTION() +#define PROFILER_RESET() + +#endif + +void SampleProfilerDumpToFile(const char* path); +bool SampleProfilerIsValid(); +std::chrono::microseconds SampleProfilerGetOverhead(); + +struct SampleProfilerTreeIterator +{ + struct Data + { + uint64_t hash; + const char* name; + bool hasChilds; + uint32_t depth; + std::chrono::microseconds time; + std::chrono::microseconds maxTime; + uint32_t calls; + }; + + virtual const Data* data() const = 0; + virtual bool isDone() const = 0; + virtual void next() = 0; + virtual void release() = 0; +}; + +SampleProfilerTreeIterator* SampleProfilerCreateTreeIterator(); + +#endif //SAMPLEPROFILER_H
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/SampleTime.h b/NvCloth/samples/SampleBase/utils/SampleTime.h new file mode 100644 index 0000000..eac895d --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/SampleTime.h @@ -0,0 +1,58 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef SAMPLE_TIME_H +#define SAMPLE_TIME_H + +#include <stdint.h> + +class Time +{ +public: + Time() : m_lastTickCount(getTimeTicks()) {} + + double Time::getElapsedSeconds() + { + const int64_t lastTickCount = m_lastTickCount; + m_lastTickCount = getTimeTicks(); + return (m_lastTickCount - lastTickCount) * s_secondsPerTick; + } + + double Time::peekElapsedSeconds() const + { + return (getTimeTicks() - m_lastTickCount) * s_secondsPerTick; + } + + double Time::getLastTime() const + { + return m_lastTickCount * s_secondsPerTick; + } + +private: + static double getTickDuration() + { + LARGE_INTEGER a; + QueryPerformanceFrequency(&a); + return 1.0 / (double)a.QuadPart; + } + + int64_t getTimeTicks() const + { + LARGE_INTEGER a; + QueryPerformanceCounter(&a); + return a.QuadPart; + } + + int64_t m_lastTickCount; + static const double s_secondsPerTick; +}; + + +#endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/UIHelpers.h b/NvCloth/samples/SampleBase/utils/UIHelpers.h new file mode 100644 index 0000000..5c4dfee --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/UIHelpers.h @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef UI_HELPERS_H +#define UI_HELPERS_H + +#include "imgui.h" +#include "PxVec3.h" + + +static void ImGui_DragFloat3Dir(const char* label, float v[3]) +{ + if (ImGui::Button("Normalize")) + { + ((physx::PxVec3*)v)->normalize(); + } + ImGui::SameLine(); + ImGui::DragFloat3(label, v); +}; + + +#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) + +template<int valuesCount = 90> +class PlotLinesInstance +{ +public: + PlotLinesInstance() + { + memset(m_values, 0, sizeof(float) * valuesCount); + } + + void plot(const char* label, float newValue, const char* overlay_text, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 80)) + { + for (; ImGui::GetTime() > m_time + 1.0f / 60.0f; m_time += 1.0f / 60.0f) + { + m_values[m_offset] = newValue; + m_offset = (m_offset + 1) % valuesCount; + } + ImGui::PlotLines(label, m_values, valuesCount, m_offset, overlay_text, scale_min, scale_max, graph_size); + } + +private: + float m_values[valuesCount]; + int m_offset; + float m_time = ImGui::GetTime(); +}; + +#endif //UI_HELPERS_H
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/Utils.cpp b/NvCloth/samples/SampleBase/utils/Utils.cpp new file mode 100644 index 0000000..a271137 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/Utils.cpp @@ -0,0 +1,13 @@ +#include "Utils.h" + +#include <string> +#include <cstdarg> + +HRESULT messagebox_printf(const char* caption, UINT mb_type, const char* format, ...) +{ + va_list args; + va_start(args, format); + char formatted_text[512]; + _vsnprintf(formatted_text, 512, format, args); + return MessageBoxA(nullptr, formatted_text, caption, mb_type); +} diff --git a/NvCloth/samples/SampleBase/utils/Utils.h b/NvCloth/samples/SampleBase/utils/Utils.h new file mode 100644 index 0000000..5e84e8e --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/Utils.h @@ -0,0 +1,89 @@ +#ifndef UTILS_H +#define UTILS_H + +#include <DeviceManager.h> +#include <assert.h> + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// MACROS +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef V_RETURN +#define V_RETURN(x) \ + { \ + hr = (x); \ + if(FAILED(hr)) \ + { \ + return hr; \ + } \ + } +#endif + +#ifndef V +#define V(x) \ + { \ + HRESULT hr = (x); \ + _ASSERT(SUCCEEDED(hr)); \ + } +#endif + +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) \ + { \ + if(p) \ + { \ + (p)->Release(); \ + (p) = NULL; \ + } \ + } +#endif + +#ifndef SAFE_DELETE +#define SAFE_DELETE(p) \ + { \ + if(p) \ + { \ + delete (p); \ + (p) = NULL; \ + } \ + } +#endif + +#define ASSERT_PRINT(cond, format, ...) \ + if(!(cond)) \ + { \ + messagebox_printf("Assertion Failed!", MB_OK | MB_ICONERROR, #cond "\n" format, __VA_ARGS__); \ + assert(cond); \ + } + +HRESULT messagebox_printf(const char* caption, UINT mb_type, const char* format, ...); + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const char* strext(const char* str) +{ + const char* ext = NULL; // by default no extension found! + while (str) + { + str = strchr(str, '.'); + if (str) + { + str++; + ext = str; + } + } + return ext; +} + +static inline float lerp(float a, float b, float t) { return a + (b - a) * t; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif
\ No newline at end of file |